数据结构——排序(C语言实现)(五)

简介: 数据结构——排序(C语言实现)

非递归

大体思路是没变的,还是需要靠新的数组,不过非递归的细节会非常的复杂:

核心思路还是两两一组,只不过是从原数组中直接去找单个元素排序,然后形成新的一组,再将新的组进行排序。(其实和希尔排序有点类似,这里的gap是一组中的元素个数)。

我们先对于上面这组数进行代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void MergeSortNonR(int* a, int start, int n)//创建新数组
{
  int* tmp = (int*)malloc(sizeof(int) * (n));
  if (tmp == NULL)
  {
    perror("malloc error");
    exit(-1);
  }
  for (int gap = 1; gap < n; gap = 2 * gap)//一组多少个元素
  {
    for (int j = 0; j < n; j += 2 * gap)//j是每组的开头
    {
      int i = j;//新数组的下标
      int left1 = j;//第一组开始
      int right1 = j + gap - 1;//第一组结束
      int left2 = j + gap;//第二组开始
      int right2 = j + 2 * gap - 1;//第二组结束
      while (left1 <= right1 && left2 <= right2)//尾插进新的数组
      {
        if (a[left1] <= a[left2])
        {
          tmp[i++] = a[left1++];
        }
        else
        {
          tmp[i++] = a[left2++];
        }
      }
      while (left1 <= right1)//哪组没走完继续走
      {
        tmp[i++] = a[left1++];
      }
      while (left2 <= right2)
      {
        tmp[i++] = a[left2++];
      }
      memcpy(a + j, tmp + j, sizeof(int) * (right2 - j + 1));//将新数组的内容拷贝到原数组
    }
  }
  free(tmp);
  tmp = NULL;
}
int main()
{
  int arr[] = { 6,2,7,1,3,4,10,8 };
  int n = sizeof(arr) / sizeof(arr[0]);
  MergeSortNonR(arr, 0, n);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

这组数非常顺利,但是我们发现,这组数是可以一直被平分成两组的,如果是奇数呢?或者是10个数呢?

这时候就会出现这种情况 ,只要不是2N就不能向上面那样解决了,肯定会有分不到的组。

那么我们可以让5和9组成的这组先等下,等前面四组排序组成新的一组时就可以变成两组排序了。

像这样就可以了,实现这个逻辑就去判断分组的时候是否下标越界。

我们也发现了一个规律,如果第一组的left1没有遇到任何数则不会有后面的数,只有left1遇到了一个数,比如这组的5(假设right1没遇到9),这才说明这组不能像之前的代码一样去解决问题,因为在第一次进行分组的时候5就不会被分配到任何一组。

分组不均匀的三种情况是:

第一组中右区间缺失

第一组无缺失,第二组全缺失

第二组右区间缺失

但是这里要注意一下,两组中只要有数据就会合成一组,并不是每组都一样的数量才会合成一组。

所以在判断第三组的右区间越界的时候不能直接跳出,需要修正区间。

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void MergeSortNonR(int* a, int start, int n)//创建新数组
{
  int* tmp = (int*)malloc(sizeof(int) * (n));
  if (tmp == NULL)
  {
    perror("malloc error");
    exit(-1);
  }
  for (int gap = 1; gap < n; gap = 2 * gap)//一组多少个元素
  {
    for (int j = 0; j < n; j += 2 * gap)//j是每组的开头
    {
      int i = j;//新数组的下标
      int left1 = j;//第一组开始
      int right1 = j + gap - 1;//第一组结束
      int left2 = j + gap;//第二组开始
      int right2 = j + 2 * gap - 1;//第二组结束
      if (right1 >= n)//第一组右区间越界
      {
        break;//跳出去原来的组会被放在tmp数组中
      }
      if (left2 >= n)//第二组全越界
      {
        break;
      }
      if (right2 >= n)//第二组右区间越界
      {
        right2 = n - 1;//修正
      }
      while (left1 <= right1 && left2 <= right2)//尾插进新的数组
      {
        if (a[left1] <= a[left2])
        {
          tmp[i++] = a[left1++];
        }
        else
        {
          tmp[i++] = a[left2++];
        }
      }
      while (left1 <= right1)//哪组没走完继续走
      {
        tmp[i++] = a[left1++];
      }
      while (left2 <= right2)
      {
        tmp[i++] = a[left2++];
      }
      memcpy(a + j, tmp + j, sizeof(int) * (right2 - j + 1));//将新数组的内容拷贝到原数组
    }
  }
  free(tmp);
  tmp = NULL;
}
int main()
{
  int arr[] = { 6,2,7,1,3,4,10,8,5,9 };
  int n = sizeof(arr) / sizeof(arr[0]);
  MergeSortNonR(arr, 0, n);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

非比较排序

计数排序

基本思想:

就像哈希表一样,用一个数组来映射另一个数组。

先找原数组最小的值和最大的值,因为创建一个新数组要和原数组最小与最大的差值一样大。

创建好一个数组之后,我们将原数组映射到新数组下标对应的位置:

最小的映射在下标为0的位置,第二小的映射在减去最小值下标的位置。

最后在映射的地方进行++,映射几次就加几次:

最后我们利用新建数组进行排序,下标加上最小值,从下标为0的地方开始,没有就不放回原数组:

代码实现

#include <stdio.h>
#include <stdlib.h>
void countingsort(int* a,int n)
{
  int max = a[0];//假设最大最和小值在下标为0的位置
  int min = a[0];
  for (int i = 0; i < n; i++)//在原数组中找最大的
  {
    if (max < a[i])
    {
      max = a[i];
    }
  }
  for (int i = 0; i < n; i++)//在原数组中找最小的
  {
    if (min > a[i])
    {
      min = a[i];
    }
  }
  int sum = abs(min) + abs(max) + 1;//新数组的长度
  int* tmp = (int*)malloc(sum * sizeof(int));//开辟新数组
  if (tmp == NULL)
  {
    perror("malloc error");
    exit(-1);
  }
  for (int i = 0; i < sum; i++)
  {
    tmp[i] = 0;// 新数组的初始化
  }
  for (int i = 0; i < n; i++)//映射到新数组
  {
    if (a[i] == min)
    {
      tmp[0]++;
    }
    else if (a[i] > min)
    {
      int x = a[i] - min;
      tmp[x]++;
    }
  }
  int j = 0;//原数组下标
  for (int i = 0; i < sum; i++)
  {
    while (tmp[i]--)
    {
      a[j] = i + min;//新建数组映射到原数组
      j++;
    }
  }
  free(tmp);
  tmp == NULL;
}
int main()
{
  int arr[] = { -1,-5,3,7,7,5,-1,3 };
  int n = sizeof(arr) / sizeof(arr[0]);
  countingsort(arr, n);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

时间复杂度:O(N+sum)

空间复杂度:O(sum)

稳定性:稳定

缺点:只能对整形排序

排序的复杂度与稳定性

什么是稳定性

上面除了计数排序,剩下的都是可以针对自定义类型进行排序的,我举个例子:

排序前红色的5在黑色的5前面,黑色的1在红色的1前面,排序后红黑颠倒了,这就是不稳定,如果是整数确实没什么,如果是自定义类型就麻烦了,红色的1中可能会有其他的成员变量,黑色的也有其他不同的成员变量。

比如高考的时候,全国有很多分数相同的同学,但是我想先看谁语文分数高,然后在进行总分的排序,如果用一个稳定的排序就不会打乱原来的排序。

直接插入排序

时间复杂度:O(N2)

空间复杂度:O(1)

稳定性:稳定

在排序的过程中遇到相同的数就会停下。

希尔排序

时间复杂度:O(N1.3)

空间复杂度:O(1)

稳定性:不稳定

分组的时候容易将相同的数据分到不同的组。

选择排序

时间复杂度:O(N2)

空间复杂度:O(1)

稳定性:不稳定

蓝色的5和黑色的5顺序没变,但是蓝色的8和黑色的8顺序变了。

堆排序

时间复杂度:O(N*logN)

空间复杂度:O(1)

稳定性:不稳定

这个太不稳定了。

冒泡排序

时间复杂度:O(N2)

空间复杂度:O(1)

稳定性:稳定

快速排序

时间复杂度:O(N*logN) 优化后的

空间复杂度:O(logN) 取决于树的深度

稳定性:不稳定

归并排序

时间复杂度:O(N*logN)

空间复杂度:O(N)

稳定性:稳定

在排序的过程中是尾插进新数组的,遇到相同的就尾插到后面,非常稳定。

相关文章
|
3月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
91 1
|
3月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
80 4
|
3月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
63 4
|
14天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
66 29
|
2天前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
20天前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
1月前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
36 10
|
1月前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
58 10
|
1月前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
41 7
|
1月前
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
本任务要求从键盘输入三个数,并按从小到大的顺序排序后输出。主要内容包括: - **任务描述**:实现三个数的排序并输出。 - **编程要求**:根据提示在编辑器中补充代码。 - **相关知识**: - 选择结构(if、if-else、switch) - 主要语句类型(条件语句) - 比较操作与交换操作 - **测试说明**:提供两组测试数据及预期输出。 - **通关代码**:完整代码示例。 - **测试结果**:展示测试通过的结果。 通过本任务,你将掌握基本的选择结构和排序算法的应用。祝你成功!
36 4

热门文章

最新文章