- 这是《算法笔记》的读书记录
- 本文参考自4.1节
1. 选择排序
-
思想:把数组分为有序的前半部分和无序的后半部分,每次从无序的后半部分选出最小的元素交换到分界处,从而使有序部分不断增长,直到完全有序
-
这个思想可以很方便地转换为递归形式,示例代码如下
//选择排序,复杂度 O(n^2) #include<iostream> using namespace std; void selectSort(int *a,int n) { //n次操作 for(int i=0;i<n;i++) { //从后n-i个数中选出最小的,下标为k int k=i; for(int j=i;j<n;j++) if(a[j] < a[k]) k=j; //交换a[k]和a[i] int temp = a[i]; a[i] = a[k]; a[k] = temp; } } //对长len的数组a的前n个元素排序 void selectSort2(int *a,int n,int len) { //退出条件:全部n个数都已经排序 if(n >= len+1) return; //从后n-i个数中选出最小的,下标为k int k=n-1; for(int i=n;i<len;i++) if(a[i] < a[k]) k=i; //交换a[k]和a[n-1] int temp = a[k]; a[k] = a[n-1]; a[n-1] = temp; //递归排序前n+1个数 selectSort2(a,n+1,len); } int main() { int a[15] = {2,4,1,3,5,7,6,8,10,9,2,2,4,5,1}; //selectSort(a,15); // 标准选择排序 selectSort2(a,1,15); // 递归选择排序 for(int i=0;i<15;i++) cout<<a[i]<<" "; cout<<endl; return 0; }
-
复杂度分析:进行n趟操作,每趟选出
[i,n)
部分中最小的元素交换,时间复杂度 O ( n 2 ) O(n^2) O(n2)
2. 插入排序
-
思想:类似选择排序,把数组分为有序的前半部分和无序的后半部分,每次把无序后半部分中第一个数移动到有序部分的合适位置,从而使有序部分不断增长,直到完全有序
-
这个思想也可以很方便地转换为递归形式,示例代码如下
//插入排序,复杂度 O(n^2) #include<iostream> using namespace std; void insertSort(int *a,int n) { //n-1趟排序 for(int i=1;i<n;i++) { //把a[i]移到合适的位置 int temp = a[i],j = i; while(j>0 && temp<a[j-1]) { a[j] = a[j-1]; j--; } a[j] = temp; } } //对长len的数组a的前n个元素排序 void insertSort2(int *a,int n,int len) { //退出条件:全部n个数都已经排序 if(n >= len+1) return; //把最后一个元素放到合适的位置 int temp = a[n-1],i = n-1; while(i>0 && temp<a[i-1]) { a[i] = a[i-1]; i--; } a[i] = temp; //递归排序前n+1个数 insertSort2(a,n+1,len); } int main() { int a[15] = {2,4,1,3,5,7,6,8,10,9,2,2,4,5,1}; //insertSort(a,15); // 标准插入排序 insertSort2(a,1,15); // 递归插入排序 for(int i=0;i<15;i++) cout<<a[i]<<" "; cout<<endl; return 0; }
-
复杂度分析:进行n-1趟操作(数量级n),每趟至多前i个数(数量级n)找出位置,时间复杂度 O ( n 2 ) O(n^2) O(n2)
3. 冒泡排序
-
思想:每轮操作从头开始依次比较相邻两个数,如果左边的数大于右边的就交换。第一轮操作中,不管最大数在哪里,都能在操作结束后交换到最右边;同样的,第二轮操作把次大数交换到最右边,循环直到有序。
-
和前两个排序方法不同,冒泡排序中数组右侧是有序部分;选择/插入排序中数组左侧是有序部分
-
同样的,这个也能改成递归写法
//冒泡排序,复杂度 O(n^2) #include<iostream> using namespace std; void bubbleSort(int *a,int n) { //n-1次操作 for(int i=1;i<n;i++) { //每次从无序部分冒一个数上来 for(int j=0;j<n-i;j++) { if(a[j] > a[j+1]) { //交换a[j]和a[j+1] int temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } } } //从长len的数组a的前len-n个元素(无序部分)中,把最大的数浮上来 void bubbleSort2(int *a,int n,int len) { if(n>=len) return; //每次从无序部分冒一个数上来 for(int j=0;j<len-n;j++) { if(a[j] > a[j+1]) { //交换a[j]和a[j+1] int temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } //递归排序,把次大的数浮动上来 bubbleSort2(a,n+1,len); } int main() { int a[15] = {2,4,1,3,5,7,6,8,10,9,2,2,4,5,1}; //bubbleSort(a,15); bubbleSort2(a,1,15); for(int i=0;i<15;i++) cout<<a[i]<<" "; cout<<endl; return 0; }
-
复杂度分析:也是两个数量级为n的二重循环,时间复杂度 O ( n 2 ) O(n^2) O(n2)
4. 小结
-
这里介绍了三种简单排序算法,时间复杂度都是 O ( n 2 ) O(n^2) O(n2)
-
每轮操作的有序部分
- 选择/插入排序:在前
- 冒泡排序:在后
-
递归公式:对n个数进行排序 = 把第n个数插入有序部分 + 对n-1个数进行排序