算法之归并排序的递归与非递归的实现

一.什么是归并排序

归并排序就是将多个有序的数据段合成一个有序的数据段,如果参与合并的只有两个有序的数据段,则称为二路归并。与快速排序和堆排序相比,其最大的特点是一种稳定的算法,算法的平均时间复杂度O(nlog2n)。

二.归并排序的基本思路

(1).对于一个原始的待排序表,可以将R[1]到R[n]可以看做是n个长度为1的有序表,即分解。

(2).进行第一趟归并,即将上述的n个子序两两合并,得到 n/2向上取整 个有序表,若n为奇数,则归并到最后一个子序列长度为1,即合并。

(3).再将两个 n/2向上取整 个有序表两两合并,如此重复下去,直到得到一个长度为n的有序表,这种排序方法也叫2-路归并排序。

原理如下图

以[63,95,84,46,18,24,27,31,46]为例

三. 实现归并排序之前,需要调用"一次归并“和”一趟归并“,那什么是一次归并?什么是一趟归并呢?

(1).一次归并:把首尾相接的两个有序表R[low...mid]、R[mid+1...high]归并成有序表R[low...high].

(2) .一趟归并:把一些相互连接的有序表依次两两归并成一些更大的有序表。

1.一次归并代码如下

//一次归并排序
void Merge(int low,int m,int high)
{                // 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]
    int i=low;
    int j=m+1;
    int p=0;            //置初始值
    int *R1;            //R1是局部向量
    R1=(int *)malloc((high-low+1)*sizeof(int));
    if(!R1)                        //申请空间失败
    {
       puts("空间申请失败");
       return;
    }
    while(i<=m&&j<=high)                  // 两子文件非空时取其小者输出到R1[p]上
        R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
    while(i<=m)                          // 若第1个子文件非空,则复制剩余记录到R1中
        R1[p++]=R[i++];
    while(j<=high)                      // 若第2个子文件非空,则复制剩余记录到R1中
        R1[p++]=R[j++];
    for(p=0,i=low;i<=high;p++,i++)
        R[i]=R1[p];                    // 归并完成后将结果复制回R[low..high]
}   

2.一趟归并排序代码如下

 1 //一趟归并排序
 2 void mergepass(int n,int len)
 3 {
 4     int i,t;
 5     i=1;
 6     while(i<=n-2*len+1)
 7     {
 8         Merge(i,i+len-1,i+2*len-1);
 9         i=i+2*len;
10     }
11     if(i+len-1<n)
12        Merge(i,i+len-1,n);
13 }

写完”一次归并“和"一趟归并”后,接下来就是编写二路归并了,二路归并就是调用“一次归并”和“一趟归并”,最后组合成一个长度为n的有序表。

二路归并有递归的方法和非递归的方法(即迭代方法),下面就来看一下递归与非递归的实现

(1)非递归的方法

 1 void mergesort(int n)       //非递归的二路归并实现
 2 {
 3     int len;
 4     int *R1;            // R1是局部向量
 5     len=1;
 6     while(len<n)
 7     {
 8       mergepass(n,len);
 9       len=2*len;
10     }
11 }

(2)递归方法实现

 1 //递归的二路归并排序
 2 void MergeSortDC(int low,int high)
 3 {                                     //用分治法对R[low..high]进行二路归并排序
 4    int mid;
 5    if(low<high)
 6    {                                 // 区间长度大于1
 7      mid=(low+high)/2;                //分解 */
 8      MergeSortDC(low,mid);           // 递归地对R[low..mid]排序
 9      MergeSortDC(mid+1,high);           //递归地对R[mid+1..high]排序
10      Merge(low,mid,high);               // 组合,将两个有序区归并为一个有序区
11    }
12 }   

讲到这里,归并排序的思想方法就讲完了,然后就可以自己写main()函数了,然后调用上面的自定义函数就可以实现了

下面是完整的代码实现,仅供参考,如有问题,欢迎指教

  1 #include <stdio.h>
  2 #include<malloc.h>
  3 #include<stdlib.h>
  4 #define MAX 100
  5 int R[MAX];
  6 //一次归并排序
  7 void Merge(int low,int m,int high)
  8 {                // 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]
  9     int i=low;
 10     int j=m+1;
 11     int p=0;            //置初始值
 12     int *R1;            //R1是局部向量
 13     R1=(int *)malloc((high-low+1)*sizeof(int));
 14     if(!R1)                        //申请空间失败
 15     {
 16        puts("空间申请失败");
 17        return;
 18     }
 19     while(i<=m&&j<=high)                  // 两子文件非空时取其小者输出到R1[p]上
 20         R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
 21     while(i<=m)                          // 若第1个子文件非空,则复制剩余记录到R1中
 22         R1[p++]=R[i++];
 23     while(j<=high)                      // 若第2个子文件非空,则复制剩余记录到R1中
 24         R1[p++]=R[j++];
 25     for(p=0,i=low;i<=high;p++,i++)
 26         R[i]=R1[p];                    // 归并完成后将结果复制回R[low..high]
 27 }
 28 //递归的二路归并排序
 29 void MergeSortDC(int low,int high)
 30 {                                     //用分治法对R[low..high]进行二路归并排序
 31    int mid;
 32    if(low<high)
 33    {                                 // 区间长度大于1
 34      mid=(low+high)/2;                //分解 */
 35      MergeSortDC(low,mid);           // 递归地对R[low..mid]排序
 36      MergeSortDC(mid+1,high);           //递归地对R[mid+1..high]排序
 37      Merge(low,mid,high);               // 组合,将两个有序区归并为一个有序区
 38    }
 39 }
 40 //一趟归并排序
 41 void mergepass(int n,int len)
 42 {
 43     int i,t;
 44     i=1;
 45     while(i<=n-2*len+1)
 46     {
 47         Merge(i,i+len-1,i+2*len-1);
 48         i=i+2*len;
 49     }
 50     if(i+len-1<n)
 51        Merge(i,i+len-1,n);
 52 }
 53 void mergesort(int n)
 54 {
 55     int len;
 56     int *R1;            // R1是局部向量
 57     len=1;
 58     while(len<n)
 59     {
 60       mergepass(n,len);
 61       len=2*len;
 62     }
 63 }
 64 void main()
 65 {
 66     int i,n,d;
 67     puts("请输入数组的长度:");
 68     scanf("%d",&n);
 69     if(n<=0||n>MAX)
 70     {
 71         printf("n 必须大于 0 小于 %d.\n",MAX);
 72         exit(0);
 73     }
 74     puts("请依次输入数组的数据:");
 75     for(i=1;i<=n;i++)
 76       scanf("%d",&R[i]);
 77     puts("排序前的数组数据为:");
 78     for(i=1;i<=n;i++)
 79        printf("%4d",R[i]);
 80    printf("\n\n      递归与非递归的合并排序       \n");
 81    printf("\n      1.递归合并排序               \n");
 82    printf("\n      2.非递归合并排序             \n");
 83    printf("\n请输入您的选择:");
 84    scanf("%d",&d);
 85    if(d==1)
 86    {
 87     MergeSortDC(1,n);
 88     puts("\n递归归并排序后:");
 89     for(i=1;i<=n;i++)
 90        printf("%4d",R[i]);
 91    }
 92    else if(d==2)
 93    {
 94        mergesort(n);
 95        puts("\n非递归归并排序后:");
 96     for(i=1;i<=n;i++)
 97        printf("%4d",R[i]);
 98    }
 99    else
100      printf("您输入的菜单号有误!");
101 }
时间: 2024-12-11 18:50:51

算法之归并排序的递归与非递归的实现的相关文章

归并排序(递归、非递归、以及自然归并排序)算法总结

注:本文所指归并排序指 二路归并排序. 归并排序是平均情况.最坏情况.最好情况时间复杂度都为O(Nlog2N)的稳定的排序算法.最近梳理了下归并排序的递归.非递归.以及自然归并排序算法. 归并排序的基础:将两个有序数组合并为一个有序数组,需要O(n)的辅助空间. 图片来自:https://www.cnblogs.com/chengxiao/p/6194356.html // array:待排序数组 //temparray: 临时数组 //startindex:起始下标 //middleindex

二叉树遍历算法总结(递归与非递归)

一:前言 二叉树的遍历方法分四种:前序,中序,后序以及层次遍历. 其中,前中后遍历方法的实现分递归和非递归,非递归遍历的实现需要借助于栈. 实际上,递归的调用就是一种栈的实现,所以,非递归遍历就需要人工借助栈结构来实现. 而层次遍历需要借助队列. 二:前中后序遍历 递归遍历: 递归遍历的思想和方法很简单,通过调整输出语句来实现前,中,后三种遍历. 代码如下: 1 void show(BiTree T) 2 { 3 if(T) 4 { 5 printf("%c ",T->data)

算法学习 - 递归与非递归,位运算与乘除法速度比较

递归调用非递归调用 运行时间比较 结论 位运算与乘除法 结论 递归调用/非递归调用 我们都知道,很多算法,都是用递归实现的.当然它们同时也是可以用非递归来实现. 一般我们在对二叉树进行遍历的时候,还有求斐波那契数的时候,递归是非常简单的.代码容易懂,好实现. 但是递归的时候,有一个问题,就是需要压栈.为什么要压栈呢?因为当我在函数内部调用自身的时候,要中断当前的操作继续跳转到下一次的实现,而当前运行的状态要保存起来.所以就把当前状态进行压栈,等到运行到递归条件结束的时候,再弹栈. 所以递归就是需

二叉树递归与非递归遍历,最近公共父节点算法

#include <iostream> #include <stack> using namespace std; #define MAX 100 //字符串最大长度 typedef struct Node //二叉树结点 { char data; Node *lchild,*rchild; } *Btree; void createBT(Btree &t); //先序构造二叉树 void preorder(Btree &t); //二叉树递归先序遍历 void i

二分查找算法(递归与非递归两种方式)

首先说说二分查找法. 二分查找法是对一组有序的数字中进行查找,传递相应的数据,进行比较查找到与原数据相同的数据,查找到了返回1,失败返回对应的数组下标. 采用非递归方式完成二分查找法.java代码如下所示. /* * 非递归二分查找算法 * 参数:整型数组,需要比较的数. */ public static int binarySearch(Integer[]srcArray,int des){ //第一个位置. int low=0; //最高位置.数组长度-1,因为下标是从0开始的. int h

全排列算法的递归与非递归实现

全排列算法的递归与非递归实现 全排列算法是常见的算法,用于求一个序列的全排列,本文使用C语言分别用递归与非递归两种方法实现,可以接受元素各不相同的输入序列. 题目来自leetcode: Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3

编程算法 - n个骰子的点数(非递归) 代码(C)

n个骰子的点数(非递归) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 把n个骰子仍在地上, 所有骰子朝上一面的点数之和为s. 输入n, 打印出s的所有可能的值出现的概率. 每次骰子的循环过程中, 本次等于上一次n-1, n-2, n-3, n-4, n-5, n-6的次数的总和. 代码: /* * main.cpp * * Created on: 2014.7.12 * Author: spike */ #include <stdio.

斐波那契递归和非递归俩种算法实例

package testcase; /** * * @decription \ \\_ .---(') o( )_-\_ 斐波那契递归和非递归俩种算法实例 * @author bjliuzezhou * @date 2016年2月23日 */ public class TypicalArithmetic_01 { public static void main(String[] args) { System.out.println(fn(6)); System.out.println(noRec

每天刷个算法题20160521:二叉树高度(递归与非递归)

版权所有.所有权利保留. 欢迎转载,转载时请注明出处: http://blog.csdn.net/xiaofei_it/article/details/51502727 为了防止思维僵化,每天刷个算法题.已经刷了几天了,现在发点代码. 我已经建了一个开源项目,每天的题目都在里面: https://github.com/Xiaofei-it/Algorithms 绝大部分算法都是我自己写的,没有参考网上通用代码.读者可能会觉得有的代码晦涩难懂,因为那是我自己的理解. 最近几天都是在写一些原来的东西