C# 插入排序 | 直接插入排序 | 二分插入排序 | 希尔排序 的原理及代码实现

题外话:感冒了,鸽了几天,满血复活!


1.什么是插入排序?

  • 想象你在打扑克牌,现在正在摸牌阶段;
  • 你摸到了一张7,为了打牌更加舒适,我们通常会选择把手里的牌按一定顺序排序,比如从小到大;
  • 那么这张7,在排序后,需要插入到5和10之间;
  • 你不断的摸牌,不断的将新的牌,插入到手牌中,来保证正确的顺序;
  • 这个过程,就是插入排序

2.直接插入排序

  • 什么是直接插入排序?

    • 和上面的例子一样,拿一张,插一张
    • 而且,在找插入位置的时候,你选择一张一张牌进行大小比较
    • 这个流程,就是直接插入排序
  • 代码实现

        /// <summary>
        /// 直接插入排序
        /// </summary>
        /// <param name="pArray">索引从1开始有效的数组</param>
        static public void DirectSort(int[] pArray)
        {
            int i, j;
            for (i = 1; i < pArray.Length; i++)
            {
                //设置哨兵
                pArray[0] = pArray[i];
                //寻找插入点的同时,向后移动元素
                for (j = i - 1; pArray[j] > pArray[0]; j--)
                    pArray[j + 1] = pArray[j];
                //插入元素
                pArray[j + 1] = pArray[0];
            }
        }
    
  • 时间复杂度

    • 最好的情况:O(n)
    • 最差的情况:O(n^2)
    • 平均:O(n^2)
  • 特点:

    • 原始数据越有序,直接插排的性能越高,越接近O(n)
    • PS: 此处的“有序”,指的是和排序后的结果同向,比如说都是从小到大or都是从大到小。
  • 空间复杂度

    • O(1)
  • 算法稳定性

    • 稳定

3.二分插入排序

  • 什么是二分插入排序?

    • 和直接插排类似,都是一张牌一张牌插入到手牌中
    • 但是,在查找插入位置时,使用二分查找来确定插入位置
  • 代码实现

        /// <summary>
        /// 二分插排
        /// </summary>
        /// <param name="pArray">索引从1开始有效的数组</param>
        static public void BinarySort(int[] pArray)
        {
            int low, high, mid;
            for (int i = 1; i < pArray.Length; i++)
            {
                //设置哨兵
                pArray[0] = pArray[i];
                //二分查找双指针设置
                low = 1;
                high = i - 1;
                //二分查找
                while (low <= high)
                {
                    mid = (low + high) / 2;
                    if (pArray[mid] > pArray[0])
                    {
                        high--;
                    }
                    else
                    {
                        low++;
                    }
                }
                //逐位向后移动
                for (int j = i; j > high + 1; j--)
                {
                    pArray[j] = pArray[j - 1];
                }
                //high + 1是需要插入的位置
                pArray[high + 1] = pArray[0];
            }
        }
    
  • 时间复杂度

    • 最好的情况:O(n^2)
    • 最差的情况:O(n^2)
    • 虽然量级和直接插排一样,单就平均性能而言,要更加优秀。
  • 空间复杂度

    • O(1)
  • 算法稳定性

    • 稳定

4.希尔排序

  • 为什么有希尔排序?

    • 在“直接插排”中,我们讲到“原始数据越有序,性能越高”;
    • 顺着这个思路,“我们可不可以在排序前,让数据大致有序,从而提高性能呢?”
    • 诶!!希尔排序他来了,就是这个思路。
  • 希尔排序的思路

    • 我们设定一个步长,比如说5,间隔为5的元素之间,进行直接插排,比如说1,6,11三个元素排序,2,7,12三个元素排序,以此类推。
    • 我们再设定一个步长,3,间隔为3的元素之间进行插排
    • 最后,我们设定步长为1,就是普通的直接插排;
    • 得到最终结果。
  • 关于步长的设定

  • 代码实现

    	/// <summary>
        /// 希尔排序
        /// </summary>
        /// <param name="pArray">索引从1开始有效的数组</param>
        /// <param name="delta">互质递减序列</param>
        static public void ShellSort(int[] pArray, int[] delta)
        {
            //对互质递减序列中,每个step进行一次排序
            for (int i = 0; i < delta.Length; i++)
            {
                ShellInsert(pArray, delta[i]);
            }
        }
        static public void ShellInsert(int[] pArray, int step)
        {
            int i, j;
            //从step + 1开始,一个元素一个元素进行普通的直接插排
            //为什么从step + 1开始?从1-step的这step个元素,并不需要走一遍循环,因为它们自身所在的排序序列中,只有他们自己
            //而从step + 1开始,可以照顾到所有的元素会进行插排,且保证每个元素前面都至少有一个元素可以进行比较。
            for (i = step + 1; i < pArray.Length; i++)
            {
                //设置哨兵
                pArray[0] = pArray[i];
                //寻找插入点的同时,向后移动元素
                for (j = i - step; j > 0 && pArray[j] > pArray[0]; j = j - step)
                    pArray[j + step] = pArray[j];
                pArray[j + step] = pArray[0];
            }
        }
    }
    
  • 时间复杂度

    • 最好的情况:O(n)
    • 最差的情况:O(n^2)
    • 平均:O(n^1.3)
  • 空间复杂度

    • O(1)
  • 算法稳定性

    • 不稳定


结束语: 这些算法,看着挺麻烦,写完了之后发现还是蛮简单的,哈哈哈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值