代码改变世界

排序的之插入排序

2011-11-19 19:40  java线程例子  阅读(188)  评论(0编辑  收藏  举报
 
一、插入排序的主要分类
1、直接插入排序;
2、折半插入排序;
3、2-路插入排序;
4、希尔排序(缩小增量排序)。
二、直接插入排序(简便,容易实现):
1、排序过程:整个排序过程为n-1趟插入,即先将序列中第1个元素看成是一个有序子序列,然后从第2个元素开始,逐个进行插入,直至整个序列有序。
2、算法实现:
void InsertSort(int a[], int len)
{
	for (int i = 1; i < len; ++i)
	{
		int temp = a[i]; //先把将要插入的元素保存
		int j = i;
		while (j > 0 && temp < a[j - 1])//通过while循环找出应该插入的位置
		{
			a[j] = a[j - 1]; //后移
			--j; 
		}
		a[j] = temp;
	}
}


3、算法评价:
(1) 时间复杂度
——若待排序元素按关键字从小到大排列(正序)
关键字比较次数:n-1
元素移动次数:0
——若待排序元素按关键字从大到小排列(逆序)
关键字比较次数:n*(n-1)/2     注意:上面算法并没设置哨兵,若有设置,则比较次数应为(n+2)(n-1)/2
元素移动次数:n*(n-1)/2     注意:上面算法并没设置哨兵,若有设置,则移动次数应为(n+4)(n-1)/2
——若待排序元素是随机的,取平均值
关键字比较次数:(n+2)(n-1)/4
元素移动次数:n*(n-1)/4
故时间复杂度为:T(n)=O(n²)
(2)空间复杂度:S(n)=O(1)
4、适应范围:当序列中元素的个数n很少时。
三、折半插入排序(减少了元素的比较次数):
1、排序过程:用折半查找方法确定插入位置。
2、算法实现:
void binsort(int a[], int len)
{
	for (int i = 1; i < len; ++i)
	{
		int temp = a[i];
		int low = 0;
		int high = i - 1; //a[0]至a[i-1]的已经排序好了。
		//执行while循环(折半查找)找出a[i]应该插入的位置
		while (low <= high)
		{
			int middle = (low + high) / 2;
			if (temp < a[middle])
				high = middle - 1;
			else
				low = middle + 1;
		}
		//a[low]至a[i-1]的元素移动到a[low+1]至a[i];
		for (int j = i; j > low; --j)
		{
			a[j] = a[j - 1];
		}
		a[low] = temp;//a[low]放置插入的元素
	}
}
3、算法评价:
(1) 时间复杂度:
折半插入排序减少了关键字比较的次数,而元素的移动次数不变,故时间复杂度仍然为:T(n)=O(n²)
(2)空间复杂度:S(n)=O(1)
4、适应范围:当序列中元素的个数n很大时。

四、2-路插入排序(减少了元素的移动次数,但是增加了空间复杂度):
1、排序过程:引进一个空序列b。把原序列中的第一个元素a[0]看成是处于排好后的序列的中间。大于a[0]的元素放于a[0]值前的有序序列中,反之,放于其后。
如下图例子:
2、算法实现: 
void TowRsort(int a[], int len)
{
	int *b = new int[len];
	int current = len; //数组b后面序列中下标最小的一个
	b[0] = a[0];//在b数组中放置第一个元素
	int k = 0;
	for (int i = 1; i < len; ++i){
		if (a[i] >= b[0]){
			int k1 = k;
			//找出应该插入的位置
			while (a[i] < b[k1]){

				b[k1 + 1] = b[k1];
				--k1;
			}
			b[k1 + 1] = a[i];
			++k;
		} else{
			if (current == len)//放置第一个在数组最后
				b[--current] = a[i];
			else{
				int tempCurrent = current;
				//找出应该插入的位置
				while (b[tempCurrent] < a[i] && tempCurrent < len){
					b[tempCurrent - 1] = b[tempCurrent];
					++tempCurrent;
				}
				b[tempCurrent - 1] = a[i];
				--current;
			}
		}
	}
	int j = 0;
	for (int i = current; i < len; ++i, ++j){
		a[j] = b[i];
	}
	for (int i = 0; i < current; ++i, ++j){
		a[j] = b[i];
	}
}
3、算法评价:
(1) 时间复杂度:时间复杂度仍然为:T(n)=O(n²)。(然而元素的移动次数约n*n/8)
(2)空间复杂度:S(n)=O(n)。
五、希尔插入排序(缩小增量法)(减少了元素的比较次数):
1、排序过程:分割成若干个较小的子文件,对各个子文件分别进行直接插入排序,当文件达到基本有序时,再对整个文件进行一次直接插入排序。
如下图例子:
2、算法实现:
void shellSort(int a[],const int& len){//希尔排序
	int b[] = {5,3,1};//增量序列
	int count = sizeof(b)/sizeof(int);
	for(int i = 0; i < count; ++i){
		for(int j = 0; j < len - b[i]; ++j){
			if(a[j] > a[j+b[i]]){
				swap(a[j],a[j+b[i]]);//交换a[j]和a[j+b[i]]的值
				int temp = j;
				while(temp >= b[i]){//向前想间隔b[i]的比较
					if(a[temp] < a[temp-b[i]]){
						swap(a[temp],a[temp-b[i]]);//交换
					}
					else//只要有一个不满足,就跳出
						break;
					temp -= b[i];//向前退
				}
			}
		}
	}
}
3、算法评价:
(1) 时间复杂度:在时间效率上较上述几种排序方法有较大的提高。时间复杂度效率介于O(n)与O(n*n)之间。
(2)空间复杂度:S(n)=O(1)。
4、补充说明:希尔排序的分析一个复杂的问题,因为它的时间复杂度与所取的增量序列有关,涉及到了数学上一些尚未解决的问题。因此到目前都没有人求出最好的增量序列。但是,应该使增量序列中没有除1外的公因子,并且最后一个增量值必须为1。
 
本文若有错误或者不足之处,请您不吝指正。谢谢。