数据结构(八)——内部排序之归并排序

本文介绍了归并排序的原理,包括将数据分为多个长度为1的子表,然后逐步归并成一个有序表的过程。详细阐述了算法思想,包括递归排序、归并操作以及如何优化代码,减少了多余的比较和判断。最后,分析了归并排序的时间复杂度为O(NlogN)和辅助空间需求为O(N),并给出了源代码及测试样例。

归并排序的概述

  • 将n个记录看成是n个长度为1的有序子表;
  • 将两两相邻的有序子表进行归并,若子 表数为奇数,则留下的一个子表直接进入下一 次归并;
  • 重复步骤(2),直到归并成一个长度为n 的有序表

归并排序

重要参数

  • a【】:存储来的数据
  • b【】:归并排序的辅助数组
  • min:输入排序序列的起始位置
  • max:输入排序序列的终点位置
  • mid:将要排序序列的中间值
  • p:从mid位置开始向后扫的一个指针
  • min:记录一开始min的值,为之后辅助数组b【】导入回a【】铺垫

算法思想

  • 首先判断一下当前将要排序的序列是否为一个单位(即长度为一),若是,则返回。(为后续的递归铺垫)
  • 通过输入的min和max求出其中间值mid,并将p指向mid;
  • 以mid为中枢,首先向mid左边进行递归排序,即min~mid;
  • 以mid为中枢,向mid右边进行递归排序,即mid~max;
  • 开始归并排序,以mid为中界限,一个指针从min开始,一个指针从mid开始,比较大小,较小的排在前面,每次排序完后判断一下min指针是否到了mid的位置或是p指针到了max的位置;
  • 若是到了,则将未排序完的序列进行补充到已排序的后面,则排序结束。
  • 将辅助数组中的数据导入回原本的数组

改进后的想法

上面的想法及代码是第一次写归并排序的时候写的,有很多地方很拙劣

  • 1.首先是利用一个for循环和两个if语句进行归并,使得代码表达的意思不清晰
  • 2.两个if语句的比较显得有些多余,因为这就是不是大于就是小于等于的情况
  • 3.累赘,每个if语句后又来了一次 if 来判断两个子序列之一是否扫描完

改进的想法(无代码)

  • 使用一个while循环来替代for循环和判断两个子序列是否扫描完,while(两个训练均为扫描完时为真)
  • 在第一个while循环完再添加两个独立的while循环,来判断是哪个子序列先排序完。

算法分析

  • 时间复杂度:O(NlogN)
  • 辅助空间:O(N)

源代码

#include <stdio.h>
#include <stdlib.h>
#define MAX 10000

void merge(int a[],int b[],int min,int max)//归并排序,a[]数组为原始数组,b[]数组为辅助的数组
{
    int mid,p;//mid为数组的中间位置,p为从中间位置开始的指针
    int min2;//记录了数组最小的位置
    int i,j;
    min2=min;//min为从最小位置开始的指针
    if((max-min)<=1)//如果数组为1则return
    {
        return;
    }
    mid=(max+min)/2;//计算中间位置
    p=mid;
    merge(a,b,min,mid);//向中间位置的左边递归
    merge(a,b,mid,max);//向中间位置的右边递归
    for(i=min;i<max;)//开始归并排序
    {
        if(a[min]>a[p])//左边大于右边
        {
            b[i]=a[p];
            p++;
            if(p==max)//判断是否排序完成
            {
                i++;
                for(j=min;j<mid;j++,i++)
                {
                    b[i]=a[j];
                }
            }
            else
            {
                i++;
            }
        }
        if(a[min]<=a[p])//右边大于左边
        {
            b[i]=a[min];
            min++;
            if(min==mid)//判断是否排序完成
            {
                i++;
                for(j=p;j<max;j++,i++)
                {
                    b[i]=a[j];
                }
            }
            else
            {
                i++;
            }
        }
    }
    for(i=min2;i<max;i++)//整理排序后的数组
    {
        a[i]=b[i];
    }
}

int main()
{
    int a[MAX],b[MAX];//a【】数组用于存储原始的数组,b【】数组用于归并排序中的辅助数组
    int length;
    int i;
    printf("请输入要排序的个数:\n");
    scanf("%d",&length);
    getchar();
    printf("请输入数据:\n");
    for(i=0;i<length;i++)
    {
        scanf("%d",&a[i]);
        getchar();
    }
    merge(a,b,0,length);
    printf("排序后:\n");
    for(i=0;i<length;i++)
    {
        printf("%d ",a[i]);
    }
}

测试样例
25
5 8 9 6 7 4 3 5 2 1 11 18 19 17 21 26 29 35 38 31 36 30 34 32 70
测试结果
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值