排序算法 -归并排序

文章目录

  • [1. 归并排序(Merge Sort)](#1. 归并排序(Merge Sort))
    • [1.1 简介](#1.1 简介)
    • [1.2 归并排序的步骤](#1.2 归并排序的步骤)
    • [1.3 归并排序c 语言实现](#1.3 归并排序c 语言实现)
    • [1.4 时间复杂度](#1.4 时间复杂度)
    • [1.5 空间复杂度](#1.5 空间复杂度)
    • [1.6 动画](#1.6 动画)

1. 归并排序(Merge Sort)

1.1 简介

归并排序(Merge Sort)是一种基于分治思想的排序算法,它的核心思想是将一个数组分成两个或多个子数组,分别对这些子数组进行排序,然后再将这些已排序的子数组合并成一个完整的、有序的数组。归并排序的主要特点是其稳定性和高效性。

1.2 归并排序的步骤

归并排序(Merge Sort)是一种基于分治思想的排序算法,它的核心思想是将一个数组分成两个或多个子数组,分别对这些子数组进行排序,然后再将这些已排序的子数组合并成一个完整的、有序的数组。归并排序的主要特点是其稳定性和高效性。

  1. 分解:将数组从中间(或按其他方式)分成两个或更多个子数组,直到每个子数组只包含一个元素(此时子数组自然有序)。

  2. 递归排序:递归地对每个子数组进行归并排序。由于每个子数组在分解过程中最终都会变成一个元素的数组(有序),因此递归的基准条件是子数组长度为1。

  3. 合并:将两个或更多个已排序的子数组合并成一个有序的数组。合并过程通常涉及比较两个子数组的元素,并按顺序将它们放入一个新的数组中。

1.3 归并排序c 语言实现

c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 合并两个已排序的子数组
void merge(int arr[], int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    // 创建临时数组
    int* L = (int*)malloc(n1 * sizeof(int));
    int* R = (int*)malloc(n2 * sizeof(int));

    // 拷贝数据到临时数组L[]和R[]
    for (int i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];

    // 合并临时数组回到arr[left..right]
    int i = 0; // 初始化左子数组的起始索引
    int j = 0; // 初始化右子数组的起始索引
    int k = left; // 初始化合并后子数组的起始索引
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    // 拷贝L[]的剩余元素(如果有)
    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    // 拷贝R[]的剩余元素(如果有)
    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }

    // 释放临时数组的内存
    free(L);
    free(R);
}

// 归并排序的主函数
void mergeSort(int arr[], int left, int right) {
    if (left < right) {
        // 找到中间点
        int mid = left + (right - left) / 2;

        // 递归排序两个子数组
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);

        // 合并两个已排序的子数组
        merge(arr, left, mid, right);
    }
}

// 打印数组
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

// 主函数
int main() {
    int arr[] = {12, 11, 13, 5, 6, 7};
    int arr_size = sizeof(arr) / sizeof(arr[0]);

    printf("Given array is \n");
    printArray(arr, arr_size);

    mergeSort(arr, 0, arr_size - 1);

    printf("\nSorted array is \n");
    printArray(arr, arr_size);
    return 0;
}

代码说明

  1. merge函数:这个函数负责合并两个已排序的子数组。它首先为两个子数组分配临时内存,然后将它们拷贝到临时数组中。接着,它比较两个临时数组的元素,并按顺序将它们拷贝回原数组。最后,它释放临时数组的内存。

  2. mergeSort函数:这是归并排序的主函数。它首先检查左索引是否小于右索引,如果是,则找到中间点,并递归地对左右两个子数组进行排序。排序完成后,它调用merge函数来合并两个已排序的子数组。

  3. printArray函数:这个函数用于打印数组的元素。

  4. main函数:这是程序的入口点。它定义了一个数组,计算其大小,打印原始数组,调用mergeSort函数对数组进行排序,然后打印排序后的数组。

编译并运行此程序,你将看到原始数组和排序后的数组的输出。

1.4 时间复杂度

归并排序的时间复杂度为O(n log ^n^),这里的n代表数组的元素数量。这一结论源自归并排序的分治特性:

  1. 分解阶段:每次都将数组一分为二,直至每个子数组仅含一个元素。此分解过程需要log n层(因为每次都将问题规模减半)。

  2. 合并阶段:在合并两个已排序的子数组时,需要遍历这两个子数组的所有元素。在最坏情况下,每层合并的总操作数为n(尽管每层合并的实际操作数可能因子数组大小而异,但总操作数在n的量级上)。

1.5 空间复杂度

  1. 递归调用栈:虽然递归调用栈的空间复杂度与递归的深度相关,但在归并排序中,递归深度为log n(因为每次数组都被一分为二)。然而,这部分空间复杂度通常被视为"辅助空间"的一部分,且在实际分析中可能不被单独计算(特别是当它与n相比不显著时)。

  2. 合并时的临时数组 :在合并两个已排序的子数组时,通常需要一个额外的、与较大子数组等大的临时数组来存储合并结果。在最坏情况下(即当两个子数组大小相近时),这个临时数组的大小为 n 2 \frac{n}{2} 2n近似为 n 2 \frac{n}{2} 2n,但在大O表示法中仍视为O(n))。

1.6 动画

相关推荐
爪哇学长2 分钟前
深入浅出:Java 中的经典排序算法详解与实现
java·算法·排序算法
赵玄月7 分钟前
网络工程实验四:NAT的配置
网络·数据结构·经验分享·笔记·智能路由器
ahadee25 分钟前
蓝桥杯每日真题 - 第17天
c语言·vscode·算法·蓝桥杯
睡觉然后上课25 分钟前
C++笔试面试题
c语言·c++·面试
fanxbl95739 分钟前
采用自适应调整参数的 BP 网络学习改进算法详解
神经网络·算法·机器学习
OT.Ter1 小时前
基于FastAPI实现本地大模型API封装调用
人工智能·算法·大模型·fastapi
oioihoii1 小时前
简单工厂模式
c++·设计模式·c#·简单工厂模式
fuxxu1 小时前
【每日题解】3239. 最少翻转次数使二进制矩阵回文 I
算法·矩阵
XY.散人1 小时前
初识算法 · 位运算(end)
算法·面试
行码棋1 小时前
【机器学习】聚类算法原理详解
算法·机器学习·聚类