我们都知道插入法是在要插入的元素前面都已序的情况下进行比较—插入,直接插入法每个元素都有可能会经历多次交换才能找到它本应该待的位置。比如第100个元素,假如它应该待的位置为2,如果用直接插入法的话,就要比较99次,搬移98次。然而它前面的99个元素是已序的,显然有优化的空间,面对已序的序列我们最先想到的就是二分算法,我们可以先用二分法找到这个元素应该待的位置,然后一次性的将这个位置后面的元素后移,再将原来第100个元素放入这个位置,这就少了很多比较的开销。
折半插入法的比较次数与待排序序列的初始状态无关,仅与数量有关,它的比较次数为O(N*lgN),空间复杂度为O(N)而且这是一个稳定的算法。
下面是排序算法的实现:
#include "compare.hpp"
template <typename T>
static int BinaryFind(T *array, T key, int left, int right)
{
assert(NULL != array);
assert(left >= 0 && right >= 0 && left < right);
if(right - left == 1 && Less<T>()(key, array[left]))
return left;
while(left < right)
{
int mid = left + ((right-left)>>1);
if((mid == left && Less<T>()(key, array[mid]))
|| (Less<T>()(key, array[mid]) && Less<T>()(array[mid-1], key)))
return mid;
else if(Less<T>()(key, array[mid]))
right = mid;
else
left = mid+1;
}
return right;
}
template <typename T>
void BinaryInsert(T *array, const int size)
{
assert(NULL != array && size > 0);
if(size == 1)
return;
for(int i = 1; i < (int)size; i++)
{
T temp = array[i];
int place = BinaryFind(array, temp, 0, i);
if(place < i)
{
for(int j = i; j > place; j--)
array[j] = array[j-1];
array[place] = temp;
}
}
}
算法中用到的比较器实现:
template <typename T>
struct Less
{
bool operator()(T &left, T &right)const
{
return left < right;
}
};
template <typename T>
struct Greater
{
bool operator()(T &left, T &right)const
{
return left > right;
}
};
测试代码:
void test2()
{
int arr[100] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
srand((unsigned int)time(0));
for(int i = 0; i < sz; i++)
{
arr[i] = rand() % sz;
}
for(int i = 0; i < sz; i++)
cout << arr[i] << " ";
cout << endl << endl << endl << "sort:" << endl;
BinaryInsert(arr, sz);
for(int i = 0; i < sz; i++)
cout << arr[i] << " ";
cout << endl;
for(int i = 1; i < sz; i++)
{
if(Less<int>()(arr[i], arr[i-1]))
cout << "insert sort error! " << i << endl;
}
}
这里最后一个for循环是用于检测数值经过排序后是否已序,如排序失败则会打出insert sort error!字样,并输出排序出错的第一个数字。下面看排序结果:
排序结果正确。