c语言数据结构-排序(快速+归并+计数+桶)

简介: c语言数据结构-排序(快速+归并+计数+桶)

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

目录

快速排序:

归并排序:

计数排序:

桶排序:


快速排序

原理:快速排序的核心思想是设立一个轴,然后其他数据都和这个轴作比较,最后把轴放在序列的中间,执行完一遍快速排序后左边的数据都比轴小,右边的数据都比轴大。然后递归下去,当递归结束的时候就拍好序了。快速排序的排序很快,但是当数据形成一边倒的情况的时候就发挥不出快速排序的优势。

代码思路:先选第一个元素作为中心轴,此时有两个下标right和left,分别指向最后一个元素位置和第一个元素位置(如图所示),首先将最后一个元素48与44比较,因为48大于44,所以将right--,使right前移指向50,因为50大于44,所以right指向19,因为19小于44,所以将19放到left对应的位置中,然后left后移指向3,因为3小于44,所以将left继续后移,到47时,因为47大于44,所以将47放到原来19的位置(那里有一个空缺),然后right前移,以此类推,最终left会和right重合,且他们重合的位置有空缺,再将中心轴44插入到空缺中

void quicksort(int *arr,int n)
{
  if (n < 2)//数组元素小于2个就不用排序
    return ;
  int tmp = arr[0];//选取最左边的数为中心轴
  int left = 0;//左下标
  int right = n - 1;//右下标
  int moving = 2;//当前应该移动的下标,1-左下标,2-右下标
  while (left < right)
  {
    if (moving == 2)//移动右下标
    {
      //如果右下标位置元素的值大于等于中心轴,继续移动右下标。
      if (arr[right] >= tmp)
      {
        right--;
        continue;
      }
      //如果右下标位置元素的值小于中心轴,吧它填到左下标的坑中。
      arr[left] = arr[right];
      left++;//左下标向右移动
      moving = 1;//下次循环将移动左下标
      continue;
    }
    if (moving == 1)//移动左下标
    {
      //如果左下标位置的元素值小于等于中心轴,继续移动左下标
      if (arr[left] <= tmp)
      {
        left++;
        continue;
      }
      //如果左下标位置元素的值大于中心轴,把他填到右下标的坑中
      arr[right] = arr[left];
      right--;//右下标向左移动
      moving = 2;//下次循环移动右下标
      continue;
    }
  }
  //如果循环结束,左右下标重合,把中心轴的值填进去
  arr[left] = tmp;
  quicksort(arr, left);//对中心轴左边的序列进行排序
  quicksort(arr + left + 1, n - left - 1);//对中心轴右边的序列进行排序
}
int main()
{
  int arr[15] = { 44,3,38,5,47,15,36,26,24,2,46,4,19,50,48 };
  quicksort(arr, 15);
  for (int i = 0; i < 15; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}

归并排序

原理:把要排序的序列拆分成多个含有一个数据的序列,然后按照从小到大(从大到小)进行合并,这样就自然的将无序的序列排好序。归是归并的归,不是递归的归,归并排序就是合并排序。

代码思路:首先将待排序数列拆分,使一个元素单独成一组,得到7个有序子数列,然后每两个一组单独排序,得到4个有序子数列,再将4个子数列每两个一组排序,得到两个子数列,最后将两个子数列排序,得到有序子数列

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int min(int x, int y)//取x和y中的较小值
{
  return x < y ? x : y;
}
void mergesort(int* arr, int n)
{
  if (n < 2)//数组元素小于2个就不用排序
    return;
  int* a = arr;//a指向待排序的数组
  int* b = (int*)malloc(n * sizeof(int));//b指向已排序的数组
  int seg;//区间分段的计数器
  int start;//区间起始位置的计数器
  //排序的次数的循环
  for (seg = 1; seg < n; seg = seg * 2)
  {
    //每次排序选取区间的循环
    for (start = 0; start < n; start = start + seg * 2)
    {
      //把每个区间分成两部分,low是起始位置,mid是中间位置,max是结束位置
      int low = start;
      int mid = min(start + seg, n);
      int max = min(start + seg * 2, n);
      int i = low;//已排序数组的计数器
      int start1 = low, end1 = mid;//待排序左边数列的起始和结束位置
      int start2 = mid, end2 = max;//待排序右边数列的起始和结束位置
      //把待排序左右两边数列合并到已排序数组
      while ((start1 < end1) && (start2 < end2))
      {
        b[i++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
      }
      //把左边数列其他元素追加到已排序数组
      while (start1 < end1)
      {
        b[i++] = a[start1++];
      }
      //把右边数列其他的元素追加到已排序数组
      while ((start2 < end2))
      {
        b[i++] = a[start2++];
      }   
    }
    //交换一下两个数组的指针,准备下一次排序
    int* tmp = a;
    a = b;
    b = tmp;  
  }
  //如果a指向的不是原始数组的指针,把a中的内容复制到arr中
  if (a != arr)
  {
    memcpy(arr, a, n * sizeof(int));
  }
  free(b);
}
int main()
{
  int arr[15] = { 44,3,38,5,47,15,36,26,24,2,46,4,19,50,48 };
  mergesort(arr, 15);
  for (int i = 0; i < 15; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}

运行结果为:

计数排序

前面的算法都是基于比较的排序,计数排序是利用了数组的下标天然有序原理进行排序,所以计数排序是基于统计而排序的排序算法。算法的核心思想是遍历一个无序数组,将遍历到的数据按它的数值找到统计数组的对应下标将其放入统计数组中,依次类推,直到无序的数组的每一个数据都被遍历完之后统计数组也已经初始化完毕,接下来就是遍历统计数组如果遍历到的空间是大于零的就将其下标存入原来的数组中,直到统计数组被遍历完,最终可以排好序。

 

代码思路:创建一个临时数组用来存放待排序的数组,如下图所示。创建的数组大小就为待排序的数组的最大值,这样就可以确保将所有数据存入临时数组中,然后开始计数,记录临时数组中每一数字对应的个数(例如1有两个,2有五个)。最后再将其按照大小一一排出形成有序数列 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int Max(int* arr, unsigned int n)
{
  int i = 0;
  int max = 0;
  for (i = 0; i < n; i++)
  {
    if (max < arr[i])
    {
      max = arr[i];
    }
  }
  return max;
}
void counsort(int* arr, unsigned int n)
{
  if (n < 2)
    return;
  int max = Max(arr, n);//获取待排序数组的最大元素的值
  int tmp[max];//临时数组的大小为max
  memset(tmp, 0, sizeof(tmp));//初始化临时数组
  int i, j, k;
  //临时数组计数
  for (i = 0; i < n; i++)
  {
    tmp[arr[i]]++;
  }
  //临时数组计数的内容填到arr中
  i = 0;
  for (j = 0; j < max + 1; j++)
  {
    for (k = 0; k < tmp[j]; k++)
    {
      arr[i++] = j;
    }
  }
}
int main()
{
  int arr[15] = { 44,3,38,5,47,15,36,26,24,2,46,4,19,50,48 };
  counsort(arr, 15);
  for (int i = 0; i < 15; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}

桶排序:

原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,然后再对每个桶分别排序,最后把全部桶的数据合并

代码思路:创建几个桶用来存放数据(10以内为一个桶,10~20为一个桶,以此类推)分别将每个桶用冒泡(其他排序方法也行)进行排序,最后将所有桶排序

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void bucketsort(int *arr,int n)
{
  int bucket[5][5];//分配5个桶
  int bucketsize[5];//每个桶中元素个数的计数器
  //初始化桶和计数器
  memset(bucket, 0, sizeof(bucket));
  memset(bucketsize, 0, sizeof(bucketsize));
  //把数组arr的数据放入桶中
  for (int i = 0; i < n; i++)
  {
    bucket[arr[i] / 10][bucketsize[arr[i] / 10]++] = arr[i];
  }
  //对每个桶进行冒泡排序
  for (int i = 0; i < 5; i++)
  {
    bubblesort(bucket[i], bucketsize[i]);
  }
  //把每个桶的数据填充到数组中
  int k = 0;
  for (int i = 0; i < 5; i++)
  {
    for (int j = 0; j < bucketsize[i]; j++)
    {
      arr[k++] = bucket[i][j];
    }
  }
}
int main()
{
  int arr[15] = { 44,3,38,5,47,15,36,26,24,2,46,4,19,49,48 };
  bucketsort(arr, 15);
  for (int i = 0; i < 15; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}


相关文章
|
27天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
41 1
|
1月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
49 4
|
1月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
45 4
|
1月前
|
存储 算法 数据处理
【趣学C语言和数据结构100例】81-85
本文介绍了五个经典算法问题及其C语言实现,涵盖图论与树结构的基础知识。包括使用BFS求解单源最短路径、统计有向图中入度或出度为0的点数、统计无向无权图各顶点的度、折半查找及二叉排序树的查找。这些算法不仅理论意义重大,且在实际应用中极为广泛,有助于提升编程能力和数据结构理解。
44 4
|
1月前
|
算法 数据可视化 数据建模
【趣学C语言和数据结构100例】76-80
本文介绍了五种图论算法的C语言实现,涵盖二叉树的层次遍历及广度优先搜索(BFS)和深度优先搜索(DFS)的邻接表与邻接矩阵实现。层次遍历使用队列按层访问二叉树节点;BFS利用队列从源节点逐层遍历图节点,适用于最短路径等问题;DFS通过递归或栈深入图的分支,适合拓扑排序等场景。这些算法是数据结构和算法学习的基础,对提升编程能力和解决实际问题至关重要。
52 4
|
1月前
|
存储 算法 vr&ar
【趣学C语言和数据结构100例】71-75
本文介绍了五个C语言数据结构问题及其实现,涵盖链表与二叉树操作,包括按奇偶分解链表、交换二叉树左右子树、查找节点的双亲节点、计算二叉树深度及求最大关键值。通过递归和遍历等方法,解决了理论与实际应用中的常见问题,有助于提升编程能力和数据结构理解。
41 4
|
1月前
|
存储 算法 C语言
【趣学C语言和数据结构100例】66-70
本书《趣学C语言和数据结构100例》精选了5个典型的数据结构问题及C语言实现,涵盖链表与数组操作,如有序集合的集合运算、有序序列表的合并、数组中两顺序表位置互换、三递增序列公共元素查找及奇偶数重排。通过详细解析与代码示例,帮助读者深入理解数据结构与算法设计的核心思想,提升编程技能。
36 4
|
28天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
54 5
|
27天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
61 1
|
1月前
|
存储 机器学习/深度学习 算法
【趣学C语言和数据结构100例】61-65
本文介绍了五个关于C语言和数据结构的经典问题及其实现方法,涵盖查找链表共同后缀、删除重复节点、重新排列链表元素、合并有序链表以及特定条件下的链表排序。每个问题通过具体的算法设计,不仅展示了链表操作的灵活性,还强调了效率优化的重要性。通过这些问题的探讨,读者可以深入理解链表的基本操作和高级应用,提升解决实际问题的能力。
52 4