分治思想01_排序算法_归并_各种写法和思路(待续)
Posted kid-yln
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分治思想01_排序算法_归并_各种写法和思路(待续)相关的知识,希望对你有一定的参考价值。
摸鱼了一个星期没更,现在补回来。上星期基本上就是邻接ddl(周一),套板子或者用现成sort修修补补过的,
今天是周六,是时候检验一下是不是真的完全会了,顺便解决一些当时没有想通的问题
先说归并排序
归并的思路,我觉得应该是很容易理解的,这里就不赘述,唯一难的就是把这个—再归并再排序—的模板自己写出来,
网上有很多模板,比较著名的是《算法导论》上的那个借助两个媒介数组实现的,以及借助一个媒介数组实现的,
停!这里其实就是第一个分歧了,似乎为了方便,大部分板子都是有辅助空间的,但是实际上还有一个原地存储不使用额外辅助空间的办法,只是写法会有所不同
- 对于递归的思路来说,整个过程主要是一个自顶向下,再自底向上的过程
分解成,递归分割到底,然后归并返回,返回时对上一个归并子区间两两排序合并,
- 对于更简洁的过程,其实只有自底向上的过程,就是所谓的非递归实现
不用考虑分割,直接实现各个部分的自底向上排序,
https://blog.csdn.net/acingdreamer/article/details/53925072?utm_source=blogxgwz8(比较详细的解释)
的,自然合并排序的思路,https://blog.csdn.net/m0_37787222/article/details/79856252
OK。这里是第二个分歧点,下面来讨论最核心的部分,合并两个有序区间该怎么操作,
对于 两个数组,1 2 4 和 3 4 6,固定一个,比如前面那个,然后一对一,从小到大,a1,b1,大就往后找,小就安插前面,
比如现在b1放在a2前面,然后接着b2,从a2开始找,
从1到n来比,小的放进媒介数组tmp,大的继续和剩下的比,比完之后,会有多余的,多余的补充到后面,
再把已经操作过的tmp中的元素再放回目标移动数组a,
这时就体现媒介数组的好处,如果原地存储需要整体移动,很麻烦,当然用stack或者set insert可能稍微好一点,
附上本人的代码,重点是第十九行
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int Max=1e6+2; 8 int a[Max]; 9 int t[Max]; 10 11 void m1(int a[],int from,int to) { 12 if(from==to)return; 13 //int mid=(from+to)>>1; 14 int mid=(from+to)/2; 15 //int mid=f+(f-t)/2 16 17 //先分割, 18 m1(a,from,mid); 19 m1(a,mid+1,to); 20 21 //分割得到的是区间脚标f,t,mid的状态,最底为f=mid,t=mid+1的极限状态 22 23 //再合并,放入中间数组保存 24 25 int x=from; 26 int y=mid+1; 27 int g=1; 28 while(x<=mid||y<=to) { 29 /*这个边界是错误的,因为y>to之后继续在第二个if进行比较 30 if(y>to||a[x]<=a[y]) {// 31 t[g++]=a[x++]; 32 } 33 if(x>mid||a[x]>a[y]) { 34 t[g++]=a[y++]; 35 } 36 */ 37 38 if(y>to||(x<=mid&&y<=to&&a[x]<=a[y])) { 39 t[g++]=a[x++]; 40 } 41 if(x>mid||(x<=mid&&y<=to&&a[x]>a[y])) { 42 t[g++]=a[y++]; 43 } 44 45 } 46 //多余的不能忽略 47 g--; 48 x=from; 49 for(int i=1; i<=g; i++)a[x++]=t[i]; 50 } 51 52 /* 53 54 5 55 5 1 3 2 4 56 6 57 5 3 2 2 1 6 58 7 59 1 2 5 8 2 4 7 60 8 61 1 5 6 3 13 5 2 9 62 63 */ 64 65 66 67 int main () { 68 int n; 69 while(~scanf("%d",&n)) { 70 for(int i=1; i<=n; i++)scanf("%d",&a[i]); 71 72 m1(a,1,n); 73 //for(int i=1; i<=n; i++)printf("%d ",a[i]); 74 puts(""); 75 //m2(); 76 // m3(); 77 78 } 79 80 81 return 0; 82 }
下面说说非递归实现, 说白了就是直接自底向上划分区间
重点是如何用循环划分区间后提取边界脚标,那比较简单的想法就是把比较的两个区间的长度作为变量,每次从1开始,
1v1的比较,得到区间长度最大为2最小为1的有序区间,
然后2v2,得到长度最大为4最小为2的有序区间,
然后4v4,得到长度最大为8最小为4的有序区间,
。。。以此类推
难点在于有些区间长度直接加不够,需要另外讨论,关键点在于
merge操作限定了mid必须小于to,不能等于,因此也不能等于n,因此上限是mid小于n
以下为非递归代码,m2+merge实现
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int Max=1e6+2; 8 int a[Max]; 9 int t[Max]; 10 11 void m1(int a[],int from,int to) { 12 if(from==to)return; 13 //int mid=(from+to)>>1; 14 int mid=(from+to)/2; 15 //int mid=f+(f-t)/2 16 17 //先分割, 18 m1(a,from,mid); 19 m1(a,mid+1,to); 20 21 //分割得到的是区间脚标f,t,mid的状态,最底为f=mid,t=mid+1的极限状态 22 23 //再合并,放入中间数组保存 24 25 int x=from; 26 int y=mid+1; 27 int g=1; 28 while(x<=mid||y<=to) { 29 /*这个边界是错误的,因为y>to之后继续在第二个if进行比较 30 if(y>to||a[x]<=a[y]) {// 31 t[g++]=a[x++]; 32 } 33 if(x>mid||a[x]>a[y]) { 34 t[g++]=a[y++]; 35 } 36 */ 37 38 if(y>to||(x<=mid&&y<=to&&a[x]<=a[y])) { 39 t[g++]=a[x++]; 40 } 41 if(x>mid||(x<=mid&&y<=to&&a[x]>a[y])) { 42 t[g++]=a[y++]; 43 } 44 45 } 46 //多余的不能忽略 47 g--; 48 x=from; 49 for(int i=1; i<=g; i++)a[x++]=t[i]; 50 } 51 //非递归实现 52 void merge(int from,int mid,int to) { 53 int x=from,y=mid+1; 54 int g=1; 55 while(x<=mid||y<=to) { 56 if(y>to||(x<=mid&&y<=to&&a[x]<=a[y])) { 57 t[g++]=a[x++]; 58 } 59 if(x>mid||(x<=mid&&y<=to&&a[x]>a[y])) { 60 t[g++]=a[y++]; 61 } 62 } 63 g--; 64 x=from; 65 for(int i=1; i<=g; i++)a[x++]=t[i]; 66 } 67 /* 68 69 5 70 5 1 3 2 4 71 6 72 5 3 2 2 1 6 73 7 74 1 2 5 8 2 4 7 75 8 76 1 5 6 3 13 5 2 9 77 78 */ 79 80 81 82 83 84 void m2(int n) { 85 //以区间长度为变量,是活动的 86 //区间长度从1-2-4-直到n 87 88 for(int i=2; i<n; i*=2) { 89 int j=1; 90 while(j<=n) { 91 int from=j; 92 int mid=j+i-1; 93 if(j+i*2-1<=n) { 94 int to=j+i*2-1;//记得-1 95 merge(from,mid,to); 96 } else if(i+j-1<n) { 97 int to=n; 98 merge(from,mid,to); 99 } else if(i+j-1>=n) { 100 break; 101 } 102 j+=(i*2); 103 // j+=(i*2+1);想太多搞错边界, 104 printf("i == %d ",i); 105 106 for(int i=1; i<=n; i++)printf("%d ",a[i]); 107 puts(""); 108 109 } 110 } 111 } 112 113 114 115 int main () { 116 int n; 117 while(~scanf("%d",&n)) { 118 for(int i=1; i<=n; i++)scanf("%d",&a[i]); 119 120 m2(n); 121 //for(int i=1; i<=n; i++)printf("%d ",a[i]); 122 puts(""); 123 //m2(); 124 // m3(); 125 126 } 127 128 129 return 0; 130 }
以上是关于分治思想01_排序算法_归并_各种写法和思路(待续)的主要内容,如果未能解决你的问题,请参考以下文章