首页 > 编程语言 >基于C语言的归并排序算法

基于C语言的归并排序算法

时间:2024-09-02 10:50:46浏览次数:6  
标签:归并 int 元素 arr C语言 数组 排序

一、归并排序的基本概念

        归并排序(Merge Sort)是一种基于分治法思想的高效稳定排序算法。其基本原理是将一个待排序的数组不断地分割成较小的子数组,直到每个子数组只包含一个元素,此时每个子数组都被认为是有序的。然后,再将这些有序的子数组合并成一个更大的有序数组。

        归并排序的特点主要包括以下几个方面:

        首先,它是一种稳定的排序算法,这意味着在排序过程中,相同元素的相对顺序不会发生改变。这对于某些需要保持原始顺序的场景非常重要。

        其次,归并排序在处理大规模数据时表现出色,具有较好的时间复杂度。其平均时间复杂度、最坏时间复杂度和最好时间复杂度均为 O (n log n),其中 n 是待排序数组的长度。

        归并排序的空间复杂度为 O (n),因为在合并过程中需要额外的辅助空间来存储临时数组。

        在实际应用中,归并排序常用于对数据规模较大且对稳定性有要求的情况。例如,在处理包含多个属性的对象数组时,如果需要按照某个属性排序,同时保持其他属性的相对顺序不变,归并排序是一个不错的选择。

        总的来说,归并排序以其稳定、高效的特点,在排序算法中占据着重要的地位。

二、归并排序的实现步骤

(一)递归分割

        归并排序通过递归的方式将数组不断地分割成两半。具体过程如下:

        首先,选择一个中间位置将数组分成左右两部分。然后,对左右两部分分别再次进行分割,重复这个过程,直到每个子数组只包含一个元素。

        在实现中,通常通过计算中间索引来确定分割位置。例如,对于数组arr,长度为n,中间索引mid可以通过mid = (left + right) / 2计算得到。

        接着,分别对左半部分arr[left, mid]和右半部分arr[mid + 1, right]进行递归分割,直到子数组的长度为 1 。        

(二)合并操作

        合并操作是归并排序的关键步骤。首先,创建一个与合并后数组大小相同的临时数组,用于存储合并过程中的元素。

        然后,设置两个指针分别指向两个待合并子数组的起始位置。通过不断比较两个指针所指向的元素,将较小的元素放入临时数组,并相应地移动指针。

        当其中一个子数组的元素全部放入临时数组后,将另一个子数组剩余的元素直接复制到临时数组的末尾。

        最后,将临时数组中的元素复制回原数组,完成两个子数组合并为一个有序数组的操作。

        在合并过程中,要确保相同元素的相对顺序不发生改变,以保证归并排序的稳定性。

三、归并排序的代码实现

 (一)函数定义

// 归并函数
void merge(int arr[], int left, int mid, int right)

    int i;// i 用于遍历左子数组 L
    int j;// j 用于遍历右子数组 R
    int k;// k 用于遍历原数组 arr
    int n1 = mid - left + 1;// n1 为左子数组的长度
    int n2 = right - mid;// n2 为右子数组的长度
    int L[n1]; // 创建左子数组 L,长度为 n1
    int R[n2]; // 创建右子数组 R,长度为 n2
    
    for (i = 0; i < n1; i++)  // 将原数组中左子数组的元素复制到 L
        L[i] = arr[left + i];
   
    for (j = 0; j < n2; j++)  // 将原数组中右子数组的元素复制到 R
        R[j] = arr[mid + 1 + j];

    // 初始化指针
    i = 0; 
    j = 0; 
    k = left; 

    // 当左子数组和右子数组都还有元素未处理时
    while (i < n1 && j < n2) {
        // 如果左子数组当前元素小于等于右子数组当前元素
        if (L[i] <= R[j]) {
            
            arr[k++] = L[i++];// 将左子数组当前元素放入原数组,并移动指针
        } else {
           
            arr[k++] = R[j++];// 将右子数组当前元素放入原数组,并移动指针
        }
    }

    // 如果左子数组还有剩余元素
    while (i < n1) {
        arr[k++] = L[i++];
    }

    // 如果右子数组还有剩余元素
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

// 归并排序函数
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);
    }
}

(二)示例应用

#include <stdio.h>

// 打印数组函数
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 arrSize = sizeof(arr) / sizeof(arr[0]);

    // 打印提示信息和原始数组
    printf("原始数组: ");
    printArray(arr, arrSize);

    // 调用归并排序函数对数组进行排序
    mergeSort(arr, 0, arrSize - 1);

    // 打印提示信息和排序后的数组
    printf("排序后的数组: ");
    printArray(arr, arrSize);

    return 0;
}

四、归并排序的性能分析

(一)时间复杂度

        归并排序的时间复杂度为 O (n log n),这是因为其基本操作是将待排序数组不断地分成两半,然后再合并。在分解阶段,每次将数组一分为二,这部分的时间复杂度为 O (log n)。在合并阶段,对于长度为 n 的数组,合并操作的时间复杂度为 O (n)。综合起来,总的时间复杂度为 O (n log n)。

        假设对长度为 n 的数组进行归并排序。每次将数组一分为二,直到每个子数组只有一个元素,共进行了 log n 次分解。每次合并两个子数组合成一个长度为 n 的有序数组,需要 n 次操作。所以总的操作次数为 n log n 。

(二)空间复杂度

        归并排序通常需要额外的 O (n) 空间来存储临时数组。在合并操作中,为了将两个有序的子数组合并成一个有序的数组,需要创建一个与原数组大小相同的临时数组来存储中间结果。这意味着在整个排序过程中,需要额外的 O (n) 空间来辅助排序。

(三)稳定性

        归并排序是稳定的排序算法。在合并两个有序子数组的过程中,如果遇到相等的元素,会按照它们在原始数组中的顺序将其放入临时数组,从而保证了相同元素的相对顺序不变。例如,当合并两个子数组时,如果两个当前元素相等,会将处于前面子数组的元素先放入临时数组,这样就确保了稳定性。这使得归并排序在处理需要保持元素原始相对顺序的场景时非常有用。

标签:归并,int,元素,arr,C语言,数组,排序
From: https://blog.csdn.net/m0_71974552/article/details/141643360

相关文章

  • priority_queue自定义排序
    priority_queue自定义排序原文章地址,本文章仅作为学习记录https://www.cnblogs.com/shona/p/12163381.htmlpriority_queue本质是一个堆。头文件是#include<queue>关于priority_queue中元素的比较模板申明带3个参数:priority_queue<Type,Container,Functional>,其中Typ......
  • 排序算法之二叉树排序详细解读(附带Java代码解读)
    二叉树排序(BinaryTreeSort)是一种基于二叉搜索树(BinarySearchTree,BST)实现的排序算法。它的基本思想是利用二叉搜索树的性质来实现排序,通过将元素插入到二叉搜索树中,然后对树进行中序遍历来获取排序后的元素。基本概念二叉搜索树(BST):对于二叉搜索树中的每一个节点,其左......
  • 插入排序
    #include<stdio.h>#include<stdlib.h>#defineASIZE(a)(sizeof(a)/sizeof(a[0]))voidinsert_sort(int*a,intsize){for(inti=1;i<size;i++){intvalue=a[i];intj=i-1;for(;j>=0;j--)......
  • 模拟实现strlen函数(C语言)
    #include<stdio.h>//strlen实现intStrlen(chararr[]){ inti=0; intnum=0;//长度的数值 for(i=0;arr[i]!='\0';i++)//当arr[i]不为\0时继续 { num++;//长度增加 } returnnum;//返回长度的值}intmain(){ //创建一个数组 chararr[100]=......
  • C语言中的#和##
    在C语言中,#和##是预处理器运算符,具有特定的功能。一、#运算符(字符串化运算符)概念:#运算符被称为字符串化运算符。它的作用是将其后面的参数转换为字符串常量。作用:在宏定义中,#可以将传入的参数转换为字符串,方便输出调试信息或者构建特定的字符串。代码例子:#incl......
  • 逆序一句话如:you like her 变为 reh ekil uoy(C语言)
    #include<stdio.h>#include<string.h>//逆序一句话如://youlikeher变为rehekiluoyintmain(){ //创建一个字符串 chararr[100]={0}; //输入字符串内容 gets(arr); //逆序整句话(即把ilike变为ekili) intsz=strlen(arr)-1; intleft=0,righ......
  • C语言 - 自包含和包含其他文件
    在C语言中,头文件的设计可以采用自包含和包含其他文件的方式,以提高代码的可维护性和可重用性。一、头文件自包含含义:头文件自包含是指一个头文件能够独立地进行编译,不依赖于其他头文件的特定包含顺序。这意味着头文件应该包含其自身所依赖的所有定义和声明,以确保无论在什么......
  • C语言 - 头文件包含
    在C语言中,条件编译是一种根据特定条件决定是否编译某段代码的机制。它可以提高代码的可移植性、灵活性和效率。一、条件编译的指令#ifdef、#ifndef、#endif:#ifdef:如果某个宏已被定义,则编译其后的代码块。#ifndef:如果某个宏未被定义,则编译其后的代码块。#endif:用于结束一......
  • JavaSE-递归法解决二分查找、快速排序
    704.二分查找https://leetcode.cn/problems/binary-search/packagedemo;publicclassBinarySearch{publicstaticvoidmain(String[]args){BinarySearchbr=newBinarySearch();System.out.println(br.search(newint[]{1,2,3,4,5,6,7......
  • 堆排序python实现
    一,树与二叉树1,树        树是一种数据结构,比如目录结构。        树是由n各节点组成的集合:    1.如果n=0,那存在一个节点作为数的根节点,其他节点可以分为m个集合,每个集合本身又是一颗树,比如:树的相关概念,比如根节点,叶子节点什么的不做过多介绍......