【C/C++】two pointers/归并排序/原理/理解/实现/算法笔记4.6
1.two pointers
思路:对序列进行扫描的时候,根据序列本身的特性用两个下标i和j对序列进行扫描,从而降低算法复杂度。
·例1 在递增序列中找a + b = M
while (i<j)
{
if(a[i] + a[j] == M)
{
i++;
j++;
}
else if (a[i] + a[j] < M)
{
i++;
}
else j--;
}
·例2 序列合并问题
思路:将两个从小到大排序的序列排序出一个新的从小到大排序的序列
用两个标记i和j比较A和B中哪个小就填入哪个,剩下的多的填完。
int merge(int A[], int B[], int C[], int n, int m) //n:A长度 m:B长度 从小到大排序
{
int i = 0, j = 0;
int index = 0;
while(i < n && j < m)
{
if (A[i] <= B[j])
{
C[index++] = A[i++];
}
else
{
C[index++] = B[j++];
}
}
while (i < n) C[index++] = A[i++];
while (j < m) C[index++] = B[j++];
return index;
}
2. 归并排序
我找到一个动图特别好理解,尤其是最后分成每个是一个。
思路:2路归并排序,就是把待排序数组每次两两分组,分到每组只剩一个以后开始合并。
合并的时候遵从上面例2的方式。
因为分组是按照数组的顺序分组的,所以分组对数组的顺序其实是没有改变的。每次使用mergeSort递归只是为了获得一个更新以后的坐标,然后带入上面的序列合并的坐标位置。
真正改变的是在合并的时候
可以说这个排序是由合并本身实现的
递归边界条件是left < right,因为最后只剩一个的时候是left = right
归并排序的实现:
1.递归方式
#include <iostream>
using namespace std;
const int maxn = 100;
//从小到大排序 合并函数
void merge(int A[], int L1, int R1, int L2, int R2)
{
int temp[maxn];
int index = 0; //记录temp的数
int i = L1;
int j = L2;
while (i <= R1 && j <= R2)
{
if (A[i] <= A[j])
{
temp[index++] = A[i++];
}
else
{
temp[index++] = A[j++];
}
}
while (i <= R1)
{
temp[index++] = A[i++];
}
while (j <= R2)
{
temp[index++] = A[j++];
}
for(i = 0; i < index; i++)
{
A[L1 + i] = temp[i];
}
}
//分裂
void mergeSort(int A[], int left, int right)
{
if (left < right)
{
int mid = (left + right)/2;
mergeSort(A, left, mid);
mergeSort(A, mid + 1, right);
merge(A, left, mid, mid+1, right); //注意这句一定要在if判断里面 这样才能退到边缘以后直接返回每一层的输出
}
}
int main()
{
int a[] = {6,5,3,1,8,7,2,4};
mergeSort(a, 0, 7);
int len = sizeof(a)/sizeof(a[0]);
for(int i = 0; i < len; i++)
{
printf("%d ", a[i]);
}
printf("\n");
system("pause");
}
这里我要加一些自己的理解。
测试序列:6 5 3 1 8 7 2 4
按照上述的原理,会被分成6 5;3 1;8 7;2 4;
当我们只剩下两个的时候,以6 5为例
他们分别是A[0]和A[1]
这个时候,left = 0, right = 1
计算出的mid = (left + right)/2 = 0
mid + 1 = 2
就是这个时候的left = 1 = mid, mid + 1 = 2 = right
把此时的数据带入下一步的mergeSort(A, left, mid)会因为left = right = 0而不满足mergesort中的left < right而导致无法进行下一步
这个时候就达到了递归转折退回的地方,回到了left = 0, right = 1的地方
带入merge函数,相当于执行merge(a, 0, 0, 1, 1)
其实就是排序了
以此类推
这是另外一种将mergesort和merge合并的写法
#include <stdio.h>
#include <stdlib.h>
// 归并排序(C-递归版)
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
void merge_sort(int arr[], const int len) {
int reg[len];
merge_sort_recursive(arr, reg, 0, len - 1);
}
int main()
{
int a[] = {6,5,3,1,8,7,2,4};
int len = sizeof(a)/sizeof(a[0]);
merge_sort(a, len);
for (int i = 0; i < len; i++)
{
printf("%d ", a[i]);
}
system("pause");
}
2、非递归实现(迭代)
(注意:n = len,相同意思,指待排序的数组长度)
由二分的性质,直接对A[]进行排序(合并)
方法是:先取step = 2,然后对前step/2和后step/2进行合并排序。
如果组内元素 <= step/2,则不操作
当step/2 > n的时候结束排序
为什么不是step = n的时候结束呢?
因为如果n是奇数的话,step得多出一个(看图,每次最后一个因为不满足mid + 1 <= n而无法排序的最后一个数,在第一轮step = 2的时候就被排除在外了,多么可怜)
而要注意的是,多出一个step不能用step <= n+1来,因为step的变化是2倍变化的(step *= 2)
所以这里只能是step/2 <= n
我自己画了一个图来理解:
其实我们可以用step/2 < n就可以了:
因为如果长度为偶数的话,只需要满足 step <= n,举个栗子
但是注意,不能是step < n,不然当数组长度刚好为step的倍数的时候最后会少一个,导致最后一个没法把两个合并。
43和46行的等于好像也可以去掉,我觉得没有太大影响?
【C/C++】two pointers/归并排序/原理/理解/实现/算法笔记4.6的更多相关文章
- 归并排序的理解和实现(Java)
归并排序介绍 归并排序(Merge Sort)就是利用归并的思想实现的排序方法.它的原理是假设初始序列含有fn个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n2\fr ...
- JUC回顾之-ConcurrentHashMap源码解读及原理理解
ConcurrentHashMap结构图如下: ConcurrentHashMap实现类图如下: segment的结构图如下: package concurrentMy.juc_collections ...
- POJ1523(割点所确定的连用分量数目,tarjan算法原理理解)
SPF Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7406 Accepted: 3363 Description C ...
- java的classLoader原理理解和分析
java的classLoader原理理解和分析 学习了:http://blog.csdn.net/tangkund3218/article/details/50088249 ClassNotFound ...
- 深入理解KMP算法
前言:本人最近在看<大话数据结构>字符串模式匹配算法的内容,但是看得很迷糊,这本书中这块的内容感觉基本是严蔚敏<数据结构>的一个翻版,此书中给出的代码实现确实非常精炼,但是个人 ...
- KMP算法详解 --- 彻头彻尾理解KMP算法
前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k. 但是问题在于如何求出这个最大前后缀长度呢? 我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破, 后来翻看 ...
- 更多细节的理解RSA算法
一.概述 RSA算法是1977年由Ron Rivest.Adi Shamir 和 Leonard Adleman三人组在论文A Method for Obtaining Digital Signatu ...
- 怎么理解RSA算法
原文地址:http://www.ittenyear.com/414/rsa/ 怎么理解RSA算法 能够把非对称加密算法里的公钥想象成一个带锁的箱子,把私钥想象成一把钥匙 能够把对称加密算法里的密钥想象 ...
- 支持向量机原理(四)SMO算法原理
支持向量机原理(一) 线性支持向量机 支持向量机原理(二) 线性支持向量机的软间隔最大化模型 支持向量机原理(三)线性不可分支持向量机与核函数 支持向量机原理(四)SMO算法原理 支持向量机原理(五) ...
随机推荐
- Failed to start connector [Connector[HTTP/1.1-8080]]
错误提示:Failed to start connector [Connector[HTTP/1.1-8080]]错误原因:Tomcat端口被占用解决方案(window下):1.cmd打开命令控制台2 ...
- 12组-Alpha冲刺-4/6
侯钦凯 过去两天完成了哪些任务 完善UI界面,复习考试 展示GitHub当日代码/文档签入记录 接下来的计划 复习考试,准备答辩 还剩下哪些任务 博客和答辩 燃尽图(团队整体) 遇到了哪些困难 在部分 ...
- 重写(Override)与重载(Overload)区别
重写是子类对父类的允许访问的方法的实现过程进行重新编写. 方法重写三要素: (1)方法名形参列表相同: (2)返回值类型和声明异常类型子类小于父类: (3)访问权限,子类大于等于父类. 重写的好处在于 ...
- Java数组声明
初始赋值值:例:一个二维数组,有二个1维数组组成,每一个一维数组有3个元素int[][] a = { {1,2,3}, {4,5,6} } ;动态二维数组声明:例:一个二维数组,有二个1维数组组成,每 ...
- [atAGC045F]Division into Multiples
令$d=\gcd(a,b)$,可以发现$c|(ax+by)$等价于$lcm(c,d)|(ax+by)$,因此不妨令$c'=lcm(c,d)$,然后将$a$.$b$和$c$同时除以$d$ 接下来设$(a ...
- 【Tool】IntelliJ IDEA 使用技巧
IntelliJ IDEA 使用技巧 2019-11-06 20:51:43 by冲冲 1.快捷键 Ctrl+w //括出相关范围 Ctrl+shift+f //按照代码段在全局搜索 Ctrl+f ...
- kibana解决Kibana server is not ready yet问题
找到kbn的config中的xml配置 将es的ip改成真正的ip
- 快来使用Portainer让测试环境搭建飞起来吧
Portainer是Docker的图形化管理工具,提供状态显示面板.应用模板快速部署.容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作).事件日志显示.容器控制台操作.Swarm集群和服 ...
- CF1264D1 Beautiful Bracket Sequence (easy version)
考虑在一个确定的括号序列中,我们可以枚举中间位置,按左右最长延伸出去的答案计算. 我们很自然的思考,我们直接维护左右两边,在删除一些字符后能够延伸的最长长度. 我们设\(f_{i,j}\)为\(i\) ...
- 洛谷 P5233 - [JSOI2012]爱之项链(Polya 定理+递推)
洛谷题面传送门 首先很明显题目暗示我们先求出符合条件的戒指数量,再计算出由这些戒指能够构成的项链的个数,因此考虑分别计算它们.首先是计算符合条件的戒指数量,题目中"可以通过旋转重合的戒指视作 ...