常用排序算法及其实现

一、常用排序算法及滑稽实现

1. 插入排序:遍历数组(n),将每个元素插入到前面子序列的合适位置(插入时采取前面的部分元素后移,再将本元素填在适当位置的方法)

  • 平均:O(n2)
  • 最坏:O(n2)
  • 最好:O(n)(有序时出现)
  • 稳定性:稳定(相同元素在排序之后相对位置不会改变)

模拟:

12 30 9 100 1 3 10

12 30 9 100 1 3 10

9 12  30 100 1 3 10

9 12 30 100 1 3 10

1 9 12 30 100 3 10

1 3 9 12  30 100  10

1 3 9 10 12 30 100 

实现代码:

 1 /*
 2 插入排序: 
 3 平均:O(n2)
 4 最好:O(n)
 5 最坏:O(n2)
 6 稳定性:稳定
 7 */
 8 void insertSort(vector<int> &nums)
 9 {
10     int len = nums.size(), i, j;
11     if(len <= 0)
12         return ;
13     for(i=1; i<len; i++)
14     {
15         for(j=0; j<i; j++)
16         {
17             if(nums[i] >= nums[j] && nums[i] < nums[j+1])
18             {
19                 int tmp = nums[i];
20                 for(int t=i; t>j; t--)
21                     nums[t] = nums[t-1];
22                 nums[j] = tmp;
23                 break;
24             }
25         }
26     }
27 }

 2. 冒泡排序:双层循环,外层从后往前,里层从前往后,如果后面比前面小,就交换。

  • 平均:O(n2)
  • 最坏:O(n2)
  • 最好:O(n) 优化后有序时出现
  • 稳定性:稳定

模拟:

1 3 2 7 5 6 9 8 4

1 2 3 5 6 7 8 4 9

1 2 3 5 6 7 4 8 9

1 2 3 5 6 4 7 8 9

1 2 3 5 4 6 7 8 9

1 2 3 4 5 6 7 8 9

实现代码:

 1 /*
 2 冒泡排序: 通过两两交换的方式,每次将子区间最大的冒向最后
 3 平均:O(n2)
 4 最好:O(n)
 5 最坏:O(n2)
 6 稳定性:稳定
 7 */
 8 //普通冒泡排序
 9 void bubbleSort(vector<int> &nums)
10 {
11     int len = nums.size();
12     if(len <= 0)
13         return ;
14     for(int i=len-1; i>=1; i--)
15     {
16         for(int j=1; j<=i; j++)
17         {
18             if(nums[j] < nums[j-1])
19             {
20                 int tmp = nums[j]; 
21                 nums[j] = nums[j-1]; 
22                 nums[j-1] = tmp; 
23             }
24         }
25     }
26 }
27 
28 //冒泡优化版本:添加标志位或者计数器判断序列是否已经有序
29 void bubbleSort2(vector<int> &nums)
30 {
31     int len = nums.size();
32     if(len <= 0)
33         return ;
34     for(int i=len-1; i>=1; i--)
35     {
36         int flag = 0;//或者使用bool值,更好一些
37         for(int j=1; j<=i; j++)
38         {
39             if(nums[j] < nums[j-1])
40             {
41                 int tmp = nums[j]; 
42                 nums[j] = nums[j-1]; 
43                 nums[j-1] = tmp; 
44             }
45             else
46                 flag ++;
47         }
48         if(flag == i)
49             return ;
50     }
51 }

3. (简单)选择排序:本位和后面子序列中最小的交换

  • 平均:O(n2)
  • 最坏:O(n2)
  • 最好:O(n2)
  • 稳定性:不稳定

模拟:

1 3 2 7 5 6 9 8 4

1 3 2 7 5 6 9 8 4

1 2 3 7 5 6 9 8 4

1 2 3 7 5 6 9 8 4

1 2 3 4 5 6 9 8 7

1 2 3 4 5 6 9 8 7

1 2 3 4 5 6 9 8 7

1 2 3 4 5 6 7 8 9

实现代码:

 1 /*
 2 选择排序: 每个子区间找到最小与第一个交换
 3 平均:O(n2)
 4 最好:O(n)
 5 最坏:O(n2)
 6 稳定性:稳定
 7 */
 8 void chooseSort(vector<int> &nums)
 9 {
10     int len = nums.size();
11     if (len <= 0)
12         return;
13     for (int i = 0; i < len - 1; i++)
14     {
15         int min = i + 1;
16         for (int j = i + 1; j < len; j++)
17         {
18             if (nums[j] < nums[min])
19                 min = j;
20         }
21         if (nums[i] > nums[min])
22             swap(nums[i], nums[min]);
23     }
24 }

 

4. 快排:思想是,一趟挑出1个数,将数组分成左右两部分,左边小于该数,右边大于该数。维护有两个索引(low, high),随机将数组中一个数定为“基准”,先从high右往左找到第一个比基准小的与基准交换,更新low和high,再从low左往右找第一个大于基准的与基准交换,更新low和high。直到low == high。进行下一趟,一般三趟足以。(第一堂完成后左边再选一个,右边再选一个即可。)

  • 平均:O(n*log2n)
  • 最好:O(n*log2n)
  • 最坏:O(n2) (当有序时出现,即每次都是划分成为1,n-1的子序列)
  • 稳定性:不稳定

模拟:

3 2 7 5 6 9 8 4

4 3 2 7 5 6 9 8 1

4 3 2 1 5 6 9 8 7

(一趟结束)

实现代码:

 1 /*
 2 快速排序: 
 3 平均:O(nlogn)
 4 最好:O(nlogn)
 5 最坏:O(n2) 完全有序时出现
 6 稳定性:不稳定
 7 */
 8 //分治法:递归实现
 9 void quickSort(vector<int> &nums, int l, int r)
10 {
11     if(l >= r)
12         return ;
13     int i = l, j = r+1;
14     int key = nums[l];
15     //按照key的大小分割两块,供后续递归排序
16     while(true)
17     {
18         //从左往右找到第一个大于key的数
19         while(nums[++i] < key)
20         {
21             if(i == r)
22                 break;
23         }
24         //从右往左找到第一个小于key的数
25         while(nums[--j] > key)
26         {
27             if(j == l)
28                 break;
29         }
30         //判断位置,交换两数
31         if(i >= j)
32             break;
33         int tmp = nums[i];
34         nums[i] = nums[j];
35         nums[j] = tmp;
36     }
37     //分割完成后,递归两部分
38     int tmp = nums[l];
39     nums[l] = nums[j];
40     nums[j] = tmp;
41     quickSort(nums, l, j-1);
42     quickSort(nums, j+1, r);
43 }

5. 堆排序:对所给数组进行建大顶堆,将堆顶元素与最后一个交换,尾索引前移,继续建立大顶堆,如此类推。

  • 平均:O(n*log2n)
  • 最坏:O(n*log2n)
  • 稳定性:不稳定
 1 /*
 2 堆排序: 在数组基础上构建大/小顶堆来实现排序 
 3 平均:O(nlogn)
 4 最好:O(nlogn)
 5 最坏:O(nlogn) 
 6 稳定性:不稳定
 7 */
 8 //调整范围堆顶值,保持大顶
 9 void heapAdjust(vector<int> &nums, int index, int length)
10 {
11     int max = index;
12     int left = index * 2 + 1;  //左子节点
13     int right = index * 2 + 2; //右子节点
14     if (left < length && nums[left] > nums[max])
15         max = left;
16     if (right < length && nums[right] > nums[max])
17         max = right;
18     if (max != index)
19     {
20         int tmp = nums[index];
21         nums[index] = nums[max];
22         nums[max] = tmp;
23         heapAdjust(nums, max, length);
24     }
25     return;
26 }
27 
28 //堆排序:递归方式实现
29 void heapSort(vector<int> &nums)
30 {
31     int len = nums.size();
32     if (len <= 1)
33         return;
34     //调整成大顶堆,数组第一个元素值就是最大值
35     for (int i = len / 2 - 1; i >= 0; i--)
36     {
37         heapAdjust(nums, i, len);
38     }
39     //堆排序
40     for (int i = len - 1; i >= 0; i--)
41     {
42         int tmp = nums[0];
43         nums[0] = nums[i];
44         nums[i] = tmp;
45         printNums(nums);
46         //继续调整
47         heapAdjust(nums, 0, i);
48     }
49 }

6.归并排序:将原始数组分成若干个子序列,两两合并排序,再合并排序......直到合并完成。

  • 平均:O(n*log2n)
  • 最坏:O(n*log2n)

 (同数量级排序方法中性能最好的---又快又稳定)

模拟:

1 3 2 7 5 6 9 8 4

[1 3] [2 7] [5 6] [8 9] [4]

[1 2 3 7] [5 6 8 9] [4]

[1 2 3 5 6 8 9] [4]

[1 2 3 4 5 6 7 8 9]

实现代码:

 1 /*
 2 二路归并排序: 
 3 平均:O(nlogn)
 4 最好:O(nlogn)
 5 最坏:O(nlogn) 完全有序时出现
 6 稳定性:稳定,但很少用
 7 */
 8 //合并二路数组
 9 void merge(vector<int> &nums, vector<int> &res, int l, int m, int r)
10 {
11     int i = l;//前半部分起点
12     int res_idx = l;//合并数组起点
13     int j = m+1;//后半部分起点
14     //谁小取谁
15     while(i <= m && j <= r)
16     {
17         if(nums[i] <= nums[j])
18             res[res_idx++] = nums[i++];
19         else
20             res[res_idx++] = nums[j++];
21     }
22     //剩下的部分
23     if(i <= m)
24     {
25         while(i <= m)
26             res[res_idx++] = nums[i++]; 
27     }
28     else
29     {
30         while(j <= r)
31             res[res_idx++] = nums[j++];
32     }
33     //将二路合并结果复制回去
34     for(int i=l; i<=r; i++)
35         nums[i] = res[i];
36 }
37 
38 //二路分割
39 void mergeSort(vector<int> &nums, vector<int> &res, int l,  int r)
40 {
41     if(l < r)
42     {
43         int m = l + (r-l)/2;
44         mergeSort(nums, res, l, m);
45         mergeSort(nums, res, m+1, r);
46         merge(nums, res, l, m, r);
47     }
48 }

7. 希尔排序:要选步长,每隔一个步长选定一个元素,对选定的序列排序,再选一次再排;完成后换步长再排。步长的选取很重要,但没有好的选取方法。


 一位大佬的总结:https://www.cnblogs.com/wskwbog/p/11236136.html

posted @ 2019-08-10 19:54  yocichen  阅读(927)  评论(0编辑  收藏  举报