数据结构的归并排序(c语言版)

2024-06-05 19:52

本文主要是介绍数据结构的归并排序(c语言版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

一.归并排序的基本概念

1.基本概念

归并排序是一种高效的排序算法,它采用了分治的思想。它的基本过程如下:

  1. 将待排序的数组分割成两个子数组,直到子数组只有一个元素为止。
  2. 然后将这些子数组两两归并,得到有序的子数组。
  3. 不断重复第二步,直到最终得到有序的整个数组。

2.核心思想

归并排序的核心是"分而治之"的思想。通过不断地将数组拆分成更小的子数组,直至子数组只有一个元素,然后再将这些有序的子数组合并起来,最终得到一个有序的数组。

与简单的冒泡排序或选择排序相比,归并排序的时间复杂度为O(nlogn),这使它能够高效地处理大规模的数据集。虽然它需要O(n)的额外空间来存储中间结果,但其优秀的时间复杂度使其成为处理大数据量排序问题的首选算法之一。

总的来说,归并排序是一种强大而高效的排序算法,它体现了分治策略在算法设计中的重要应用。如果您有任何其他问题,欢迎随时向我咨询。

3.优点

优点:

  1. 时间复杂度稳定:归并排序的时间复杂度为O(nlogn),不管输入数据的初始状态如何,时间复杂度都是稳定的。这使它能够高效处理大规模数据。

  2. 稳定排序:归并排序是一种稳定的排序算法,也就是说,当两个相等的元素出现时,它们在输出序列中的相对顺序与输入序列中的相对顺序一致。这对某些应用场景很重要。

  3. 并行计算友好:归并排序的"分而治之"特性使得它很容易并行化,在多核处理器上可以获得很好的性能提升。

4.缺点

缺点:

  1. 需要额外空间:归并排序需要额外的内存空间来存储中间结果,空间复杂度为O(n)。这可能成为一个瓶颈,尤其是在内存受限的环境中。

  2. 数据交换频繁:归并排序需要频繁地将数据从输入数组复制到临时数组,这在某些情况下可能会降低性能。

  3. 无法就地排序:归并排序无法在原数组上就地排序,需要使用额外的空间。这对于某些内存受限的场景可能是个问题。

二.归并排序的功能

归并排序的基本功能就是对一组数据进行排序。具体来说,它可以实现以下几个功能:

  1. 将无序的数组或列表排序为有序的数组或列表。归并排序可以将任意大小的输入集合有效地排序,包括大型数据集。

  2. 保持数据的相对位置关系。如果输入数据中存在相等的元素,归并排序会保留它们原有的相对顺序,这在某些应用场景中很重要。

  3. 支持并行计算。由于归并排序的"分而治之"特性,它非常适合在多核处理器上并行执行,从而获得大幅的性能提升。

  4. 可以用于外部排序。当数据量太大无法一次性装入内存时,可以采用外部排序的方式,先将数据划分成多个小块,然后使用归并排序分别对这些小块进行排序,最后合并这些有序块。

  5. 可以作为其他算法的子过程。归并排序常被用作其他算法的核心步骤,比如快速排序、外部排序等。

归并排序是一种通用且高效的排序算法,它在各种规模和类型的数据排序中都有重要应用。它的功能十分强大,能够满足绝大多数排序需求。

三.归并排序的代码实现

1.合并两个有序数组

  1. 定义三个索引变量 ijk,分别用来遍历左数组、右数组和目标数组。
  2. 使用 while 循环比较左右数组当前元素的大小,将较小的元素依次添加到目标数组中。
  3. 当左数组或右数组中还有剩余元素时,将它们依次添加到目标数组的末尾。
// 合并两个有序数组
void merge(int arr[], int left[], int left_size, int right[], int right_size, int size) {int i = 0, j = 0, k = 0;while (i < left_size && j < right_size) {if (left[i] <= right[j]) {arr[k++] = left[i++];} else {arr[k++] = right[j++];}}while (i < left_size) {arr[k++] = left[i++];}while (j < right_size) {arr[k++] = right[j++];}
}

2.递归实现

  1. 首先,它检查数组大小是否小于等于 1,如果是,则直接返回,因为单个元素已经是有序的了。

  2. 接下来,它将数组分为两个子数组:左子数组 left 和右子数组 right。计算中点 mid 作为分割点,将原数组 arr 分为左右两部分。

  3. 然后,它将原数组 arr 的元素复制到左右两个临时数组 left 和 right 中。

  4. 递归地对左右两个子数组分别调用 merge_sort 函数,对它们进行排序。

  5. 最后,它调用 merge 函数,将已经排序的左右子数组合并回原数组 arr

// 递归实现归并排序
void merge_sort(int arr[], int size) {
if (size <= 1) {
return;
}
int mid = size / 2;
int left[mid];
int right[size - mid];
for (int i = 0; i < mid; i++) {
left[i] = arr[i];
}
for (int i = mid; i < size; i++) {
right[i - mid] = arr[i];
}
merge_sort(left, mid);
merge_sort(right, size - mid);
merge(arr, left, mid, right, size - mid);
}

3.非递归实现

  1. 首先,它申请了一个临时数组 temp,用来存储合并后的有序子数组。这是非递归实现所需要的额外空间。

  2. 然后,它使用一个外层循环来控制子数组的宽度 width。初始值为 1,每次翻倍,直到 width 大于等于数组大小 size

  3. 内层循环遍历数组,每次处理长度为 2 * width 的两个相邻子数组。

  4. 对于每个子数组对,它计算左子数组的长度 left_size 为 width,右子数组的长度 right_size 可能会小于 width (当剩余元素不足 width 时)。

  5. 然后调用 merge 函数,将左右两个有序子数组合并为一个有序子数组,存储在原数组 arr 中。

  6. 最后,释放临时数组 temp 占用的动态内存。

// 非递归实现归并排序
void iterative_merge_sort(int arr[], int size) {int *temp = (int *)malloc(size * sizeof(int));if (!temp) {printf("Memory allocation failed.\n");return;}for (int width = 1; width < size; width *= 2) {for (int i = 0; i < size; i += 2 * width) {int left_size = width;int right_size = (i + 2 * width < size) ? width : size - i - width;merge(arr + i, arr + i, left_size, arr + i + width, right_size, size);}}free(temp);
}

四.归并排序的源代码

1.递归

#include <stdio.h>
#include <stdlib.h>// 合并两个有序数组
void merge(int arr[], int left[], int left_size, int right[], int right_size) {int i = 0, j = 0, k = 0;while (i < left_size && j < right_size) {if (left[i] <= right[j]) {arr[k++] = left[i++];} else {arr[k++] = right[j++];}}while (i < left_size) {arr[k++] = left[i++];}while (j < right_size) {arr[k++] = right[j++];}
}// 递归实现归并排序
void merge_sort(int arr[], int size) {if (size <= 1) {return;}int mid = size / 2;int left[mid];int right[size - mid];for (int i = 0; i < mid; i++) {left[i] = arr[i];}for (int i = mid; i < size; i++) {right[i - mid] = arr[i];}merge_sort(left, mid);merge_sort(right, size - mid);merge(arr, left, mid, right, size - mid);
}int main() {int arr[] = {5, 2, 4, 6, 1, 3, 2, 6};int size = sizeof(arr) / sizeof(arr[0]);merge_sort(arr, size);for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

2.非递归

#include <stdio.h>
#include <stdlib.h>// 合并两个有序数组
void merge(int arr[], int left[], int left_size, int right[], int right_size, int size) {int i = 0, j = 0, k = 0;while (i < left_size && j < right_size) {if (left[i] <= right[j]) {arr[k++] = left[i++];} else {arr[k++] = right[j++];}}while (i < left_size) {arr[k++] = left[i++];}while (j < right_size) {arr[k++] = right[j++];}
}// 非递归实现归并排序
void iterative_merge_sort(int arr[], int size) {int *temp = (int *)malloc(size * sizeof(int));if (!temp) {printf("Memory allocation failed.\n");return;}for (int width = 1; width < size; width *= 2) {for (int i = 0; i < size; i += 2 * width) {int left_size = width;int right_size = (i + 2 * width < size) ? width : size - i - width;merge(arr + i, arr + i, left_size, arr + i + width, right_size, size);}}free(temp);
}int main() {int arr[] = {5, 2, 4, 6, 1, 3, 2, 6};int size = sizeof(arr) / sizeof(arr[0]);iterative_merge_sort(arr, size);for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

这篇关于数据结构的归并排序(c语言版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1034019

相关文章

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

Python中lambda排序的六种方法

《Python中lambda排序的六种方法》本文主要介绍了Python中使用lambda函数进行排序的六种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录1.对单个变量进行排序2. 对多个变量进行排序3. 降序排列4. 单独降序1.对单个变量进行排序

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In