单链表的冒泡,选择和插入排序

今天我们来看看单链表排序中的冒泡排序,插入排序,选择排序。

文章目录

  • 冒泡排序
    • 交换值
    • 交换节点
  • 插入排序
    • 交换节点
  • 选择排序
    • 交换值
    • 交换节点

冒泡排序

交换值

首先我们来看看不交换节点,只进行值交换的形式,与数组下的实现思路一致。

外层循环通过 turn 指针遍历链表,每次迭代将当前节点作为的“轮次”节点,记录趟数,直到链表中最后一个节点(即 turn->next 为 NULL)。

内层循环通过 move 指针遍历链表,来进行两两比较。

void bubbleSort(ListNode* head) {if (head == NULL || head->next == NULL) {return;}for (ListNode* turn = head; turn->next != NULL; turn = turn->next) {for (ListNode* move = head; move->next != NULL; move = move->next) {if (move->val > move->next->val) {int temp = move->val;move->val = move->next->val;move->next->val = temp;}}}
}

我们还可以在创建一个save指针,由于每一趟冒泡排序都会从后向前排序好一个元素,闹我们每一趟结束时都使用save指针最后一个元素,并在下一趟不再遍历它,这样下一次内部循环就会排除已经排序好的节点。

void bubblesort(ListNode *head) {ListNode *turn = head;ListNode *move = head;ListNode *save = NULL;if (head == NULL || head->next == NULL) {return;}for (turn = head; turn->next != NULL; turn = turn->next) {for (move = head; move->next != save; move = move->next) {if (move->val > move->next->val) {int temp = move->val;move->val = move->next->val;move->next->val = temp;}}save = move;}
}

这是另外一种写法

swapped:标志位,用于指示在当前遍历中是否发生了节点交换。

ptr1:指向当前正在比较的节点的指针。

lptr:用于标记每次遍历的最后一个未排序节点的指针。

具体思路是:

  • 在每次循环中,遍历链表并比较相邻的节点的值。
  • 如果当前节点的值大于下一个节点的值,则交换它们的值,并将 swapped 标志位置为 1,表示发生了交换。
  • 遍历完成后,将 lptr 指向最后一个已排序节点。
void swap(ListNode* a, ListNode* b) {int temp = a->val;a->val = b->val;b->val = temp;
}
void bubbleSort(ListNode* start) {int swapped;ListNode* ptr1;ListNode* lptr = NULL;// 如果链表为空或只有一个节点,则无需排序if (start == NULL)return;do {swapped = 0;ptr1 = start;while (ptr1->next != lptr) {if (ptr1->val > ptr1->next->val) {swap(ptr1, ptr1->next);swapped = 1;}ptr1 = ptr1->next;}lptr = ptr1;} while (swapped);
}

交换节点

接下来看看交换节点而非交换值的排序方法

在外层循环中,条件是 head->next->next != tail,这意味着只要链表中至少有两个未排序的节点,就会继续排序。内层循环通过 curprev 遍历链表,如果发现 cur->val > cur->next->val(即当前节点的值大于下一个节点的值),则执行交换操作。

交换节点具体为:

  • prevnext 指针指向 cur 的下一个节点(即要交换的节点)。
  • curnext 指针指向 cur 下一个节点的下一个节点。
  • 将交换节点的 next 指针指向 cur,完成交换。

在内层循环的末尾,更新 curprev 指针,以便继续遍历。内层循环结束后,更新 tail 指针,使其指向已排序的链表尾部。

void BubbleSort(ListNode* head) {ListNode *tail = NULL, *prev, *cur;while ((head->next->next) != tail) {prev = head;cur = head->next;while ((cur->next) != tail) {if ((cur->val) > (cur->next->val)) {prev->next = cur->next;cur->next = cur->next->next;prev->next->next = cur;cur = prev->next;}cur = cur->next;prev = prev->next;}tail = cur; // 更新尾节点}
}

插入排序

交换节点

插入排序的核心思想是将待排序部分的每一个元素插入到已排序部分的合适位置。

具体步骤如下:

  1. 创建一个新的头节点 shead,作为哨兵节点,可以简化插入操作。

  2. lastSorted 指针指向当前已排序的最后一个节点,初始时是链表的第一个节点。

  3. cur 指针指向 lastSorted 的下一个节点,即尚未排序的节点。

  4. 在一个while循环中,代码遍历所有尚未排序的节点。

    • 如果当前节点的值 cur->val 大于或等于 lastSorted 节点的值,说明当前节点在排序序列中的位置是正确的,只需要移动 lastSorted 指针到下一个节点。
    • 如果当前节点的值小于 lastSorted 节点的值,则需要将当前节点 cur 插入到已排序序列的正确位置。
  5. 插入操作是通过从哨兵节点 shead 开始,查找直到找到一个节点 pre,使得 pre->next->val 大于或等于 cur->val。这表明 cur 应该插入到 prepre->next 之间。

  6. 插入操作完成后,cur 指针被移动到 lastSorted 的下一个节点,以便在下一轮循环中继续排序。

struct ListNode* insertionSortList(struct ListNode* head) {if (head == NULL) {return head;}// 创建一个新的头节点 shead 作为辅助节点struct ListNode* shead = (struct ListNode*)malloc(sizeof(struct ListNode));shead->next = head;shead->val = 0;//定义两个指针,lastSorted 指向已排序部分的最后一个节点,cur 指向当前待插入节点的下一个节点struct ListNode* lastSorted = head;struct ListNode* cur = head->next;while (cur) {//如果当前节点的值大于等于已排序部分的最后一个节点的值,则直接将 lastSorted 向后移动一位if (cur->val >= lastSorted->val) {lastSorted = lastSorted->next;} else {// 否则,从头节点开始遍历链表,找到第一个比当前节点值大的节点的前一个节点 prestruct ListNode* pre = shead;while (pre->next->val <= cur->val) {pre = pre->next;}// 将当前节点插入到 pre 后面lastSorted->next = cur->next;cur->next = pre->next;pre->next = cur;}// 更新当前待插入节点为 lastSorted 的下一个节点cur = lastSorted->next;}return shead->next;
}

选择排序

选择排序的基本思想:

每一轮选取未排定的部分中最小的元素交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。

交换值

  1. min_node 用来记录遍历过程中找到的最小值节点。另一个指针 ptr2ptr1 的下一个节点开始遍历剩余的链表,找到未排序部分的最小节点。
  2. ptr2 的遍历过程中,如果发现一个节点的值比 min_node 的值小,则更新 min_node
  3. ptr2 遍历完未排序部分后,检查 min_node 是否就是 ptr1。如果不是,交换 ptr1min_node 的值。
  4. 最后,ptr1 向前移动一个节点,对下一对节点进行同样的操作。
struct ListNode* sectionSort(struct ListNode* head) {struct ListNode* ptr1 = head;// 当前未排序链表的第一个节点while (ptr1 && ptr1->next) {// 未排序链表中的最小值节点struct ListNode* min_node = ptr1;struct ListNode* ptr2 = ptr1->next;// 找到未排序链表中的最小值节点while (ptr2) {if (ptr2->val < min_node->val) {min_node = ptr2;}ptr2 = ptr2->next;}// 交换最小值节点与未排序链表中第一个节点的值if (ptr1 != min_node) {int temp = ptr1->val;ptr1->val = min_node->val;min_node->val = temp;}ptr1 = ptr1->next;}return head;
}

交换节点

定义了几个指针tailprevcurmaxmax_prev,用于在链表中遍历和记录最大值节点及其前一个节点。

外层循环通过 tail 遍历链表的每个部分,直到链表尾部。内层循环通过 curprev 寻找从 tail->next 开始到链表末尾的最大节点 max 和它的前一个节点 max_prev。如果找到的最大节点不是 tail 的下一个节点,说明最大节点不在正确位置,通过交换节点将其移动到 tail 之后。

void SelectSort(ListNode* head) {ListNode *tail, *prev, *cur, *max, *max_prev;for (tail = head; tail && tail->next; tail = tail->next) {max = tail->next;for (cur = tail->next, prev = tail; cur != NULL; prev = cur, cur = cur->next) {if (cur->val > max->val) {max = cur;max_prev = prev;}}if (tail->next != max) {max_prev->next = max->next;max->next = tail->next;tail->next = max;}}
}

如有错误烦请指正。

感谢您的阅读

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

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

相关文章

文本检索粗读

一.前情提要 1.本文理论为主&#xff0c;并且仅为个人理解&#xff0c;能力一般&#xff0c;不喜勿喷 2.本文理论知识较为散碎 3.如有需要&#xff0c;以下是原文&#xff0c;更为完备 Neural Corpus Indexer 文档检索【论文精读47】_哔哩哔哩_bilibili 二.正文 &#xf…

CommunityToolkit.Mvvm笔记1---Instruction

CommunityToolkit.Mvvm是一个官方社区套件(Windows Community Toolkit)&#xff0c;延续了MVVMLight的风格&#xff0c;是一个现代、快速和模块化的 MVVM 库。 它是 .NET 社区工具包的一部分。 第一&#xff1a;入门安装 1&#xff0c;用NuGget安装&#xff0c;搜索Community…

最短响应时间负载均衡算法Golang实现

最短响应时间负载均衡算法&#xff08;Least Response Time Load Balancing Algorithm&#xff09;&#xff0c;顾名思义&#xff0c;它的主要目标是最小化用户的响应时间。在这种算法下&#xff0c;负载均衡器会跟踪后端服务器的历史响应时间&#xff0c;并将新的连接请求分配…

【菜狗学前端】ES6+笔记(包含Promise及async、await等)

老样子。复制上来的图片都没了&#xff0c;想看原版可以移步对应资源下载(资源刚上传&#xff0c;还在审核中) &#xff08;免费&#xff09;菜狗学前端之ES6笔记https://download.csdn.net/download/m0_58355897/89135424 一 解构赋值 解构赋值 解构指的是把一个数据…

马上拥有“钞能力”!!24个Python接单平台,赶紧码住!!

学Python能兼职挣米吗&#xff1f;怎么挣&#xff1f; 一、Python兼职种类&#xff1a; 接私活刚学会python那会&#xff0c;就有认识的朋友介绍做一个网站的私活&#xff0c;当时接单赚了4K&#xff0c;后又自己接过开发网站后台接口、做数据处理等事情&#xff0c;都赚了一…

【AcWing】蓝桥杯集训每日一题Day15|并查集|528.奶酪(C++)

528.奶酪 528. 奶酪 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;3800总尝试数&#xff1a;10480来源&#xff1a;NOIP2017提高组算法标签并查集BFSDFS 题目内容 现有一块大奶酪&#xff0c;它的高度为 ℎ&#xff0c;它的长度和宽…

Java并发编程和JUC

Java并发编程、JUC&#xff08;java.util.concurrent包&#xff09; 参考&#xff1a;JUC详解 概念辨析 进程、线程、管程 进程 进程&#xff1a;进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。 它是操作系统动态执行的基本单元&#xff0c;是操作系统…

【机器学习300问】66、均方误差与交叉熵误差,两种损失函数的区别?

一、均方误差&#xff08;Mean Squared Error, MSE&#xff09; 假设你是一个教练&#xff0c;在指导学生射箭。每次射箭后&#xff0c;你可以测量子弹的落点距离靶心的差距&#xff08;误差&#xff09;。MSE就像是计算所以射击误差的平方后的平均值。它强调了每一次偏离靶心的…

2024.04.01校招 实习 内推 面经

绿*泡*泡VX&#xff1a; neituijunsir 交流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 2024届零跑汽车春季校园招聘正式启动&#xff08;内推&#xff09; 校招 | 2024届零跑汽车春季校园招聘正式启动&#xff08;内推&#xff09; 2、校招 & 实习 | 航天二院2…

了解 Vue 工程化开发中的组件通信

目录 1. 组件通信语法 1.1. 什么是组件通信&#xff1f; 1.2. 为什么要使用组件通信&#xff1f; 1.3. 组件之间有哪些关系&#xff08;组件关系分类&#xff09;&#xff1f; 1.4. 组件通信方案有哪几类 &#xff1f; 2. 父子通信流程图 3. 父传子 3.1. 父传子核心流程…

力扣练习题(2024/4/14)

1接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2…

SpringBoot 整合RocketMQ

目录 一、引入依赖 二、配置文件 三、生产者 四、消费者 五、结果 一、引入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.0</version> </d…

leetcode热题100.爬楼梯(从二进制到快速幂)

Problem: 70. 爬楼梯 文章目录 题目思路Code复杂度 题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方…

numpy学习笔记(5),其他实用函数

8. 更多函数 8.1 随机数 8.1.1 常用随机数 8.1.1.1 numpy.random.rand(d0, d1, …, dn) 返回[0.0, 1.0)随机浮点数&#xff0c;即大于等于0.0&#xff0c;小于1.0。d0, d1, …, dn&#xff1a;返回的数组形状 # 使用numpy.random.rand函数 import numpy as np np.random.r…

每日一题:无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是"abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为无重…

2009-2021年上市公司僵尸企业识别数据(含原始数据+计算代码+计算结果)

2009-2021年上市公司僵尸企业识别数据&#xff08;含原始数据计算代码计算结果&#xff09; 1、时间&#xff1a;2009-2021年 2、指标&#xff1a; 证券代码 、证券简称、上市日期、year、净利润、政府补助、流动负债合计、负债合计、财务费用明细利息支出、资产总计、长期负…

SpringBoot中的常见注解详细介绍,附带代码示例

注意&#xff1a;本文不仅含有SpringBoot中常见的注解&#xff0c;还有其它框架的注解。因为注解之间会相互配合使用&#xff0c;所以每一个注解的示例与其他的注解都会有部分相似&#xff0c;请选择查看。 文章目录 01、SpringBootApplication02、Configuration03、EnableAuto…

算法设计中的一些核心原则

1.命名并实体化一切非自解释的概念 这是算法设计的核心原则之一。可以进一步归结为&#xff1a;你不可让逻辑匿名地裸露出来。你必须命名一切你知晓的概念&#xff0c;并且通过注释、适当的代码粒度管理&#xff0c;命名&#xff0c;妥善封装这些概念&#xff0c;比如&#xf…

(Oracle)SQL优化案例:隐式转换优化

项目场景 项目现场的某个kettle模型执行非常缓慢&#xff0c;原因在于某个SQL执行效率非常的低。甲方得知此事要求公司赶紧优化&#xff0c;负责该模块的同事对SQL优化并不熟悉。所以作为一个立志成为优秀DBA的ETL工程师&#xff0c;我自告奋勇&#xff1a;不是DBA&#xff0c;…

ES6 关于Class类的继承 extends(2024-04-10)

1、简介 类Class 可以通过extends关键字实现继承&#xff0c;让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承&#xff0c;要清晰和方便很多。 class Foo {constructor(x, y) {this.x x;this.y y;console.log(父类构造函数)}toString() {return ( this.x …