数据结构期末复习:排序算法
来源:互联网 发布:php跟java的区别 商城 编辑:程序博客网 时间:2024/06/07 15:17
图片来源toptal
本文用几种排序算法演示对一个大小为20的数组排序,主要参考课本《C++数据结构与算法(第四版)》,数组下标默认从0开始。
插入排序
复杂度
先对前
# include <iostream>using namespace std;void insertion_sort(int a[], int n){ for(int i=1; i<n; ++i) { int j, tmp = a[i]; for(j=i; j>0 && a[j-1]>tmp; --j) a[j] = a[j-1]; a[j] = tmp; }}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; insertion_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
选择排序
复杂度
将第
# include <iostream>using namespace std;void selection_sort(int a[], int n){ for(int i=0; i<n; ++i) { int tmp = i; for(int j=i+1; j<n; ++j) if(a[j] < a[tmp]) tmp = j; swap(a[tmp], a[i]); }}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; selection_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
冒泡排序
复杂度
从后往前比较相邻两位,不断将较小值交换到前面。
# include <iostream>using namespace std;void bubble_sort(int a[], int n){ for(int i=0; i<n; ++i) for(int j=n-1; j>i; --j) if(a[j-1] > a[j]) swap(a[j-1], a[j]);}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; bubble_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
希尔排序
复杂度下界
一般采用插入排序,算是插入排序的升级版,先将数组分成几个子数组,对相隔较远的元素进行插入排序,再对相隔较近的元素进行插入排序,通过广泛的研究,这个间隔为代码中的
# include <iostream>using namespace std;int increments[23]={1};void shell_sort(int a[], int n){ for(int i=1; i<n; ++i) increments[i] = 3*increments[i-1] + 1;//设置增量值 for(int i=n-1; i>=0; --i) { int h = increments[i];//当前增量值 for(int j=h; j<2*h; ++j)//h到2h的位置都要跑一遍 { for(int k=j; k<n; k+=h) { int tmp = a[k]; int pos = k; while(pos-h >= 0 && a[pos-h] > tmp)//类似插入排序 { a[pos] = a[pos-h]; pos -= h; } a[pos] = tmp; } } }}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; shell_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
堆排序
复杂度
首先介绍大根堆:
于是堆顶元素一定是最大的,利用此性质每次将堆顶元素交换到数组的最后(类似选择排序的相反版本),然后恢复堆,重复这一过程实现对数组的升序排序。
用数组实现堆参考课本
# include <iostream>using namespace std;void movedown(int a[], int left, int right)//将根元素沿树向下移动{ int big_son = 2*left+1; while(big_son <= right) { if(big_son<right && a[big_son] < a[big_son+1]) ++big_son;//找出较大的那个儿子 if(a[left] < a[big_son])//如果爸爸比儿子小 { swap(a[left], a[big_son]);//就执行交换 left = big_son;//然后继续更新儿子 big_son = left*2+1; } else break;//否则可以退出了 }}void heap_sort(int a[], int n){ for(int i=n/2-1; i>=0; --i)//建堆 movedown(a, i, n-1); for(int i=n-1; i>=0; --i) { swap(a[0], a[i]);//将最值放到末尾 movedown(a, 0, i-1);//恢复堆,即重新将最值放到堆顶部 }}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; heap_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
快速排序
复杂度
将数组分为两个子数组,设置一个基准值,使得左边数组小于等于该基准值,右边数组大于等于该基准值,对两个子数组递归此过程实现升序排序。
课本中快排之前进行预处理将最大值放到数组末尾,原因就是课本的代码首先将基准值换到数组的首位,最后再换到正确的位置,那么第一次快排时如果最大值刚好被换到首位,low指针就会永无止境地加下去导致数组越界,因此该预处理是必要的。
# include <iostream>using namespace std;void qucik_sort(int a[], int left, int right){ swap(a[left], a[(left+right)/2]);//先将基准元素放到前面,防止它来回移动(swap(a[low],a[up])),结束再放回正确的位置。 int low = left+1, up = right, bound = a[left]; while(low <= up) { while(a[low] < bound) ++low; while(a[up] > bound) --up; if(low < up) { swap(a[low], a[up]);//将左边>=基准值和右边<=基准值的数字交换 ++low;--up; } else break; } swap(a[up], a[left]);//将基准值放回去正确位置,显然可以是up所在位置 if(left < up-1) qucik_sort(a, left, up-1);//递归对左子数组排序,up为分界点,a[up]不用排 if(right > up+1) qucik_sort(a, up+1, right);//递归对左子数组排序,up为分界点}void quick_sort(int a[], int n){ if(n < 2) return; int imax = 0; for(int i=1; i<n; ++i)//预处理,将最大的元素调到数组最后,否则第一次调用快排可能会使low指针越界 if(a[i] > a[imax]) imax = i; swap(a[n-1], a[imax]); qucik_sort(a, 0, n-2);}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; quick_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
归并排序
复杂度
将一个数组拆成两个子数组,分别排序后再合并在一起,这是一个递归的过程。因为两个子数组已经有序,合并操作可以比较快的完成。
# include <iostream>using namespace std;int temp[20];//临时数组void merge(int a[], int left, int right){ int cnt = 0, mid = (left+right)/2; int l=left, r=mid+1;//l和r分别是左子数组的开头和右子数组的开头 while(l<=mid && r<=right)//如果两个子数组均有元素 { if(a[l] < a[r]) temp[cnt++] = a[l++];//按升序放到temp数组 else temp[cnt++] = a[r++];//按升序放到temp数组 } while(l <= mid) temp[cnt++] = a[l++];//将左子数组剩余元素放到temp数组,当然本循环不会和下面的循环同时出现 while(r <= right) temp[cnt++] = a[r++];//将右子数组剩余元素放到temp数组,当然本循环不会和上面的循环同时出现 cnt = 0; for(int i=left; i<=right; ++i)//更新整个数组 a[i] = temp[cnt++];}void merge_sort(int a[], int left, int right){ if(left >= right) return; int mid = (left+right)/2; merge_sort(a, left, mid);//对左子数组排序 merge_sort(a, mid+1, right);//对右子树组排序 merge(a, left, right);//合并两个子数组,因为他们已经别排序了,合并操作只需O(right-left+1)复杂度}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; merge_sort(a, 0, n-1); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
基数排序
复杂度
比较神奇的算法,平常我们判断两个数的大小是先从最高位开始判断的,最高位相同就按剩下的低位数判断。假如我们只按高位排序,有没有办法使得低位也自动排好序呢?基数排序就是这个原理,它从最低位开始,对每一个独立的数位进行排序,具体就是放到一个二维队列里面(维度一般为10,因为单个数位只有0到9),队列是线性结构,按顺序放进去必然能保持数据的有序性,所以同一个队列里面的数值永远都是有序的,根据这个性质就能实现升序排序了。
# include <iostream># include <queue>using namespace std;void radix_sort(int a[], int n){ int digits = 2, radix = 10;//digits是最长那个数字的长度,radix是单个数位的范围。 queue<int>q[radix]; for(int i=0, fac=1; i<digits; ++i,fac*=10) { for(int j=0; j<n; ++j) q[(a[j]/fac)%10].push(a[j]);//按当前数位的大小进队 for(int j=0,cnt=0; j<radix; ++j)//从小到大更新原数组 { while(!q[j].empty()) { a[cnt++] = q[j].front(); q[j].pop(); } } }}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; radix_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
计数排序
复杂度
比较巧妙的思路,当
# include <iostream>using namespace std;int count[100], tmp[100];void counting_sort(int a[], int n){ int biggest = 0;//最大的数 for(int i=0; i<n; ++i) biggest = max(biggest, a[i]); for(int i=0; i<n; ++i) ++count[a[i]]; for(int i=1; i<=biggest; ++i) count[i] += count[i-1];//前缀和 for(int i=0; i<n; ++i) { tmp[count[a[i]]-1] = a[i]; --count[a[i]]; } for(int i=0; i<n; ++i) a[i] = tmp[i];}int main(){ int a[20] = {42,76,17,1,45,23,98,22,77,83,58,41,64,74,4,96,22,84,45,40}; int n = 20; counting_sort(a, n); for(int i=0; i<n; ++i) cout << a[i] << " "; return 0;}
- 数据结构期末复习:排序算法
- 堆排序 数据结构期末复习
- 数据结构期末复习
- 数据结构期末复习
- 数据结构期末复习
- 算法期末复习
- 《数据结构》复习之排序算法
- 数据结构基本结构 数据结构期末复习
- 数据结构与算法复习笔记--排序算法
- [数据结构复习]八大内排序算法总结
- 面试复习-------算法与数据结构------排序
- 算法导论期末复习(一)
- 复习数据结构:排序算法(二)——冒泡排序
- 复习数据结构:排序算法(四)——归并排序
- 复习数据结构:排序算法(六)——堆排序
- 复习数据结构:排序算法(七)——桶排序
- 复习数据结构:排序算法(八)——基排序
- 数据结构复习笔记01_排序算法_交换排序
- 722. Remove Comments
- php 登录后返回上一页面
- spring boot 中 Mybatis plus 多数据源的配置
- vim 批量重载缓冲区
- android 使用相机拍照以及FileProvider源码浅析
- 数据结构期末复习:排序算法
- jquery下的ajax详解
- 直播SDK加入混响效果,创造演唱会现场沉浸感音效
- Git开源三方库
- 深入了解ThreadLocal
- Intellij IDEA的使用
- **决策树基础以及Python代码实现**
- vue 动态加载图片src的解决办法
- Java根据模板创建excel文件