数据结构和算法笔记4:排序算法-归并排序

归并排序算法完全遵循分治模式。直观上其操作如下:

  • 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。
  • 解决:使用归并排序递归地排序两个子序列。
  • 合并:合并两个已排序的子序列以产生已排序的答案。

我们直接来看例子理解算法的过程,下面是要排序的数组,总共8个元素,我们划分为左右两个数组L和R(L和R都已经是有序的),L是原数组左边4个元素,R是原数组右边4个元素,为了让排序终止,两个数组的末尾加了一个无穷,用k指针指向原数组第一个元素2,i指针指向L数组第一个元素2,j指针指向R数组第一个元素1:

在这里插入图片描述

比较i指针所指2和j指针所指1:j指针所指1更小,k指针对应第一个元素赋值为1,j和k指针右移。
在这里插入图片描述

比较i指针所指2和j指针所指2:一样大,k指针对应第2个元素赋值为2,i和k指针右移。
在这里插入图片描述

比较i指针所指4和j指针所指2:j指针所指2更小,k指针对应第3个元素赋值为2,j和k指针右移。
在这里插入图片描述
如此做下去,直到i和j指针都遍历到最后:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对于上面的逻辑我们可以写作一个函数叫merge,它的目的是合并两个有序数组为一个新的有序数组,它的伪代码是:

在这里插入图片描述

其中A是一个数组,p、q和r是数组下标,满足p≤q<r。该过程假设子数组A[p.q]和A[q+1,r]都已排好序。它合并这两个子数组形成单一的已排好序的子数组并代替当前的子数组A[p.r]。

用C++实现一下:

void merge(std::vector<int>& A, int p, int q, int r)
{int n1 = q - p + 1;int n2 = r - q;std::vector<int> L(n1, 0);//[p,q]std::vector<int> R(n2, 0);//[q+1,r]for (int i = 0; i < n1; ++i)L[i] = A[p + i];for (int i = 0; i < n2; ++i)R[i] = A[q + i - 1];int i = 0;int j = 0;for (int k = p; k <= r; ++k){if (i == n1){A[k] = R[j];j++;}else if (j == n2){A[k] = L[i];i++;}else if (L[i] <= R[j]){A[k] = L[i];i++;}else{A[k] = R[j];j++;}}
}

这样我们可以得到自底向上的排序:
在这里插入图片描述

算法的伪代码是:
在这里插入图片描述

void mergeSort(std::vector<int> &A, int p, int r)
{if (p < r){int q = (p + r) / 2;mergeSort(A, p, q);mergeSort(A, q + 1, r);merge(A, p, q, r);}
}

测试代码:

int main()
{std::vector<int> A({1, 3, 2, 4, 5, 6, 2, 3});for (auto &a : A){std::cout << a << " ";}std::cout << std::endl;int len = A.size();mergeSort(A, 0, len - 1);for (auto &a : A){std::cout << a << " ";}return 0;
}

第一行是未排序,第二行是排序以后的结果。
在这里插入图片描述
完整代码:

#include <iostream>
#include <string>
#include <vector>void merge(std::vector<int> &A, int p, int q, int r)
{int n1 = q - p + 1;int n2 = r - q;std::vector<int> L(n1, 0); //[p,q]std::vector<int> R(n2, 0); //[q+1,r]for (int i = 0; i < n1; ++i)L[i] = A[p + i];for (int i = 0; i < n2; ++i)R[i] = A[q + i + 1];int i = 0;int j = 0;for (int k = p; k <= r; ++k){if (i == n1){A[k] = R[j];j++;}else if (j == n2){A[k] = L[i];i++;}else if (L[i] <= R[j]){A[k] = L[i];i++;}else{A[k] = R[j];j++;}}
}void mergeSort(std::vector<int> &A, int p, int r)
{if (p < r){int q = (p + r) / 2;mergeSort(A, p, q);mergeSort(A, q + 1, r);merge(A, p, q, r);}
}
int main()
{std::vector<int> A({1, 3, 2, 4, 5, 6, 2, 3});for (auto &a : A){std::cout << a << " ";}std::cout << std::endl;int len = A.size();mergeSort(A, 0, len - 1);for (auto &a : A){std::cout << a << " ";}return 0;
}

力扣相关题目

21.合并两个有序链表

学完了归并排序可以顺带把力扣的21.合并两个有序链表也做一下,等同于上面的merge函数

在这里插入图片描述

模拟的写法

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* dummy = new ListNode(0);ListNode* node = dummy;while (list1 != NULL && list2 != NULL){if (list1->val < list2->val){node->next = list1;list1 = list1->next;}else{node->next = list2;list2 = list2->next;}node = node->next;}node->next = list1 == NULL ? list2 : list1;return dummy->next;}
};

另一种递归的写法:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if (list1 == NULL)return list2;else if (list2 == NULL)return list1;else if (list1->val < list2->val){list1->next = mergeTwoLists(list1->next, list2);return list1;}else{list2->next = mergeTwoLists(list1, list2->next);return list2;}}
};

148. 排序链表

在这里插入图片描述

自顶向下的排序。使用的是左闭右开的思路,传入的两个指针一个是头指针,一个可以是空指针(开区间)。然后使用快慢指针找中点确定mid的位置。这样就可以递归地合并链表mergeTwoLists(sortList(head, mid), sortList(mid, tail))

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* sortList(ListNode* head) {return sortList(head, NULL);}ListNode* sortList(ListNode* head, ListNode* tail){if (head == NULL)return head;if (head->next == tail)//左闭右开{head->next = NULL;return head;}ListNode* slow = head;ListNode* fast = head;while (fast != tail){slow = slow->next;fast = fast->next;if (fast != tail)fast = fast->next;}ListNode* mid = slow;return mergeTwoLists(sortList(head, mid), sortList(mid, tail));}ListNode* mergeTwoLists(ListNode* head1, ListNode* head2){ListNode* dummy = new ListNode(0);ListNode* cur = dummy;ListNode* cur1 = head1;ListNode* cur2 = head2;while (cur1 != NULL && cur2 != NULL){if (cur1->val < cur2->val){cur->next = cur1;cur1 = cur1->next;}else{cur->next = cur2;cur2 = cur2->next;}cur = cur->next;}cur->next = (cur1 == NULL ? cur2 : cur1);return dummy->next;}
};

还有一种自底向上的排序,使用的就是前面讲的方法。自底向上的排序,先排序1个长度的链表,然后2个,4个,以此类推,这里每两个链表也是使用最重要的是找到两个链表的头指针:

查找最后一个不为空的链表指针:

while (node->next != NULL)//node不为空
{node = node->next;
}

从node开始第subLength个指针(node不为空)

for (int i = 1; node->next != NULL && i < subLength; i++)//node确定不为空
{node = node->next;//到subLength的尾部
}

从node开始第subLength个指针,如果node可能为空:

for (int i = 1; node != NULL && node->next != NULL && i < subLength; i++)//node确定不为空
{node = node->next;//到subLength的尾部
}

每次找到subLength个指针的位置,把它后面的位置记录下来,然后它后面的位置设为空指针。方便后面的链表的合并。用mergeTwoLists合并两个链表,赋值头结点给pre的next指针,然后next指针移动到这个链表的尾部,后面还要继续相同的操作。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* sortList(ListNode* head) {if (head == NULL)return head;ListNode* dummy = new ListNode(0, head);ListNode* temp = dummy;int length = 0;while (temp->next != NULL){temp = temp->next;length++;}for (int subLength = 1; subLength < length; subLength <<= 1){ListNode* pre = dummy;ListNode* cur = dummy->next;while (cur != NULL){ListNode* head1 = cur;for (int i = 1; cur->next != NULL && i < subLength; i++){cur = cur->next;//到subLength的尾部}ListNode* head2 = cur->next;cur->next = NULL;cur = head2;for (int i = 1; cur != NULL && cur->next != NULL && i < subLength; i++){cur = cur->next;}ListNode* next = NULL;if (cur != NULL){next = cur->next;cur->next = NULL;}pre->next = mergeTwoLists(head1, head2);while (pre->next != NULL){pre = pre->next;}cur = next;}}return dummy->next;}ListNode* mergeTwoLists(ListNode* head1, ListNode* head2){ListNode* dummy = new ListNode(0);ListNode* cur = dummy;ListNode* cur1 = head1;ListNode* cur2 = head2;while (cur1 != NULL && cur2 != NULL){if (cur1->val < cur2->val){cur->next = cur1;cur1 = cur1->next;}else{cur->next = cur2;cur2 = cur2->next;}cur = cur->next;}cur->next = (cur1 == NULL ? cur2 : cur1);return dummy->next;}
};

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

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

相关文章

Flutter 与 Android原生 相互通信:BasicMessageChannel、MethodChannel、EventChannel

前言 本文主要讲解&#xff0c;使用不同的 Channel 让 Flutter 和 Android原生 进行通信&#xff0c;由于只是讲解两端通信&#xff0c;所以可视化效果不好&#xff1b; 不过我写了一篇专门讲解 Flutter 嵌入 Android原生View的文章 Flutter 页面嵌入 Android原生 View-CSDN…

小程序使用echarts图表-雷达图

本文介绍下小程序中如何使用echarts 如果是通过npm安装&#xff0c;这样是全部安装的&#xff0c;体积有点大 我这边是使用echarts中的一个组件来实现的&#xff0c;下边是具体流程&#xff0c;实际效果是没有外边的红色边框的&#xff0c;加红色边框的效果是这篇说明 1.echa…

IDEA的database使用

一、数据据库 在使用database之前&#xff0c;首先你的电脑要安装好了数据库并且启动。 MySQL卸载手册 链接&#xff1a;https://pan.baidu.com/doc/share/AVXW5SG6T76puBOWnPegmw-602323264797863 提取码&#xff1a;hlgf MySQL安装图解 链接&#xff1a;https://pan.baidu.…

机器学习笔记——机器学习的分类

1 机器学习是啥 机器学习是人工智能的一个分支&#xff0c;它是一门研究机器获取新知识和新技能&#xff0c;并识别现有知识的学问。 机器学习已广泛应用于数据挖掘、计算机视觉、自然语言处理、生物特征识别、搜索引擎、医学诊断、检测信用卡欺诈、证券市场分析、DNA 序列测…

用Python实现Excel中的Vlookup功能

目录 一、引言 二、准备工作 三、实现Vlookup功能 1、导入pandas库 2、准备数据 3、实现Vlookup功能 4、处理结果 5、保存结果 四、完整代码示例 五、注意事项 六、总结 一、引言 在Excel中&#xff0c;Vlookup是一个非常实用的函数&#xff0c;它可以帮助我们在表…

Web概述

Web 概述&#xff1a;Web是World Wide Web的简称&#xff0c;是一个由许多互联网服务组成的信息空间。它由超文本文档、图像、视频和其他多媒体资源组成&#xff0c;并通过超文本传输协议&#xff08;HTTP&#xff09;进行传输。特点&#xff1a;Web的主要特点是其开放性和可访…

java数据结构与算法刷题-----LeetCode485. 最大连续 1 的个数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 法一&#xff0c;双指针2. 法二&#xff1a;变量计数 1. 法一…

面对不平衡二元分类问题是否需要使用SMOTE技术?

摘要 在训练分类模型之前平衡数据是解决表格数据中不平衡二元分类任务的流行技术。平衡通常是通过复制少数样本或生成合成少数样本来实现的。虽然众所周知&#xff0c;平衡对每个分类模型的影响不同&#xff0c;但大多数先进的实证研究并未将强大的最先进&#xff08;SOTA&…

Qt6入门教程 8:信号和槽机制(连接方式)

目录 一.一个信号与槽连接的例子 二.第五个参数 1.Qt::AutoConnection 2.Qt::DirectConnection 3.Qt::QueuedConnection 4.Qt::BlockingQueuedConnection 5.Qt::UniqueConnection 三.信号 四.connect函数原型 五.信号与槽的多种用法 六.槽的属性 一.一个信号与槽连接…

R语言【cli】——builtin_theme():内置的CLI主题

Package cli version 3.6.0 Description 此主题始终处于活动状态&#xff0c;并且位于主题堆栈的底部。 Usage builtin_theme(dark getOption("cli.theme_dark", "auto")) Argument 参数【dark】&#xff1a;是否使用黑暗主题。cli.theme_dark选项可用…

3d渲染软件有哪些?3d云渲染推荐

目前市面上的3D渲染软件非常多&#xff0c;不同的建模软件都有自己的渲染方式&#xff0c;根据所处行业的不同和项目需要&#xff0c;设计师可以选择不同的软件帮助展示最终效果。 主流的渲染软件有&#xff1a;VRay和Corona&#xff1a;一般用于室内效果图渲染&#xff0c;与3…

go 语言爬虫库goquery介绍

文章目录 爬虫介绍goquery介绍利用NewDocumentFromReader方法获取主页信息Document介绍通过查询获取文章信息css选择器介绍goquery中的选择器获取主页中的文章链接 爬取总结 爬虫介绍 爬虫&#xff0c;又称网页抓取、网络蜘蛛或网络爬虫&#xff0c;是一种自动浏览互联网并从网…

chapter10-让你拥有“火眼金睛”的 Fiddr4 和其他工具

在前面的课程中&#xff0c;我们通过一个简单的天气预报服务&#xff0c;拓展了如何使用邮件、短信&#xff0c;以及部署在服务器上&#xff0c;完整的开发了一款可以正式使用的小程序。但是有的同学可能也会产生抱怨&#xff1a;这门课不是是爬虫入门吗&#xff1f;为什么讲的…

如何在Servlet中获取请求参数的值

看看这个大佬做的动图吧&#xff01; 在Servlet中&#xff0c;你可以使用HttpServletRequest对象来获取请求参数的值。HttpServletRequest对象提供了一些方法&#xff0c;允许你访问从客户端发送的请求信息。以下是一些获取请求参数的常用方法&#xff1a; getParameter(String…

node.js项目express的初始化

目录 1.初始化项目2.配置跨域3.开始编写API3.1准备3.2路由处理函数router_make下的user.js3.3路由模块router下的user.js3.4入口文件app.js里面去新增这段代码3.5启动项目进行测试 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你…

小程序学习-19

Vant Weapp - 轻量、可靠的小程序 UI 组件库 ​​​​​ Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装出现问题&#xff1a;rollbackFailedOptional: verb npm-session 53699a8e64f465b9 解决办法&#xff1a;http://t.csdnimg.cn/rGUbe Vant Weapp - 轻量、可靠的小程序…

Unity中URP下的SimpleLit片元着色器

文章目录 前言一、SimpleLit片元着色器大体框架1、传入 和 返回2、GPU实例化部分3、准备 BlinnPhong 光照模型计算需要的 SurfaceData4、准备 BlinnPhong 光照模型计算需要的 InputData5、进行 BlinnPhong 的计算、雾效颜色混合及透明度计算 二、准备SurfaceData1、SurfaceData…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十三)

地址簿相关功能 1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码实现1.2.1 Mapper层1.2.2 Service层1.2.3 Controller层 1.1 需求分析和设计 1.1.1 产品原型 地址簿&#xff0c;指的是消费者用户的地址信息&#xff0c;用户登录成功后可以维护自己的地…

Salesforce开发者 - 从入门到精深

# 前言 1.1 Salesforce 平台简介 Salesforce 是一种云计算平台&#xff0c;专注于客户关系管理&#xff08;CRM&#xff09;。它提供了一整套工具和服务&#xff0c;用于开发、定制和管理企业应用程序。 1.2 为什么选择 Salesforce 开发 - 快速开发&#xff1a;通过Salesfo…

51单片机中断

1、什么是中断&#xff1f; CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;&#xff1b; CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务&#xff09;&#xff1b; 待CPU将事件B处理完…