数据结构第九周笔记——排序(上1)(慕课浙大版本--XiaoYu)

简介: 排序是很经典的算法

9.1 简单排序(冒泡、插入)

9.1.1 概述

voidX_Sort(ElementTypeA[],intN)//sort就是排序的意思,X是排序算法的名称

   //统一默认输入的参数有两个(一个是待排的元素放在一个数组里,数据类型为ElementType任意类型。另外一个是正整数N,表示的是我们要排的元素到底有多少个,默认讨论整数(从小到达)的排序)

   1.N是正整数

   2.只讨论基于比较的排序(>=<有定义)

   3.只讨论内部排序(假设我们内存空间足够大,所有数据可以一次性导入内存空间里,然后所有的排序是在内存里面一次性完成的)

   //外部排序(假设我有的内存空间有2GB,但是要求我们对10TB的数据进行排序,这个时候内部排序就不行了)

   4.稳定性:任意两个相等的数据,排序先后的相对位置不发生改变

   5.没有一种排序是任何情况下都表现最好

9.1.2 冒泡排序

voidBubble_Sort(ElementTypeA[],intN)

{

   for(i=0; i<P;P--){

       flag=0;//表示还没有执行果任何一次交换

       for(i=0;i<P;i++ ){//一趟冒泡

           if(A[i] >A[i+1] ){

               Swap(A[i],A[i+1]);

               flag=1;//要交换的时候标识变为1

           }

   }

       if( flag==0 ) break;//全程无交换

   }

}

最好情况:顺序T=O(N) 全程无交换

最坏情况:逆序T=O()

冒泡排序优点:

  1. 所有的待排元素是放在一个单向链表里的(冒泡排序可以对数组,对单项链表都可以实现,其他排序不好实现)
  2. 算法稳定

9.1.3 插入排序

voidInsertion_Sort( ElementTypeA[],intN )

{

   for( P=1;P<N; P++ ){

       Tmp=A[P];//摸下一张牌,Tmp为临时存放的位置

       for( i=P; i>0&&A[i-1] >Tmp;i--)//旧牌大

           A[i] =A[i-1];//移除空位

       A[i] =Tmp;//新牌落位

   }

}

//最好情况:顺序T = O(N)

//最坏情况:逆序T = O(N²)

插入排序好处:

  1. 程序短,简单
  2. 比冒泡排序好在:冒泡排序是两两交换,两两元素互换的时候他要涉及到第三步。而插入排序则是每个元素向后错,最后他一次性放到他那个空位里面去(不是插入排序最主要的)
  3. 稳定

给定初始序列{34, 8, 64, 51, 32, 21},冒泡排序和插入排序分别需要多少次元素交换才能完成?

冒泡9次,插入9次

对一组包含10个元素的非递减有序序列,采用插入排序排成非递增序列,其可能的比较次数和移动次数分别是?

45, 44

9.1.4 时间复杂度下界

网络异常,图片无法展示
|

问题:序列{34,8,64,51,32,21}中有多少逆序对?9对

网络异常,图片无法展示
|

逆序对的数量跟交换元素次数是一样的,也就说明了交换两个相邻元素正好消去一个逆序对!

插入排序:T(N,I) = O(N+I)

  1. 如果序列基本有序,则插入排序简单且非常高效

网络异常,图片无法展示
|

逆序对平均个数:大O(N2)数量级的,不管是冒泡排序还是插入排序,他们的平均时间复杂度是跟逆序对的个数有关系的

网络异常,图片无法展示
|

Ω:指的是下界

这意味着:要提高算法效率,我们必须

  1. 每次消去不止一个逆序对
  2. 每次交换相隔较远的2个元素,这样一次性就消掉了不止一个逆序对

9.2 希尔排序(by Donald Shell)

基本思路:利用了插入排序的简单,同时克服插入排序每次只交换相邻两个元素的缺点

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|

到最后使用1-间隔的排序来保证序列有序(彻底的插入排序)。但此时这个序列已经基本有序了,大多数的逆序对已经在前面两趟5-间隔和3-间隔里面被消除掉了

网络异常,图片无法展示
|

重要性质:3-间隔有序的序列还保持了前面5-间隔有序的这个性质(没有把上一步的结果变坏)

网络异常,图片无法展示
|

希尔增量序列

  1. 原始希尔排序
    网络异常,图片无法展示
    |

voidShell_Sort( ElementTypeA[],intN )

{

   for(D=M/2; D>0; D/=2 ){//希尔增量序列

       for( P=D; P<N; P++ ){//插入排序,D是距离(第0张牌在我手里,下一张牌从第D张牌开始摸)

           Tmp=A[P];

           for( i=P; i>=D&&A[i-D] >Tmp;i-=D )

               A[i] =A[i-D];

           A[i] =Tmp;

       }

   }

}

  1. 最坏情况:
    网络异常,图片无法展示
    |

    O是一个上界(可能达不到)
    网络异常,图片无法展示
    |

    增长速度跟N²一样快
    坏例子
    网络异常,图片无法展示
    |

    增量元素不互质,则小增量可能根本不起作用
    更多的增量序列
    网络异常,图片无法展示
    |

用Sedgewick增量序列

voidShellSort( ElementTypeA[], intN )

{ /* 希尔排序 - 用Sedgewick增量序列 */

    intSi, D, P, i;

    ElementTypeTmp;

    /* 这里只列出一小部分增量 */

    intSedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};

   

    for ( Si=0; Sedgewick[Si]>=N; Si++ )

        ; /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */

    for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] )

        for ( P=D; P<N; P++ ) { /* 插入排序*/

            Tmp=A[P];

            for ( i=P; i>=D&&A[i-D]>Tmp; i-=D )

                A[i] =A[i-D];

            A[i] =Tmp;

        }

}

目录
相关文章
|
8月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
292 29
|
9月前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
205 10
|
9月前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
150 10
|
9月前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
203 7
|
12月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
177 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
12月前
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
109 1
|
12月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
|
11月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
223 59
|
4月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
52 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。