内存有限 数据量比较大的时候
如果是对内存中的数据排序:内部排序
如果是对外存中的数据进行排序:外部排序
内部排序
4G的虚拟内存
直接插入:
从第一个元素开始,到最后一个元素,依次把数组中的元素插入到前面,使之保持有序
例:1024 999 1212 8888 521 1314 9527
一开始1024前面没有元素
1024 第一次
999 1024… 第二次
下标为i的元素要插入到[0,i-1]区间,使插入arr[i]元素局部[0,i]区间保持有序
i+1 插入到[0,i] 是[0,i+1]有序
把前面的数组作为有序序列,逐次插入一个元素之后,使之保持有序
//时间复杂度O(n^2) 空间复杂度O(1)
void insertSort(int arr[],int n){
int i,j;
//i=0的时候只有一个元素,本身就有序,所以不需要插入,
//所以从i=1开始,表示从第二个元素开始,逐一往前插入,插入后使之保持有序
for(i=1;i<n;i++){//i=1 1+2+3+4+...+n-1 n(n-1)/2
int key = arr[i];//提前保存要插入的元素 因为前面的元素往后移,会覆盖
//[0,i-1]有序 插入key之后使 [0,i]区间有序
for(j=i-1;j>=0&&arr[j]>key;--j){
arr[j+1] = arr[j];
}
//if(i!=j+1){//判断其实不需要,因为本身j就是按规则在变
arr[j+1] = key; //arr[i] = key;
//}
}
}
如何分析排序(从3个方面):
1.时间复杂度
2.空间复杂度
3.稳定性:原始数量列中有Ri和Rj两个元素,数据元素相等,Ri==Rj,
且i<j ,i和j表示数据元素Ri,Rj的位置 Ri元素在Rj元素前面,
如果经过某种排序后,得到Ri的元素的位置为i’,Rj元素的位置为j’。
如果i’<j’,排序之后,Ri元素依然在Rj前面,称这种排序为稳定排序 否则i’和j’为不稳定排序。
稳定性无关一个排序的好坏,只是考虑特定的场合
稳定的排序一定是好的排序吗?不一定
不稳定的一定是不好的吗?不一定
折半插入
//时间复杂度O(n^2) 空间复杂度是O(1)
void binInsertSort(int arr[],int n){
int left,right,mid,i,j,key;
for(i=1;i<n;i++){//arr[1]----arr[n-1]所有的元素依次往前插入
key = arr[i];//保存要插入的元素 arr[i] arr[0]--arr[i-1]
for(left=0,right=i-1;left<=right;){//找到arr[i]插入的位置 折半查找
mid = (left+right)/2;
if(key < arr[mid]){
right = mid-1;
}else{
left = mid+1;
}
}
//right+1 位置要存储arr[i] key
for(j=i-1;j>right;--j){
arr[j+1] = arr[j];
}
arr[right+1] = key;
}
}
(这是效率最低的情况)
原始数列 10 9 8 7 6 要求升序
原始序列和最终的序列正好相反的情况,每次插入一个元素,都需要把该元素插入到最前面,
需要把该元素之前所有的元素都往后移动
10 9
9 10 8
8 9 10 7
7 8 9 10
2-路插入排序
1212 618 8888 999 9527 1111 1314 521 555 1024
brr数组的左边和右边
max min
1024, 1212,1314,8888, 9527 521, 555,618,999,1111,
申请和原始数列同样大的内存空间,最开始往左插入,遇到比brr[0]更小的值,插入到右边
第二个小的元素放到第二个数组的最右边right[n-1]
这个方法就不用每次都全部移动,比上面的那个少一点
//时间复杂度O(n^2) 空间复杂度O(n)
void twoRoadInsert(int arr[],int n){
int *brr = (int *)malloc(sizeof(arr[0])*n);//brr
int left = -1,right = n;
int i,j;
for(i=0;i<n;i++){
if(left==-1||arr[i]>brr[0]){//大于最左端的元素
for(j=left;j>=0&&arr[i]<brr[j];--j){
brr[j+1] = brr[j];
}
brr[j+1] = arr[i];
++left;
}else{//小于brr[0] 如果要插入左边 左边所有的元素都要移动
for(j=right;j<n&&arr[i]>brr[j];++j){
brr[j-1] = brr[j];
}
brr[j-1] = arr[i];
--right;
}
}
for(i=right;i<n;i++){
arr[i-right] = brr[i];
}
for(i=0;i<=left;i++){
arr[n-right+i] = brr[i];
}
free(brr);
}
希尔排序
9527 1314 8888 1212 521 555 618 999 1024 1111
1212 1314 8888 9527
每次插入 9527离最终的位置近了一步
10 元素 分组10/2 5分组
9527 555 1314 618 8888 999 1212 1024 521 1111
9527 1314 8888 1212 521 555 618 999 1024 1111
555 9527
555 618 9527 1314
555 618 999 1024 521 9527 1314 8888 1212 1111
再2组 5/2
555 999 521 1314 1212
618 1024 9527 8888 1111
组内排序按顺序插回原来的队列中
521 618 555 1024 999 1111 1212 8888 1314 9527
再1组2/2
组内排序
//时间复杂度是O(nlogn) 希尔排序 O(n^1.3) 空间复杂度O(1) 不稳定
void shellSort(int arr[],int n){
//分组 步长 分为step小组 组内元素步长step
int step,i,j;
for(step = n/2;step>0;step=step/2){
//组内排序 [0,step) 组内的第一个元素
for(i=step;i<n;i++){//除了组内第一个元素 其它元素都需要进行组内插入
int key = arr[i];
for(j=i-step;j>=0&&key<arr[j];j=j-step){
arr[j+step] = arr[j];//组内的元素往移
}
arr[j+step] = key;
}
}
}
main.c
void showArr(int arr[],int len){
int i;
for(i=0;i<len;i++){
printf("%d ",arr[i]);
}
printf("\n");
}
int main(int argc, char *argv[]) {
//数组为10,元素只有9个
// int arr[10] = {521,618,999,1024,1111,1212,1314,8888,9527};//555
// int key = 0;
// scanf("%d",&key);
// insertNum(arr,10,key);
int arr[10] = {1212,618,8888,999,9527,1111,1314,521,555,1024};
//insertSort(arr,10);
//binInsertSort(arr,10);
//twoRoadInsert(arr,10);
shellSort(arr,10);
showArr(arr,10);
return 0;
}
有一组升序的数据,插入一个数据元素,使之插入该序列后保持升序
从最后一个元素开始比较,如果数组中的元素比要插入的元素key大,
则把数组中该元素往后移动一个位置,知道数组中所有元素比较完 或者 遇到比key小的则循环退出
key插入的位置为最后一次比较元素(位置i)的后一个位置(i+1)
void insertNum(int arr[],int len,int key);
//arr数组宏原来有Len-1个元素,[0,len-2] 升序
插入一个元素Key,使插入之后依然有序
普通插入保持有序
//arr是升序 插入key之后 依然要保持升序
void insertNum(int arr[],int len,int key){
if(len==1)
return;
int i;
for(i=len-2;i>=0&&arr[i]>key;--i){//arr[i]比key要大,说明key在arr[i]前面
arr[i+1] = arr[i];//arr[i]要往后移一个位置
}
//i==-1 key比数组中所有元素都要小 arr[0]
//i>=0 arr[i]<=key key要放在arr[i]后面 arr[i+1]
arr[i+1] = key;
}
折半插入保持有序
void insertNum(int arr[],int len,int key){
int left,mid,right;
for(left=0,right=len-2;left<=right;){//left>right
mid = (left+right)/2;
if(key<arr[mid]){
right = mid-1;
}else{
left = mid+1;
}
}
int i;
for(i=len-2;i>right;--i){
arr[i+1] = arr[i];
}
arr[right+1] = key;
}