1、折半排序实际是利用二分法确定待插入数据位置的排序算法,而二分法是一种将插入区间不断折半的方法,适用于数据量比较大的情况。


2、凭空说二分法确定位置可能比较抽象,特别是插入位置的确定,下面以一个例子来说明折半排序的过程:

(1)以一组数据{1, 3, 5, 8, 9, 4}为例,为了简单起见,待插入数据只有一个,即4;

(2)a[0] = 1,a[1] = 3,a[2] = 5,a[3] = 8,a[4] = 9,a[0] = 4;

(3)现在low = 0,high = 4;计算middle = (0 + 4) / 2 = 2;

(4)a[middle] = a[2] = 5;

(5)因为5大于4,所以high = middle - 1 = 2 - 1 = 1;

(6)现在low = 0,high = 1;计算middle = (0 + 1) / 2 = 0;

(7)a[middle] = a[0] = 1;

(8)因为1小于4,所以low = middle + 1 = 0 + 1 = 1;

(9)现在low = 1,high = 1;计算middle = (1 + 1) / 2 = 1;

(10)a[middle] = a[1] = 1;

(11)因为1小于4,所以low = middle + 1 = 1+ 1 = 2;

(12)现在low = 2,high = 1;计算middle = (2 + 1) / 2 = 1;

(13)显然,现在low比high大了,此时其实数据插入的位置已经确定了,那就是a[high + 1];


3、上面的内容可能比较多,下面是精简后的内容:

在将一个新元素插入有序数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high],求得middle = (low + high) / 2,比较时将待插入数据与a[middle]相比较,如果a[middle]比待插入数据大,则选择a[low]到a[middle - 1]为新的插入区域(即high = middle - 1),否则选择a[middle + 1]到a[high]为新的插入区域(即low = middle + 1),如此直到low <= high不成立,即将此位置(a[high])之后所有元素后移一位,并将新元素插入a[high + 1]。


4、下面是实现代码:


void        BinaryInsertionSort1(       int       * a,        int        n)      


       {      


              int        nLow;      


              int        nHigh;      


              int        nMiddle;      


              


              for        (       int        i = 1; i < n; ++i)      


              {      


              nLow = 0;      


              nHigh = i - 1;      


              


              // 求取插入位置      


              while        (nLow <= nHigh)      


              {      


              nMiddle = (nLow + nHigh) / 2;      


              if        (a[nMiddle] > a[i])      


              {      


              nHigh = nMiddle - 1;      


              }      


              else      


              {      


              nLow = nMiddle + 1;      


              }      


              }      


              


              // 插入      


              for        (       int        j = i - 1; j > nHigh; --j)      


              {      


              swap(a[j], a[j + 1]);      


              }      


              


              }      


       }


5、代码说明(虽然前面的文章都提过,但是还是说明以下):

(1)传入参数:整型数组的首地址 和 数组大小;

(2)swap宏函数的定义(实际上有多种定义方式,这只是其中一种):

#define swap(x, y) (x = (x) + (y), y = (x) - (y), x = (x) - (y))

(3)折半插入排序是稳定的。


6、事实上,由代码可以看出,折半插入排序与直接插入排序相比,仅仅是减少了比较的次数,但是交换的次数并未减少。


7、关于折半插入排序的稳定性,下面以一组数来说明:

(1)以一组数{1, 2, 3, 4, 4, 5, 4}为例,现在需要将最后一个数据4,插入到前面的有序数据中;

(2)按照折半插入法的思想,列表如下:




low = middle + 1

high = middle - 1

middle = (low + high) / 2

a[middle]

待插入数据4与a[middle]比较

0

5

2

3

4 >= 3 

3

5

4

4

4 >= 4

5

5

5

5

4 < 5

5

4

 

 

 



(3)由上表可知,数据4应插入a[high + 1]的位置,即a[5]的位置。



8、关于“哨兵”的使用,在上一篇文章《排序算法(三)直接插入排序》中已有详述,这里就不再介绍了,只贴出代码如下:

(1)循环赋值代替连续交换


void        BinaryInsertionSort2(       int       * a,        int        n)      


       {      


              int        nLow;      


              int        nHigh;      


              int        nMiddle;      


              int        nTemp;      


              


              for        (       int        i = 1; i < n; ++i)      


              {      


              nLow = 0;      


              nHigh = i - 1;      


              nTemp = a[i];      


              


              // 求取插入位置      


              while        (nLow <= nHigh)      


              {      


              nMiddle = (nLow + nHigh) / 2;      


              if        (a[nMiddle] > nTemp)          // 一般来说,读写数组中的元素速度通常都慢于访问一个简单变量,所以此处用nTemp代替a[i]      


              {      


              nHigh = nMiddle - 1;      


              }      


              else      


              {      


              nLow = nMiddle + 1;      


              }      


              }      


              


              // 插入      


              for        (       int        j = i - 1; j > nHigh; --j)      


              {      


              a[ j + 1] = a[j];      


              a[j] = nTemp;      


              }      


              


              }      


       }


(2)a[0]不含待排序数据


void        BinaryInsertionSort3(       int       * a,        int        n)      


       {      


              int        nLow;      


              int        nHigh;      


              int        nMiddle;      


              


              for        (       int        i = 1; i < n; ++i)      


              {      


              nLow = 0;      


              nHigh = i - 1;      


              a[0] = a[i];      


              


              // 求取插入位置      


              while        (nLow <= nHigh)      


              {      


              nMiddle = (nLow + nHigh) / 2;      


              if        (a[nMiddle] > a[i])      


              {      


              nHigh = nMiddle - 1;      


              }      


              else      


              {      


              nLow = nMiddle + 1;      


              }      


              }      


              


              // 插入      


              for        (       int        j = i - 1; j > nHigh; --j)      


              {      


              a[ j + 1] = a[j];      


              a[j] = a[0];      


              }      


              


              }      


       }