【C++】排序算法

一、排序算法概述

在C语言中,通常需要手写排序算法实现对数组或链表的排序,但是在C++中,标准库中的<algorithm>头文件中已经实现了基于快排的不稳定排序算法 std::sort() ,以及稳定的排序算法 std::stable_sort()

排序算法通常会伴随着容器使用,当然有些时候,我们也可以直接通过容器的特性来实现对元素的去重和排序,比如 map 和 unordered_map 底层的红黑树,就可以在插入数据的时候实现排序。

虽然C++标准库中已经封装有了现成排序算法了,那么我们为何还要学习排序算法呢?

对于应聘者而言,排序算法无论是笔试还是面试,都是常考点,所以我们需要掌握排序算法的算法原理与代码实现,通过学习排序算法,能够加深我们对于代码的复杂度分析,同时也能加深我们对于数据的稳定性分析

复杂度分为时间复杂度和空间复杂度,在早期的程序中,内存往往是限制程序运行的关键因素,因此在算法设计的时候,都会尽可能在考虑代码效率的同时去考虑节约内存,从而做到时间复杂度和空间复杂度的相对平衡,但是随着硬件技术的发展,计算机的内存越来越大,已经不再是紧缺资源了,所以如今的算法设计,会尽可能地优先考虑时间复杂度,在最大化地降低时间复杂度之后,才会考虑去节约内存,甚至有些算法会考虑牺牲空间复杂度来提高效率,比如归并排序。

稳定性在排序算法中也是需要重点考虑的一个因素,稳定性是指在排序过程中,两个等值的元素它们的相对位置是否会在排序过程中改变,如果它们的位置不变,即这是稳定的排序算法,否则就是不稳定的排序算法。

排序算法经历了长远的发展历程,到如今最广为人知的排序算法有:冒泡排序、插入排序、选择排序、希尔排序、桶排序、归并排序、快速排序、计数排序、基数排序、桶排序、外排序……

算法思维进行分类的话,可以分为以下几类:

  1. 暴力求解思想:冒泡排序
  2. 基于插入思想:插入排序、希尔排序
  3. 基于选择思想:选择排序、堆排序
  4. 基于分治思想:归并排序、快速排序
  5. 基于哈希思想:计数排序、基数排序
  6. 基于分割思想:桶排序、外排序

算法稳定性进行分类的话,可以进行如下分类:

  • 稳定:冒泡排序、插入排序、归并排序、计数排序、基数排序、桶排序
  • 不稳定:选择排序、希尔排序、堆排序、快速排序
  • 不确定:外排序

堆排序和外排序通常应用于理大量数据的排序,因为数据量太大而内存存不下,所以需要先将数据分割再进行排序,桶排序利用分布式思想,先将数据分割在多个桶中,每个桶中的数据量差不多,然后对每个桶中的数据进行排序最后再将数据合并,外排序是通过将数据存储在磁盘上来缓解内存不足的问题,通过内存与磁盘间进行多次数据的交换实现对全部数据的排序。

所以通常情况下,桶排序是稳定的,它在每个桶中的排序算法同行会保证元素间的相对顺序,最后的合并过程也会保持稳定性,而外排序稳定性不确定,因为外排序通常会采用归并排序或者快速排序来进行小批量数据的排序。

二、初级排序算法

初级排序算法是指冒泡排序插入排序选择排序

初级排序算法时间复杂度高,很少会在项目中直接使用,但是初级排序算法的算法思想是其他排序算法的基础,我们可以做了解,以此来对比出其他排序算法的高效性。

// 冒泡排序
// 每次遍历都将最值移动到数组末端
// 时间复杂度:O(N^2)
void BubbleSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n-1; i++) {
        bool swapped = false;
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                std::swap(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        if (!swapped) {
            break;
        }
    }
}
// 插入排序
// 每次遍历都将当前元素往前进行比较,找到它适合插入的位置
// 时间复杂度和数组的原始有序度有关,为 O(N) ~ O(N^2)
void InsertSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j];
            j = j - 1;
        }
        arr[j+1] = key;
    }
}
// 选择排序
// 每次遍历会找到最小值和最大值将其放入数组的最前端和最后端
// 只是简单实现的话,可以每次遍历只选择一个最值进行交换
// 同时选择最小值和最大值进行交换,提高了效率
// 时间复杂度:O(N^2)
void SelectSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n / 2; i++) {
        int minIndex = i;
        int maxIndex = i;
        
        for (int j = i + 1; j < n - i; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
            if (arr[j] > arr[maxIndex]) {
                maxIndex = j;
            }
        }

        if (minIndex != i) {
            std::swap(arr[i], arr[minIndex]);
        }
        if (maxIndex == i) {
            maxIndex = minIndex;
        }
        if (maxIndex != n - i - 1) {
            std::swap(arr[n - i - 1], arr[maxIndex]);
        }
    }
}

// void SelectionSort(std::vector<int>& arr) {
//     int n = arr.size();
//     for (int i = 0; i < n-1; i++) {
//         int minIndex = i;
//         for (int j = i+1; j < n; j++) {
//             if (arr[j] < arr[minIndex]) {
//                 minIndex = j;
//             }
//         }
//         std::swap(arr[i], arr[minIndex]);
//     }
// }

三、进阶排序算法

进阶排序算法是指在初级排序算法上进行了升级,从而实现的算法,如:希尔排序堆排序

希尔排序是插入排序的升级,堆排序是选择排序的升级

插入排序的时间复杂度受原始数组有序度的影响,所以希尔排序的核心思想就是先提高原始数组的有序度,然后再采用插入排序,所以对于希尔排序的具体实现就是先将原始数组的元素按照一个间隔进行分组,先确保每个分组的有序,最后不断缩小这个间隔,最后以此间隔为1,那么就是在进行插入排序了,这既是预排序机制。

堆排序利用二叉树的思想来实现对于数组最值的寻找,在进行升序排序的时候建大根堆,在进行降序排序的时候建小根堆,在建堆的时候,从后往前的父节点进行向下调整,在进行排序的时候,将最值范围内最后的元素交换,再不断降低排序范围从根节点往下进行调整。

堆排序常常用于解决Top-K的问题,若要从海量的数据中寻找K个最大值,我们可以建立一个空间复杂度为O(1)的小根堆,然后遍历海量数据,只要出现比堆顶元素大的数据,则删去堆顶元素,该数据入堆,时间复杂度是O(N)。

// 希尔排序
// 时间复杂度无法准确计算,在O(N)~O(N^2)之间,但远低于O(N^2)
void ShellSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j;
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}
// 堆排序
// 向下调整可以采用循环或者递归都可以
// 时间复杂度:O(N * log N)
void DownAdjust(int* arr, int parent, int n) {
	int child = parent * 2 + 1;
	while (child < n) {
		if (child + 1 < n && arr[child] < arr[child + 1])
			child++;
		if (arr[parent] < arr[child]) {
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else {
			break;
		}
	}
}
 
void HeapSort(int* arr, int n) {
	int parent = (n - 2) / 2;	// 最后一个节点的父节点
	while (parent >= 0) {
		DownAdjust(arr, parent, n);
		--parent;
	}
	while (--n) {
		Swap(&arr[0], &arr[n]);
		DownAdjust(arr, 0, n);
	}
}

四、分治思想排序

分治思想的排序是指归并排序快速排序,它们的算法逻辑受二分法影响。

归并排序和快速排序算法各有优缺点,分不清孰优孰劣,只是应用场景不同。

在排序算法中,通常将额外消耗掉内存空间视为空间复杂度,大多数排序算法不会消耗额外的内存空间,即空间复杂度为O(1),但是归并排序的空间复杂度为O(N),从内存损耗的角度来看,快速排序算法更优,但是归并排序是稳定的排序算法,从稳定性来说,归并排序更优。

归并排序是先将数组无限分割,然后再将一块块分割后的数据进行填入额外开辟的空间中,再用额外空间中已经合并好的有序数据对原数组进行copy覆盖,先无限分割、再合并为一,最后实现了数组的排序。

void __MergeSort(int* arr, int begin, int end, int* tmp) {
	if (begin >= end)
		return;
 
	// 递归,先将数组无限分割,直到每个子数列的长度小于等于2
	int mid = (begin + end) / 2;
	_MergeSort(arr, begin, mid, tmp);
	_MergeSort(arr, mid + 1, end, tmp);
 
	int leftBegin = begin;
	int leftEnd = mid;
	int rightBegin = mid + 1;
	int rightEnd = end;
 
	int pos = begin;
	while (leftBegin <= leftEnd && rightBegin <= rightEnd) {
		// 从前往后同步遍历两个子数列,将相对较小的值先写入tmp中
		if (arr[leftBegin] < arr[rightBegin])
			tmp[pos++] = arr[leftBegin++];
		else
			tmp[pos++] = arr[rightBegin++];
	}
 
	// 将还没遍历完的子数列的值一次性写入tmp中
	while (leftBegin <= leftEnd) {
		tmp[pos++] = arr[leftBegin++];
	}
	while (rightBegin <= rightEnd) {
		tmp[pos++] = arr[rightBegin++];
	}
 
	// 内存数据copy,将临时存储有序子数列的tmp数组的值写入arr对应位置
	memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
 
void MergeSort(int* arr, int n) {
	int* tmp = (int*)malloc(sizeof(int) * n);
	__MergeSort(arr, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}

快速排序的思想是找到一个关键Key值,每一次遍历都将比Key值小的数据放在Key左边,比Key值大的数据放在Key值右边,然后通过递归对Key值左右两边的子数组用同样的方法进行排序,最终实现了数组的排序。但是当子数组范围已经很小的时候,此时再通过递归调用的效率损耗是很大的,而且此时的数组有序度已经相当高了,所以归并排序可以配合着插入排序使用,即将子数组分割到一定的范围后,就不再分治,而是直接调用插入排序算法。

快速排序算法有多种实现方式,如Hoare法挖坑法前后指针法非递归法。虽然快排有多种实现方式,但是他们的算法原理是一样的。

// Hoare法
// 选左边为key值,则右边先走,最后左右相遇时比key值小
// 选右边为key值,则左边先走,最后左右相遇时比key值大
int __Hoare(std::vector<int>& arr, int begin, int end) {
    int ikey = begin;
    while (begin < end) {
        while (arr[end] >= arr[ikey] && end > begin) {
            --end;
        }
        while (arr[begin] <= arr[ikey] && begin < end) {
            ++begin;
        }
        std::swap(arr[begin], arr[end]);
    }
    swap(arr[ikey], arr[end]);
    return end;
}

void QuickSort(std::vector<int>& arr, int begin, int end) {
    if (begin >= end)
        return;

    int ikey = __Hoare(arr, begin, end);
    QuickSort(arr, begin, ikey - 1);
    QuickSort(arr, ikey + 1, end);
}

// 挖坑法
int __DigHole(std::vector<int>& arr, int begin, int end) {
    int key = arr[begin];
    while (begin < end) {
        while (arr[end] >= key && end > begin) {
            --end;
        }
        arr[begin] = arr[end];

        while (arr[begin] <= key && begin < end) {
            ++begin;
        }
        arr[end] = arr[begin];
    }
    arr[end] = key;
    return end;
}

void QuickSort(std::vector<int>& arr, int begin, int end) {
    if (begin >= end)
        return;

    int ikey = __DigHole(arr, begin, end);
    QuickSort(arr, begin, ikey - 1);
    QuickSort(arr, ikey + 1, end);
}

// 前后指针法
// 三数取中是快速排序算法的优化方案,目的是为了避免最坏情况的发送
// 除了三数取中,还可以在将子数组分到足够的小的范围的时候采用插入排序进行优化
// 时间复杂度:O(N * log N)
int __MidIndex(vector<int>& arr, int r, int m, int l) {
    if ((arr[r] - arr[m]) * (arr[m] - arr[l]) >= 0)
        return m;
    else if ((arr[m] - arr[r]) * (arr[r] - arr[l]) >= 0)
        return r;
    else
        return l;
}

int __Ptrptr(std::vector<int>& arr, int begin, int end) {
    int key = arr[begin];
    int prev = begin;
    int cur = begin + 1;
    while (cur <= end) {
        if (arr[cur] < key && ++prev != cur) {
            std::swap(arr[prev], arr[cur]);
        }
        ++cur;
    }
    std::swap(arr[begin], arr[prev]);
    return prev;
}

void QuickSort(std::vector<int>& arr, int begin, int end) {
    if (begin >= end)
        return;

    int ikey = (arr, 0, (begin + end) / 2, end);
    std::swap(arr[begin], arr[ikey]);

    ikey = __Ptrptr(arr, begin, end);
    QuickSort(arr, begin, ikey - 1);
    QuickSort(arr, ikey + 1, end);
}
// 非递归快排
// 在某些极端的情况下,大量的递归会导致堆栈移除,所以需要使用非递归快排算法
// 非递归快排的本质是通过栈或队列来实现对分治的下标控制
// 无论是Hoare法、挖坑法、前后指针法都可以改写为非递归形式
void QuickSort(std::vector<int>& arr, int begin, int end) {
    if (begin >= end)
        return;

    int ikey = (arr, 0, (begin + end) / 2, end);
    std::swap(arr[begin], arr[ikey]);

    std::stack<std::pair<int, int>> stk;
    stk.push(std::make_pair(begin, end));
    while (!stk.empty()) {
        int b = stk.top().first;
        int e = stk.top().second;
        stk.pop();

        //ikey = __Hoare(arr, b, e);
        //ikey = __DigHole(arr, b, e);
        ikey = __Ptrptr(arr, b, e);

        if (ikey - 1 > b)
            stk.push(std::make_pair(b, ikey - 1));
        if (ikey + 1 < e)
            stk.push(std::make_pair(ikey + 1, e));
    }
}

五、哈希思想排序

哈希思想的排序是指计数排序和基数排序,哈希思想的排序算法时间复杂度都非常低,但是他们都有一定的局限性,使用计数排序需要数组中的最大值和最小值差值不能太大,否则空间损耗很大,使用基数排序需要明确知道最大值的位数。

// 计数排序
// 时间复杂度取决于最值之差
void CountSort(std::vector<int>& arr) {
    int max = *std::max_element(arr.begin(), arr.end());
    int min = *std::min_element(arr.begin(), arr.end());
    int range = max - min + 1;

    std::vector<int> count(range);
    
    for (int i = 0; i < arr.size(); ++i) {
        count[arr[i] - min]++;
    }

    int j = 0;
    for (int i = 0; i < range; ++i) {
        while (count[i] != 0) {
            arr[j++] = i + min;
            count[i] -= 1;
        }
    }
}
// 基数排序
// 计数排序可以是做一种特殊的桶排序
// 时间复杂度为O(K * N)
// 设数据最大位数
#define K 3		
 
// 取模的类别数(桶数、基数)
#define RADIX 10
 
// 桶
queue<int> _q[RADIX];
 
// 获取val值对应桶的位置,即哈希映射
int GetPos(int a, int k)
{
	int pos = a % RADIX;
	while (k--)
	{
		a /= RADIX;
		pos = a % RADIX;
	}
	return pos;
} 
 
// 分发数据,将数组数据按模分发到哈希桶上
void Distribute(int* arr, int left, int right, int k)
{
	for (int i = left; i < right; ++i)
	{
		int pos = GetPos(arr[i], k);
		_q[pos].push(arr[i]);
	}
} 
 
// 收集数据,根据队列的特性从哈希桶上收集数据,存入数组
void Collect(int* arr)
{
	int pos = 0;
	for (int i = 0; i < RADIX; ++i)
	{
		while (!_q[i].empty())
		{
			arr[pos++] = _q[i].front();
			_q[i].pop();
		}
	}
} 
 
void RadixSort(int* arr, int n)
{
	int k = 0;
	while (k < K)
	{
		Distribute(arr, 0, n, k++);
		Collect(arr);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/448802.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c# combox 行间距调整

初始化combox comboBox1.DropDownStyle ComboBoxStyle.DropDownList;comboBox1.ItemHeight 25; // 设置 combox 的行高comboBox1.DrawMode DrawMode.OwnerDrawVariable; 添加 DrawItem 事件 private void comboBox1_DrawItem(object sender, DrawItemEventArgs e){if (…

鸿蒙Harmony应用开发—ArkTS声明式开发(模态转场设置:全屏模态转场)

通过bindContentCover属性为组件绑定全屏模态页面&#xff0c;在组件插入和删除时可通过设置转场参数ModalTransition显示过渡动效。 说明&#xff1a; 从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 不支持横竖屏切换。…

docker安装各种组件

一 docker基本命令 镜像操作 ① 列举镜像 docker images ② 搜索镜像&#xff08;以jdk为例&#xff09; docker search jdk ③ 下载镜像 docker pull java ④ 查看所有镜像 docker images ⑤ 启动镜像&#xff08;以jdk8为例&#xff09; docker run -it --name jdk…

AI会砸了我们的饭碗?

Sora&#xff0c;由OpenAI推出&#xff0c;是一款创新的文本到视频生成模型。它能够将文本描述转化为引人入胜的高清视频片段。采用了扩散模型和变换器架构&#xff0c;Sora实现了高效的训练。其方法包括统一表示法、基于补丁的表示法、视频压缩网络和扩散变换器。 Sora具备多种…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:CalendarPicker)

日历选择器组件&#xff0c;提供下拉日历弹窗&#xff0c;可以让用户选择日期。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 CalendarPicker(options?: CalendarOptions) …

conda安装playwright

进入conda安装目录激活环境 D:\Anacoda3>conda activate base 安装playwright &#xff08;base&#xff09;D:\Anacoda3>pip3 install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple &#xff08;base&#xff09;D:\Anacoda3>python -m playwright insta…

深入理解React中的useState:函数组件状态管理的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

transformer参数推导

一、目录 1.Bert Embedding 参数量计算 2.多头自注意力self_attention 参数计算: d_model* d_model 3*(d_model* d_qkvnum_heads) 3. 全连接层参数量 4.layerNormer 参数量 2hidden 5. 编码器 解码器参数 6. 语言模型head 参数&#xff1a;hidden* vocab 二、实现 参考&…

仿生蝴蝶制作——蝴蝶翅膀制作

前言 上一次已经设计好了的翅膀图纸 接下来就是根据这个图纸来制作翅膀。 过程中其实可以不用尺子准确测量&#xff0c;直接用碳纤维棒比着剪下来就好了&#xff0c;然后把减下来的一截比着剪下另一只翅膀需要的材料。因为左右两只翅膀差别不能太大&#xff0c;所以这样是最好…

【Java设计模式】十四、策略模式

文章目录 1、策略模式2、案例&#xff1a;促销策略3、总结4、在源码中的实际应用 1、策略模式 从A地到B地&#xff0c;出行方式可选汽车、火车、飞机中的一种 日常开发&#xff0c;开发工具可IDEA&#xff0c;可Eclipse 不管你选飞机还是火车&#xff0c;你最终都可以实现从A地…

SpringBoot注解--08--注解@JsonInclude

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 JsonInclude注解是jackSon中最常用的注解之一&#xff0c;是为实体类在接口序列化返回值时增加规则的注解 1.JsonInclude用法2.JsonInclude注解中的规则有 案例需求…

vue自定义主题皮肤方案

方案一&#xff1a;CSS变量换肤&#xff08;推荐&#xff09; 利用css定义变量的方法&#xff0c;用var在全局定义颜色变量&#xff08;需将变量提升到全局即伪类选择器 :root&#xff09;然后利用js操作css变量&#xff0c;document.getElementsByTagName(‘body’)[0].style…

网络套接字1

网络套接字1 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容讲解了udp的Linux环境下的使用&#xff0c…

鼠标在QTreeView、QTableView、QTableWidget项上移动,背景色改变

目录 1. 前言 2. 需求 3. 功能实现 3.1. 代码实现 3.2. 功能讲解 4. 附录 1. 前言 本博文用到了Qt的model/view framework框架,如果对Qt的“模型/视图/委托”框架不懂&#xff0c;本博文很难读懂。如果不懂这方面的知识&#xff0c;请在Qt Assistant 中输入Model/View…

一款适合程序员开发复杂系统的通用平台——JNPF 开发平台

在过去&#xff0c;很多开发工具更侧重代码编辑&#xff0c;针对数据库增删改查&#xff08;CRUD&#xff09;类的 Web 系统开发&#xff0c;在界面设计、前后端数据交互等环节主要还是靠写代码&#xff0c;效率比较低。目前很多所谓的低代码开发平台&#xff0c;大多数也都是基…

SQLServer数据库系列之:查询SQLServer数据库上面的连接信息、session信息、sql语句

SQLServer数据库系列之:查询SQLServer数据库上面的连接信息、session信息、sql语句 一、查询数据库上的连接信息二、查询SQLServer数据库的session信息SQLServer数据库从入门到精通系列文章之:SQLServer数据库百篇技术文章汇总 数据库专栏系列文章阅读传送门:详细整理汇总M…

超越 Siri 和 Alexa:探索LLM(大型语言模型)的世界

揭秘LLM&#xff1a;语言模型新革命&#xff0c;智能交互的未来趋势 近年来&#xff0c;虚拟助手的世界发生了重大转变。 虽然 Siri 和 Alexa 本身就是革命性的&#xff0c;但一种称为大型语言模型 (LLM) 的新型人工智能正在将虚拟助手的概念提升到一个全新的水平。 在这篇博文…

农业四情监测系统---气象科普

农业四情监测系统是一个集环境数据采集、分析与决策支持于一体的智能化系统。它主要通过对农田的土壤、气候、作物生长及病虫害等四个关键要素的实时监测&#xff0c;为农业生产提供精准的数据支持和科学的决策依据。 在土壤方面&#xff0c;系统能够检测土壤温度、湿度、pH值及…

我终于解决MathPage.wll文件找不到问题|(最新版Word上亲测)运行时错误,53’: 文件未找到:athPage.WLL

1、问题症状&#xff1a; 运行时错误&#xff0c;53’: 文件未找到:athPage.WLL 2、 解决方案 第一步 首先我们要先找到MathType安装目录下MathPage.wll文件&#xff0c;直接在此电脑中搜索MathPage.wll&#xff0c;找到文件所在位置。 第二步 打开Word文件&#xff0c…

App拉起微信小程序参考文章

App拉起微信小程序参考文章h5页面跳转小程序-----明文URL Scheme_weixin://dl/business/?appid*appid*&path*path*&qu-CSDN博客文章浏览阅读561次&#xff0c;点赞16次&#xff0c;收藏5次。仅需两步&#xff0c;就能实现h5跳转小程序&#xff0c;明文 URL Scheme&…