• 常用排序算法及其实现


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

    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

  • 相关阅读:
    【Log历练手册】Spring事务管理不能提交异常
    【网络安全】如何使用OpenSSL工具生成根证书与应用证书
    【网络安全】如何使用OpenSSL工具生成根证书与应用证书
    【JAVA笔记——器】Spring Aop 实现Log日志系统——基本实现
    jdbc连接池配置方法
    用于读/写配置的工具,下面列出了各种配置(从最高优先级到最低优先级)
    文件复制Util写法,可以适用于多种条件
    记录一个工作中遇到的问题,svn拉的项目,pom.xml报错
    layui的js写法,部分代码
    JDBCUtil连接数据库的写法
  • 原文地址:https://www.cnblogs.com/yocichen/p/10457067.html
Copyright © 2020-2023  润新知