插入排序
来源:互联网 发布:云计算专业认识 编辑:程序博客网 时间:2024/05/24 00:36
中心思想:考虑你正在斗地主,对方发给你一堆牌,你右手摸牌,左手拿牌,第一次摸到10放到左手,第二次摸到3就放到10左边,第三次摸到K放10右边,左手上的牌一直保持有序,直到左手拿着所有的牌为止。这就是插入排序。
插入排序从第二个元素开始,在前面已排好序的序列中寻找合适的位置并插入,使之仍然有序。
插入排序最多的步骤在于移位操作,每个元素插入之前,数组的大于待插入值的元素必须事先挪位,故插入排序在最坏情况下是o(n*n)的复杂度。
直接插入:
//插入排序,不支持参数检查void insert_sort(int * a , int n){ int i,j,key; for(i=1;i<n;++i) { key = a[i];//key 为待插入的元素 //挪位 for( j = i-1;j>=0 && a[j] >key;--j) { a[j+1] = a[j];//两重循环,故O(n*n) } //在合适的位置插入 a[j+1] = key; }}
在一万个数据的运行结果:
插入排序有个挪位操作,每次循环必须判断移动的元素是否大于待插入的元素,为了省去不必要的判断,我们可以先用二分法寻找合适的插入位置。
折半插入:
void insert_sort(int * a , int n){ int i,j,key,low,high,mid; for(i=1;i<n;++i) { key = a[i]; //二分查找运算 low = 0,high = i-1; while(low <= high) { mid = (low + high)/2; if(key > a[mid]) low = mid + 1; else// if(key <= a[mid]) high = mid - 1; } //挪位 for( j = i-1;j >= low;--j) { a[j+1] = a[j]; } a[j+1] = key; }}
在一万个数据的运行结果:
结果快了大概十多毫秒。。。我想应该是移位操作步数并没有因此减少,只是对比较操作优化了一下而已。所以效果并不明显。
二路插入:
那么有没有办法减少移位的步数呢?仔细一想,有一种情况是不需要移位的,那就是待插入的元素刚好插入到有序序列的最后。我们想一下,如果有序序列前面还有位置放会怎样?本来需要把所有有序序列移位的现在也变成不需要移位了。那么如何实现呢?我们可以利用循环数组的想法来做。用first表示有序序列的开始,last表示有序序列的结尾,如果待插入元素小于first值或者大于last值,就放到外面去;如果待插入元素刚好处于first值和last值之间,那么就插入到有序序列中间,这和直接插入一样。
举例:如果有序序列是 1 2 3 5,待插入的是0,那么我就把它放到数组的最后,让first等于最后一个(first = n -1),让last = 2。
二路插入的思想是以空间换取时间的想法。
void insert_sort(int * a , int n){ int *t; t = new int [n]; int i,j,key,first=0,last=0; t[0] = a[0]; for(i=1;i<n;++i) { key = a[i];//key 为待插入的元素 //如果待插入元素比first值还小 if(key <= t[first]) { first = (first-1+n)%n; t[first] = key; } //如果待插入元素比last值还大 else if(key >= t[last]) { last = last + 1; t[last] = key; } //如果待插入元素处于first和last之间 else //if(key > t[first] && key < t[last]) { //挪位 for( j = last;j != first && t[j] > key; j= (j-1+n)%n) { t[(j+1)%n] = t[j];//两重循环,故O(n*n) } //在合适的位置插入 t[(j+1)%n] = key; last = (last + 1)%n; } } //赋值给a数组 j=0; i =first; do{ a[j++] = t[i]; i = (i+1)%n; }while(i != (last + 1)%n);}
但是运行结果却出乎意料:
但是对于完全逆序的数据却出乎意料的快:
经过仔细分析,原来是取余(循环链表需要用到模运算)造成的影响,于是修改一下:
void insert_sort(int * a , int n){ int *t; t = new int [n]; int i,j,key,first=0,last=0; t[0] = a[0]; for(i=1;i<n;++i) { key = a[i];//key 为待插入的元素 //如果待插入元素比first值还小 if(key <= t[first]) { first = (first-1+n)%n; t[first] = key; } //如果待插入元素比last值还大 else if(key >= t[last]) { t[++last] = key; } //如果待插入元素处于first和last之间 else //if(key > t[first] && key < t[last]) { //挪位 int k1,k2; for( j = last;j != first && t[j] > key; j= (!j)?(j-1+n):j-1)//避免取余带来的性能损耗 { k1= (!(j-n+1))?j+1-n:j+1; t[k1] = t[j];//两重循环,故O(n*n) } //在合适的位置插入 t[(!(j-n+1))?j+1-n:j+1] = key; ++last; } } //赋值给a数组 j=0; i =first; do{ a[j++] = t[i]; i = (!(i-n+1))?i+1-n:i+1; }while(i != last + 1);}
运行结果:
我只能说我已经尽力了,对于均匀随机产生的数据,二路插入只有70多毫秒,比直接插入还要慢一点,但是对于极端情况即完全逆序或者完全升序的情况来说是很快的,权衡一下应该选择二路插入好一点。
希尔排序:
为了省略移位带来的影响,希尔提出了这样一种思路:如果待排序的数组元素相对有序,那么是否会降低移位操作的频率?
如果在插入元素之前,数组都相对有序,大的元素都在后头,小的元素都在前面,这样只需移动少许的元素就可以使数组升序。
上代码:
void insert_sort(int * a , int n){ int i,j,key,step;//step 为步长 step = n >>1; while(step!=0) { //cout<<step<<endl; for(i=0;i<n;i += step) { key = a[i];//key 为待插入的元素 //挪位 for( j = i-step;j>=0 && a[j] >key;j -= step) { a[j+step] = a[j];//两重循环,故O(n*n) } //在合适的位置插入 a[j+step] = key; } step = step >>1; }}
我感觉希尔排序跟直接插入排序的代码差不多,不同的是下标值的变换,希尔排序会依次缩小步长,增大待排序的元素,直到步长为1,即变成了直接插入排序,但由于之前的排序是数组相对有序(大小错落有致),所以移位运算大大减少,因此性能得到提高:
以上,便是插入排序的大致内容,这是我的第一篇博客,谢谢观看。
- 插入排序-【插入排序】
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 电容器及介质种类
- C++基础篇(2)——面向过程的特点
- createrepo -v -g 产生的源的名字不相同
- Android上,使用JNI调用C的函数,hellojni
- unix 网络编程 Fgets Fputs Fclose Fopen Fdopen
- 插入排序
- 面试题--C++基础篇
- HLS协议
- Android中通过GPS或NetWork获取当前位置的经纬度
- 案例借鉴 | 某通讯巨头的IT建设方案
- CSS控制缩略缺省显示...
- Objective-C修改运动步数
- 免费切图标记外挂神器Assistor PS
- C++基础篇(3)——从面向过程到面向对象