[算法] 十个经典排序算法

动图演示参考:https://www.cnblogs.com/onepixel/articles/7674659.html

基数排序参考:https://blog.csdn.net/double_happiness/article/details/72452243

1、常见的排序算法

2、算法分析

3、算法的实现

1)排序类

 1 #ifndef _SORT_H_
 2 #define _SORT_H_
 3
 4 #include<vector>
 5
 6 class Sort {
 7 public:
 8     //交换排序:冒泡和快排
 9     void bubbleSort(std::vector<int> &nums);
10     void quickSort(std::vector<int> &nums, int left, int right);
11
12     //插入排序:简单插入和希尔排序
13     void insertSort(std::vector<int>&nums);
14     void shellSort(std::vector<int>&nums);
15
16     //选择排序:简单选择排序和堆排序
17     void selectSort(std::vector<int>&nums);
18     void heapSort(std::vector<int>&nums);
19
20     //归并排序:二路归并和多路归并
21     void mergeSort2(std::vector<int>&,int,int);
22
23     //计数排序
24     void countingSort(std::vector<int>&);
25     //桶排序——计数排序的进阶版
26     void bucketSort(std::vector<int>&);
27     //基数排序
28     void RadixSort(std::vector<int>&nums);
29 private:
30     void swap_ele(int &a, int &b) {
31         int tmp = a;
32         a = b;
33         b = tmp;
34     }
35 };
36
37 #endif // !_SORT_H_

2)排序算法的具体实现

  1 /**
  2 * 冒泡排序是一种简单的排序算法。
  3 * 它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。
  4 * 走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
  5 * 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
  6 * 时间复杂度n*n
  7 * 稳定性:稳定
  8 */
  9 void Sort::bubbleSort(std::vector<int>&nums) {
 10     for (int i = 0; i < nums.size() - 1; ++i) {
 11         for (int j = 0; j < nums.size() - 1 - i; ++j) {
 12             if (nums[j] > nums[j + 1])
 13                 swap_ele(nums[j], nums[j + 1]);
 14         }
 15     }
 16 }
 17
 18 /**
 19 * 快速排序
 20 * 通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,
 21 * 则可分别对这两部分记录继续进行排序,以达到整个序列有序。
 22 * 利用了分治的思想
 23 * 时间复杂度 n*logn
 24 * 稳定性:不稳定
 25 */
 26 void Sort::quickSort(std::vector<int> &nums, int left, int right) {
 27     if (left >= right)
 28         return;
 29     int i = left, j = right;
 30     int base = nums[left];
 31     while (i < j) {
 32         while (i<j&&nums[j]>=base)//注意等号
 33             --j;
 34         if (i < j)
 35             nums[i] = nums[j];
 36         while (i < j&&nums[i] <= base)//注意等号
 37             ++i;
 38         if (i < j)
 39             nums[j] = nums[i];
 40     }
 41     nums[i] = base;
 42     quickSort(nums, left, i - 1);
 43     quickSort(nums, i + 1, right);
 44 }
 45
 46 /**
 47 * 插入排序
 48 * 从第一个元素开始,该元素可以认为已经被排序;
 49 * 取出下一个元素,在已经排序的元素序列中从后向前扫描;
 50 * 如果该元素(已排序)大于新元素,将该元素移到下一位置;
 51 * 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
 52 * 将新元素插入到该位置后;
 53 * 重复步骤2~5。
 54 * 时间复杂度n*n
 55 * 稳定性:稳定
 56 */
 57 void Sort::insertSort(std::vector<int>&nums) {
 58     int tmp,pos;
 59     for (int i = 1; i < nums.size(); ++i) {
 60         tmp = nums[i];
 61         pos = i - 1;
 62         while (pos >= 0 && nums[pos] > tmp) {
 63             nums[pos + 1] = nums[pos];
 64             --pos;
 65         }
 66         nums[pos + 1] = tmp;
 67     }
 68 }
 69
 70 /**
 71 * 希尔排序
 72 * 希尔排序又叫做缩小增量排序,首先选择一个增量increment,比较距离差为increment的元素,对他们进行简单插入排序
 73 * 然后缩小增量,直到计算增量为1的情况
 74 * 增量的选择对排序的效果至关重要,希尔提出的是increment/2向下取整,直到incremtn==1,Knuth提出取increment/3+1,直到为1
 75 * 我们选用的是Knuth的方法
 76 * 时间复杂度低于n*n
 77 * 稳定性:有的稳定有的不稳定——不稳定
 78 */
 79 void Sort::shellSort(std::vector<int>&nums) {
 80     int n = nums.size();
 81     if (n < 2)return;
 82     int increment = n / 3 + 1;
 83     int tmp,i,j;
 84     while (increment > 0) {
 85         for ( i = increment; i < n; ++i) {
 86             tmp = nums[i];
 87             for (j = i - increment; j >= 0 && nums[j] > tmp; j -= increment) {//简单插入排序
 88                 nums[j + increment] = nums[j];
 89             }
 90             nums[j + increment] = tmp;
 91         }
 92         //更新increment
 93         if (increment == 1)break;//已经计算过1的情况,全部都有顺序了
 94         increment = increment / 3 + 1;
 95     }
 96 }
 97
 98 /**
 99 * 简单选择排序
100 * 分成两部分:已经排序序列和未排序序列,前者初始为空;
101 * 通过扫描,找到为排序序列中的最大或者最小元素,放到未排序序列的起始位置,也是已经排序序列的末尾位置
102 * 时间复杂度为n*n
103 * 稳定性:不稳定
104 */
105 void Sort::selectSort(std::vector<int>&nums) {
106     int Min, min_pos;
107     int n = nums.size();
108     for (int i = 0; i < n - 1; ++i) {
109         Min = nums[i];
110         min_pos = i;
111         for (int j = i + 1; j < n ; ++j) {
112             if (nums[j] < Min) {
113                 Min = nums[j];
114                 min_pos = j;
115             }
116         }
117         nums[min_pos] = nums[i];
118         nums[i] = Min;
119     }
120 }
121
122 /**
123 * 堆排序
124 * 利用堆的特性,父节点一定比子节点大(小),这样每次取出根节点,就是最大(小)的
125 * 时间复杂度:对于n个节点的堆,对每个元素执行pop()操作,时间复杂度是n*logn
126 * 稳定性:不稳定
127 */
128 void Sort::heapSort(std::vector<int>&nums) {
129     //首先堆化
130     std::make_heap(nums.begin(), nums.end());
131     //然后进行排序——排序中用到了pop_heap
132     std::sort_heap(nums.begin(), nums.end());
133 }
134
135 /**
136 * 2路归并排序
137 * 采用分治的思想,不断把序列划分成两部分,分别对两部分进行排序
138 * 然后把两部分合并成一个完整的有序序列
139 * 时间复杂度:n*logn ??如何计算
140 * 稳定性:稳定
141 */
142 void Sort::mergeSort2(std::vector<int>&nums, int start, int end) {
143     std::vector<int> tmp_v;
144     if (start < end) {
145         int mid = start + (end - start) / 2;
146         mergeSort2(nums, start, mid);
147         mergeSort2(nums, mid + 1, end);
148         //将结果合并到tmp_v数组中
149         int i = start, j = mid + 1;
150         while (i <= mid||j <= end) {
151             if (i > mid||(j<=end&& nums[i] > nums[j])) {//注意j的界限的判断
152                 tmp_v.push_back(nums[j++]);
153             }
154             else if (j > end || (i<=mid&&nums[j] >= nums[i])) {//注意等号,这里就决定了归并排序是稳定的
155                 tmp_v.push_back(nums[i++]);
156             }
157         }
158         //从tmp_v拷贝回nums
159         for (i = start; i <= end; ++i) {
160             nums[i] = tmp_v[i - start];
161         }
162     }
163 }
164
165 /**
166 * 计数排序
167 * 找出数组nums中最大的数K,建立辅助数组V,V的长度是K+1
168 * 遍历nums,统计nums[i]的出现次数,并填入V[nums[i]]中
169 * 遍历V,用V的下标i填充nums,直到V[i]为0
170 * 计数排序具有一定的局限性,首先只能针对整数,并且在最大值不算太大并且序列比较集中的时候效率很高
171 * 最好的时间复杂度n+K,最坏的时间复杂度n+K,平均时间复杂度:n+K
172 * 额外空间复杂度:K,K为最大值
173 * 稳定性:稳定
174 */
175 void Sort::countingSort(std::vector<int>&nums) {
176     int n = nums.size();
177     if (n < 2)return;
178     int Max = nums[0];
179     int i;
180     for (i = 1; i < n; ++i) {
181         if (nums[i] > Max)
182             Max = nums[i];
183     }
184     std::vector<int>V(Max + 1, 0);
185     for (i = 0; i < n; ++i) {
186         ++V[nums[i]];
187     }
188     int idx = 0;
189     for (i = 0; i < V.size(); ++i) {
190         while (V[i] > 0) {
191             nums[idx++] = i;
192             --V[i];
193         }
194     }
195 }
196
197 /**
198 * 桶排序——计数排序的进阶版
199 * 思想是将数组nums中的元素通过映射函数分到数量有限的桶里
200 * 然后再使用其他的排序算法把每个桶里的数据进行排序
201 * 最后把各个桶中的记录列出来即可得到有序序列
202 * 桶排序的效率取决于两方面:一是桶的数目尽可能大;二是映射函数尽量能够使n个数据平均分配
203 * 可以发现,计数排序就是桶排序的特殊情况,桶的数目最大,并且每个桶中只有一个数据的情况
204 * 最好的时间复杂度:有n个桶的时候,每个桶都只有一个元素,不用排序,时间复杂度为n
205 * 最坏的时间复杂度:只有1个桶,这取决于采用的排序方法,时间复杂度是n*n或者n*logn
206 * 平均时间复杂度:假设有k个桶,时间复杂度是n+k
207 * 额外的空间复杂度n
208 * 稳定性:取决于单个桶中采用的排序算法
209 */
210 void Sort::bucketSort(std::vector<int>&nums) {
211     int n = nums.size();
212     if (n < 2)return;
213     int Default_Size = 5;
214     int Max=nums[0], Min=nums[0];
215     int i;
216     for (i = 1; i < n; ++i) {
217         if (nums[i] > Max)
218             Max = nums[i];
219         if (nums[i] < Min)
220             Min = nums[i];
221     }
222     int BucketNum = (Max - Min) / Default_Size + 1;
223     std::vector<std::vector<int>>buckets(BucketNum);
224
225     for (i = 0; i < n; ++i) {
226         //映射函数采用(nums[i]-Min)/Default_Size
227         buckets[(nums[i] - Min) / Default_Size].push_back(nums[i]);
228     }
229     //对每个桶中的元素进行快速排序
230     int j;
231     for (j = 0; j < buckets.size(); ++j) {
232         if(buckets[j].size()>1)
233             quickSort(buckets[j],0,buckets[j].size()-1);//这里采用的方法决定了桶排序是不是稳定的
234     }
235     //按照顺序取出元素
236     int idx = 0;
237     for (j = 0; j < buckets.size(); ++j) {
238         for (i = 0; i < buckets[j].size(); ++i)
239             nums[idx++] = buckets[j][i];
240     }
241 }
242
243 /**
244 * 基数排序
245 * 把nums中所有的数的位数看成是相同长度的
246 * 从个位开始比较各个数的大小
247 * 统计出每位出现的次数,然后根据次数计算出起始位置
248 * 根据起始位置,把nums[i]映射到bucket中
249 * 用bucket的结果覆盖nums,计算更高位
250 * 时间复杂度 n
251 * 额外的空间 n
252 * 稳定性:稳定
253 */
254
255 void Sort::RadixSort(std::vector<int>&nums) {
256     int n = nums.size();
257     if (n < 2)return;
258     int Max = nums[0];
259     int i;
260     for (i = 0; i < n; ++i) {
261         if (nums[i] > Max)
262             Max = nums[i];
263     }
264     std::vector<int>pos(10,0);
265     std::vector<int>bucket(n+1);//桶用来记录一次排序后的结果
266     int exp = 1,idx=0;
267     while (Max /exp) {
268         for (i = 0; i < n; ++i) {
269             ++pos[(nums[i] / exp) % 10];//pos[i]用来记录每一位出现的次数
270         }
271         for (i = 1; i < 10; ++i) {
272             pos[i] += pos[i - 1];//这个时候pos[i]记录的是 当前位是i的数字 在桶中的起始位置,注意是起始位置
273         }
274         for (i = 0; i < n; ++i) {//给nums[i]重新排序
275             idx = (nums[i] / exp) % 10;
276             bucket[pos[idx]++] = nums[i];
277             //这一步是关键
278             //pos[idx]代表当前这一位的数字是idx的起始位置,每次用完起始位置之后,要向后移动
279             //这也决定了基数排序是稳定的
280         }
281         for (i = 1; i <= n; ++i) {
282             nums[i-1] = bucket[i];//重新给nums赋值
283         }
284         if (INT_MAX / exp < 10) {//exp已经到了整数极限
285             break;
286         }
287         exp *= 10;//计算更高位
288         for (i = 0; i < 10; ++i) {
289             pos[i] = 0;//还原pos
290         }
291     }
292 }

3)排序算法的调用

 1 template <typename T>
 2 void printVector(std::vector<T>nums) {
 3     for (int i = 0; i < nums.size(); ++i) {
 4         std::cout << nums[i] << " ";
 5     }
 6     std::cout << std::endl;
 7 }
 8 int main()
 9 {
10     Sort mysort;
11     std::vector<int> nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
12     std::vector<int> res;
13     std::cout << "冒泡排序前:";
14     printVector(nums);
15     std::cout << "冒泡排序后:";
16     mysort.bubbleSort(nums);
17     printVector(nums);
18     std::cout << "---------------------------------" << std::endl;
19
20     nums= { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
21     std::cout << "快速排序前:";
22     printVector(nums);
23     std::cout << "快速排序后:";
24     mysort.quickSort(nums,0,nums.size()-1);
25     printVector(nums);
26     std::cout << "---------------------------------" << std::endl;
27
28     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
29     std::cout << "插入排序前:";
30     printVector(nums);
31     std::cout << "插入排序后:";
32     mysort.insertSort(nums);
33     printVector(nums);
34     std::cout << "---------------------------------" << std::endl;
35
36     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
37     std::cout << "希尔排序前:";
38     printVector(nums);
39     std::cout << "希尔排序后:";
40     mysort.shellSort(nums);
41     printVector(nums);
42     std::cout << "---------------------------------" << std::endl;
43
44     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
45     std::cout << "简单选择排序前:";
46     printVector(nums);
47     std::cout << "简单选择排序后:";
48     mysort.selectSort(nums);
49     printVector(nums);
50     std::cout << "---------------------------------" << std::endl;
51
52     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
53     std::cout << "堆排序前:";
54     printVector(nums);
55     std::cout << "堆排序后:";
56     mysort.heapSort(nums);
57     printVector(nums);
58     std::cout << "---------------------------------" << std::endl;
59
60     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
61     std::cout << "2路归并排序前:";
62     printVector(nums);
63     std::cout << "2路归并排序后:";
64     mysort.mergeSort2(nums,0,nums.size()-1);
65     printVector(nums);
66     std::cout << "---------------------------------" << std::endl;
67
68     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
69     std::cout << "计数排序前:";
70     printVector(nums);
71     std::cout << "计数排序后:";
72     mysort.countingSort(nums);
73     printVector(nums);
74     std::cout << "---------------------------------" << std::endl;
75
76     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
77     std::cout << "桶排序前:";
78     printVector(nums);
79     std::cout << "桶排序后:";
80     mysort.bucketSort(nums);
81     printVector(nums);
82     std::cout << "---------------------------------" << std::endl;
83
84     nums = { 9,4,23,67,234,6,3,2,7,43,2134,643,23 };
85     std::cout << "基数排序前:";
86     printVector(nums);
87     std::cout << "基数排序后:";
88     mysort.bucketSort(nums);
89     printVector(nums);
90     std::cout << "---------------------------------" << std::endl;
91
92     return 0;
93 }

4)结果

原文地址:https://www.cnblogs.com/zhizhiyu/p/10255677.html

时间: 2024-11-08 21:53:01

[算法] 十个经典排序算法的相关文章

算法之经典排序算法小归纳

前言 数据结构和算法是写代码的基础.基本功很重要,所谓根基深度决定成长高度.以前没吃好的饭,总有一天要回来吃的.这段时间项目不忙,回来吃饭,决定花一段时间捋一捋数据结构和算法的基础知识. 正文     这篇博客简要总结了七个算法:冒泡排序,选择排序,插入排序,希尔排序,快速排序,归并排序和堆排序.本文所有的描述都是根据自己的理解手打的,为的是方便读懂,示例代码可以实现算法,但是不敢保证就是最优的.如描述内容有误,请指正. 好了开始吧.. 1.冒泡排序 从数组的一端开始两两比较,依次将当前最值移动

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

经典排序算法

经典排序算法(via  kkun) 经典排序算法,以下文章参考了大量网上的资料,大部分都给出了出处 这一系列重点在理解,所以例子什么的都是最简单的情况,难免失误之处,多指教 大多数排序算法都给出了每一步的状态,以方便初学者更容易理解,通俗易懂,部分难以理解的排序算法则给出了大量的图示,也算是一个特色吧 经典排序算法 - 快速排序Quick sort 经典排序算法 - 桶排序Bucket sort 经典排序算法 -  插入排序Insertion sort 经典排序算法 - 基数排序Radix so

C#实现所有经典排序算法

C# 实现所有经典排序算法 1 .选择排序 选择排序 原理: 选择排序是从冒泡排序演化而来的,每一轮比较得出最小的那个值,然后依次和每轮比较的第一个值进行交换. 目的:按从小到大排序. 方法:假设存在数组:72, 54, 59, 30, 31, 78, 2, 77, 82, 72 第一轮依次比较相邻两个元素,将最小的一个元素的索引和值记录下来,然后和第一个元素进行交换. 如上面的数组中,首先比较的是72,54,记录比较小的索引是54的索引1.接着比较54和59,比较小的索引还是1.直到最后得到最

经典排序算法的PHP实现类

近期广受笔试摧残,对于各种排序也是晕头转向. 更坑爹的是貌似大多都是用C++.Java实现相关算法,让我搞PHP的情何以堪,更何况,PHP本身就有排序函数sort(),其实来说,是很简单的,这也可能是为什么不用PHP进行排序吧. 但考虑到PHP毕竟也是一门面向对象的语言吧,我们利用原生的语法,也是可以实现经典排序算法的,先不说性能如何,切不要妄自菲薄吧. 下面为具体的经典排序算法的PHP实现类. <?php /** * Author: helen * CreateTime: 2016/4/15

经典排序算法 - 归并排序Merge sort

经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到所有合并完,形成有序的数组 举例 无序数组[6 2 4 1 5 9] 先看一下每一个步骤下的状态,完了再看合并细节 第一步 [6 2 4 1 5 9]原始状态 第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍 第三步 [1 2 4 6] [5 9]继续两组两组合并 第四步 [1 2 4 5 6 9]合并完成,排序完成 输出结

(转载)经典排序算法-希尔排序

经典排序算法 - 希尔排序Shell sort 希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组, 就是每

七种经典排序算法最全攻略

经典排序算法在面试中占有很大的比重,也是基础.包括冒泡排序,插入排序,选择排序,希尔排序,归并排序,快速排序,堆排序.希望能帮助到有需要的同学.全部程序采用JAVA实现. 本篇博客所有排序实现均默认从小到大. 一.冒泡排序 BubbleSort 介绍: 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来. 步骤: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对第0个到第n-1个数据做同样的工作.这时,最大的数就"浮"到了

【转】经典排序算法

地址:http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html 大多数排序算法都给出了每一步的状态,以方便初学者更容易理解,通俗易懂,部分难以理解的排序算法则给出了大量的图示,也算是一个特色吧 经典排序算法 - 快速排序Quick sort 经典排序算法 - 桶排序Bucket sort 经典排序算法 -  插入排序Insertion sort 经典排序算法 - 基数排序Radix sort 经典排序算法 - 鸽巢排序Pigeonho