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、下面是实现代码:
|
5、代码说明(虽然前面的文章都提过,但是还是说明以下):
(1)传入参数:整型数组的首地址 和 数组大小;
(2)swap宏函数的定义(实际上有多种定义方式,这只是其中一种):
|
(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)循环赋值代替连续交换
|
(2)a[0]不含待排序数据
|