考研数据结构chap8排序

目录

一、概念

1.评价

(1)稳定性

(2)Tn、Sn

2.分类

(1)内部排序

(2)外部排序

二、插入排序

1.直接插入排序(InsertSort)

(1)思路

(2)实现

(3)性能分析

1)Tn

2)Sn

3)稳定性

4)适用性

2.折半插入排序

(1)思路

(2)实现

(3)性能分析

1)Tn

best:O(n^2)

2)Sn

3)稳定性

4)适用性

3.希尔排序(ShellSort)(手算)

(1)背景

(2)思路

(3)实现

(4)性能分析

1)Tn

2)Sn

3)稳定性

4)适用性

三、交换排序

1.冒泡排序(BubbleSort)

(1)思路

(2)实现

(3)性能分析

1)Tn

2)Sn

3)稳定性

4)适用性

2.快速排序(QuickSort)

(1)思路

(2)实现

(3)性能分析

1)Tn

2)Sn

3)稳定性

4)适用性

(4)一次划分 vs 一趟排序

四、选择排序

1.简单选择排序

(1)思想

(2)实现

2.堆排序(以大根堆为eg)

(1)思想

(2)实现

五、归并排序(MergeSort)

1.基本概念

2.思路

3.实现

4.性能分析

(1)Tn

(2)Sn

(3)稳定性

六、基数排序(RadixSort)

1.手算

2.思路

(1)初始化

(2)分配

(3)收集

3.效率分析

(1)Tn

(2)Sn

(3)稳定性

4.application

(1)可以分成的类d较少

(2)每个位置的取值r范围较小

(3)总数n较大

七、外部排序

1.准备

2.思路

3.k路归并

4.Tn

5.优化

(1)多路归并

(2)败者树

1)background

2)说明

3)构建

(3)置换-选择排序(手算)

1)background

2)思想

3)构建

(4)最佳归并树

1)background

2)计算

3)构建(3路平衡归并树)

4)确定虚结点个数


一、概念

1.评价

(1)稳定性

排序前后相同元素的相对位置是否改变

(2)Tn、Sn

2.分类

(1)内部排序

关注算法的Sn、Tn

(2)外部排序

还关注硬盘的读写次数尽量少

二、插入排序

1.直接插入排序(InsertSort)

(1)思路

遍历i个eles,第一个ele当作有序,从第二个ele开始,依次与前面ele比较,if <,交换

(2)实现

//不使用哨兵
void InsertSort(SqList &L,int n){ElemType tmp;//记录待交换结点for (int i = 1; i < n; ++i) {tmp = L->data[i];int j;for ( j = i-1 ; j >=0 && L->data[j] > tmp; j--) {L->data[j+1] = L->data[j];}L->data[j] = tmp;}
}//有哨兵
void InsertSort2(SqList &L,int n){int i,j;for (i = 2; i < n; ++i) {if(L->data[i] < L->data[i-1]){L->data[0] = L->data[i];}for (j = i-1; L->data[0] < L->data[j]; --j) {L->data[j+1] = L->data[j];}L->data[j+1] = L->data[0];}L->len -=1;
}

(3)性能分析

1)Tn

对n个eles,so需要执行n-1次,主要Tn为找位置和交换,so Tn =  O(n-1 * n) ~ O(n)

best:全部有序,仅查找即可不用交换 soTn = O(n)

由此可知,在数组有序or大概有序时,直接插入排序效率较高

worst:全部逆序,查找n-1次,每次交换i-1次,so O(n^2)

avg:O(n^2)

2)Sn

O(1)

3)稳定性

当相等时设置不会交换,so稳定

4)适用性

线性表,顺序表和链表均可

2.折半插入排序

(1)思路

利用二分查找查找目标ele应该插入的pos,pos前面的ele前移

(2)实现

//定一个元素位置操作
//挖坑法
int partition(ElemType A[],int low,int high){ElemType pivot=A[low];//存分割值,最左边的元素//∵用pivot存储了定位的值,so可以直接覆盖,不交换了,effective//high 大 high--, 小 A[low] = A[high] low++ high--//间隔着换,low换完high换,high换完low换while(low<high){while(low<high && A[high]>=pivot){//找到比分割值小的元素high--;}A[low]=A[high];while(low<high && A[low] <= pivot){low++;}A[high] = A[low];}//找到位置了A[low] =pivot;return low;
}
void QuickSort(ElemType A[],int low,int high){//low最左边元素,high最右边元素if(low <high) {//至少2个元素//partition->O(n)分成两半while->O(logn)//O(n)O(logn)//最差O(n^2)// reason:有序的数组,递归n次,递归一次O(n)int pivot_pos = partition(A, low, high);//找定哪个位置//pivot:n.支点,中心点//之后再递归前一半QuickSort(A, low, pivot_pos - 1);//后一半QuickSort(A, pivot_pos + 1, high);}
}

(3)性能分析

1)Tn

折半查找仅在查找时做了优化,但是在交换时O(n),总共需要n-1轮,so Tn  = O(n^2)

best:O(n^2)

worst:O(n^2)

avg:O(n^2)

2)Sn

O(1)

3)稳定性

查找时可能会发生交换,so不稳定

4)适用性

二分查找适用于顺序表且有序的,so链式表不行

3.希尔排序(ShellSort)(手算)

(1)背景

因为直接插入排序在有序or大致有序时效率较高,so在希尔提出将数组分成若干了子数组,那么在各个子数组中就是大致有序的

(2)思路

依次减少增量d,以距离为d的子ele作为一个子列,在子列中进行插入排序

d一般为数组长度依次/2 ,直到1为止

(3)实现

颜色分组

(4)性能分析

1)Tn

当d直接为1时,直接退化为直接插入排序,O(n^2)

当d在某些范围内,可达到O(n^1.3)

2)Sn

O(1)

3)稳定性

分成不同组的时候可能会交换相对位置,so不稳定,上例可知

4)适用性

因为要求随机存取,so只适用于顺序表

三、交换排序

1.冒泡排序(BubbleSort)

(1)思路

从后->前 or 从前->后,两两交换,大的往后走,一轮可以定一个,so之后就不用比较了,if在一轮中无ele交换,则说明已经有序,排序停止(*考趟数)

(2)实现

void BubbleSort(SSTable A,int n){//排序往往用两层循环,优先写内层,之后写外层//内层循环控制比较交换,外层控制有序数的个数int i,j;bool flag;//添加哨兵,才能实现最高时间复杂度O(n)for (int i=0;i<n-1;i++) {flag = false;//一轮比较下来没有交换的,说明已经有序了//每一轮就定一个,因此j比较次数是动的,用外层控制for (int j = n-1; j > i ; j--) {if(A.elem[j] < A.elem[j-1]){swap(A.elem[j],A.elem[j-1]);flag = true;}}if(false == flag ){//未进行交换return;}}
}
void swap(int &a,int &b){int tmp;tmp = a;a = b;b =tmp;
}

(3)性能分析

1)Tn

best:都是有序的,只用进行n-1轮比较,O(n)

worst:都是逆序,比较n-1轮,每轮交换i-1次,O(n^2)

avg:O(n^2)

2)Sn

O(1)

3)稳定性

当相同时设置不交换,so稳定

4)适用性

顺序表和链表均可

2.快速排序(QuickSort)

(1)思路

以第一个ele作为枢轴,依次与后面eles进行比较,大的放右面,小的放左面,这样每一轮就可以确定一个ele,然后左右分治递归即可

(2)实现

void QuickSort(ElemType A[],int low,int high){//low最左边元素,high最右边元素if(low <high) {//至少2个元素//partition->O(n)分成两半while->O(logn)//O(n)O(logn)//最差O(n^2)// reason:有序的数组,递归n次,递归一次O(n)int pivot_pos = partition(A, low, high);//找定哪个位置//pivot:n.支点,中心点//之后再递归前一半QuickSort(A, low, pivot_pos - 1);//后一半QuickSort(A, pivot_pos + 1, high);}
}
//定一个元素位置操作
//挖坑法
int partition(ElemType A[],int low,int high){ElemType pivot=A[low];//存分割值,最左边的元素//∵用pivot存储了定位的值,so可以直接覆盖,不交换了,effective//high 大 high--, 小 A[low] = A[high] low++ high--//间隔着换,low换完high换,high换完low换while(low<high){while(low<high && A[high]>=pivot){//找到比分割值小的元素high--;}A[low]=A[high];while(low<high && A[low] <= pivot){low++;}A[high] = A[low];}//找到位置了A[low] =pivot;return low;
}

(3)性能分析

1)Tn

主要取决于递归的次数,相当于二叉树

best:每次枢轴可以将左右平分 O(nlogn)

worst:有序or大致有序 O(n^2)

avg: 整体效率接近 O(nlogn)

if每次次枢轴可以将左右平分,那么就可以提高效率

way1:取头部中间尾部,比较大小,取中间

way2:随机取

2)Sn

递归栈的层数(二叉树的高度)O(logn)

3)稳定性

不稳定

4)适用性

顺序表链表均可,但是顺序表效率高

(4)一次划分 vs 一趟排序

408要求一次划分只能定一个ele,一趟排序可以定多个,相当于QuickSort的一层

四、选择排序

每次选择一个定下来最终位置

1.简单选择排序

(1)思想

遍历arr,选择min与第一个交换

(2)实现

void SelectionSort(int A[], int n) {for (int i = 0; i < n - 1; ++i) {int min = i;for (int j = i + 1; j < n; ++j) {if (A[j] < A[min]) min = j;}if (min != i) swap(A[i], A[min]);}
}

2.堆排序(以大根堆为eg)

(1)思想

初始化堆,建立大根堆

选择根节点,之后重复建堆

(2)实现

void HeapAdjust(int A[],int k,int len){A[0] = A[k];for (int i = 2*k; i <=len ; i*=2) {if(i<len&&A[i]<A[i+1]) i++;if(A[0] >= A[i]) break;else{A[k] = A[i];k=i;}}A[k] = A[0];
}void BuildMaxHeap(int A[],int len){for (int i = len/2; i >0 ; i--) {HeapAdjust(A,i,len);}    
}void HeapSort(int A[],int len){BuildMaxHeap(A,len);for (int i = len; i >1 ; --i) {swap(A[i],A[1]);HeapAdjust(A,i,i-1);}
}

五、归并排序(MergeSort)

1.基本概念

归并:将k个子数列归并成一个子数列,同时变为升序

2.思路

归并排序一般是二路归并,即把两个arr变成一个arr

so先把A[]分成一个个子arr,然后依次进行merge即可

attn:合并时每组需要排序,此处省略

3.实现

int *B = (int *) malloc(7 * sizeof(int));//辅助空间void Merge(int A[], int low, int mid, int high) {int i, j, k;for (k = low; k <= high; ++k) {B[k] = A[k];//先把A中复制到B中}for (i = low, j = mid + 1, k = i; i <= mid && j <= high; ++k) {if (B[i] <= B[j])//排序,升序A[k] = B[i++];elseA[k] = B[j++];}//有一个子arr为nullwhile (i <= mid) A[k++] = B[i++];while (j <= high) A[k++] = B[j++];
}//总过程
void MergeSort(int A[], int low, int high) {if (low < high) {int mid = (low + high) / 2;//先递归只剩1个eleMergeSort(A, low, mid);MergeSort(A, mid + 1, high);//依次mergeMerge(A, low, mid, high);}
}

4.性能分析

(1)Tn

MergeSort可以视作倒的二叉树,so高度为logn

每次归并Tn= O(n)

so O(nlogn)

(2)Sn

一个辅助空间 O(n)

(3)稳定性

稳定,当子arr中有相同ele,要求先复制前面arr中ele

六、基数排序(RadixSort)

1.手算

eg:520、211、438、888、007、111、985、666、996、233、168

第一次分配收集

第二次分配收集

第三次分配收集

2.思路

(1)初始化

先确定分成几类(几趟),即d,之后根据每一类的最大最小值,确定r(辅助队列的范围)

(2)分配

根据每一位上数字大小分配到第几个队列上

(3)收集

从大到小排序

3.效率分析

(1)Tn

d趟O(d),一趟O(r+n) so O(d(n+r))

(2)Sn

辅助队列O(r)

(3)稳定性

稳定

4.application

三个条件同时满足

(1)可以分成的类d较少

(2)每个位置的取值r范围较小

(3)总数n较大

eg:10000个学生的年龄排序,分成年份、月份、日 

5个人的身份证号排序  x

10000个人的身份证号排序 √   d = 18 r = 10

七、外部排序

1.准备

因为数据量过大,so不能将所有数据放入内存,只能放在磁盘中

so排序前需要将data读入内存,排序之后写入磁盘

2.思路

读写+归并排序

step1:构建初始归并段,根据输入缓冲区的大小确定

step2:读入内存,进行归并排序

step3:写入磁盘

3.k路归并

eg:

step1:因为输入缓冲区只有两块,so只能放入前两块

第一轮之后:(仅以前两块为例)

再分归并段:(2块1段)

ATTn:当归并段第一块读空,需要先补充第二块,再归并排序

reason:防止乱序

第二轮之后:

以此类推最后全部有序

4.Tn

总花费时间= 读写时间+归并时间+排序时间

ATTn:读写占主要时间

5.优化

(1)多路归并

同时将多块数据读入内存,从而可以减少读写的次数

(2)败者树

1)background

if仅使用多路平衡归并,则排序占的时间增多,败者树用来提高排序的效率

2)说明

当第一次构建败者树的时候需要比较n-1次,之后比较仅需log2n up次(树高)

因为在排序之前需要初始归并段,用bn表示,so叶节点表示的不是数的大小,而是第几个归并段

3)构建

小的晋级,大的留下

解释:圆形结点表示是哪个归并段的data,线上的是晋级的,so下次比较仅需沿着path依次比较即可

(3)置换-选择排序(手算)

1)background

选择合适归并段的个数可以提高效率,置换选择排序是用来确定几个归并段

置换表示每次确定ele之后换新的,选择表示选择min

2)思想

输入缓冲区的大小是固定的,so将待排序data依次放入输入缓冲区中,依次选出最小的ele放入归并段,并记录该归并段最大数,当缓冲区中所有ele都小于该归并段的最大数,停止,之后进行下一个归并段的构建

3)构建

eg:4 6 9 7 13 11 16 14 10 22 30 2 3 19 20 17 1 23 5 36 12 18 21 39 输入缓冲区容量为3

此时输入缓冲区中的所有data都<Max,so第一个归并段确定

此时第二个归并段确定

最后确定

(4)最佳归并树

1)background

当经过置换-选择排序之后,归并段的长度不同,那么如何选择哪两个归并段进行归并排序的效率最高呢?即使用最佳归并树,本质就是k叉的哈夫曼树

2)计算

I/O次数=2*WPL

3)构建(3路平衡归并树)

eg: 2 3 6 9 12 17 24 18

WPL = (9+12+17+18+24)*2+(2+3+6)*3=193

if按正常步骤去构建,可知该树并非严格3叉树

so 需要添加一个为0的虚结点

WPL = 24*1+(6+9+12+17+18)*2+(2+3)*3=163

4)确定虚结点个数

已知待排序结点个数,严格k叉树度为k的结点数nk,度为0的结点数n0,总结点数n

so n = nk+n0

n = k*nk+1

nk = (n0-1) / (k-1)

so 要求可以整除

即   n0-1 % k-1 =0

if == 0 不需要虚节点

else if n0-1 % k-1 = u ,则需要 k-1-u个

reason:多了u个,so补到k-1个

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

四元数代数

书籍&#xff1a;Quaternion Algebras 作者&#xff1a;John Voight 出版&#xff1a;Springer 书籍下载-《四元数代数》这本教科书全面介绍了四元数代数和阶的算术理论&#xff0c;这一主题在数学的不同领域都有应用。这本书为研究生读者撰写&#xff0c;易于阅读和理解&am…

24华东杯A题9页完整思路+代码+可视化图表

​比赛题目的完整版思路可执行代码数据参考论文都会在第一时间更新上传的&#xff0c;大家可以参考我往期的资料&#xff0c;所有的资料数据以及到最后更新的参考论文都是一次付费后续免费的。注意&#xff1a;&#xff08;建议先下单占坑&#xff0c;因为随着后续我们更新资料…

[]2024年第⼗五届蓝桥杯全国软件和信息技术专业人才大赛(Web 应用开发)

一、爱拼才会赢&#xff08;5分&#xff09; 介绍 由爱拼社举办的拼图⼤赛进⾏到最后⼀关&#xff0c;1 号选⼿⼩蓝披荆斩棘成为全场⿊⻢。本关卡需要选⼿使⽤ CSS Grid 布局完成拼图⻚⾯&#xff0c;但是由于⼩蓝技术⽔平有限&#xff0c;拼图的效果没有达到预期。现在邀请你…

Flutter 弃用 WillPopScope 使用 PopScope 替代方法

Flutter 弃用 WillPopScope 使用 PopScope 替代方法 视频 https://youtu.be/u3qdqUvFWiM https://www.bilibili.com/video/BV1aJ4m1n7FZ 前言 原文 https://ducafecat.com/blog/migrating-from-willpopscope-to-popscope-in-flutter 了解如何在 Flutter 3.16 中将弃用的 Wil…

C++笔试练习笔记 【2】: 数字统计 BC153 两个数组的交集 NC313 点击消除 AB5

文章目录 数字统计分析题目代码部分 两个数组的交集题目分析代码部分 点击消除题目解析代码部分 数字统计 分析题目 这个题涉及到两个知识点&#xff0c;就是枚举和数字的拆分 那么我的思路是进行遍历&#xff0c;拆分数字判断二的个数&#xff0c;枚举进行计数 那么数字的拆分…

如何通过前后端交互的方式制作Excel报表

前言 Excel拥有在办公领域最广泛的受众群体&#xff0c;以其强大的数据处理和可视化功能&#xff0c;成了无可替代的工具。它不仅可以呈现数据清晰明了&#xff0c;还能进行数据分析、图表制作和数据透视等操作&#xff0c;为用户提供了全面的数据展示和分析能力。 今天小编就…

labview中TDMS读写波形图

TDMS与二进制读写速度区别不大&#xff0c;但是它具备关系型数据库的一些优点&#xff0c;经常用于存取波形数据。

操作系统的运行机制详解

操作系统的 运行机制 #mermaid-svg-jVBbLUJa6gITOo7L {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jVBbLUJa6gITOo7L .error-icon{fill:#552222;}#mermaid-svg-jVBbLUJa6gITOo7L .error-text{fill:#552222;stroke…

Spring实战项目【从0到1】:博客系统(上)

目录 1. 项目介绍2. 项目准备2.1 数据库准备2.2 创建项目2.3 配置文件2.4 准备前端页面2.5 测试 3. 项目公共模块3.1 实体类3.2 公共层 4. 业务代码4.1 持久层代码4.2 实现博客列表4.3 实现博客详情 1. 项目介绍 使用SSM框架&#xff08;Spring、Spring MVC、MyBatis框架)实现…

电脑技巧:轻松查看笔记本电脑电池的使用情况

目录 方法一&#xff1a;手工执行cmd命令 方法二&#xff1a;直接封装为Bat脚本 电池损耗程度介绍 Battery report字段中英文对照表 在大家日常办公和生活当中&#xff0c;笔记本电脑已成为非常重要工具。然而&#xff0c;随着笔记本电脑用的越久&#xff0c;电池的损耗难以…

创新指南|人工智能行为预测如何改变营销

在我们现在工作的人工智能营销新世界中&#xff0c;人工智能行为预测不仅作为一个流行词出现&#xff0c;而且作为一股革命力量&#xff0c;有望重新定义营销格局。 这种创新方法利用人工智能 (AI)的强大功能 来预测消费者行为&#xff0c;利用庞大而复杂的数据集来收集以前无法…

企业级数据治理学习总结

1. 水在前面 “数据治理”绝对是吹过的牛里面最高大上的题目了&#xff0c;本来想直接以《企业级数据治理》为题来水的&#xff0c;码字前又跑去图书馆借了几本书&#xff0c;翻了几页才发现自己连半桶水都提不起&#xff0c;撑死只能在小屁孩跟前吹吹牛。 好吧&#xff0c;实在…

【前端】-【防止接口重复请求】

文章目录 需求实现方案方案一方案二方案三 需求 对整个的项目都做一下接口防止重复请求的处理 实现方案 方案一 思路&#xff1a;通过使用axios拦截器&#xff0c;在请求拦截器中开启全屏Loading&#xff0c;然后在响应拦截器中将Loading关闭。 代码&#xff1a; 问题&…

详详详解动归数组常见习题(C/C++)

文章目录 最长递增数组序列&#xff08;必须连续&#xff09;dp[i] dp[i - 1] 1;最长递归子序列&#xff08;不需要连续&#xff09;dp[i] max(dp[i], dp[j] 1);俩层循环总结一维dp最长重复子数组最长公共子序列总结二维dp最终目标[3692. 最长连续公共子序列 - AcWing题库]…

【C++庖丁解牛】C++11---lambda表达式 | 包装器

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. lambda表达式1.1 C98中…

ip地址与硬件地址的区别是什么

在数字世界的浩瀚海洋中&#xff0c;每一台联网的设备都需要一个独特的标识来确保信息的准确传输。这些标识&#xff0c;我们通常称之为IP地址和硬件地址。虽然它们都是用来识别网络设备的&#xff0c;但各自扮演的角色和所处的层次却大相径庭。虎观代理小二将带您深入了解IP地…

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域&#xff0c;主成分分析&#xff08;PCA&#xff09;是一种广泛使用的技术&#xff0c;主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式&#xff0c;减少数据集的复杂性&#xff0c;同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

PID详解汇总

一、参照文章 PID的各种算法优缺点 二、位置式PID 优点:静态误差小,溢出的影响小。 缺点:计算量很大&#x

【PCL】教程 example2 3D点云之间的精确配准(FPFH特征对应关系估计变换矩阵)

这段代码主要实现了点云之间的配准功能&#xff0c;旨在通过估计点云的特征并找到最佳的对应关系来计算一个变换矩阵&#xff0c;从而可以将源点云&#xff08;src&#xff09;变换到目标点云&#xff08;tgt&#xff09;的坐标系统中。 代码功能和方法总结如下&#xff1a; 估…

上位机开发PyQt5(二)【单行输入框、多行输入框、按钮的信号和槽】

目录 一、单行输入框QLineEdit QLineEdit的方法&#xff1a; 二、多行输入框QTextEdit QTextEdit的方法 三、按钮QPushButton 四、按钮的信号与槽 信号与槽简介&#xff1a; 信号和槽绑定&#xff1a; 使用PyQt的槽函数 一、单行输入框QLineEdit QLineEdit控件可以输入…