(1.3.3.2)插入排序:表折半插入

来源:互联网 发布:密度泛函理论 软件 编辑:程序博客网 时间:2024/05/22 04:25

我们用静态链表的存储方式,直接插入的策略,构建了一种新的插入排序算法:表插入。有人可能会想到:同样是静态链表的形式,为什么不使用更高效的折半插入策略呢?这种想法真的很好,如果做到了,显然是极大的优化。

    我在网上还真看到了相关的内容,大家可搜下《表插入方法的改进》,里面有此想法的介绍。这篇博客就是介绍表插入的另一种实现:表折半插入。看完一定让你彻底理解它!

与一般的折半插入相比,有如下的几点变化:

  1. 为了实现折半查找,我们对静态链表的节点类型做了一些变化:添加了一个前驱指针。它的意义很显然,以前是high=mid-1,在单向链表中我们是做不到的(其实可以换种方式做到,不过相对麻烦),于是添加一指向其前驱的指针,构成双向链表,方便进行此操作。
  2. while循环的结束条件,有所不同。这个要仔细理解!
其他细节,代码中有详细解释
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. const int MAX=100;  
  2. typedef struct rec  
  3. {  
  4.     int data;  
  5.     int pre;   //前驱   
  6.     int next;  //后继   
  7. }Rec;  
  8. void InsertSort(int a[], int n)  //表折半插入   
  9. {  
  10.     Rec *rec=new Rec[n+1];  
  11.     for(int i=0; i<n; i++)  
  12.     {  
  13.         rec[i+1].data=a[i];  
  14.         rec[i+1].next=rec[i+1].pre=0;  
  15.     }  
  16.     rec[0].data=MAX;  
  17.     rec[0].next=rec[0].pre=1;  
  18.     int low,high,mid;  
  19.     int p,k,l;  
  20.     for(int i=2; i<n+1; i++)  
  21.     {  
  22.         //根据以下的赋值,我们可以看出,这里使用的是左闭右闭区间   
  23.         low=rec[0].next;    //low指向最小的   
  24.         high=rec[0].pre;    //high指向最大的   
  25.         l=i-1;      //已有序的元素个数   
  26.         while(low!=0 && high!=0 && rec[low].data<=rec[high].data)  //循环结束条件得理解,特别是前两个条件。准确的是,第一个条件可以不要   
  27.         {  
  28.             mid=low;  
  29.             k=1;  
  30.             l/=2;   // l>>=2 减半,为下次循环做好准备  
  31.             while(k<l)  //寻找mid位置   
  32.             {  
  33.                 mid=rec[mid].next;  
  34.                 k++;  
  35.             }  
  36.             if(rec[i].data<rec[mid].data)  
  37.             high=rec[mid].pre;  
  38.             else  
  39.             low=rec[mid].next;  
  40.         }  
  41.         //插入第i个节点,类似于双向链表的插入   
  42.         rec[rec[low].pre].next=i;  
  43.         rec[i].pre=rec[low].pre;  //添加前驱指针的作用体现在这里  
  44.         rec[i].next=low;  
  45.         rec[low].pre=i;   
  46.     }  
  47.     //顺着next指针方向打印   
  48.     printf("表折半插入排序后\n");  
  49.     p=rec[0].next;  
  50.     while(p!=0)  
  51.     {  
  52.         printf("%-4d",rec[p].data);  
  53.         p=rec[p].next;  
  54.     }  
  55.     printf("\n");  
  56. }  

仔细看完代码,我想大多数人只剩一个问题可能没明白,那就是while循环的结束条件为什么还得加上low!=0 和high!=0
为了解释清楚,我们画一个图,图中正在插入i=2的节点:
初始化后,low,mid,high显然都指向1,经过下一步rec[i].data与rec[mid].data比较后,无论结果怎样,循环都应结束。可如果rec[i].data<rec[mid].data,就有high=rec[mid].pre,即high=1.此时显然有rec[low]<rec[high],也就是说循环还得接着经进行下去,问题就出在这里!说到这里,你应该明白:即使出现low为0,它也会违反第三条件:rec[low].data<=rec[high].data)(因为rec[0]的值域是最大的),这就是为什么说,第一个条件low!=0可以去掉。
到此,你应该明白了代码中所有的注释。

测试走起啊……

p.s 对rec数组1-n号元素进行重排也是可以的,做法参照上一篇博客哦,方法一模一样。

转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/28635157



目录:
[Java成长之路——总目录](http://blog.csdn.net/fei20121106/article/details/44301177)
[各查找和排序等算法一览](http://blog.csdn.net/fei20121106/article/details/44306475)

0 0