基本排序算法 之三 ——插入排序

 


基本排序算法总结与对比 之三  ——插入排序 


1、直接插入排序

       直接插入排序过程如下图所示:

       ① 从第一个元素开始 仅一个3 为有序数列,紧跟有序数列后的 第一个元素 1 往有序数列里插入,形成包含 2 个元素 1, 3 的有序数列; 

       ② 紧跟有序数列后的 第一个元素 4 往有序数列里插入,形成包含 3 个元素 1, 3, 4 的有序数列; 

       ③ 紧跟有序数列后的 第一个元素 5 往有序数列里插入,形成包含 4 个元素 1, 3, 4, 5 的有序数列; 

       ④ 紧跟有序数列后的 第一个元素 2 往有序数列里插入,形成包含 5 个元素 1, 2, 3, 4, 5 的有序数列;    排序完成。

       显然,最耗时的过程为:插入元素后,插入点后边元素 往后移的过程。

        直接插入排序代码如下:

template<typename T>
void insertSort(T arr[], int lo, int hi)
{
    T tmp;
    for(int i = lo + 1; i < hi; i++)    //待插入的元素从lo+1开始,到hi-1停止;区间[i,hi)
    {
        tmp = arr[i];
        int j = i - 1;
        while(j >= lo && tmp < arr[j])    //遍历有序数组[lo,i),大于arr[i]的往后移
        {
            arr[j + 1] = arr[j];
            j--;
        }    //循环结束后,j 为 小于等于 arr[i] 的最大秩
             //       j + 1 为 大于     arr[i] 的最小秩
        arr[j + 1] = tmp;
    }
}

       下面是另一种写法,两种写法实现过程、原理都是一样的,二者取一即可。(下面这种写法的 移动元素的部分 与折半插入排序 的写法 相同。 )

template<typename T>
void insertSort(T arr[], int lo, int hi)
{
	T tmp;
	for (int i = lo + 1; i < hi; i++)    //待插入的元素从lo+1开始,到hi-1停止;区间[i,hi)
	{
		for (int j = lo; j < i; j++)    //遍历[lo,i)的有序数列
		{
			if (arr[i] < arr[j])    //如果在有序数列中发现第一个比arr[i]大的元素arr[j]则
			{
				tmp = arr[i];
				for (int k = i; k > j; k--)    //从arr[j]开始到arr[i-1]的每个元素往后挪一位
				{
					arr[k] = arr[k - 1];
				}
				arr[j] = tmp;    //再arr[i]插到arr[j]前面
			}
		}
	}
}

       以上代码所示的插入排序:

       在完全顺序时,比较次数为n(n-1)/2,交换次数为0,平均时间复杂度为O(n^2)

       在完全逆序时,比较次数为n-1,交换次数为n(n-1)/2,平均时间复杂度也为O(n^2)

       即使忽视比较所耗时间,算法的平均复杂度仍然为O(n^2)

 2、折半插入排序

       直接插入排序在寻找待排元素在有序数列中的位置时,使用的是线性查找方法;既然已知是有序数列,在查找待排元素位置时也可以使用二分查找方法。 使用二分查找的插入排序及折半插入排序。

template<typename T>
void insertSort(T arr[], int lo, int hi)
{
    T tmp;
    int _lo, _hi;

    for(int i = lo + 1; i < hi; i++)
    {
        /**
            此处 二分查找部分 可参考 邓俊辉 《数据结构》 p56
        */
        _lo = lo, _hi = i;    //在有序向量的 区间[lo, i) 内查找元素
        while(_lo < _hi)
        {
            int _mi = _lo + (_hi - _lo) >> 1;
            arr[i] < arr[_mi] ? _hi = _mi : _lo = _mi + 1; 
        }    //循环结束时 _lo 即为大于 arr[i] 的最小秩
             //查找失败标志 为 _lo = i (i 为右开区间,即 查找的数 比 有序数列 中 任何数 都大)

        if(_lo != i)
        {
            tmp = arr[i];
            for(int k = i; k > _lo; k--)
            {
                arr[k] = arr[k - 1];
            }
            arr[_lo] = tmp;
        }
    }
}

       需要指出的是,尽管使用了二分查找方法定位元素位置,但折半插入排序的时间复杂度仍然为O(n^2),交换操作的时间复杂度仍为O(n^2);但是,对于有些 比较操作 比较耗时的数据类型,适合采取折半插入排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值