[算法] 十个经典排序算法
Posted zhizhiyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[算法] 十个经典排序算法相关的知识,希望对你有一定的参考价值。
动图演示参考: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)结果
以上是关于[算法] 十个经典排序算法的主要内容,如果未能解决你的问题,请参考以下文章