常用排序算法及其实现

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

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的子序列)
  • 稳定性:不稳定

模拟:

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

原文地址:https://www.cnblogs.com/yocichen/p/10457067.html

时间: 2024-08-27 02:31:10

常用排序算法及其实现的相关文章

Java常用排序算法+程序员必须掌握的8大排序算法+二分法查找法

Java 常用排序算法/程序员必须掌握的 8大排序算法 本文由网络资料整理转载而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配排序(基数排序) 所需辅助空间最多:归并排序 所需辅助空间最少:堆排序 平均速度最快:快速排序 不稳定:快速排序,希尔排序,堆排序. 先来看看 8种排序之间的关系: 1.直接插入排序 (1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2]

常用排序算法比较与分析

一.常用排序算法简述 下面主要从排序算法的基本概念.原理出发,分别从算法的时间复杂度.空间复杂度.算法的稳定性和速度等方面进行分析比较.依据待排序的问题大小(记录数量 n)的不同,排序过程中需要的存储器空间也不同,由此将排序算法分为两大类:[内排序].[外排序]. 内排序:指排序时数据元素全部存放在计算机的随机存储器RAM中. 外排序:待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中还需要对外存进行访问的排序过程. 先了解一下常见排序算法的分类关系(见图1-1) 图1-1 常见排

七种常用排序算法

七种常用排序算法 一.常见排序算法一览: 时间复杂度: 是一个函数,它定量描述了该算法的运行时间. 空间复杂度:一个算法在运行过程中临时占用存储空间大小的量度. 稳定性:保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同就稳定,反之不稳定. 视觉直观感受 7 种常用的排序算法 二.算法C#实现: 1. 直接插入排序: using System; using System.Collections.Generic; using System.Linq; using Sys

常用排序算法之——归并排序

归并排序的原理: 如果数组的元素个数大于1,则: 将数组平均分为两部分: 左边的数组归并排序:递归 右边的数组归并排序:递归 将两个各自有序的数组合并,需要一个额外的辅助数组,暂时保存合并结果:返回 否则,数组元素个数为1时,已经有序:直接返回. 稳定排序.时间复杂度在最坏.最好.平均情况下都为O(N lgN),空间复杂度为O(N). 代码: 1 #include <iostream> 2 using namespace std; 3 4 template<typename T>

常用排序算法之——快速排序

快速排序的原理: 首先找一个标兵值,等于某一个元素值:遍历数组,将数组分为小于标兵值和大于标兵值的两部分:然后分别对两个部分采用快速排序,递归. 分开数组时,维持一个指针,指向已找到小部分的最后一个元素:一个指针用于遍历. 不稳定排序算法.当数组已经有序时,时间复杂度最差,为O(N2),平均.最优情况下都为O(N lgN). 代码如下: 1 #include <iostream> 2 using namespace std; 3 4 template<typename T> 5 v

javascript常用排序算法实现

毕业后,由于工作中很少需要自已去写一些排序,所以那些排序算法都忘得差不多了,不过排序是最基础的算法,还是不能落下啦,于是找了一些资料,然后用Javascript实现了一些常用的算法,具体代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>

常用排序算法实现[交换排序之冒泡排序、快速排序]

相关知识 1. 稳定排序和非稳定排序: 稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序. 如果排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前. 2. 内排序和外排序 在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序: 在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序. 3.算法分类 排序算法从理论上分为如下几类: (1) 交换排序法:

常用排序算法时间复杂度和空间复杂度简析

1. preface /**** *    This article will try to explain something about: *        --Bubble sort. *        --Quick sort. *        --Merge sort. *        --Heap sort. *    To read this, some prerequisites is necessary: *        --a survive skill in C pr

视觉直观感受7种常用排序算法

视觉直观感受若干常用排序算法 1 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性. 步骤: 从数列中挑出一个元素,称为 "基准"(pivo

Java 常用排序算法/程序员必须掌握的 8大排序算法

Java 常用排序算法/程序员必须掌握的 8大排序算法 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配排序(基数排序) 所需辅助空间最多:归并排序 所需辅助空间最少:堆排序 平均速度最快:快速排序 不稳定:快速排序,希尔排序,堆排序. 先来看看 8种排序之间的关系: 1.直接插入排序 (1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排 好顺序的,现在要把第n 个数插到前