排序算法(C )

描述

介绍了常用的几种排序算法:冒泡排序、选择排序、插入排序、快排、希尔排序、归并排序、堆排
为什么后面两个没有加租?因为目前的我还没发熟练使用
后期如果有时间会出分析讲解视频

加油!愿祖国强盛😊😊😊

冒泡排序

冒泡排序原理

从第一个元素开始比较相邻的元素,如果顺序错误则调换顺序,继续往下一一对比,直到序列末尾。

冒泡排序分析

冒泡排序的核心就是:多趟排序

冒泡排序模板

/* 冒泡排序 ,升序排序为例 */
void BubbleSort(int *arr, int len) {
int temp; // 用于缓存交换的变量 两数交换,使用第三者
for(int i = 0; i < len; i++) /* 两两比较把最大的放到“最后一个位置” ,所以需要遍历n边*/
for(int x = 0; x < len - 1; x++) /* 两两比较,不能超出数组范围,所以x+1 和 x都不能超过len */
if(arr[x] > arr[x+1]) /* 如果前面的数比后面的数大,则将两个数进行调换 */
{
temp = arr[x]; /* 进行交换 */
arr[x] = arr[x+1];
arr[x+1] = temp;
}
}

冒泡排序时间复杂度

时间复杂度:O (N^2)

选择排序

选择排序原理

通过遍历数组,选出该数组中较大或者较小的,放在数组的起始位置,当遍历完整个数组时排序完成

选择排序分析

选择排序的核心就是多次选择

  • 遍历整个数组,选择最小(最大)的一个数与第一个数进行交换;
  • 其次,在第二位开始的范围中选择次小(大)的数与第二位进行交换……
  • 直到最后一个数,经过 N-1 次遍历,就可以得到一个有序的数组

选择排序模板

/* 选择排序 ,降序排序为例 */
void SelectSort(int *arr, int len) {
int max, temp; //max用于存储最大的下标, temp是缓存交换数据
for(int i = 0; i < len; i++) { /* 从第i个数开始比较,选择到结尾最大的结果 */
max = i; /* 假设最小的数就是开始的值 */
for(int x = i; x < len; x++) /* 从下一个数到结尾一一比对,选择大的进行替换 */
if(arr[x] > arr[max]) { /* 当前的数,比max下标的数大,进行替换 */
max = x;
}
temp = arr[i]; /* 进行数据交换的操作 */
arr[i] = arr[max];
arr[max] = temp;
}
}

选择排序复杂度

时间复杂度:O (N^2)
空间复杂度:O (1)

插入排序

插入排序原理

将待排序的数,按照顺序插入到一个有序的数组中,直到所有记录都插入,那么就获得一个有序的数组

插入排序分析

插入排序的核心就是多趟选择插入

  • 当插入第 i 个元素的时候,那么此时 0……i-1 数组之间是有序的数组
  • 在有序数组中比较,选择合适的插入位置,将 i 元素插入
  • 其后的元素向后移动一格。直到所有的数据都进行进入操作

插入排序模板

/* 插入排序 */
void InsertSort(int *arr, int len) {
for(int i = 1; i < len; i++) { /* 从下标为1 的数开始,与此前面的有序数组进行比较并插入到合适的位置 */
int temp = arr[i]; /* 缓存一下要插入的数,一定要缓存,否则会被覆盖掉 */
int a; /* 要插入数的位置 */
for(a = i-1; a >= 0; a--){ /* 从有序数组的后面开始遍历 */
if(arr[a] > temp){ /* 比较的数比要插入的数大,那么插入的位置还在前面 */
arr[a+1] = arr[a]; /* 如果比他大,就往后移,留出空位 */
}else{ /* 有序数组的数小于等于插入的数字了,那么就插入它上一个位置 */
break;
}
}
arr[a+1] = temp; /* 将数插入到比它小的下一个位置 */
}
}

插入排序复杂度

时间复杂度:O (N^2)
空间复杂度:O (1)

快速排序算法

快速排序算法原理

快速排序就是将数组以一个节点,分为左右两部分,左右两部分数值分别小于或大于该节点
再对左右两边进行同样处理,最后得到一个有序的数组

快速排序分析

  • 选取一个基准点,然后分别扫描数组的两端,设有两个 “指针” 指向起始和结束位置
  • 先从一段开始,如果发现有元素比基准带你的数值小(大),则交换 low 和 high 的位置,然后从另一部分开始,发现元素大于(小于)基准点,则交换 low 和 high 的位置
  • 如此循环,直到 low>=high,然后把基准点放在 high 上,完成第一次排序
  • 后续使用函数递归完成剩下的部分的排序

快速排序模板

/* 快速排序 */
void Quick_Sort(int *arr, int low, int high) {
if (low >= high) /* 递归结束条件 */
return;
int low_num = low;
int high_num = high;
int temp = arr[low]; /* 取出基准元素 */
while(low != high) { /* 当前后节点重合时结束循环 */
while (low < high && arr[high] >= temp) high--; /* 从右向左找小于基准的元素 */
arr[low] = arr[high]; /* 将右指针的元素移动到左指针位置 */
while (low < high && arr[low] <= temp) low++; /* 从左向右找大于基准的元素 */
arr[high] = arr[low]; /* 将左指针的元素移动到右指针位置 */
}
arr[low] = temp; /* 将基准元素放到正确位置 */
Quick_Sort(arr, low_num, high-1); /* 在对基准元素的左部分进行遍历 */
Quick_Sort(arr, high+1, high_num); /* 在对基准元素的右部分进行遍历 */
}
/* 快速排序入口 */
void Quick_Sort_Entry(int *arr, int len){
Quick_Sort(arr, 0, len-1); /* 进入快排函数 */
}

快速排序复杂度

时间复杂度 O (n log n)
空间复杂度根据堆栈深度有关,最好情况 O (log n),最坏情况下为 O (n)

希尔排序

希尔排序原理

希尔排序其实就是由插入排序衍生出来的,当一个升序的有序队列,需要改为降序数组的时候,那么此时使用插入排序的时间复杂度为 n^2,那么选择插入排序效率就不合适了,此时就引入了希尔排序。

希尔排序分析

希尔排序就是在插入排序之前将数组做预处理。

我们把数组分为三组,对着三组进行插入排序,这是第一次预排序。
那么上面的数组为什么是分为 3 组呢?这就涉及一个名词 “增量”—— 增量就是相聚多少个数的数据为一组,对这一组进行单独插入排序
完成增量为 3 的排序,再进行增量为 2 的排序。

然后进行增量为 1 的插入排序

那么如何设置开始的增量呢?
这是希尔排序的核心,就是设置不同的增量序列来优化插入排序的性能。增量排序的选择对排序速度有显著影响。比较常用效果比较好的增量设定为 size/3+1,size 为数组长度

希尔排序模块

/* 希尔排序 */
void ShellSort(int *arr, int len){
int increa = len / 3 + 1; /* 设置增量 */
int temp;
int count = 0;
while (increa > 0) { /* 增量为n,就要进行n次插入排序 */
for(int i = increa; i < len; i++){ /* 对每组的数进行单独排序 */
temp = arr[i]; /* 缓存要插入的数据 */
int j;
for(j = i; j >= increa && arr[j - increa] > temp; j -= increa) { /* 插入排序 */
arr[j] = arr[j - increa];
count++;
}
arr[j] = temp;
}
increa = increa - 1; /* 减少增量 */
}
}

我们可是使用 count 来计算循环的次数,发现直接插入和希尔之间的效率,那么为什么呢?
如果有人这么问你,你可以回答 “ 因为它大大的减少了数据挪动次数,在做预排序的时候它是以增量的跨度去挪动的,这就使一个数据更快的接近它的准确位置”

希尔排序复杂度

时间复杂度 O (n^(1.3-2))
空间复杂度 O (1)

归并排序算法(递归方式)

归并排序算法原理

归并排序是建立在归并操作上的一种稳定的排序算法,认识之前首先了解什么归并?
归并操作,即为将两个有序的数组合并为一个新的有序数组。即将已有序的子序列合并,得到完全有序的序列;若将两个有序表合并为一个有序表,称为二路合并

归并排序算法分析

  • 首先将数组递归分治,将区间进行分解;将数据分割成两个数组,再分别将两个数组细分为两个数组,直到每个数组都是一个元素,这时候每个组都是有序数组了(只有一个数)
  • 再将分割时候的 “有序数组” 进行排序,排成有序数组之后继续为上一个分割的数组合并,直到数组合并为原来的数组

我们在对分割的每组数组进行排序时,需要开辟一个临时数组空间,因为原地排序非常困难,很容易混乱,将排序的结果存放在临时数组,之后再拷贝回原数组

归并排序算法模板

/* 归并函数 */
void merge(int *arr, int l, int mid, int r) {
int n1 = mid - l + 1; // 左子数组的长度
int n2 = r - mid; // 右子数组的长度
// 创建临时数组
int *left = (int *)malloc(n1 * sizeof(int));
int *right = (int *)malloc(n2 * sizeof(int));
// 将数据拷贝到临时数组
for (int i = 0; i < n1; i++)
left[i] = arr[l + i];
for (int j = 0; j < n2; j++)
right[j] = arr[mid + 1 + j];
// 归并临时数组到原数组
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
// 拷贝左子数组的剩余元素
while (i < n1) {
arr[k] = left[i];
i++;
k++;
}
// 拷贝右子数组的剩余元素
while (j < n2) {
arr[k] = right[j];
j++;
k++;
}
// 释放临时数组
free(left);
free(right);
}
/* 递归归并排序 */
void merge_sort_recursive(int *arr, int l, int r) {
if (l < r) {
int mid = l + (r - l) / 2
// 递归排序左半部分
merge_sort_recursive(arr, l, mid);
// 递归排序右半部分
merge_sort_recursive(arr, mid + 1, r);
// 合并两部分
merge(arr, l, mid, r);
}
}
/* 归并排序入口 */
void merge_sort(int *arr, int n) {
merge_sort_recursive(arr, 0, n - 1);
}

归并排序算法复杂度

时间复杂度
最坏情况 O (NlogN)
最好情况 O (NlogN)
平均情况 O (NlogN)
空间复杂度
0(n)

堆排序算法

堆排序原理

堆是一种数据结构:
堆是一颗完全二叉树
堆中每个节点的值必须大于等于(小于等于)其子节点的值

堆排序是一种基于二叉堆数据结构的排序算法,堆排序一般用于数组排序

堆排序分析

堆排序的核心就是利用堆的性质来排序。太复杂了,我能力不足以我灵活使用这样的排序算法😊,真感兴趣的话可以去查一下,等我有机会好好整理一下

堆排序模板

/* Function: 交换交换根节点和数组末尾元素的值*/
void Swap(int *heap, int len) {
int temp;
temp = heap[0];
heap[0] = heap[len-1];
heap[len-1] = temp;
}
/* Function: 构建大顶堆 */
void BuildMaxHeap(int *heap, int len) {
int i,temp;
for (i = len/2-1; i >= 0; i--) {
if ((2*i+1) < len && heap[i] < heap[2*i+1]) { /* 根节点大于左子树 */
temp = heap[i];
heap[i] = heap[2*i+1];
heap[2*i+1] = temp;
/* 检查交换后的左子树是否满足大顶堆性质 如果不满足 则重新调整子树结构 */
if ((2*(2*i+1)+1 < len && heap[2*i+1] < heap[2*(2*i+1)+1]) || (2*(2*i+1)+2 < len && heap[2*i+1] < heap[2*(2*i+1)+2])) {
BuildMaxHeap(heap, len);
}
}
if ((2*i+2) < len && heap[i] < heap[2*i+2]) { /* 根节点大于右子树 */
temp = heap[i];
heap[i] = heap[2*i+2];
heap[2*i+2] = temp;
/* 检查交换后的右子树是否满足大顶堆性质 如果不满足 则重新调整子树结构 */
if ((2*(2*i+2)+1 < len && heap[2*i+2] < heap[2*(2*i+2)+1]) || (2*(2*i+2)+2 < len && heap[2*i+2] < heap[2*(2*i+2)+2])) {
BuildMaxHeap(heap, len);
}
}
}
}

堆排序复杂度

时间复杂度 O (n log n)
空间复杂度 O (1)

验证代码

/*
* 排序常用算法
*/
#include "stdio.h"
#include "stdlib.h"
/* 冒泡排序 ,升序排序为例 */
void BubbleSort(int *arr, int len) {
int temp; // 用于缓存交换的变量 两数交换,使用第三者
for(int i = 0; i < len; i++) /* 两两比较把最大的放到“最后一个位置” ,所以需要遍历n边*/
for(int x = 0; x < len - 1; x++) /* 两两比较,不能超出数组范围,所以x+1 和 x都不能超过len */
if(arr[x] > arr[x+1]) /* 如果前面的数比后面的数大,则将两个数进行调换 */
{
temp = arr[x]; /* 进行交换 */
arr[x] = arr[x+1];
arr[x+1] = temp;
}
}
/* 选择排序 ,降序排序为例 */
void SelectSort(int *arr, int len) {
int max, temp; //max用于存储最大的下标, temp是缓存交换数据
for(int i = 0; i < len; i++) { /* 从第i个数开始比较,选择到结尾最大的结果 */
max = i; /* 假设最小的数就是开始的值 */
for(int x = i; x < len; x++) /* 从下一个数到结尾一一比对,选择大的进行替换 */
if(arr[x] > arr[max]) { /* 当前的数,比max下标的数大,进行替换 */
max = x;
}
temp = arr[i]; /* 进行数据交换的操作 */
arr[i] = arr[max];
arr[max] = temp;
}
}
/* 插入排序 */
void InsertSort(int *arr, int len) {
int count;
for(int i = 1; i < len; i++) { /* 从下标为1 的数开始,与此前面的有序数组进行比较并插入到合适的位置 */
int temp = arr[i]; /* 缓存一下要插入的数,一定要缓存,否则会被覆盖掉 */
int a; /* 要插入数的位置 */
for(a = i-1; a >= 0; a--){ /* 从有序数组的后面开始遍历 */
if(arr[a] > temp){ /* 比较的数比要插入的数大,那么插入的位置还在前面 */
arr[a+1] = arr[a]; /* 如果比他大,就往后移,留出空位 */
}else{ /* 有序数组的数小于等于插入的数字了,那么就插入它上一个位置 */
break;
}
count ++;
}
arr[a+1] = temp; /* 将数插入到比它小的下一个位置 */
}
}
/* 希尔排序 */
void ShellSort(int *arr, int len){
int increa = len / 3 + 1; /* 设置增量 */
int temp;
int count = 0;
while (increa > 0) { /* 增量为n,就要进行n次插入排序 */
for(int i = increa; i < len; i++){ /* 对每组的数进行单独排序 */
temp = arr[i]; /* 缓存要插入的数据 */
int j;
for(j = i; j >= increa && arr[j - increa] > temp; j -= increa) { /* 插入排序 */
arr[j] = arr[j - increa];
count++;
}
arr[j] = temp;
}
increa = increa - 1; /* 减少增量 */
}
}
/* 快速排序 */
void Quick_Sort(int *arr, int low, int high) {
if (low >= high) /* 递归结束条件 */
return;
int low_num = low;
int high_num = high;
int temp = arr[low]; /* 取出基准元素 */
while(low != high) { /* 当前后节点重合时结束循环 */
while (low < high && arr[high] >= temp) high--; /* 从右向左找小于基准的元素 */
arr[low] = arr[high]; /* 将右指针的元素移动到左指针位置 */
while (low < high && arr[low] <= temp) low++; /* 从左向右找大于基准的元素 */
arr[high] = arr[low]; /* 将左指针的元素移动到右指针位置 */
}
arr[low] = temp; /* 将基准元素放到正确位置 */
Quick_Sort(arr, low_num, high-1); /* 在对基准元素的左部分进行遍历 */
Quick_Sort(arr, high+1, high_num); /* 在对基准元素的右部分进行遍历 */
}
/* 快速排序入口 */
void Quick_Sort_Entry(int *arr, int len){
Quick_Sort(arr, 0, len-1); /* 进入快排函数 */
}
/* 归并函数 */
void merge(int *arr, int l, int mid, int r) {
int n1 = mid - l + 1; // 左子数组的长度
int n2 = r - mid; // 右子数组的长度
// 创建临时数组
int *left = (int *)malloc(n1 * sizeof(int));
int *right = (int *)malloc(n2 * sizeof(int));
// 将数据拷贝到临时数组
for (int i = 0; i < n1; i++)
left[i] = arr[l + i];
for (int j = 0; j < n2; j++)
right[j] = arr[mid + 1 + j];
// 归并临时数组到原数组
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
// 拷贝左子数组的剩余元素
while (i < n1) {
arr[k] = left[i];
i++;
k++;
}
// 拷贝右子数组的剩余元素
while (j < n2) {
arr[k] = right[j];
j++;
k++;
}
// 释放临时数组
free(left);
free(right);
}
/* 递归归并排序 */
void merge_sort_recursive(int *arr, int l, int r) {
if (l < r) {
int mid = l + (r - l) / 2
// 递归排序左半部分
merge_sort_recursive(arr, l, mid);
// 递归排序右半部分
merge_sort_recursive(arr, mid + 1, r);
// 合并两部分
merge(arr, l, mid, r);
}
}
/* 归并排序入口 */
void merge_sort(int *arr, int n) {
merge_sort_recursive(arr, 0, n - 1);
}
int main() {
int a[] = { 29,10,14,37,12,6,32 };
int sz = sizeof(a) / sizeof(a[0]); // 获取数组的大小
// BubbleSort(a, sz); // 冒泡排序
// SelectSort(a, sz); // 选择排序
// InsertSort(a, sz); // 插入排序
// ShellSort(a, sz); // 希尔排序
// Quick_Sort_Entry(a, sz); // 快速排序
// merge_sort(a, sz); // 归并排序
for(int i = 0; i < sz; i++) {
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
如果您觉得这篇文章不错,且手里较为宽裕,可以支持一下博主,一分也是缘分😊
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇