排序算法

#define LENGTH(array) ( (sizeof(array)) / (sizeof(array[0])) )
#define swap(a,b) do{a=a+b;b=a-b;a=a-b;}while(0) //两个数相同时 会导致结果为0

图片来源:https://blog.csdn.net/dyl_love98/article/details/8809000

图片来源 https://www.zhihu.com/question/280279208/answer/829134919

一、插入排序

1、直接插入排序

 1 /**
 2 **  直接插入排序(插入到准确的位置)  不利于二分查找 直接遍历
 3 **  时间复杂度:比较和移动和平均时间复杂度为O(n^2)  适合基本有序的序列,此时复杂度接近O(n)
 4 **  空间复杂度:O(1)
 5 **  稳定性:稳定
 6 **/
 7 void InsertSort(int a[], int n)
 8 {
 9     int i, j;
10     for (i = 2; i <=n; i++)//假设第一项已经排好,所以从第二项开始
11     {
12         if (a[i] < a[i - 1])//若此项小于 前面已经排好的最后一项(已排好的最大项)
13         {
14             a[0] = a[i];//0号位置为哨兵节点
15             for (j = i - 1; a[0] < a[j]; j--)//从后 向前查找待插入位置
16             {
17                 a[j+1] = a[j];//记录后移
18             }
19             a[j+1] = a[0];//复制到插入的位置 此时a[j]已经是小于a[0]的了
20         }
21     }
22 }

2、利用二分查找的直接插入排序

 1 /**
 2 **  插入排序(插入到准确的位置) 利用二分查找
 3 **  比较复杂度:O(nlogn)
 4 **  移动复杂度:O(n^2) 
 5 **  时间复杂度为:O(n^2) 
 6 **  稳定性:稳定
 7 **/
 8 void BInsertSort(int a[], int n)
 9 {
10     int i, j, low, high, mid;
11     for (i = 2; i <= n; i++)
12     {
13         a[0] = a[i];//哨兵元素
14         low = 1; high = i - 1;
15         while (low <= high)
16         {
17             mid = (low + high) / 2;
18             if (a[mid] > a[0])
19                 high = mid - 1;
20             else
21                 low = mid + 1;
22         }
23         for (j = i - 1; j >=high+1; j--)
24             a[j + 1] = a[j];
25         a[high + 1] = a[0];
26     }
27 }

3、希尔排序

 1 /**
 2 **  希尔排序 (缩小增量的排序)
 3 **  最坏时间复杂度:O(n^2)
 4 **  平均时间复杂度:O(n^1.3)
 5 **  空间复杂度:O(1)
 6 **  稳定性:不稳定 当相同关键字被划分到不同子表时 可能会造成不稳定
 7 **/
 8 void ShellSort(int a[], int n)
 9 {
10     //a[0] 是暂存单元 不是哨兵
11     //进行插入排序的步长是dk 而不是1
12     int i, j, dk;
13     for (dk = n / 2; dk >= 1; dk = dk / 2)//步长变化
14     {
15         for (i = dk + 1; i <= n; i++)//对已经分好组的进行插入 排序 每一组都交替进行
16         {
17             if (a[i] < a[i - dk])//若当前项小于本组前一项
18             {
19                 a[0] = a[i];//暂存在a[0]
20                 for (j = i - dk; j > 0 && a[0] < a[j]; j-=dk)//从本组已经排好序的序列中找到合适的插入位置
21                     a[j + dk] = a[j];//记录后移
22                 a[j + dk] = a[0];//插入
23             }
24         }
25     }
26 }

二、交换排序

1、冒泡排序

 1 /**
 2 **  冒泡排序(每次交换,将相邻中大的一个放在后面)
 3 **  最坏时间复杂度:O(n^2)
 4 **  平均时间复杂度:O(n^2)
 5 **  最好情况下的复杂度:O(n) 初始序列有序时
 6 **  空间复杂度:O(1)
 7 **  稳定性:稳定
 8 **/
 9 //最大的上浮
10 void BubbleSort(int a[], int n)
11 {
12     for (int i = 0; i < n - 1; i++)
13     {
14         int flag = 0;
15         for (int j = 0; j < n - 1 - i; j++)//最大的向上浮
16         {
17             if (a[j] > a[j + 1])//此处若写等号 则变成不稳定的排序
18             {
19                 swap(a[j], a[j + 1]);
20                 flag = 1;
21             }
22         }
23         if (flag == 0) return;//一整趟都未交换一次 则已排序好 退出
24     }
25 }
 1 //最小的下浮
 2 void BubbleSort_m(int a[], int n)
 3 {
 4     for (int i = 0; i < n - 1; i++)
 5     {
 6         int flag = 0;
 7         for (int j = n-1; j >i; j--)//最小的向下浮
 8         {
 9             if (a[j-1] > a[j])//此处若写等号 则变成不稳定的排序
10             {
11                 swap(a[j], a[j-1]);
12                 flag = 1;
13             }
14         }
15         if (flag == 0) return;//一整趟都未交换一次 则已排序好 退出
16     }
17 }

2、快速排序

 1 /**
 2 **  快速排序(任取枢轴pivot,将其分为小于pivot,和大于pivot的两部分,此时pivot放在了最终的位置上)
 3 **  最坏空间复杂度O(n)
 4 **  平均空间复杂度O(logn)
 5 **  最坏时间复杂度:O(n^2)
 6 **  平均时间复杂度:O(nlogn)
 7 **  稳定性:不稳定 如若3为枢轴{3,2,2'} 经过一趟排序后 {2',2,3}
 8 **/
 9 int Partition(int a[], int low, int high)//根据枢轴划分两个区间
10 {
11     int pivot = a[low];//将枢轴的值存起来
12     while (low < high)
13     {
14         while (low < high && a[high] >= pivot) --high;//若高位有比枢轴小的 结束循环
15         a[low] = a[high];//将高位比枢轴小的值放到低位上 
16         while (low < high && a[low] <= pivot) ++low;//若低位有比枢轴大的值 结束循环 
17         a[high] = a[low];//将低位比枢轴大的值放在高位上
18     }
19     a[low] = pivot;//将枢轴的值放到正确的位置上
20     return low;//返回枢轴的位置
21 }
22 void QuickSort(int a[], int low, int high)
23 {
24     if (low < high)
25     {
26         int pivotloc = Partition(a, low, high);
27         QuickSort(a, low, pivotloc - 1);//依次对两个子表进行递归排序
28         QuickSort(a, pivotloc + 1, high);
29     }
30 }

三、选择排序

1、选择排序

 1 /**
 2 **  选择排序(每次选最小的)
 3 **  空间复杂度O(1)
 4 **  时间复杂度:O(n^2)
 5 **  稳定性:不稳定 如若{2,2',1} 经过一趟排序后 {1,2',2}
 6 **/
 7 void SelectSort(int a[], int n)
 8 {
 9     for (int i = 0; i < n - 1; i++)
10     {
11         int min = i;
12         for (int j = i + 1; j < n; j++)
13         {
14                 if (a[j] < a[min])
15                     min = j;
16         }
17         if(min!=i)
18             swap(a[i], a[min]);
19     }
20 }

2、堆排序

 1 /**
 2 **  堆排序(每次选最小的)
 3 **  空间复杂度O(1)
 4 **  时间复杂度:O(nlogn) 建堆时间O(n) 向下平均调整时间O(nlogn)
 5 **  稳定性:不稳定 如若{1,2,2'} 构造初始堆时将2交换到堆顶 {2,1,2'} 最终排序为{1,2',2}
 6 **/
 7 
 8 //将堆中小的节点调整至下方
 9 void AdjustDown(int a[], int k, int len)
10 {
11     a[0] = a[k];//用a[0]暂存根节点
12     int i;
13     for (i = 2 * k; i <= len; i *= 2)
14     {
15         if (i < len && a[i] < a[i + 1])//找出子数较大的一个
16             i++;
17         if (a[0] >= a[i])break;//如果根节点本来就大 无须调整
18         else//叶子节点比较大
19         {
20             a[k] = a[i];//将较大的叶子节点调整到根节点
21             k = i;//以原先未调整的较大的叶子节点的位置为根再进行调整 
22         }
23     }//for
24     a[k] = a[0];//将最初暂存的根节点的值 放到最后一个做调整的叶子节点上去
25 }
26 //建大根堆
27 void BuildMaxHeap(int a[], int len)
28 {
29     for (int i = len / 2; i > 0; i--)//从i=n/2到1 反复调整堆
30         AdjustDown(a, i, len);
31 }
32 //堆排序
33 void HeapSort(int a[], int len)
34 {
35     BuildMaxHeap(a, len);//建立大根堆
36     show(a, 1,len, "after_build_max_heap:");
37     for (int i = len; i > 1; --i)//将最后一个记录与大根堆的根节点对换
38     {
39         swap(a[1], a[i]);
40         AdjustDown(a, 1, i - 1);//对根节点向下调整,序列长度为i-1 第i项为已经排列好的最大项
41     }
42 
43 }

四、归并排序

1、一个无序数组归并

 1 /**
 2 **  归并排序(分治法 每次分成左右两个子序列 且左右两个子序列有序)
 3 **  空间复杂度O(n) 需要辅助数组b[]
 4 **  时间复杂度:O(nlogn)
 5 **  稳定性:稳定
 6 **/
 7 void Merge(int a[], int b[], int low, int mid, int high)
 8 {
 9     //a是原数组 b是辅助数组 low-mid;mid+1-high 各自有序 将他们合并成一个有序表存放在a中
10     int i, j, count;//count代表当前排序好的数据
11     for (int k = low; k <= high; k++)//将a中所有的数据放到b中
12         b[k] = a[k];
13     for (i = low, j = mid + 1, count = low; i <= mid && j <= high; count++)//选择两个有序组中更小的一个放在a中
14     {
15         if (b[i] < b[j])
16             a[count] = b[i++];
17         else
18             a[count] = b[j++];
19     }
20     //如下两个循环只会执行一个
21     while (i <= mid) a[count++] = b[i++];//若第一个表未检测完 复制
22     while (j <= high) a[count++] = b[j++];//若第二个表未检测完 复制
23 }
24 void MergeSort(int a[],int b[], int low, int high)
25 {
26     if (low < high)
27     {
28         int mid = (low + high) / 2;//划分子序列
29         MergeSort(a,b ,low, mid);//对左侧递归
30         MergeSort(a,b, mid + 1, high);//右侧递归
31         Merge(a, b, low, mid, high);//排序
32         //show(b, 0, high+1, "b:");
33     }
34 }

 2、两个有序数组归并

最差情况下:比较次数为 m+n-1

此时,将数组 arr1 与数组 arr2 中的元素两两比较,将值小的放进数组 arr3, 直到数组 arr3 填满为止。

因为 arr3 有 m+n 个空位,每次两两比较就放进去一个数,而最后一个剩下的元素可以不用比较直接放进去,所以一共两两比较了 m+n-1 次。

 

最好情况下:比较次数为 min{m, n}

有个疑问: 若一个数组为 1, 2,3 ; 另一个数组为 4, 5, 6, 7; 则直接将后一个数组最小的与前一个数组最大的比较,仅需一次比较就行了

一定要注意,这是简单归并排序,必须从第一个元素比。

因此,最好情况下,至少需要两两比较一个数组的长度,比较次数为 min{m,n}
————————————————
版权声明:本文为CSDN博主「心态与做事习惯决定人生高度」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/robert_chen1988/article/details/78718395

 1 //双指针法 temp为辅助数组
 2 class Solution {
 3 public:
 4     void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
 5         vector<int> temp(m);
 6         for(int i=0;i<m;i++)//将nums1的值复制到temp中
 7             temp[i]=nums1[i];
 8         nums1.clear();//clear之后size变为0 但容量不变
 9         int p=0,q=0;
10         while(p<temp.size()&&q<nums2.size())
11         {
12             if(temp[p]<nums2[q])nums1.push_back(temp[p++]);//push_back 会使nums1的值++
13             else nums1.push_back(nums2[q++]);
14         }
15         while(p<temp.size())
16             nums1.push_back(temp[p++]);
17         while(q<nums2.size())
18             nums1.push_back(nums2[q++]);
19         temp.swap(temp);//释放temp
20     }
21 };

五、基数排序

 1 /**
 2 **  基数排序 基于关键字各位的大小进行排序
 3 **  r代表基数 10进制时r=10
 4 **  d代表最大数据的位数 
 5 **  空间复杂度O(r)
 6 **  时间复杂度:O(d(n+r)) 需要d趟分配和收集 一趟分配是O(n) 一趟收集是O(r)
 7 **  稳定性:稳定
 8 **  适用于k不是很大 序列较为集中 排序速度快于任何比较排序
 9 **/
10 int maxbit(vector<int>& data) //辅助函数,求数据的最大位数
11 {
12     int d = 1; //保存最大的位数
13     int p = 10;
14     int len = data.size();
15     for (int i = 0; i < len; i++)
16     {
17         while (data[i] >= p)
18         {
19             p *= 10;
20             ++d;
21         }
22     }
23     return d;
24 }
25 void RadixSort(vector<int> &data) //基数排序
26 {
27     int n = data.size();
28     int r = 10;//基数r 此处为10
29     vector<queue<int>> bucket(r);//r个队列 辅助存储空间
30     int d = maxbit(data);
31     int radix=1;//用来计算放在那个桶
32     for (int k = 0; k < d; k++)//进行d趟分配和收集
33     {
34         //分配 
35         for (int i = 0; i < n; i++)
36             bucket[(data[i] / radix) % 10].push(data[i]);//(data[i] / radix) % 10指的是某数的某一位
37         //收集
38         int index = 0;//初始化数组大小
39         for (int i = 0; i < r; i++)
40         {
41             while (!bucket[i].empty())
42             {
43                 data[index++] = bucket[i].front();
44                 bucket[i].pop();
45             }
46         }
47         radix *= 10;
48     }
49 }
原文地址:https://www.cnblogs.com/lancelee98/p/11663379.html