赞
踩
视第一个数为已经排好序的数,将接下来未被排序的数插进前面已经排好序的数中,直至全部插完,排序结束。
例:将数组[1 5 3 2]按照从小到大的顺序排序。
注:【】内为已经排好序的数
【
1
】
5
3
2
\color{red}{【1】5\ 3 \ 2}
【1】5 3 2 :
【 1 5 】 3 2 \color{red}【1\ 5】3\ 2 【1 5】3 2:
【 1 3 5 】 2 \color{red}【1\ 3\ 5】2 【1 3 5】2:
【 1 2 3 5 】 \color{red}【1\ 2\ 3\ 5】 【1 2 3 5】:
【
1
2
3
5
】
排
序
完
成
\color{red}【1\ 2\ 3\ 5】排序完成
【1 2 3 5】排序完成
时间复杂度T(n)=O(n^2)
算法描述:
从第二个数开始,找到当前的位置i(前a[i-1]已经排好序),从第i-1个数到第1个数中为a[i]找位置。倒着遍历能通过向后移出一个空位以供新的数来插入。
用a[0]暂存a[i],j=i-1以供倒着对数组进行遍历。将a[i]的前一个数a[j]覆盖a[i],同时j- -(这样能空出来一位以供a[0]插入)直至a[0]>a[j]跳出循环(此时a[0]大于前一个数a[j],那么他的位置就是a[j]的后面一位j+1,即a[j+1]=a[0])
#include <iostream> #include <bits/stdc++.h> using namespace std; void Insertsort(int a[100],int n)//直接插入 {//数组a是从1到n储存数据 int i,j; for(i=2;i<=n;i++)//从第二个数开始 { a[0]=a[i];//a[0]暂存当前数据 j=i-1;//当前为i,所以前i-1个数已经排好序 //从第i-1个数开始比,将每个数都后移,直到找到插入位置 while(a[0]<a[j]) { a[j+1]=a[j]; j--; } a[j+1]=a[0];//插入 } } int main() { int a[100],x,i=1,n; while(cin>>x&&x)//输入0表示结束 { a[i++]=x; } n=i-1;//n记录数组长度 Insertsort(a,n); for (i=1;i<=n;i++) printf("%d ", a[i]); return 0; } /* 输入: 2 4 3 9 6 8 7 5 1 0 输出: 1 2 3 4 5 6 7 8 9 */
从大到小排序:
while(a[0]<a[j]) 改为 while(a[0]>a[j])
折半插入的被插入的部分必须是已经排好序的数,类似“直接插入”中不断地将未被遍历的数插到已经排序的数的过程。
折半插入相较于直接插入是在查找a[i]的位置上进行了优化,减少关键字间的比较次数,具体的优化是通过判断a[i]的所在区间而不断移动左区间(left)右区间(right)直至满足a[left]<a[i]<a[right]。判断是否满足a[left]<a[i]<a[right]需要引入区间的中间位置mid:
以此缩小left与right之间的夹取空间,若要满足a[left]<a[i]<a[right]则a[left]与a[right]之间原本没有数(即left+1=right,a[mid]=a[l])
这时a[i]>a[mid]使left右移导致left=left+1=right,a[m]=a[right],此时a[i]<a[right]那么a[i]的位置为a[left]与a[right]之间,同时由于a[i]<a[right],right=mid-1破坏了原来left=right的平衡,l>r。这就是跳出循环也就是找到a[i]位置的标志!
移动过程通过下面的代码模拟:(从1开始存储,共n个数,为a[m]找位置)
left=1,right=n,mid=(left+right)/2
if(a[m]>a[0])//a[0]暂存a[i]
r=m-1;
else
l=m+1;
(以下left简称为l,right简称为r,mid简称为m)
例:将数组[1 5 3 0]按照从小到大的顺序排序。
即a[1]=1,a[2]=5,a[3]=3,a[4]=0
【
1
】
5
3
0
\color{red}【1】5\ 3\ 0
【1】5 3 0 :
【 1 】 5 3 0 − > 【 1 5 】 3 0 \color{red}【1】5\ 3\ 0\ ->【1\ 5】 3\ 0 【1】5 3 0 −>【1 5】3 0:
【 1 5 】 3 0 − > 【 1 3 5 】 0 \color{red}【1\ 5】 3\ 0\ ->【1\ 3\ 5】0 【1 5】3 0 −>【1 3 5】0:
【 1 3 5 】 0 − > 【 0 1 3 5 】 \color{red}【1\ 3\ 5】0\ ->【0\ 1\ 3\ 5】 【1 3 5】0 −>【0 1 3 5】:
【
0
1
3
5
】
排
序
完
成
\color{red}【0\ 1\ 3\ 5】排序完成
【0 1 3 5】排序完成
时间复杂度为:T(n)= O(n^2)
算法描述
从第二个数开始,找到当前的位置i,left从1开始,right从i-1开始(因为前i-1个数已经排好序)mid=(left+right)/2,让a[i]与a[mid]比较(要始终保证在左右区间内a[left]<a[i]<a[right]),那么当left>right时左右的数都被遍历过,此时a[left]则为a[i]的插入位置,同"直接插入法"倒着将a[i]插入在正确位置上
#include <iostream> #include <bits/stdc++.h> using namespace std; void Halfsort(int a[100],int n)//n表示个数 { int i,j,l,r,m;//l表示left,r表示right,m表示mid for(i=2;i<=n;i++)//n-1次排序完成 { a[0]=a[i]; l=1; r=i-1;//前i-1个数已经排好序 while(l<=r)//找a[i]的插入位置 { m=(l+r)/2; if(a[0]<a[m])//从大到小排序则改为a[0]>a[m] r=m-1; else l=m+1; } for(j=i-1;j>=l;j--)//插入:向后覆盖,直至j<l, { a[j+1]=a[j]; } a[l]=a[0]; } } int main() { int a[100],x,i=1,n; while(cin>>x&&x) { a[i++]=x; } n=i-1; Halfsort(a,n); for (i=1;i<=n;i++) printf("%d ", a[i]); return 0; } /* 输入: 2 4 3 9 6 8 7 5 1 0 输出: 1 2 3 4 5 6 7 8 9 */
从大到小排序:
if(a[0]<a[m]) 改为 if(a[0]>a[m])
希尔排序又称缩小增量法
先将整个待排记录序列分割成若干子序列,子序列内部分别进行直接插入排序,通过不断减少子序列内部的元素数,使整个序列中的记录“基本有序”,此时再对全体记录进行一次直接插入排序。
例:将下列元素从小到大排序
1 2 3 4 5 6 7 8 9 10 11 12 13 (下标)
7 8 4 6 1 9 5 3 2 12 11 13 -1 (数据)
时间复杂度:
T
(
n
1.25
)
~
T
(
1.6
n
1.25
)
T(n^{1.25})~T(1.6n^{1.25})
T(n1.25)~T(1.6n1.25)
空间复杂度:S(n)=O(1)
如何选择最佳d序列?目前尚未解决
最后一个增量值必须为1,无除1以外的公因子
不宜在链式存储结构上实现
算法描述
先取一个正整数d1<n,把所有相隔d1的记录放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止
#include <iostream> #include <bits/stdc++.h> using namespace std; void ShellSort(int a[],int n) { int d,i,j; for(d=n/2;d>=1;d/=2)//间隔从每次间隔取d/2,当间隔取1时排序完成 { //组内直接插入排序,只是元素间隔为d,其他同直接插入排序 for(i=d+1;i<=n;i++)//从每组的第二个数据开始 { if(a[i]<a[i-d])//后一个小于前一个,交换 { a[0]=a[i];//暂存 for(j=i-d;j>0&&a[j]>a[0];j-=d) { a[j+d]=a[j];//组内元素不断后移的过程 } a[j+d]=a[0]; } } } } int main() { //数组中第0号位置的值不是有效值 int a[100],x,i=1,n; while(cin>>x&&x) { a[i++]=x; } n=--i; ShellSort(a,n); //将已排序的结果逐个输出 for (i=1;i<=n;i++) { printf("%d ", a[i]); } return 0; } /* 输入: 1 8 4 6 7 9 5 3 2 12 11 13 -1 0 输出: -1 1 2 3 4 5 6 7 8 9 11 12 13 */
从大到小排序需要改动两个地方:
(1)if(a[i]<a[i-d]) 变为 if(a[i]>a[i-d])
(2)for(j=i-d;j>0&&a[j]>a[0];j-=d) 变为 for(j=i-d;j>0&&a[j]<a[0];j-=d)
两两比较,如果发生逆序则交换,直到所有记录都排好序为止。保证每轮比较时最后一个元素最大,结束时能将最大值排到最后面位置,还能同时部分理顺其他元素。
例:将数组[1 8 4 7 9 5 -1]按照从小到大的顺序排序,n=7。
时间复杂度T(n)=O(
n
2
n^2
n2)
空间复杂度S(n)=O(1)
#include <iostream> #include <bits/stdc++.h> using namespace std; void BubbleSort(int a[100],int n) { int i,j,t; for(i=1;i<=n-1;i++)//轮数 { for(j=1;j<=n-i;j++)//让当前最大与下一个比 { if(a[j]>a[j+1])//交换 { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } } } int main() { int a[100],x,i=1,n; while(cin>>x&&x) { a[i++]=x; } n=--i; BubbleSort(a,n); for (i=1;i<=n;i++) printf("%d ", a[i]); return 0; } /* 输入: 1 8 4 6 7 9 5 3 2 12 11 13 -1 0 输出: -1 1 2 3 4 5 6 7 8 9 11 12 13 */
从大到小排序:
函数交换位置那步的if(a[j]>a[j+1]) 改为 if(a[j]<a[j+1])
参考连接处视频https://www.bilibili.com/video/BV1at411T75o?from=search&seid=7469410686447511593
任取一个元素 (如第一个) 为中心,所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表。对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个。
例:将数组[1 8 4 7 5 -1]按照从小到大的顺序排序,n=7。
取第一个数1为中心,L指向第一个位置1,R指向最后一个数-1,L和R交替遍历数,依次与中心进行比较,规则如下:
——————————————————————————————————————
当前为-1,-1<1,-1移到L指向的位置得到下图,交替移动L指向8;
当前为8,8>1,8移到R指向的位置得到下图,交替移动R指向,5;
当前为5,4>1,不动,R顺延指向7;
当前为7,7>1,不动,R顺延指向4;
当前为4,4>1,不动,R顺延与L重合;
得到左序列-1,右序列 1 4 7 5 8 。即-1 1 4 7 5 8
时间复杂度:T(n)=O(n²)
空间复杂度:S(n)=O(
l
o
g
2
n
log_2n
log2n)
①每一趟的子表的形成是采用从两头向中间交替式逼近法;
②由于每趟中对各子表的操作都相似,可采用递归算法。递归结束的条件是两边递归的结果为一个数,此时排序完成。
#include <iostream> #include <bits/stdc++.h> using namespace std; void Kuaipai(int a[],int left, int right)//整数+从小到大 { int i=left; int j=right; int key=a[i];//key记录中心数 int n=a[0]; if(left>right) return;//设置递归的结束条件 while(i<j) { while(i<j&&key<a[j])//找到第一个右指针指向的数<中心的数 j--; a[i]=a[j];//将小的挪到左边 while(i<j&&key>a[i])//找到第一个左指针指向的数>中心的数 i++; a[j]=a[i];//将大的挪到右边 } a[i]=key;//left=right时的位置为中心数的位置 Kuaipai(a,left,i-1);//对当前的中心左边进行快排 Kuaipai(a,i+1,right);//对当前的中心右边进行快排 } int main() { int a[100],x,i=1,n; while(cin>>x&&x) { a[i++]=x; } n=--i; Kuaipai(a,1,n); for (i=1;i<=n;i++) printf("%d ", a[i]); return 0; } /* 输入: 1 8 4 6 7 9 5 3 2 12 11 13 -1 0 输出: -1 1 2 3 4 5 6 7 8 9 11 12 13 */
从大到小排序:
那么指针指向的数与中心数进行比较时挪动的法则应相反
while(i<j&&key<a[j]) 改为 while(i<j&&key>a[j])
while(i<j&&key>a[i]) 改为 while(i<j&&key<a[i])
剩下的排序方法将放在另一篇博客中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。