【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)

简介: 【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)

归并排序

归并过程如下:




 代码实现(递归)


//时间复杂度:O(N*logN)
//空间复杂度:O(N)
void _MergeSort(int* a,int begin, int end,int* tmp)
{
  if (begin >= end)
  return;
  int mid = (begin + end) / 2;
  //[begin,mid][mid+1,end]
  _MergeSort(a, begin, mid, tmp);
  _MergeSort(a, mid+1, end, tmp);
  //[begin,mid][mid+1,end]归并
  int begin1 = begin, end1 = mid;
  int begin2 = mid+1, end2 = end;
  int i = begin;
  while (begin1 <= end1 && begin2 <= end2)
  {
  if (a[begin1] <= a[begin2])
  {
    tmp[i++] = a[begin1++];
  }
  else
  {
    tmp[i++] = a[begin2++];
  }
  }
  while (begin1 <= end1)
  {
  tmp[i++] = a[begin1++];
  }
  while (begin2 <= end2)
  {
  tmp[i++] = a[begin2++];
  }
  memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
  perror("malloc fail");
  return;
  }
  _MergeSort(a, 0, n - 1, tmp);
  free(tmp);
}


分析:因为是使用递归实现,开始时需要创建一个新的数组,且递归需要一个区间,因此我们要另外定义一个函数来实现递归。递归的过程跟二叉树的后序遍历类似,应当注意递归的取值范围和结束条件。归并时,我们把左右两个区间的数从头开始比较,小的就放到tmp数组中。第一个while循环的结束条件是直到某一边的数全部放到tmp就结束。然后就把另一个区间的数,全部依次放入tmp数组中,最后再把tmp的数复制给原数组。


代码实现(非递归)

void MergeSortNonR(int* a, int n)
{
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
  perror("malloc fail");
  return;
  }
  int gap = 1;
  while (gap < n)
  {
  for (int i = 0; i < n; i += 2 * gap)
  {
    int begin1 = i, end1 = i + gap - 1;
    int begin2 = i+gap, end2 = i +2* gap-1;
    //[begin1,end1][begin2,end2]归并
    if (end1 >= n || begin2 >= n)
    {
    break;
    }
    if (end2 >= n)
    {
    end2 = n - 1;
    }
    int j = begin1;
    while (begin1 <= end1 && begin2 <= end2)
    {
    if (a[begin1] <= a[begin2])
    {
      tmp[j++] = a[begin1++];
    }
    else
    {
      tmp[j++] = a[begin2++];
    }
    }
    while (begin1 <= end1)
    {
    tmp[j++] = a[begin1++];
    }
    while (begin2 <= end2)
    {
    tmp[j++] = a[begin2++];
    }
    memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
  }
  gap *= 2;
  }
  free(tmp);
}


分析:gap表示每组数的个数。非递归的实现是,开始每组一个数,两两合一,后面比较的过程和递归一样。不过需要注意越界的问题,当end1或者begin2>=n时,就已经越界,这时候就结束循环。当只是end2>=n时,前面数据没有越界,只需要把end2改成n-1即可。一趟归并结束后,gap变为2倍,进行后面的归并,直到gap>=n就停止。


计数排序(非比较排序)

代码实现

void CountSort(int* a, int n)
{
  int min = a[0], max = a[0];
  for (int i = 1; i < n; i++)
  {
  if (a[i] < min)
    min = a[i];
  if (a[i] > max)
    max = a[i];
  }
  int range = max - min + 1;
  int* count = (int*)calloc(range, sizeof(int));
  if (count == NULL)
  {
  perror("calloc fail");
  return;
  }
  //统计次数
  for (int i = 0; i < n; i++)
  {
  count[a[i] - min]++;
  }
  //排序
  int i = 0;
  for (int j = 0; j < range; j++)
  {
  while (count[j]--)
  {
    a[i++] = j + min;
  }
  }
}


分析:计数排序主要有两步:


1.统计相同元素出现的次数

2.根据统计的结果将序列回收到原来的序列中

计数排序需要我们新创建一个统计数组,按理来说,数组下标就可以用来当作统计的数,该位置就来存放该数出现的次数。但是,如果要排序的数是从一千多开始的,这样前面的空间就全部浪费了。所以我们采用相对映射的方法。即用待排序的数中,最大的数-最小的数+1就可以得到范围,从而减少空间浪费。接着用原数组的数减去最小值,将该值作为count数组的下标,即相对映射。最后进行排序,记得加回最小值min,这样数据才不会被改变。


排序算法的复杂度及稳定性

稳定性:指的是相同的数,在排序之后的相对位置没有改变。


分析:


                    时间                                    空间                                              


直接插入:明显的等差数列             无新空间开辟

希尔:前面文章已分析                          无  

选择:参考动图                                     无

堆排序:前面文章已分析                       无

冒泡:等差数列                                      无

快排:二分的思维                             看递归深度

归并:二分的思维                          开辟新的数组

稳定性通过假设来确定,只要有特例是不稳定的,那就是不稳定的。


秦jh
+关注
目录
打赏
0
0
0
0
80
分享
相关文章
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
279 1
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
231 4
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
177 10
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
121 6
C语言算法复杂度
【10月更文挑战第20天】
81 5
C语言算法复杂度
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力
本文探讨了C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力。文章还分析了数据结构的选择与优化、算法设计的优化策略、内存管理和代码优化技巧,并通过实际案例展示了C语言在排序和图遍历算法中的高效实现。
214 2
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
167 1
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
196 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等