排序
- 概述
分类
内排序:数据量少,在内存中
插入
交换
选择
归并
外排序:数据量大,内外存需要交换数据
按复杂度分
简单算法:冒泡、简单选择、直接插入
改进算法:希尔、堆、归并、快速
稳定性:两个一样的数字,排序后相对位置不变,则稳定
- 冒泡排序
思想:从上到下/从下到上,与相邻元素比较
改进:设置有无交换的标志位
复杂度:最好/最坏
稳定:arr[j]严格>arr[j+1]时,才做交换
void BubbleSort(vector<int> arr) { int sz=arr.size(); bool flag=true; for(int i=0;i<sz-1&&flag;i++) { flag=false;//没交换 for(int j=sz-2;j>=i;j--) { if(arr[j]>arr[j+1]) { swap(arr[j],arr[j+1]); flag=true; } } } }
- 简单选择排序
思想:通过n-i次比较,从n-i+1中选出最小的,与位置i的交换
复杂度
void SelectSort(vector<int> arr) { int sz=arr.size(); int min; for(int i=0;i<sz-2;i++) { min=i; for(j=i+1;j<sz-1;j++) { if(arr[min]>arr[j]) min=j; } if(i!=min) swap(arr[i],arr[min]); } }
- 直接插入排序
思想:扑克牌 将一个元素插入已经排好序的序列中
复杂度
稳定
void InsertSort(vector<int> arr) { int sz=arr.size(); for(int i=1;i<sz;i++) { int tmp=arr[i];//摸下一张牌 for(int j=i;j>0&&arr[j-1]>tmp;j--) arr[j]=arr[j-1];//为新牌找位置,比它大的都向后挪 arr[j]=tmp;//新牌落座 } }
- 希尔排序
思想:定义增量序列(最后一定是1);对每个增量序列进行排序;基本有序;跳跃;
更多增量序列
复杂度
不稳定
void ShellSort(vector<int> arr) { int N=arr.size(); for(int D=N/2;D>0;D/=2)//增量序列 { for(int i=D;i<N;i++)//插入排序 { int tmp=arr[i]; for(int j=i;j>=D&&arr[j-D]>tmp;j-=D) arr[j]=arr[j-D]; arr[j]=tmp; } } }
- 堆排序(选择排序的改进)
思路:构建最大堆;堆顶与堆尾元素交换;删去堆尾后重建最大堆;重复
(利用堆的性质:完全二叉树;编号;父/子节点编号)
数组编号从0开始,堆从1开始
复杂度(最好/坏/平均一样)
不稳定
不适合待排序序列个数少的
// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标) void adjust(vector<int> &arr, int len, int index) { int left = 2*index + 1; // index的左子节点 int right = 2*index + 2;// index的右子节点 int maxIdx = index;//maxIdx保存根、左、右最大的下标 if(left<len && arr[left] > arr[maxIdx]) maxIdx = left; if(right<len && arr[right] > arr[maxIdx]) maxIdx = right; if(maxIdx != index) { swap(arr[maxIdx], arr[index]); adjust(arr, len, maxIdx);//从此次最大子节点的位置递归 } } // 堆排序 void HeapSort(vector<int> &arr, int size) { // 构建大根堆(从最后一个非叶子节点向上) for(int i=size/2 - 1; i >= 0; i--) { adjust(arr, size, i); } // 调整大根堆 for(int i = size - 1; i >= 1; i--) { swap(arr[0], arr[i]); // 将当前最大的放置到数组末尾 adjust(arr, i, 0); // 去除最后位置的元素,将未完成排序的部分继续进行堆排序 } }
- 归并排序
思想:有序子列的归并
复杂度:最好/坏/平均一样
空复:常用于外排序
稳定:两两比较,无跳跃
递归: void Merge(int a[], int b[], int left, int mid, int right)//归并到a { int i=left,j=mid+1; int k=left;//存放结果数组的初始位置 while(i<=mid&&j<=right) { if(a[i]<=a[j]) b[k++]=a[i++]; else b[k++]=a[i++]; } while(i<=mid) b[k++]=a[i++]; while(j<=right) b[k++]=a[j++]; for(i=left;i<right-left+1;i++) a[i]=b[i]; } void Msort(int a[], int b[], int l, int r) { if (l >= r) //必须有等号 return; int m = (l + r)/2; Msort(a, b, l, m); Msort(a, b, m + 1, r); Merge(a, b, l, m, r); } void MergeSort(int a[], int len) { int *b = new int[len]; //int *b; //b=malloc(len*sizeof(int)); if(!b) { Error("空间不足"); } else { Msort(a, b, 0, len - 1); delete[] b; //free(b); } }
//非递归: void MergePass(int a[],int b[],int len,int sz) { int i; for(i=0;i<=len-2*sz;i+=2*sz) Merge1(a,b,i,i+sz,i+2*sz-1);//将a归并到b if(i+sz<len)//归并最后两个子列 Merge1(a,b,i,i+sz,len-1); else//最后只剩一个子列 for(int j=i;j<len;j++) b[j]=a[j]; } void MergeSort(int a[], int len) { int sz=1;//初始化子序列长度 int *b = new int[len]; //int *b; //b=malloc(len*sizeof(int)); if(!b) { Error("空间不足"); } else { while(sz<len) { MergePass(a,b,len,sz); sz*=2; MergePass(b,a,len,sz); sz*=2; } delete[] b; //free(b); } }
- 稳定:冒泡、直接插入、归并
不稳定:简单选择、希尔、堆、快速