数据结构复习指导之插入排序

文章目录

排序

考纲内容

知识框架

复习提示

1.排序的基本概念

1.1排序的定义

2.插入排序

2.1直接插入排序

2.2折半插入排序

2.3希尔排序

知识回顾 


排序

考纲内容

(一)排序的基本概念
(二)插入排序
           直接插入排序;折半插入排序;希尔排序(shell sort)
(三)交换排序
           冒泡排序(bubble sort);快速排序
(四)选择排序
           简单选择排序;堆排序
(五)二路归并排序(merge sort)
(六)基数排序
(七)外部排序
(八)排序算法的分析和应用

知识框架

复习提示

堆排序、快速排序和归并排序是本章的重难点。读者应深入掌握各种排序算法的思想、排序过程(能动手模拟)和特征(初态的影响、复杂度、稳定性、适用性等),通常以选择题的形式考查不同算法之间的对比。此外,对于一些常用排序算法的关键代码,要达到熟练编写的程度:看到某特定序列,读者应具有选择最优排序算法(根据排序算法特征)的能力。

1.排序的基本概念

1.1排序的定义

排序,就是重新排列表中的元素,使表中的元素满足按关键字有序的过程。

为了查找方便,通常希望计算机中的表是按关键字有序的。排序的确切定义如下:

输入:n个记录 R₁ ,R₂,…,Rₙ,对应的关键字为k₁, k₂,…,kₙ

输出:输入序列的一个重排R₁' ,R₂',…,Rₙ',使得 k₁'≤k'₂≤…≤kₙ',(其中“≤”可以换成其他的比较大小的符号)。

算法的稳定性。若待排序表中有两个元素Rᵢ 和Rⱼ ,其对应的关键字相同,即 keyᵢ  =keyⱼ ,且在排序前 Rᵢ 在Rⱼ  的前面,若使用某一排序算法排序后,Rᵢ仍然在Rⱼ 的前面,则称这个排序算法是稳定的,否则称这个排序算法是不稳定的

需要注意的是,算法是否具有稳定性并不能衡量一个算法的优劣它主要是对算法的性质进行描述

若待排序表中的关键字不允许重复,排序结果是唯一的,则对于排序算法的选择,稳定与否无关紧要。

注意:对于不稳定的排序算法,只需举出一组关键字的实例,说明它的不稳定性即可

在排序过程中,根据数据元素是否完全存放在内存中,可将排序算法分为两类:

  • 内部排序,是指在排序期间元素全部存放在内存中的排序;
  • 外部排序,是指在排序期间元素无法全部同时存放在内存中,必须在排序的过程中根据要求不断地在内、外存之间移动的排序。

一般情况下,内部排序算法在执行过程中都要进行两种操作:比较和移动

通过比较两个关键字的大小,确定对应元素的前后关系,然后通过移动元素以达到有序。

当然,并非所有的内部排序算法都要基于比较操作,事实上,基数排序就不基于比较操作。

每种排序算法都有各自的优缺点,适合在不同的环境下使用,就其全面性能而言,很难提出一种被认为是最好的算法。

通常可以将排序算法分为插入排序、交换排序、选择排序、归并排序基数排序五大类,后面几节会分别进行详细介绍。

内部排序算法的性能取决于算法的时间复杂度和空间复杂度,而时间复杂度一般是由比较和移动的次数决定的。

注意:大多数的内部排序算法都更适用于顺序存储的线性表

2.插入排序

插入排序是一种简单直观的排序算法,其基本思想是每次将一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成。

由插入排序的思想可以引申出三个重要的排序算法:直接插入排序、折半插入排序和希尔排序

2.1直接插入排序

【在本章中,凡是没有特殊注明的,通常默认排序结果为非递减有序序列。】

根据上面的插入排序思想,不难得出一种最简单也最直观的直接插入排序算法。

假设在排序过程中,待排序表 L[1..n]在某次排序过程中的某一时刻状态如下:

要将元素 L(i) 插入已有序的子序列 L[1...i-1],需要执行以下操作(为避免混淆,下面用 L[ ] 表示一个表,而用 L() 表示一个元素):

  • 1)査找出 L(i) 在 L[1..i-1] 中的插入位置 k。
  • 2)将 L[k..i-1]中的所有元素依次后移一个位置。
  • 3)将 L(i) 复制到 L(k)。

为了实现对 L[1..n] 的排序,可以将 L(2)~L(n) 依次插入前面已排好序的子序列,初始 L[1]可以视为一个已排好序的子序列。

上述操作执行n-1次就能得到一个有序的表。

插入排序在实现上通常采用原地排序(空间复杂度为 O(1)),因而在从后往前的比较过程中,需要反复把已排序元素逐步向后挪位,为新元素提供插入空间。

下面是直接插入排序的代码,其中再次用到了前面提到的“哨兵”(作用相同)。

void InsertSort(ElemType A[],int n){int i j;for(i=2;i<=n;i++)        //依次将A[2]~A[n]插入前面已排序序列if(A[i]<A[i-1]){     //若A[i]关键码小于其前驱,将A[i]插入有序表A[0]=A[i];           //复制为哨兵,A[0]不存放元素for(j=i-1;A[0]<A[j];--j) //从后往前查找待插入位置A[j+1]=A[j];     //向后挪位A[j+1]=A[0];         //复制到插入位置}
}

假定初始序列为 49,38,65,97,76,13,27,\overline{49},初始时 49 可以视为一个已排好序的子序列,

按照上述算法进行直接插入排序的过程如图8.1所示,括号内是已排好序的子序列。

直接插入排序算法的性能分析如下:

空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)


时间效率:在排序过程中,向有序子表中逐个地插入元素的操作进行了n-1趟,

  • 每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。
  • 在最好情况下,表中元素已经有序,此时每插入一个元素,都只需比较一次而不用移动元素,因而时间复杂度为 O(n)。
  • 在最坏情况下,表中元素顺序刚好与排序结果中的元素顺序相反(逆序),总的比较次数达到最大,总的移动次数也达到最大,总的时间复杂度为O(n²)。
  • 平均情况下,考虑待排序表中元素是随机的,此时可以取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,总的比较次数与总的移动次数均约为n²/4。
  • 因此,直接插入排序算法的时间复杂度为 O(n²)

稳定性:因为每次插入元素时总是从后往前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序算法。


适用性直接插入排序适用于顺序存储和链式存储的线性表,采用链式存储时无须移动元素

2.2折半插入排序

从直接插入排序算法中,不难看出每趟插入的过程中都进行了两项工作:

①从前面的有序子表中查找出待插入元素应该被插入的位置;

②给插入位置腾出空间,将待插入元素复制到表中的插入位置。

注意到在该算法中,总是边比较边移动元素。

下面将比较和移动操作分离,即先折半查找出元素的待插入位置,然后统一地移动待插入位置之后的所有元素

当排序表为顺序表时,可以对直接插入排序算法做如下改进:因为是顺序存储的线性表,所以查找有序子表时可以用折半查找来实现。

确定待插入位置后,就可统一地向后移动元素。算法代码如下:

void InsertSort(ElemType A[],int n)int i j,low,high,mid;for(i=2;i<=n;i++){         //依次将A[2]~A[n]插入前面的已排序序列A[0]=A[i];             //将 A[i]暂存到 A[0]low=1;high=i-1;        //设置折半查找的范围while(low<=high){      //折半查找(默认递增有序)mid=(low+high)/2;  //取中间点if(A[mid]>A[0]) high=mid-1;   //查找左半子表    else low=mid+1;    //查找右半子表}for(j=i-1;j>=high+1;--j)A[j+1]=A[j];       //统一后移元素,空出插入位置A[high+1]=A[0];        //插入操作}
}

命题追踪——直接插入排序和折半插入排序的比较(2012)】

从上述算法中,不难看出折半插入排序仅减少了比较元素的次数,时间复杂度约为 O(nlog₂n),

该比较次数与待排序表的初始状态无关,仅取决于表中的元素个数n;

而元素的移动次数并未改变它依赖于待排序表的初始状态。

因此,折半插入排序的时间复杂度仍为 O(n²),但对于数据量不很大的排序表,折半插入排序往往能表现出很好的性能。

折半插入排序是一种稳定的排序算法。

折半插入排序仅适用于顺序存储的线性表

2.3希尔排序

从前面的分析可知,直接插入排序算法的时间复杂度为O(n²),但若待排序列为“正序”时,其时间效率可提高至 O(n),由此可见它更适用于基本有序的排序表和数据量不大的排序表

希尔排序正是基于这两点分析对直接插入排序进行改进而得来的,又称缩小增量排序

命题追踪——希尔排序中各子序列采用的排序算法(2015)】

希尔排序的基本思想是:先将待排序表分割成若干形如 L[i,i+d,i+ 2d.…,i+kd]的“特殊”子表,即把相隔某个“增量”的记录组成一个子表,对各个子表分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。

命题追踪——根据希尔排序的中间过程判断所采用的增量(2014、2018)】

希尔排序的过程如下

先取一个小于n的增量d₁,把表中的全部记录分成d₁组,所有距离为d₁的倍数的记录放在同一组,在各组内进行直接插入排序;

然后取第二个增量d₂ <d₁,重复上述过程,直到所取到的 dₜ=1,即所有记录已放在同一组中,再进行直接插入排序,由于此时已经具有较好的局部有序性,因此可以很快得到最终结果。

到目前为止,尚未求得一个最好的增量序列。

仍以2.1节的关键字为例,假定第一趟取增量d₁=5,将该序列分成5个子序列,即图中第2行至第6行,分别对各子序列进行直接插入排序,结果如第7行所示;

假定第二趟取增量 d₂=3,分别对三个子序列进行直接插入排序,结果如第11行所示;

最后对整个序列进行一趟直接插入排序,整个排序过程如图 8.2 所示。

希尔排序算法的代码如下:

void ShellSort(ElemType A[],int n){
//A[0]只是暂存单元,不是哨兵,当j<=0时,插入位置已到int dk,i,j;for(dk=n/2;dk>=1;dk=dk/2)        //增量变化(无统一规定)for(i=dk+1;i<=n;++i)if(A[i]<A[i-dk]){        //需将A[]插入有序增量子表A[0]=A[i];           //暂存在 A[0]for(j=i-dk;j>0 && A[0]<A[j];j-=dk)A[j+dk]=A[j];    //记录后移,查找插入的位置A[j+dk]=A[0];        //插入}
}

希尔排序算法的性能分析如下:

空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)


时间效率:因为希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上尚未解决的难题,所以其时间复杂度分析比较困难。

  • 当n在某个特定范围时,希尔排序的时间复杂度约为O(n^{1.3})
  • 在最坏情况下希尔排序的时间复杂度为O(n²)。

稳定性:当相同关键字的记录被划分到不同的子表时,可能会改变它们之间的相对次序,

因此希尔排序是一种不稳定的排序算法。例如,图82中49与49的相对次序已发生了变化。


适用性:希尔排序仅适用于顺序存储的线性表。

知识回顾 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/20003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程基础知识-09

ThreadLocal 是什么 ThreadLocal 是 Java 中的一个类&#xff0c;它提供了线程局部变量的功能。每个 ThreadLocal 对象都维护了一个线程私有的变量副本&#xff0c;不同线程之间无法访问彼此的副本。这样可以保证每个线程都能够独立地操作自己的变量副本&#xff0c;而不会发生…

内网不能访问域名怎么办?

在网络应用中&#xff0c;我们常常遇到内网不能访问域名的问题。这是由于内网环境限制导致的&#xff0c;内网无法直接连接到公网&#xff0c;因而无法访问互联网上的域名。我们可以利用一些特殊技术和工具来解决这个问题。 天联组网技术的应用 天联组网是一种非常受欢迎的解决…

Java实战项目开发的个人技巧分享

前言 笔者目前从事一线 Java 开发今年是第 3 个年头了&#xff0c;从 0-1的 SaaS、PaaS 的项目做过&#xff0c;多租户下定制化开发项目也做过&#xff0c;项目的 PM 也做过... 在实际的开发中积累了一些技巧和经验&#xff0c;包括线上 bug 处理、日常业务开发、团队开发规范…

NetApp财季报告亮点:全闪存阵列需求强劲,云计算收入增长放缓但AI领域前景乐观

在最新的财季报告中&#xff0c;NetApp的收入因全闪存阵列的强劲需求而显著增长。截至2024年4月26日的2024财年第四季度&#xff0c;NetApp的收入连续第三个季度上升&#xff0c;达到了16.7亿美元&#xff0c;较前一年同期增长6%&#xff0c;超出公司指导中值。净利润为2.91亿美…

前端开发:$nextTick()的使用及原理

目录 前言 $nextTick()的概念 $nextTick()的用法和原理 1、$nextTick()用法 2、$nextTick()原理 $nextTick()的具体使用示例 拓展&#xff1a;面试中考察$nextTick()的底层原理 最后 前言 在前端开发中&#xff0c;涉及到JS原生的使用原理是非常重要的知识点&#xff0…

Kafka 消息保留策略及其影响详解

目录 Kafka 消息保留策略及其影响详解消息保留策略超过保留时间的消息删除过程删除过程对正在消费的消息和偏移量的影响不同保留时间设置的区别总结 博客示例题目&#xff1a;详解 Kafka 消息保留策略及其影响消息保留策略超过保留时间的消息删除过程删除过程对正在消费的消息和…

使用pytorch搭建textCNN、BERT、transformer进行文本分类

首先展示数据处理后的类型&#xff1a; 第一列为文本&#xff0c;第二类为标注的标签&#xff0c;数据保存在xlsx的表格中&#xff0c;分为训练集和验证集。 textCNN 直接上整个工程代码&#xff1a; import pandas as pd import numpy as np import torch from torch.util…

springMVC返回对象或List集合时报错 无法解析

报错信息 &#xff1a;No converter found for return value of type: class java.util.ArrayList 原因&#xff1a;没有导入json 所以java在返回对象时解析错误 解决&#xff1a;1、添加所需的依赖&#xff1a;确保在项目的依赖管理中添加了适当的 JSON 库&#xff0c;如 J…

c++函数基础总结

在给出的代码片段中&#xff0c;我们看到两部分内容&#xff1a;一个类定义和一个全局函数声明。让我们逐一分析它们&#xff1a; 类定义&#xff1a; cpp复制代码 class { public: void a(); }; 这个类定义是不完整的&#xff0c;因为它没有类名。但为了说明&#xff0c;我…

Linux开发工具(个人使用)

Linux开发工具 1.Linux yum软件包管理器1.1Linux安装程序有三种方式1.2注意事项1.3如何查看&#xff0c;安装&#xff0c;卸载软件包1.3.1查看软件包1.3.2安装软件包1.3.3卸载软件 2.Linux vim编辑器2.1vim的基本操作2.2vim正常模式命令集2.3vim底行模式命令集2.4vim配置 3.Lin…

金色晚年的温暖旋律

​在小镇的东头&#xff0c;有一座带着些许岁月痕迹的二层小楼。这里住着一位刚刚退休的老人&#xff0c;李大爷。他的退休生活并不像人们想象的那样枯燥无味&#xff0c;反而充满了色彩和活力。 退休后的李大爷并没有选择整日坐在摇椅上晒太阳&#xff0c;而是找到了新的生活…

如何设置eclipse中web.xml 文件的地址

新学了一个项目 &#xff0c;项目结构与平常自己构建的web项目不同 &#xff0c;用eclipse打开之后&#xff0c;eclipse竟然自己创建了一个web.xml 而项目里面原本的web.xml 文件eclipse没有识别出来&#xff0c;导致后来浏览器访问任何路径都报错404 一、修改项目中web.xml的…

Centos7.9环境下安装Keepalived(亲测版)

目录 一、在线安装 二、离线安装 (1)、 下载 (2)、安装依赖包 (3)、解压文件 (4)、编译 (4.1)、进入 keepalived-2.2.8 目录中 (4.2)、安装Keepalived (5)、配置文件修改 (6)、启动 (7)、检查启动状态 (8)、 设置开机自启 (9)、配置从节点 (10)、启动从节点keepalived…

vue3中实现鼠标点击后出现点击特效

一、效果展示 图片下方为效果体验地址 缓若江海凝清光 二、代码 js中&#xff1a; <script setup lang"ts"> window.addEventListener("click", (e: MouseEvent) > {const pointer document.createElement("div");pointer.classLi…

Oracle索引的在线添加方法

在Oracle中&#xff0c;索引的在线添加方法主要是通过使用CREATE INDEX语句时添加ONLINE选项来实现的。以下是关于Oracle索引在线添加方法的详细步骤和要点&#xff1a; 了解在线添加索引的概念&#xff1a; 在线添加索引意味着在创建索引的过程中&#xff0c;其他数据库操作…

头歌数据结构与算法课程设计中-硬币找零

给定n种不同面值的硬币k_i和每种硬币的数量x_i以及一个总金额k,请编写一个程序计算最少需要几枚硬币凑出这个金额k,凑出的方案是什么? 如果凑不出则输出“凑不出” 输入描述: 第一行两个正整数,n和k 然后n行每行两个数k_i和x_i 表示k_i面值的硬币有x_i个,中间以空格分隔 输…

k8s处于pending状态的原因有哪些

k8s处于pending状态的原因 资源不足&#xff1a;集群中的资源&#xff08;如CPU、内存&#xff09;不足以满足Pod所需的资源请求&#xff0c;导致Pod无法调度。 调度器问题&#xff1a;调度器无法为Pod找到合适的节点进行调度&#xff0c;可能是由于节点资源不足或调度策略配置…

数模混合芯片之可靠性设计

一、可靠性设计目的 数模混合芯片设计之所以需要可靠性设计&#xff0c;主要原因有以下几点&#xff1a; 工艺与环境影响&#xff1a; 半导体制造工艺存在着不可避免的随机和系统性偏差&#xff0c;这可能导致芯片内部的模拟电路和数字电路参数发生变化&#xff0c;影响性能…

CobaltStrike基本渗透

目录 CobaltStrike简介 主要功能&#xff1a; 使用注意&#xff1a; 在使用CobaltStrike进行渗透测试时&#xff0c;务必遵守法律法规&#xff0c;并获得合法授权。 CobaltStrike安装 前提 安装 服务端安装 windows安装 CS基本使用 监听器配置 一些基本的攻击…

算法(十四)动态规划

算法概念 动态规划&#xff08;Dynamic Programming&#xff09;是一种分阶段求解的算法思想&#xff0c;通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推&#xff08;分治&#xff09;的方式去解决。动态规划中有三个重点概念&#xff…