C++排序算法代码汇总

寵の児 提交于 2019-12-02 06:48:39

排序算法稳定性:两个相同的元素排序前后的相对位置关系不会发生改变。

复杂度比较

算法平均时间复杂度最好情况最坏情况空间复杂度稳定性
冒泡排序O(N^2)O(N^2)O(N^2)O(1)稳定
插入排序O(N^2)O(N)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)稳定
希尔排序O(N ^ 3/2 ) O(N^2)O(1)不稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(N)稳定
快速排序O(NlogN)O(NlogN)O(N^2)O(logN)不稳定

冒泡排序

排序过程

  1. 将第一个元素与第二个元素比较大小,如果第一个元素大于第二个元素则调换他们两的位置;
  2. 比较第二个元素和第三个元素的大小,如果第二个元素大于第三个元素则调换他们两的位置;
  3. 依次类推,进行两两元素的比较和交换,最终最大的元素排在了最后面;
  4. 重复1到3过程,直到所有元素都排序。

图片演示

//冒泡排序 //平均时间复杂度:O(N^2) //最坏情况复杂度:O(N^2) //空间复杂度:O(1) //稳定排序 void bubblesort(vector<int>& a) {     int n = a.size();     for (int i = 0; i < n; i++)     {         for (int j = 0; j < n - 1 - i; j++)         {             if (a[j] > a[j+1])                 swap(a[j], a[j+1]);         }     } } 

//冒泡排序升级版1 //设置一个标记来标志一趟比较是否发生交换 //如果没有发生交换,则数组已经有序 void bubblesort2(vector<int>& a) {     int n = a.size();     bool flag;     for (int i = 0; i < n; i++)     {         flag = false;         for (int j = 0; j < n - 1 - i; j++)         {             if (a[j] > a[j+1])             {                 swap(a[j], a[j+1]);                 flag = true;             }         }         if (!flag)             break;     } }

//冒泡排序优化二 //用一个变量记录下最后一个发生交换的位置,后面没有发生交换的已经有序 //所以可以用这个值来作为下一次比较结束的位置 void bubblesort3(vector<int>& a) {     int n = a.size();     int flag = n;     int stop_pos;     for (int i = 0; i < n; i++)     {         stop_pos = flag - 1;         flag = 0;         for (int j = 0; j < stop_pos; j++)         {             if (a[j] > a[j+1])             {                 swap(a[j], a[j+1]);                 flag = j + 1;             }         }     } }


插入排序

  1. 对于第K个元素,将该元素的值存储在零时变量中,比较第前一个元素与该元素的大小,如果大于该元素就将前一个元素往后移动一步;
  2. 比较前面第二个元素与该元素的大小,如果大于该元素就将前第二个元素往后移动一步;
  3. 重复上述过程直到找到小于等于原来第K个元素(保存在零时变量中)的位置,并将第K个元素插入到这个元素的后面。或者找不到小于等于第K个元素的位置,就将原来第K个元素插入到数组的首地址。

图片演示

//插入排序 //平均时间复杂度:O(N^2) //最坏情况复杂度:O(N^2) //最好情况复杂度:O(N) //空间复杂度:O(1) //最多需要n(n−1)/2次比较 //最少需要n−1次比较 //稳定排序 void insertsort(vector<int>& a) {     int n = a.size();     for (int i = 1; i < n; i++)     {         int insert_num = a[i], j;         for (j = i - 1; j >= 0; j--)         {             if (a[j] > insert_num)                 a[j + 1] = a[j];             else                 break;         }         a[j + 1] = insert_num;     } } 

选择排序

1、每一次从后面选择出一个最小的值(swap_pos),替换到前面来(i)。


//选择排序 //平均时间复杂度 O(n^2) //最坏时间复杂度 O(n^2) //最好时间复杂度 O(n^2) //空间复杂度 O(1) //我这个写法 是稳定排序 void select_sort(vector<int>& vt) {     for (int i = 0; i < vt.size() - 1; i ++)     {         int swap_pos = i;         for (int j = i + 1; j < vt.size(); j++)         {             if (vt[swap_pos] > vt[j])             {                 swap_pos = j;             }         }                  if (swap_pos != i)         {             swap(vt[swap_pos], vt[i]);         }     } }

希尔排序

希尔排序是在插入排序的基础上进行发展的,通过一个希尔增量先排序一定间隔的数据。

排序过程

  1. 插入排序每次与前面一个比较,然后再往前一个,而希尔排序每次往前K个;
  2. 当增量为1的时候,希尔排序与插入排序就完全是一样的过程;
  3. 所以代码也很好实现,将插入排序中增1的地方改为增K就行。

图片演示

初始81941196123517952858
第一趟5排序后35171128128194959658
第二趟3排序后28121135178158959694
第三趟1排序后11121728355881949596

复杂度分析

希尔排序的时间复杂度比较复杂,选用不同的希尔增量也会导致复杂度不同。

//希尔排序 //最坏情况复杂度:O(N^2) //不稳定排序 void shellsort(vector<int>& a) {     int n = a.size();     for (int increment = n / 2; increment > 0; increment /= 2)     {         for (int i = increment; i < n; i++)         {             int insert_num = a[i], j;             for (j = i - increment; j >= 0; j -= increment)             {                 if (a[j] > insert_num)                     a[j + increment] = a[j];                 else                     break;             }             a[j + increment] = insert_num;         }     } }


堆排序

排序过程

  1. 建立最大堆,建堆的过程是从N/2的位置开始,将父节点与子节点比较,如果子节点大于父节点则交换。为什么是N/2,是因为堆中树叶的个数是N/2。
  2. 从堆中删除堆顶元素,对于最大堆而言,堆顶元素也就是最大元素。每删除一个堆顶元素,就将堆顶元素放在数组的后面,因为每删除一个就出现一个空位,所以数组后面是有地方存放的。
  3. 进行N-1次的删除以后,整个数组就是排序的状态了。

图片演示


//堆排序 //建堆的平均时间是:O(N) //建堆的最坏情况是:O(NlogN) //删除元素的时间是:O(logN) //整个排序平均时间复杂度:O(N+NlogN)=O(NlogN) //最坏情况复杂度:O(NlogN) //不稳定排序  //建立一个大顶堆O(n),要求就是 把最大的元素 移动到堆顶 也就是a[0] void make_heap(vector<int>& a, int size) //size的当前堆的大小,也就是数组的前size个数 {     for (int i = size - 1; i > 0; i--)     {         if (i % 2 && a[i] > a[(i - 1) / 2])//奇数             swap(a[i], a[(i - 1) / 2]);         else if (i % 2 == 0 && a[i] > a[(i - 2) / 2])//偶数             swap(a[i], a[(i - 2) / 2]);     } } void heapsort(vector<int>& a) {     int n = a.size();     while (n)     {         make_heap(a, n); //每次把新的最大元素移到堆顶,也就是a[0]         n--;         swap(a[0], a[n]); //然后把当前最大移动到后面来作为排好序的元素     } } 


归并排序

排序过程

  1. 将数组N从中间分成两个数组N1和N2;
  2. 将N1和N2分别递归用归并排序来排序。
  3. 归并N1与N2。

图片演示

//归并排序 //平均时间复杂度:O(NlogN) //稳定排序 vector<int> mergeHelper(vector<int> &a, int left, int right) {     if (left == right) return vector<int> (1, a[left]);     int mid = (right - left) / 2 + left;     vector<int> l = mergeHelper(a, left, mid);     vector<int> r = mergeHelper(a, mid + 1, right);     //merge     vector<int> ret;     int ll = 0, rr = 0;     while (ll < l.size() && rr < r.size())     {         if (l[ll] <= r[rr])     ret.push_back(l[ll++]);         else                    ret.push_back(r[rr++]);     }     while (ll < l.size()) ret.push_back(l[ll++]);     while (rr < r.size()) ret.push_back(r[rr++]);     return ret; }  void mergesort(vector<int>& a) {     a = mergeHelper(a, 0, a.size() - 1); } 



快速排序

排序过程

  1. 选择一个枢纽元,可以选择首,尾,中三个数的中位数作为枢纽元;
  2. 将枢纽元的为止与数组的尾地址进行交换;
  3. 定义两个指针,P1指向数组首地址,P2指向数组倒数第二个位置,P1所指元素的值与枢纽元比较,如果小于枢纽元则后移一位,如果大于就停下来。P1所指元素的值与枢纽元比较,如果大于枢纽元则前移一位,如果小于就停下来;
  4. 交换P1和P2所指向的元素;
  5. 重复3和4直到P1大于P2;
  6. 对数组的分割过程同样采用递归的方法。

快排是必须背住的排序!

//快速排序 //平均时间复杂度:O(NlogN) //最坏情况复杂度:O(N^2) //不稳定排序 void quicksortHelper(vector<int>& a, int start, int end) {     if (start >= end) return;     int l = start, r = end;     int pivot = a[(end - start) / 2 + start];     while (l <= r)     {         while (l <= r && a[r] > pivot)  r--;         while (l <= r && a[l] < pivot)  l++;         if (l <= r) swap(a[l++], a[r--]);     }     quicksortHelper(a, start, r);     quicksortHelper(a, l, end); } void quicksort(vector<int>& a) {     quicksortHelper(a, 0, a.size() - 1); } 
//快排的最差时间复杂度为O(n²) //通常出现在选择的轴值(pivot)不能将数组划分为两个长度相等的子数组的时候 //一个较好的办法是“三数取中”,查看当前数组的第一个、中间一个和最后一个位置的数组,取其中位数,以此来降低轴值选择得不好的可能性。 int findmiddle(int a, int b, int c) {     if (a >= b && a <= c)         return a;     else if (b >= a && b <= c)         return b;     else         return c; } void quicksortHelper(vector<int>& a, int start, int end) {     if (start >= end) return;     int l = start, r = end;     int pivot = findmiddle(a[start], a[end], a[(end - start) / 2 + start]);     while (l <= r)     {         while (l <= r && a[r] > pivot)  r--;         while (l <= r && a[l] < pivot)  l++;         if (l <= r) swap(a[l++], a[r--]);     }     quicksortHelper(a, start, r);     quicksortHelper(a, l, end); } void quicksort(vector<int>& a) {     quicksortHelper(a, 0, a.size() - 1); }







易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!