排序之插入排序

根据排序过程中涉及的存储器的不同,可将排序方法分为两大类:内部排序和外部排序。

  1. 内部排序:指待排序记录存放在计算机随机存储器中进行的排序过程
  2. 外部排序:待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
内部排序分类:

  1. 根据排序过程中依据的不同原则对内部排序方法进行分类,大致分为:插入排序、交换排序、选择排序、归并排序和计数排序等
  2. 根据内部排序过程中所需的工作量区分,可分为:简单的排序方法(时间复杂度:O(n2))、先进的排序方法(时间复杂度:O(nlogn))、基数排序(时间复杂度:O(d*n))
排序的过程中需要进行两种基本的操作:(1)比较关键字的大小;(2)将记录从一个位置移动到另一个位置。


插入排序

直接插入排序:

将一个记录插入到已排序的有序表中,从而得到一个新的、记录数增1的有序表。

空间上,需要增加一个存储空间,用于保存带排序的关键字

时间上,基本操作为比较两个关键字的大小和移动记录。最好的情况是待排序列为正序(关键字非递减有序排列),只需比较n-1次(即),不需要移动,最坏的情况是待排序列为逆序(关键字非递增有序排列),比较次数为(n+2)(n-1)/2(即),记录需要移动次数也最多,即(n+4)(n-1)/2(即),所以,直接插入排序的时间复杂度为O(n2)。

直接插入排序算法简单,容易实现,当排序记录的数量n很小时,这种排序方法很好,但是当n很大时,不宜采用此方法。
直接插入排序代码如下:
/// <summary>
        /// 直接插入排序(时间复杂度O(n2))
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        private int[] StraightInsertion(int[] arr)
        {
            for (int i = 1, j = arr.Length; i < j; i++)
            {
                int ing = arr[i];
                int m = i - 1;
                while (m >= 0 && arr[m] > ing)
                {
                    arr[m + 1] = arr[m];
                    arr[m] = ing;
                    m--;
                }
            }
            return arr;
        }

折半插入排序:

在进行关键字查找比较时,采用“折半查找”实现。排序过程中,所需附加存储空间和直接插入排序相同,只是折半插入排序减少了关键字间的比较次数,而移动次数不变。比较次数为O( nlog 2 n),且与原始数据的排列无关,移动次数为O(n 2),即时间复杂度为O(n 2)。
/// <summary>
        /// 折半插入排序(时间复杂度O(n2))
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        private static int[] BinaryInsertion(int[] arr)
        {
            for (int i = 1, j = arr.Length; i < j; i++)
            {
                int ing = arr[i];
                int low = 0, high = i - 1;
                while (low <= high)
                {
                    int m = (low + high) / 2;
                    if (ing < arr[m])
                    {
                        high = m - 1;
                    }
                    else
                    {
                        low = m + 1;
                    }
                }
                for (int n = i - 1; n >= high + 1; n--)
                {
                    arr[n + 1] = arr[n];
                }
                arr[high + 1] = ing;
            }
            return arr;
        }

2-路插入排序:

此排序是在折半插入排序基础上改进的,目的是减少排序过程中移动记录的次数,但需要n个记录的辅助空间。

private static int[] TwoWayInsertion(int[] arr)
        {
            int[] d = new int[arr.Length];//辅助数组
            int first = arr.Length, last = 1;//first:记录排序过程中得到的有序序列中的第一个记录,last:最后一个记录在d中的位置
            d[0] = arr[0];//把数据第0个付给辅助数组第0位,并将其看成是排好序的序列中处于中间位置的记录
            for (int i = 1, j = arr.Length; i < j; i++)//从第一位遍历数组
            {
                if (arr[i] > d[0])//如果关键字大,则需放在辅助数组d[0]的右侧
                {
                    d[last] = arr[i];//向d中添加关键字
                    int tlast = last;
                    while (tlast > 1 && d[tlast] < d[tlast - 1])//让d[1]-d[last]之间的关键字有序
                    {
                        d[tlast] = d[tlast - 1];//后移数组
                        d[tlast - 1] = arr[i];
                        tlast--;
                    }
                    last++;
                }
                else//关键字小,需放在d[0]的"左侧",即辅助数组的末尾,
                {
                    int tfirst = first;
                    first--;
                    d[first] = arr[i];
                    while (tfirst < d.Length && d[tfirst - 1] > d[tfirst])//让d[first]-d[d.length]之间的关键字有序
                    {
                        d[tfirst] = d[tfirst - 1];//后移数组
                        d[tfirst - 1] = arr[i];
                        tfirst++;
                    }
                }
            }
            return d;
        }

返回的结果如图(first指向1所在位置):



希尔排序:又称缩小增量排序

先将整个待排记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的关键字“基本有序”时,在对全体记录进行一次直接插入排序。其特点是子序列的构成不是简单的“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。但是希尔排序的分析是一个复杂的问题,因为它的时间是所取“增量”序列的函数。
/// <summary>
        /// 核心
        /// </summary>
        /// <param name="arr">待排数组</param>
        /// <param name="skep">增量</param>
        private static void ShellInsert(int[] arr, int skep)
        {
            for (int i = skep; i <arr.Length; i++)//去增量后数据比较
            {
                if (arr[i] < arr[i - skep])//跟对应增量的前一个关键字比较
                {
                    int init = arr[i];
                    for (int j = i - skep; j >= 0 && init < arr[j]; j -= skep)//对按增量所取的关键字进行排序
                    {
                        arr[j + skep] = arr[j];
                        arr[j] = init;
                    }                    
                }
            }
        }

        /// <summary>
        /// 希尔排序
        /// </summary>
        /// <param name="arr">待排数组</param>
        /// <param name="skeps">增量数组</param>
        /// <param name="t">增量数组长度</param>
        /// <returns></returns>
        private static int[] ShellSort(int[] arr, int[] skeps, int t)
        {
            for (int i = 0; i < t; i++)
            {
                ShellInsert(arr, skeps[i]);
            }
            return arr;
        }

增量序列可以可以有各种取法,但需注意,应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
选择排序插入排序都是常见的排序算法。 选择排序是一种简单直观的排序算法。它的基本思想是每一次从待排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾。具体的实现过程是,首先找到未排序部分的最小元素,然后将其与未排序部分的第一个元素交换位置,然后再从剩余的未排序部分中找到最小元素,依次类推,直到所有的元素都排序完成。 插入排序也是一种简单直观的排序算法。它的基本思想是将待排序的数据分成已排序和未排序两部分,初始时已排序部分只有一个元素。然后依次将未排序部分的元素插入到已排序部分的合适位置,直到所有的元素都插入完成。具体的实现过程是,将待插入的元素与已排序部分的元素逐个比较并移动,直到找到合适的位置插入。 选择排序的代码可以参考引用中的直接选择排序代码示例,其中使用了两个for循环来实现选择排序的过程。 插入排序的代码可以参考引用中的选择排序代码示例,其中同样使用了两个for循环来实现插入排序的过程。 需要注意的是,选择排序插入排序都是简单的排序算法,对于小规模的数据集来说效果良好,但对于大规模数据集来说,其时间复杂度较高,效率较低。在实际应用中,可以考虑使用更高效的排序算法,如快速排序、归并排序等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【数据结构】常见排序插入排序与选择排序](https://blog.csdn.net/vpurple_/article/details/126568614)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++ 冒泡排序/选择排序/插入排序/快速排序/希尔排序](https://blog.csdn.net/jolin678/article/details/128269096)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值