数据结构和算法笔记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. 法一…

Unity 面试篇|(八)Unity机试篇 【全面总结 | 持续更新】

目录 1.假设当前市场价一只鸡10元&#xff0c;一只鸭12元5角。请写一个函数ShowPrice&#xff0c;输入参数分别为鸡和鸭的个数&#xff08;非负整型&#xff09;&#xff0c;功能为显示出总价钱&#xff0c;精确到分。例如调用ShowPrice&#xff08;5,10&#xff09;后输出175.…

63 C++ 多线程 timed_mutex,recursive_timed_mutex

前提 &#xff1a;以往的mutex如果拿锁子拿不到&#xff0c;就会一直等待。 timed_mutex 和 recursive_timed_mutex则不同&#xff0c;这两个提供了方法&#xff0c;可以不一直等待。 try() 方法--mutex 和 timed_mutex 都有&#xff0c;且说明都一样 bool try_lock();(C11 起)…

vant组件库的简单使用

1. 组件库的选择 组件库并不是唯一的&#xff0c;常用的组件库还有以下几种&#xff1a; pc 端:element-uielement-plus iviewelement-plus iviewant-design移动端&#xff1a;vant-uiMint UI (饿了么)Cube UI (滴滴)… 2. vant组件库 主要参考官网&#xff1a;Vant官网 3…

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

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

python实例100第30例:一个5位数,判断它是不是回文数

题目&#xff1a;一个5位数&#xff0c;判断它是不是回文数。即12321是回文数&#xff0c;个位与万位相同&#xff0c;十位与千位相同。 程序分析&#xff1a;即12321是回文数&#xff0c;个位与万位相同&#xff0c;十位与千位相同。 程序源代码&#xff1a; #!/usr/bin/pyt…

elementbi表格的列合并,注意这是列合并不是行合并

先准备列合并代码 //先准备列合并代码 export function rowMergeHandle(arr, data) {if (!Array.isArray(arr) && !arr.length) return false;if (!Array.isArray(data) && !data.length) return false;let needMerge {};arr.forEach((i, idx) > {needMer…

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选项可用…

AIGC时代高效阅读论文实操

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

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;为什么讲的…

HttpServletRequest HttpEntity StringEntity 区别

HttpEntity&#xff1a; 在 Apache HttpClient 中是一个接口&#xff0c;它代表一个可读或可写的HTTP消息实体&#xff0c;包括请求体和响应体。 在发送HTTP请求时&#xff0c;可以附带各种类型的实体内容&#xff0c;而在接收HTTP响应时&#xff0c;则会从服务器获取相应的实…