6.1排序——插入排序与希尔排序

2024-09-07 18:12
文章标签 排序 希尔 插入排序 6.1

本文主要是介绍6.1排序——插入排序与希尔排序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇博客来梳理两种常见排序算法:插入排序与希尔排序
常见的排序算法如图
常见的排序算法
写排序算法的原则:先写单趟,再写整体

一、直接插入排序

1.算法思想

先假定第一个数据有序,把第二个数据插入;再假设前两个数据有序,把第三个数据插入…以此类推,直到整个序列有序

2.具体操作(以排成升序为例)

(1)单趟:针对单个数据

假设[0,end]有序,处理end+1处数据(用tmp存起来,原因:挪数据的时候会覆盖),依次与前面的数比,用while循环控制

  • tmp更大:插入
  • tmp更小:挪数据,同时–end
    插入排序动图

程序结构如下

while(end>=0)
{if(tmp更小)//挪数据;--end;elsebreak;
}
//插入数据

注意:如果处理最小的数据,end会是-1(数据要插入到a[0]的位置)

在这里插入代码片

(2)单趟变整体:用for循环控制end从0->n-2

end最大是n-2的原因:要动end+1处数据,保证不越界

(3)具体代码实现

// 直接插入排序(假设排成升序)
void InsertSort(int* a, int n)
{for (int i = 0; i < n-1; i++){//单趟int end = i;//end最大是n-2int tmp = a[end + 1];//end+1最大是n-1while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];//往后挪数据--end;}else{break;}}a[end + 1] = tmp;//插入数据}
}

3.特性总结

(1)时间复杂度:o(N²)
(2)空间复杂度:o(1)
(3)稳定性:稳定
(4)元素集合越接近有序,算法效率越高

二、希尔排序(缩小增量排序)

1.算法思想

先对整个序列进行预排序,使数组接近有序,最后进行直接插入排序的时候数组已经很接近有序,因此可以大大提升算法的效率

2.具体操作(以排成升序为例)

(1)第一步:预排序(让数组接近有序)——本质:gap>1的插入排序

取gap==3,把整组数据分成了3组
①单趟:处理一次之后黄色分组边界上的数字(9,5,8,5)就有序了,相当于对9,5,8,5进行插入排序,只不过中间跨越了gap这么多数据

//以下两层循环实际上就是直接插入排序的变种,把1换成了gap
for (int j = 0; j < n - gap; j+=gap)
{//单趟int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + 1] = tmp;
}

对比一下插入排序的代码,就是把1换成了gap
②单趟变整体——处理红色和蓝色的组别(在外面再用一层for循环控制)

//以下两层循环实际上就是直接插入排序的变种,把1换成了gap
for(int i = 0;i < gap; i++)
{for (int j = 0; j < n - gap; j+=gap){//单趟int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + 1] = tmp;}
}

希尔排序1
希尔排序2
三层循环看起来太多了,有时候可能会不好分析,于是就可以对(1)(2)进行优化
上面代码的逻辑:先处理黄色组,再处理红色组,最后处理蓝色组,其中对红色组和蓝色组的处理需要在最外层套入第三层循环
优化操作:“每组并着走”,而不是像上面那样“一组一组来”,把最外层循环去掉,然后第二层循环中j+=gap改成j++,处理顺序变成:黄->红->蓝->黄->红->蓝…

		for (int j = 0; j < n - gap; j++)//j+=gap改成了j++{//单趟int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + 1] = tmp;}

(2)第二步:插入排序——gap==1的插入排序

注意:gap越大,大的就更快跳到后面,小的就更快跳到前面,但不那么有序
gap越小,跳得更慢,但更有序
所以:gap要不断变小,直到最后变成1,这样最终插入排序处理的序列就更有序——再来一层循环控制gap(此处对上面优化版的代码进行操作,否则四层循环看着都晕了)

常见调整方式:gap=gap/3+1(保证最后一个gap是1)
此处没有单独写插入排序的原因:gap最后会迭代成1,此时执行的就是直接插入排序
至此,完整的希尔排序代码就出来了

(3)具体代码实现

// 希尔排序(假设排成升序)
void ShellSort(int* a, int n)
{//预排序int gap = 3;while(gap > 1)//外面再来一层循环,实现gap的迭代{gap = gap / 3 + 1;//保证最后一个gap是1//以下两层循环实际上就是直接插入排序的变种,把1换成了gapfor (int j = 0; j < n - gap; j++){//单趟int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + 1] = tmp;}}
}

3.特性总结

(1)希尔排序是对直接插入排序的优化
(2)gap>1时是预排序,目的是让数组更接近有序。当gap==1的时候,数组已经接近有序了,此时进行直接插入排序就会很快,性能得到了优化
(3) 希尔排序时间复杂度计算难度大,限于本人水平,只能在这里写个结论,大约是o(N^1.3)
(4)稳定性:不稳定

这篇关于6.1排序——插入排序与希尔排序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145784

相关文章

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

鸡尾酒排序算法

目录 引言 一、概念 二、算法思想 三、图例解释 1.采用冒泡排序:   2.采用鸡尾酒排序:  3.对比总结 四、算法实现  1.代码实现  2.运行结果 3.代码解释   五、总结 引言 鸡尾酒排序(Cocktail Sort),也被称为双向冒泡排序,是一种改进的冒泡排序算法。它在冒泡排序的基础上进行了优化,通过双向遍历来减少排序时间。今天我们将学习如何在C

快速排序(java代码实现)

简介: 1.采用“分治”的思想,对于一组数据,选择一个基准元素,这里选择中间元素mid 2.通过第一轮扫描,比mid小的元素都在mid左边,比mid大的元素都在mid右边 3.然后使用递归排序这两部分,直到序列中所有数据均有序为止。 public class csdnTest {public static void main(String[] args){int[] arr = {3,

O(n)时间内对[0..n^-1]之间的n个数排序

题目 如何在O(n)时间内,对0到n^2-1之间的n个整数进行排序 思路 把整数转换为n进制再排序,每个数有两位,每位的取值范围是[0..n-1],再进行基数排序 代码 #include <iostream>#include <cmath>using namespace std;int n, radix, length_A, digit = 2;void Print(int *A,