【数据结构】手撕排序NO.1

在这里插入图片描述
🔥博客主页 小羊失眠啦.
🎥系列专栏《C语言》 《数据结构》 《Linux》《Cpolar》
❤️感谢大家点赞👍收藏⭐评论✍️


在这里插入图片描述

文章目录

  • 一、排序的概念及其运用
    • 1.1 排序的概念
    • 1.2 常见的算法排序
  • 二、 冒泡排序
  • 三、直接插入排序
  • 四、希尔排序
  • 五、 选择排序

一、排序的概念及其运用

1.1 排序的概念

排序:所谓排序就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

1.2 常见的算法排序

排序算法分为比较类排序非比较类排序,如下图所示:

在这里插入图片描述


二、 冒泡排序

  • 思想 : 在一个序列中,每次对序列中的相邻记录的键值大小进行比较,将较大(升序)或较小(降序)的记录向后移动。如此循环,大/小的记录会慢慢“”到序列的后端,整个过程就像是冒泡一样,顾称之为冒泡排序。
  • 冒泡过程:以下是对某个序列进行冒泡排序的过程

在这里插入图片描述

可以看出,对于上面具有5个元素的无序数组,我们通过4趟的冒泡后就将其变为有序数组,每一趟冒泡后都可以使最大的数沉底。

  • 动图演示:我们可以通过一下动图感受一下冒泡两两比较的过程:

img

  • 循环控制:很明显,我们需要两层循环来控制冒泡排序的过程。内层循环控制当前趟的数据交换,外层循环控制冒泡排序的趟数
  • 外层循环结束条件:由于每一趟结束后都有一个数冒到序列后端,因此对于N个数的序列来说,一共需要N-1趟(只剩一个数不需要冒泡)。
for (int i = 0; i < n - 1; i++) //外层循环,N-1趟
{
     ; 
}
  • 内层循环结束条件:内层循环用于数据的比较。已知N个数据共需比较N-1次,由于每一趟结束后就有数据到正确的位置,下一趟需要比较的数据个数就会少1,因此每趟的比较次数随着趟数的增加呈递减趋势,初始为N-1次
for (int i = 0; i < n - 1; i++) //外层循环,N-1趟
{
	for (int j = 0; j < n - 1 - i; j++) //内层循环,次数随趟数增加而递减,初始为N-1
	{
           ;
	}
}
  • 完整代码:
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void BubblingSort(int* a, int n)
{

	for (int i = 0; i < n - 1; i++) //外层循环,N-1趟
	{
		for (int j = 0; j < n - 1 - i; j++) //内层循环,次数随趟数增加而递减,初始为N-1
		{
			if (a[j] > a[j + 1]) //升序排列,较大的往后移
			{
				swap(&a[j], &a[j + 1]); //交换
			}
		}
	}
}
  • 改进优化:上面的代码还存在着改进空间,我们来看下面两个情景:

在这里插入图片描述

对于情境1,我们只需一趟冒泡即可让数组有序,而如果按照上面的代码,我们依旧要进行4趟的冒泡,即有三趟是无效的。

情境2就更夸张了,数组已经有序,我们却傻乎乎的做了4趟无效冒泡。无疑是非常浪费时间的。


考虑到这些情况,我们提出了优化方案在每趟结束后判断一下当前趟是否发生了元素交换,如果没有,则说明序列已经有序了,及时止损,反之继续。优化后的代码如下:

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void BubblingSort(int* a, int n)
{

	for (int i = 0; i < n - 1; i++) //外层循环,N-1趟
	{
		int flag = 0;
		for (int j = 0; j < n - 1 - i; j++) //内层循环,次数随趟数增加而递减,初始为N-1
		{
			if (a[j] > a[j + 1]) //升序排列,较大的往后移
			{
				swap(&a[j], &a[j + 1]); //交换
				flag = 1;
			}
		}
		if (flag == 0)
			break;
	}
}
  • 时间/空间复杂度:结合上面的图片和代码我们可以看出,总共N-1趟,每趟N-1,N-2…次比较,共比较 (N-1) + (N-2) + (N-3) + (N-4) + (N-5) + … + 1次,时间复杂度为O(N^2);而由于没有额外的辅助空间,空间复杂度为O(1)。
  • 稳定性分析:由于我们是将较大的或较小的进行交换,当两个数相等时并不会进行交换,因而不会改变相同元素的先后次序,所以冒泡排序是稳定的排序。

三、直接插入排序

  • 思想:把待排序的数据按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的数据插入完为止,得到一个新的有序序列 。 实际中我们玩扑克牌时,就用了插入排序的思想。

img

注意:数组中除了第一个元素(默认有序),其余所有元素都看作待插入数据。

  • 排序步骤
  1. 将有序数据的最后一个元素的下标记为 end,则第一个待插入元素的下标为 end+1,记作 tmp
  2. 将 tmp 与有序数据从后向前依次比较
  3. 如果 tmp < a[end],就将 a[end] 向后移动,end–,再去找下一位进行比较
  4. 直到 tmp > a[end] 或者 end < 0,将 tmp 插入到 end+1 的位置
  5. 重复步骤,就可以实现排序
  • 代码实现
void InsertSort(int* a, int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int end = i;
        int tmp = a[end + 1];
        while (end >= 0)
        {
            if (tmp < a[end])
            {
                a[end + 1] = a[end];
            }
            else
            {
                break;
            }
            --end;
        }
        a[end + 1] = tmp;
    }
}

在这里插入图片描述

  • 时间/空间复杂度:结合上面的图片和代码我们可以看出时间复杂度为O(N^2);由于没有额外的辅助空间,空间复杂度为O(1)。
  • 稳定性分析:是稳定的排序。

四、希尔排序

  • 思想

    先选定一个整数,把待排序文件中所有记录分成个组,所有距离为3的数据分在同一组内,并对每一组内的数据进行排序。然后,重复上述分组和排序的工作。当距离=1时,所有数据在统一组内排好序。

    希尔排序分为两步:

    1. 预排序
    2. 直接插入排序

    当元素集合越接近有序,直接插入排序的效率很高,希尔排序就是通过预排序使元素集合接近有序,再进行直接插入排序,这样大大提高了效率。

img

  • 排序步骤:
  1. 选取一个合适的 gap 作为间距
  2. 将间距为 gap 的数据分为一组,分成 gap 组
  3. 每一组数据都进行直接插入排序,使数据接近有序
  4. 不断缩小间距,当 gap==1 时,数据进行直接插入排序,实现排序
  • 代码实现
void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1)
    {
        gap = gap / 3 + 1;
        for (int i = 0; i < n - gap; i++)
        {
            int end = i;
            int tmp = a[end + gap];
            while (end >= 0)
            {
                if (tmp < a[end])
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }
}
  • 特点:
    1. 希尔排序是对直接插入排序的优化。
    2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
    3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。
    4. 空间复杂度:O(1)
    5. 稳定性:不稳定

五、 选择排序

  • 思想 : 每一次从待排序的数据元素中选出最小(升序)或最大(降序)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
  • **选择过程:**以下是对某个序列进行选择排序的过程:

在这里插入图片描述

  • 动图演示:我们一样通过动图感受一下选择排序的过程:

img

  • 循环控制:类似的,我们需要两层循环来控制选择排序的过程。内层循环遍历序列找出最大/最小值,外层循环控制选择的次数
  • 外层循环结束条件:每一次遍历完都可以选出一个数换到起始位置,一共N个数,故要选N-1次(最后一个数不需要选择)
for (int i = 0; i < n-1; i++) //外层循环,共要选择n-1次
{
      ;
}
  • 内层循环结束条件:内层循环通过比较进行选数,一开始N个数需要比较N-1次,然后每趟结束后下一次选择的起始位置就往后移动一位,比较次数减1
for (int i = 0; i < n-1; i++) //外层循环,共选择n-1次
{
	for (int j = i + 1; j < n; j++) //内层循环,起始位置开始向后进行比较,选最小值
	{
           ;
	}
}
  • 完整代码:
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}


void SelectSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++) //外层循环,共选择n-1次
	{
		int mini = i; //记录最小值的下标,初始为第一个数下标
		for (int j = i + 1; j < n; j++) //内层循环,起始位置开始向后进行比较,选最小值
		{
			if (a[mini] > a[j]) //比最小值小,交换下标
			{
				mini = j;
			}
		}
		swap(&a[mini], &a[i]); //将最小值与起始位置的数据互换
	}
}
  • 时间/空间复杂度:一共选了N-1次,每次选择需要比较N-1,N-2,N-3…次,加起来和冒泡一样时间复杂度为O(N);没有用到辅助空间,空间复杂度为O(1)
  • 稳定性分析:由于是选数交换,在交换的过程中很可能会打乱相同元素的顺序,例如下面这个例子:

在这里插入图片描述

我们发现,在第一趟交换中,黑5被交换到了红5后面,在整个排序结束后,黑5依然在红5的后方,与最开始的顺序不一致。由此我们可以得出,选择排序是不稳定的排序。

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,小羊一定认真修改,写出更好的文章~~

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【注册表】Sublime Text添加到右键菜单

官网下载 windows下地地址: http://www.sublimetext.com/download_thanks?targetwin-x64设置右键菜单和菜单小图标 win R打开运行&#xff0c;并输入regedit打开注册表编辑器依次找到HKEY_CLASSESS_ROOT -> * -> Shell&#xff0c;下面新建项&#xff0c; 这个项的名…

HTML5+CSS3+Vue小实例:浪漫的心形文字动画特效

实例:浪漫的心形文字动画特效 技术栈:HTML+CSS+Vue 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conte…

HTML5+CSS3+Vue小实例:饮料瓶造型文字旋转特效

实例:饮料瓶造型文字旋转特效 技术栈:HTML+CSS+Vue 效果: 源码: 【HTML】【JS】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" …

协同过滤算法:个性化推荐的艺术与科学

目录 引言&#xff1a; 一、协同过滤算法的基本原理 二、协同过滤算法的应用领域 三、协同过滤算法的优缺点 四、协同过滤算法的未来发展方向 五、结论 引言&#xff1a; 在当今数字化时代&#xff0c;信息过载成为了一个普遍的问题。为了帮助人们更好地发现符合个性化需…

用户枚举CSRF漏洞

一、XSS漏洞 在商城的搜索处&#xff0c;输入标准语句的传参直接就可以弹窗 二、逻辑漏洞-用户枚举 在用户注册界面&#xff0c;点击发送验证码&#xff0c;然后用BURP发包 更改手机号传参&#xff0c;这里手机号传参没有进行加密&#xff0c;直接用手机号的位置进行爆破 正确的…

光伏设计方案中最重要的是什么?

随着人们对可再生能源的关注度不断提高&#xff0c;光伏发电成为了越来越受欢迎的选择。然而&#xff0c;在设计和实施光伏项目时&#xff0c;有很多因素需要考虑。那么&#xff0c;在光伏设计方案中&#xff0c;最重要的是什么呢&#xff1f; 地理位置和环境&#xff1a;选择合…

如何在Linux上搭建本地Docker Registry镜像仓库并实现公网访问

Linux 本地 Docker Registry本地镜像仓库远程连接 文章目录 Linux 本地 Docker Registry本地镜像仓库远程连接1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址…

《消息队列MyMQ》——参考RabbitMQ实现

一、什么是消息队列&#xff1f; 消息队列是一种用于在应用程序之间或不同组件之间进行异步通信的软件架构模式。它允许发送方&#xff08;生产者&#xff09;将消息发送到队列中&#xff0c;而接收方&#xff08;消费者&#xff09;可以从队列中获取消息并进行处理。 消息队列…

leecode | 从二叉搜索树到更大和树

官方的题目解释永远晦涩难懂 这就是最大的拦路虎 简单介绍&#xff0c;将二叉搜索树&#xff0c;转换成“更大和树”&#xff0c;“最大的和树”&#xff0c;就是更新节点val&#xff0c;二叉树中所有大于等于该节点的的val 总和&#xff0c;包括本身 #对着图看&#xff0c;会更…

【ARM Trace32(劳特巴赫) 使用介绍 12 -- Trace32 常用命令之 d.dump | data.dump 介绍】

文章目录 Trace32 常用命令之 d.dump | data.dump 介绍1 字节显示 (Byte)4 字节显示&#xff08;word&#xff09;8 字节显示&#xff08;通常long&#xff09;十进制显示显示指定列数显示地址范围内的值 Trace32 常用命令之 d.dump | data.dump 介绍 在 TRACE32 调试环境中&a…

这是我见过最好用的销售预测模型!附完整解析

以上&#xff0c;摘自网络&#xff0c;属于给了碗汤但没给勺的那种~   下面&#xff0c;简单聊聊“勺”的问题~   有个段子这么说&#xff1a;“掐指一算&#xff0c;明年多挣5000万。”听起来简单&#xff0c;但在真实的业务环境中&#xff0c;要实现高质量的销售预测却相当…

linux服务器环境搭建(使用yum 安装mysql、jdk、redis)

一:yum的安装 1:下载yum安装包并解压 wget http://yum.baseurl.org/download/3.2/yum-3.2.28.tar.gz tar xvf yum-3.2.28.tar.gz 2.进入yum-3.2.28文件夹中进行安装,执行安装指令 cd yum-3.2.28 sudo apt install yum 3.更新版本 yum check-update yum update yum cle…

MacBook Pro 安装Redis【超详细图解】

目录 一、使用brew安装Redis 二、查看安装及配置文件位置 三、启动Redis 3.1 查看redis服务进程 3.2 redis-cli连接redis服务 四、关闭Redis 因项目需要&#xff0c;顺便记录安装过程 一、使用brew安装Redis brew install redis 如图所示即为安装成功&#xff01; 二…

yolov5实现多图形识别和图像训练

1.使用了yolov7,检测更好,但是训练上有问题,运行不起来,转了一圈发现yolov5是应用更广泛使用简单 2.怎么使用 //下载代码 https://github.com/ultralytics/yolov5 //安装依赖 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple some-package //按…

it统一运维平台怎么样?有可以推荐的品牌吗?

随着互联网化&#xff0c;随着信息化的不断发展&#xff0c;企业IT系统的规模和复杂性也在日益增加。在这个背景下&#xff0c;IT统一运维平台就应用而生了。它以一种全面、集成的方式管理企业IT资源&#xff0c;从而提高效率、降低成本、改善服务&#xff0c;为企业提供更快更…

如何使用内网穿透工具实现公网访问GeoServe Web管理界面

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址6. 结语 前言 GeoServer是OGC Web服务器规范的J2EE实现&#xff0c;利用GeoServer可以方便地发布地图数据&#xff0c;允许用户对要素数据进行更新、删除…

Java 使用Graphics生成海报图片(附效果图)

生成流程 1、创建画布 2、开启画图 3、画布上加载背景图片 4、画布上指定坐标绘制二维码&#xff08;关于二维码实现的参考文后的链接&#xff09; 5、将最终的图存放在本地 6、将图片url返回给前端 主要代码&#xff1a; PostMapping(value "/getPoster")public R…

C++ 系列 第五篇 C++ 算术运算符及类型转换

系列文章 C 系列 前篇 为什么学习C 及学习计划-CSDN博客 C 系列 第一篇 开发环境搭建&#xff08;WSL 方向&#xff09;-CSDN博客 C 系列 第二篇 你真的了解C吗&#xff1f;本篇带你走进C的世界-CSDN博客 C 系列 第三篇 C程序的基本结构-CSDN博客 C 系列 第四篇 C 数据类型…

实验案例二:多表查询

1、表联接类型。 表联接类型可以分为内联接&#xff0e;外联接和交叉联接等。 1&#xff0e;内联接。 内联接〈 inner join&#xff09;是最常用的-一-种联接方式&#xff0c;只返回两个数据集合之间匹配关系的行&#xff0c;将位于两个互相交叉的数据集合中重叠部分以内的数…

Flink核心概念

并行度 当要处理的数据量非常大时&#xff0c;我们可以把一个算子操作&#xff0c;“复制”多份到多个节点&#xff0c;数据来了之后就可以到其中任意一个执行。这样一来&#xff0c;一个算子任务就被拆分成了多个并行的“子任务”&#xff08;subtasks&#xff09;&#xff0…