数据结构~17.插入类排序

插入类排序

本文是上一篇文章的后续,详情点击该链接~

简介

       在一个已经有序的序列中,插入一个新的元素到合适的位置中。我们就把它称之为插入类排序。这类排序比较常见的有直接插入排序、折半插入排序、希尔排序~


直接插入排序

执行思想

       就比如说吧,我现在有一个序列。他们分别是 28,12,63,96,25,55,42,39,27。现在这个序列中我就先只看28这一个数字。因为只有一个数字,所以它就肯定是有序的,那么现在我们再把第二个数字插入进来,而这第二个数字是12。我们将12和28进行比较,12比28小,那么我们就把12排到前面,28排到后面。这样一来,12和28这两个数字组成的序列就又变成有序的了。那么现在我们再把第三个数字也就是63插入进来,以此类推~

代码实现

void InsertSort(int arr[],int n) {
	int i, j,temp;
	for (i = 0; i < n; i++) {
		//将待插入的元素暂时存到temp里面
		temp = arr[i];
		j = i - 1;
		while (j >= 0 && temp < arr[j]) {
			arr[j + 1] = arr[j];
			j--;
		}
		//找到插入位置.将temp中暂存的待排关键字插入
		arr[j + 1] = temp;
	}
}

算法复杂度分析

时间复杂度

       考虑最坏的情况,当整个序列都是逆序的时候,内层循环中temp < arr[ j ] 这个条件是始终成立的。此时对于每一次外层循环,最内层循环的执行次数达到最大值,也就是 i 次。i 取值为 1 到 n - 1。那么基本操作的执行次数就是 n ( n - 1 ) / 2。所以它的时间复杂度就是 O( n² )

       考虑最好的情况,整个序列都是有序的,那么 temp < arr[ i ] 这个条件就一直不会成立。内层循环不执行,双层循环就变成了单层循环。时间复杂度就是 O( n ) 了。

       综上所述,这个算法的平均时间复杂度就是O( n² )

空间复杂度

       这个算法的辅助存储空间不会随着待排序列的规模变化而变化。是个常量,那么这个空间复杂度就是 O( 1 )

折半插入排序

执行思想

       折半插入排序和直接插入排序的思想比较类似,只是说插入位置的方法不同。折半插入排序其实就是采用折半查找的方法来查找插入位置的。折半查找法的一个基本条件也是这个序列井然有序。

       现在就来举个例子,按照前面直接插入排序的思想,那么现在我们有一个已经排好的序列,它们分别是: 13,28,36,55,66,77,98。那么这个时候,我要插入一个数字为33的元素怎么办? 其实也很简单。我们可以把现有的元素个数除以2。就比如说我们现在有七个元素,除以2就是3。这个时候第3个坐标元素是55,55大于33,那么这个时候我们就把从0到3的坐标再除以2,这个时候3/2得到坐标1,也就是28。元素28小于33。那么我们就把33排在28后面就可以了~

代码实现(折半查找排序)

void InsertSort(int arr[], int n) {
	int i, j, low, high, mid,tmp;
	for (i = 1; i < n; i++) {
		tmp = arr[i];
		low = 0;
		high = i - 1;
		//在arr[low,high]中二分查找有序插入的位置
		while (low <= high) {
			mid = (low + high) / 2;
			if (tmp < arr[mid]) {
				high = mid - 1;	//左半区
			}
			else {
				low = mid + 1;//	右半区
			}
		}
		//把记录后移
		for (j = i - 1; j >= high + 1; j--) {
			arr[j + 1] = arr[j];
		}
		arr[high + 1] = tmp;
	}
}

算法复杂度分析

时间复杂度

       折半插入排序适合元素比较多的场景,和直接插入排序相比,折半插入排序在查找插入位置上面所花的时间大大的减少。折半插入排序和直接插入排序移动的次数是一样的,所以说时间复杂度也都是一样的。最好的情况是O(n log 2 n),最坏的情况是 O ( n² )。平均算下来依然还是 O ( n² )

空间复杂度

       空间复杂度和直接插入排序一样~

希尔排序

介绍

       希尔排序也叫做缩小量排序,当然了,它的本质上还是插入排序。只不过是把待排序的序列按照某种规则分成几个子序列,分别对这几个子序列进行插入排序。这个规则的体现就是增量选取,如果增量是1那就直接插入排序。

       打个比方,我们先以增量5来分割序列,那么就将下标 0 、 5 、 10 、 15 ... 的关键字分成一组。把下标为 1 , 6, 11, 16 ... 的关键字分成另外一组。然后我们就分别对这些组进行插入排序。那么这个操作过程就是一趟希尔排序。

       然后再将刚刚排好序的整个序列以增量2分割,也就是把下标0,2,4,6,8...的关键字分成一组,再把下标1,3,5,7,9的关键字变成另一组等,然后分别对这些组进行直接插入排序,这就又完成了一趟希尔排序。最后以增量1分割整个序列,其实就是对整个序列进行一趟直接插入排序,从而完成整个希尔排序。

代码实现
//根据当前增量进行插入排序
void InsertSort(int array[], int n, int data)
{
    int i, j, temp;
    //分别向每组的有序区域插入
    for (i = data; i < n; i++)
    {
        temp = array[i];
        //比较与记录后移同时进行
        for (j = i - data; (j >= i % data) && array[j] > temp; j -= data)
            array[j + data] = array[j];
        if (j != i - data)
            array[j + data] = temp;//插入
    }
}
//希尔排序
void shellSort(int array[], int n, int t)
{
    void InsertSort(int array[], int n, int data);
    int i;
    for (i = 1; i <= t; i++) {
        InsertSort(array, n, (int)(pow(2, (t - i + 1)) - 1));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41424688/article/details/108268701