分治思想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\n",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 }

原文地址:https://www.cnblogs.com/KID-yln/p/12636211.html

时间: 2024-11-01 13:35:43

分治思想01_排序算法_归并_各种写法和思路(待续)的相关文章

排序算法大集锦_二路归并排序_2&3(分治思想)

第一段代码和合并排序差不多,用它来和第二段代码--二路归并排序作对比. 这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <limits.h> #include <malloc.h> void merge(int *m, int x, int y, int z) { int b1,b2,i,j,k; b1=y

排序算法之归并算法

/* 本例拟在实现排序算法的归并算法,归并算法遵循分治法的思想 归并算法: 归并算法主要用来合并两个已经排好序的序列.用Merge(A,p,q,r)来实现合并, 其中A代表数组,A[p,q]和A[q+1,r]A的两个子数组,且两个数组都已经排好序,归并算法 就是将这两个子数组合并成一个排好序的数组并替代当前的数组A[p,r]. */ public class Merge { public static void main(String[] args) { int[] a = {1,2,3,4,5

排序算法大集锦_各种排序算法性能比较

对10000个1-10000的随机数进行排序,并显示出运行时间 数组是用以前用VC++&MatLab生成的,比较长...哈哈,感受一下计算机的速度! #include <stdio.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <malloc.h> #include <time.h> int a[10000]={ 5282,330

排序算法大集锦_线性时间_计数排序

这个之前在<编程珠玑>上面看到过,当时就感觉特别神奇! 速度突破了其他排序算法的下限 后来在<算法导论>上面又看到了,感触颇深!所以一定好好啃透<算法导论> 这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <string.h> int a[10]={2,8,6,7,3,3,1,9,6

排序算法大集锦_合并排序_1(分治思想)

这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <limits.h> #include <malloc.h> int a[10]={2,8,5,7,4,3,1,9,6,10}; void merge(int *m, int x, int y, int z) { int b1,b2,i,j,k; b1=y-x

排序算法大集锦_插入类——直接插入排序

这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好.<算法导论>上面那个比喻,比做打扑克牌的插入新牌,就比较形象.所以这些博客就算是对自己的总结吧. #include <stdio.h> void InsertSort(int *m, int n) { int i,j,temp; for(i=1;i<n;i++) { temp=m[i]; j=i-1; while(j>=0 && te

排序算法大集锦_交换类——快速排序

感觉<算法导论>上面的那个例子要更容易理解,因为那个最起码比较直观! #include <stdio.h> int a[10]={2,8,5,7,4,3,1,9,6,10}; void QuickSort(int m, int n) { int s,begin,end; if(m>n) return; begin=m; end=n; s=a[m]; while(begin!=end) { while(a[end]>=s && begin<end)

排序算法大集锦_选择类——直接选择排序

这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> void SelectSort(int *m, int n) { int i,j,k,temp; bool flag; for(i=0;i<n-1;i++) { k=i; flag=true; for(j=i+1;j<n;j++) if(m[j]<m[k]) k=j; if(k!=i

排序算法大集锦_插入类——希尔(shell)排序

这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> void ShellSort(int *m, int n) { int i,flag,gap; for(gap=n;gap!=1;) { gap/=2; do { flag=0; for(i=0;i<n-gap;i++) if(m[i]>m[i+gap]) { m[i]=m[i]^m[i+