数据结构之排序(5)

news2024/10/6 9:56:50

摘要:本文主要讲各种排序算法,注意它们的时间复杂度

概念

将各元素按关键字递增或递减排序顺序重新排列

评价指标

稳定性: 关键字相同的元素经过排序后相对顺序是否会改变

时间复杂度、空间复杂度

分类

内部排序——数据都在内存中

外部排序——数据太多,无法全部放入内存

一、插入排序

算法思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成

比27大的数往后挪

void InsertSort() {
	int i, j ,temp;
	for (i = 1;i <= n;i++)//将各元素插入已排好序的序列中
	{
		if (A[i] < A[i - 1]){//若A[i]关键字小于前驱
			temp = A[i];//用temp暂存A[i]
			for (j = i - 1;j >= 0 && A[j] > temp;--j)//检查所有前面已排好序的元素
			{
				A[j + 1] = A[j];//所有大于temp的元素都向后挪位
			}
			A[j + 1] = temp;//复制到插入位置
			}
	}
}
void InsertSort() {
    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];//复制到插入位置
        }
    }
}

优点:不用每轮循环判断j>=0

空间复杂度:O(1)

时间复杂度:主要来自对比关键字、移动元素 若n个元素,则需要n-1趟处理

最好时间复杂度(全部有序):O(n)

最坏(逆序):O(n^2)

平均O(n^2)

算法稳定性:稳定

优化——折半插入排序

当low>high时折半查找停止,应将[low,i-1]内的元素全部右移,并将A[0]复制到low所指位置

当A[mid]==A[0]时,为了保证算法的“稳定性”,应继续在mid 所指位置右边寻找插入位置

void InsertSort() {
    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];//插入操作
    }
}

二、希尔排序(Shell sort)

最好情况:原本就有序

比较好的情况:基本有序

希尔排序:先追求表中元素部分有序,再逐渐逼近全局有序

先将待排序表分割成若干形如L[i,i+d,i+2d,…,i+kd]的“特殊”子表,对各个子表分别进行直接插入排序。缩小增量d,重复上述过程,直到d=1为止

重点:给出增量序列,分析每一趟排序后的状态

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

三、交换排序

1、冒泡排序

从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。称这样过程为“一趟”冒泡排序。

void BubbleSort() {
    for (int i = 0;i < n - 1;i++) {
        bool flag = false;//表示本趟冒泡是否发生交换的标志
        for (int j=n;j > i;j--)//一趟冒泡过程
            if (A[j - 1] > A[j]) {//若为逆序
                swap(A[j - 1], A[j]);//交换
                flag = true;
            }
        if (flag == false)
            return;//本趟遍历后没有发生交换,说明表已经有序
    }
}

2、快速排序

算法思想:

在待排序表L[1,…n]中任取元素pivot作为枢轴(或基准,通常取首元素),

通过一趟排序将待排序表划分成独立的两部分L[1…k-1]和L[k+1…n], 使得L[1…k-1]中的所有元素小于pivot,L[k+1…n]大于等于,则pivot放在了其最终位置L(k)上,这个过程称为一次“划分”。

然后分别递归地对两个子表重复上述过程,直到每部分内只有一个元素或空为止,即所有元素放在了最终位置上

//用第一个元素将待排序序列划分为左右两个部分
int Partition( int low, int high) {
    int pivot = A[low];//第一个元素作为枢轴
    while (low < high) {//用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(int low, int high) {
    if (low < high) {//递归跳出的条件
        int pivotpos = Partition( low, high);//划分
        QuickSort( low, pivotpos - 1);//划分左子表
        QuickSort(pivotpos + 1, high);//划分右子表
    }
} 

算法表现主要取决于递归深度,若每次“划分”越均匀,则递归深度越低。“划分”越不均匀,递归深度越深

三、选择排序

每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列

1、简单选择排序

void SelectSort() {
    for (int i = 0;i < n - 1;i++) {//一共进行n-1趟
        int min = i;//记录最小元素位置
        for (int j = i + 1;j < n;j++)//在A[i…n-1]中选择最小元素
            if (A[j] < A[min]) min = j;//更新最小元素位置
        if (min != i) swap(A[i], A[min]);//封装的swap()函数共移动元素3次
    }
}

2、堆排序

若满足L(i)>=L(2i)且L(i)>=L(2i+1)(1<=i<=n/2)——大根堆(大顶堆)

思路:

把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整

顺序存储的完全二叉树中,非终端结点编号i<=[n/2]

检查当前结点是否满足根>=左、右

若不满足,将当前结点与更大的一个孩子互换

i的左孩子2i, 右孩子2i+1,父结点[i/2]

若元素互换破坏了下一级的堆,则采用相同的方法继续往下调整(小元素不断“下坠”)

(这个比较复杂,附视频:8.4_2_堆排序_哔哩哔哩_bilibili)

void BuildMaxHeap(int len) {
	for (int i = len / 2;i > 0;i--)//从后往前调整所有非终端结点,即非叶子结点
		HeadAdjust(i,len);
}
//将以k为根的子树调整为大根堆
void HeadAdjust(int k,int len) {
	A[0] = A[k];//A[0]暂存子树的根节点
	for (int i = 2*k;i <= len;i*=2) {//从左子树开始,沿key较大的子结点向下筛选
		if (i < len && A[i] < A[i + 1])//i < len 保证有右兄弟
			i++;//取key较大的子结点的下标
		if (A[0] >= A[i]) break;//筛选结束
		else {
			A[k] = A[i];//将A[i]调整到双亲结点上
			k = i;//修改k值,以便继续向下筛选
		}
	}
	A[k] = A[0];//被筛选结点的值放入最终位置
}

结论:一个结点,每“下坠”一层,最多只需对比关键字2次

若树高为h,某结点在第i层,则将这个结点向下调整最多只需要”下坠“h-i层,关键字对比次数不超过 2(h-i)

基于大根堆进行排序

//堆排序的完整逻辑
void HeapSort(int len) {
	BuildMaxHeap(len);//初始建堆
	for (int i = len;i > 1;i--) {//n-1趟的交换和建堆过程
		swap(A[i], A[1]);//堆顶元素和堆底元素互换
		HeadAdjust(1, i - 1);//把剩余的待排序元素整理成堆
	}
}

堆排序:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)

并将待排序序列中再次调整为大根堆(小元素不断:下坠)

得到“递增序列”

时间复杂度:O(n)+O(nlog2n)=O(nlog2n)

(O(n)建堆,O(nlog2n)排序)

稳定性:不稳定

堆的插入删除

插入:对于小根堆,新元素放到表尾,与父结点对比,若新元素比父结点更小,则将二者互换。新元素就这样一路“上升”,直到无法继续上升为止。

删除:被删除的元素用堆底元素替代,然后让该元素不断“下坠“,直到无法下坠为止

关键字对比次数

每次“上升”调整只需对比关键字1次

每次“下坠”调整可能需要对比关键字2次,也可能只需对比1次

四、基数排序

要求:得到按关键字“递减”的有序序列

基数排序不是基于“比较”的排序算法

第一趟:以“个位”进行“分配”

第一趟“收集”结束:得到按“个位”递减排序的序列

算法思想

将整个关键字拆分为d位(组)

按照各个关键字位权重递增的次序(如:个、十、百),做d趟“分配”和“收集”,若当前处理的关键字位可能取得r个值,则需要建立r个队列

分配:顺序扫描各个元素,根据当前处理的关键字位,将元素插入相应队列。一趟分配耗时O(n)

收集:把各个队列中的结点依次出队并链接。一趟收集耗时O(r)

性能

空间复杂度 O(r)

时间复杂度 O(d(n+r)) (一趟分配O(n),一趟收集O(r))

稳定

擅长处理

1、数据元素的关键字可用方便地拆分为d组,且d较小

2、每组关键字的取值范围不大,即r较小

3、数据元素个数n较大

五、归并排序

归并:把两个或多个已经有序的序列合并成一个

2路合并  k路合并

空间复杂度O(n)

时间复杂度O(nlogn)

稳定

//B是辅助数组
const int SIZE = sizeof(A) / sizeof(A[0]);
int B[SIZE];  

void Merge(int A[], int low, int mid, int high) {
    for (int k = low; k <= high; k++)
        B[k] = A[k]; 

    int i = low, j = mid + 1, k = i;
    while (i <= mid && j <= high) {
        if (B[i] <= B[j])
            A[k++] = B[i++];  
        else
            A[k++] = B[j++];
    }

    while (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;   
        MergeSort(A, low, mid);      
        MergeSort(A, mid + 1, high); 
        Merge(A, low, mid, high);    
    }
}

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

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

相关文章

涂色问题 乘法原理(2024CCPC 山东省赛 C)

//*下午打得脑子连着眼睛一起疼 很多很基础的题目都没有做出来&#xff0c;规律题也找得很慢。比如下面这题&#xff0c;一定要多做&#xff0c;下次看到就直接写。 原题链接&#xff1a;https://codeforces.com/group/w6iGs8kreW/contest/555584/problem/C C. Colorful Segm…

LabVIEW光偏振态检测系统

开发一套LabVIEW的高精度光偏振态检测系统&#xff0c;采用机械转动法结合光电探测器和高性能数据采集硬件&#xff0c;能快速、准确地测量光的偏振状态。该系统广泛应用于物理研究、激光技术和光学工业中。 系统组成 该光偏振态检测系统主要由以下硬件和软件模块构成&#xf…

无人机+无人车+机器狗+无人船:大规模组网系统技术详解

无人机、无人车、机器狗和无人船的大规模组网系统技术&#xff0c;是实现海陆空全空间无人设备协同作业的关键。这种组网系统技术通过集成先进的通信、控制、感知和决策技术&#xff0c;使得不同类型的无人平台能够高效、准确地完成各种复杂任务。以下是对该技术的详细解析&…

SysML案例-呼吸机

DDD领域驱动设计批评文集>> 《软件方法》强化自测题集>> 《软件方法》各章合集>> 图片示例摘自intercax.com&#xff0c;作者是Intercax公司总裁Dirk Zwemer博士。

【项目安全设计】软件系统安全设计规范和标准(doc原件)

1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 资料获取&#xff1a;私信或者进主页。…

将列表中的各字符串sn连接成为一个字符串s使用;将各sn间隔开os.pathsep.join()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 将列表中的各字符串sn 连接成为一个字符串s 使用;将各sn间隔开 os.pathsep.join() [太阳]选择题 下列说法中正确的是? import os paths ["/a", "/b/c", "/d&q…

Android开发修改为原生主题(在Android Studio开发环境下)

结构如下图&#xff1a; 修改方法&#xff1a;在Android模式目录下&#xff0c;将res下的values文下的themes.xml文件中的 &#xff1a; parent"Theme.Material3.DayNight.NoActionBar" 修改为&#xff1a; parent"Theme.MaterialComponents.DayNight.Bridge&…

Meta 推出Movie Gen

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

ChatGPT 更新 Canvas 深度测评:论文写作这样用它!

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 ChatGPT又又更新了&#xff1a;基于ChatGPT 4o模型的Canvas 写作和代码功能。目前&#xff0c;仅针对Plus和Team用户。是一个独立的模块&#xff0c;如下所示&#xff1a; 官方…

【Python】simplejson:Python 中的 JSON 编解码利器

simplejson 是一个高效且功能丰富的 Python JSON 编码和解码库。它能够快速地将 Python 数据结构转换为 JSON 格式&#xff08;序列化&#xff09;&#xff0c;或将 JSON 格式的字符串转换为 Python 对象&#xff08;反序列化&#xff09;。相比标准库中的 json 模块&#xff0…

数据结构实验二 顺序表的应用

数据结构实验二 顺序表的应用 一、实验目的 1、掌握建立顺序表的基本方法。 2、掌握顺序表的插入、删除算法的思想和实现&#xff0c;并能灵活运用 二、实验内容 用顺序表实现病历信息的管理与查询功能。具体要求如下: 1.利用教材中定义顺序表类型存储病人病历信息(病历号…

直立行走机器人技术概述

直立行走机器人技术作为现代机器人领域的重要分支&#xff0c;结合了机械工程、计算机科学、人工智能、传感技术和动态控制等领域的最新研究成果。随着技术的不断发展&#xff0c;直立行走机器人在救灾、医疗、家庭辅助等领域开始发挥重要作用。本文旨在对直立行走机器人的相关…

Java 注释新手教程一口气讲完!ヾ(≧▽≦*)o

Java 注释 Java面向对象设计 - Java注释 什么是注释&#xff1f; Java中的注释允许我们将元数据与程序元素相关联。 程序元素可以是包&#xff0c;类&#xff0c;接口&#xff0c;类的字段&#xff0c;局部变量&#xff0c;方法&#xff0c;方法的参数&#xff0c;枚举&…

【STM32开发之寄存器版】(五)-窗口看门狗WWDG

一、前言 窗口看门狗简介&#xff1a; 窗口看门狗通常被用来监测&#xff0c;由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在T6位变成0前被刷新&#xff0c;看门狗电路在达到预置的时间周期时&#xff0c;会产生一个M…

C语言 | Leetcode C语言题解之第459题重复的子字符串

题目&#xff1a; 题解&#xff1a; bool kmp(char* query, char* pattern) {int n strlen(query);int m strlen(pattern);int fail[m];memset(fail, -1, sizeof(fail));for (int i 1; i < m; i) {int j fail[i - 1];while (j ! -1 && pattern[j 1] ! pattern…

Pikachu-PHP反序列化

从后端代码可以看出&#xff0c;拿到序列化后的字符串&#xff0c;直接做反序列化&#xff1b;并且在前端做了展示&#xff1b; 如果虚拟化后的字符串&#xff0c;包含alert 内容&#xff0c;反序列化后&#xff0c;就会弹出窗口 O:1:"S":1:{s:4:"test";s…

佑航科技Pre-A+轮融资成功:加速车载超声波芯片研发与量产

近日,超声波芯片领域的领先企业珠海佑航科技有限公司(简称“佑航科技”)宣布成功完成数千万元的Pre-A+轮战略融资。本轮融资由上市公司思瑞浦微电子旗下的芯阳基金进行战略投资,标志着佑航科技在车载超声波芯片及传感器领域的研发与量产能力迈上了新台阶。此次融资不仅为佑…

《Linux从小白到高手》理论篇:深入理解Linux的网络管理

今天继续宅家&#xff0c;闲来无事接着写。本篇详细深入介绍Linux的网络管理。 如你所知&#xff0c;在Linux中一切皆文件。网卡在 Linux 操作系统中用 ethX,是由 0 开始的正整数&#xff0c;比如 eth0、eth1… ethX。而普通猫和ADSL 的接口是 pppX&#xff0c;比如 ppp0 等。 …

Golang | Leetcode Golang题解之第459题重复的子字符串

题目&#xff1a; 题解&#xff1a; func repeatedSubstringPattern(s string) bool {return kmp(s s, s) }func kmp(query, pattern string) bool {n, m : len(query), len(pattern)fail : make([]int, m)for i : 0; i < m; i {fail[i] -1}for i : 1; i < m; i {j : …

【Python游戏开发】贪吃蛇游戏demo

准备步骤 项目开发使用【Mu 编辑器】 1.新建项目&#xff0c;并导入游戏图片 游戏编写 1.创建场景 SIZE 15 # 每个格子的大小 WIDTH SIZE * 30 # 游戏场景总宽度 HEIGHT SIZE * 30 # 游戏场景总高度def draw():screen…