数据结构 — 排序 — 交换排序
- 一.交换排序
- 1.基本思想
- 2.冒泡排序
- 2.1.算法讲解
- 2.2.代码实现
- 2.2.1.函数定义
- 2.2.2.算法接口实现
- 2.2.3.测试代码实现
- 2.2.4.测试展示
- 3.快速排序
- 3.1.算法讲解
- 3.2.各大算法分别单独实现
- 3.2.1快速排序hoare版本
- 3.2.2.快速排序hoare改进版三数取中选key法
- 3.2.3.快速排序hoare版本改进版小区间优化法
- 3.2.4.快速排序挖坑法
- 3.2.5.快速排序双指针法
- 3.2.6.快速排序非递归版
- 3.3.算法完整源码分享
- 3.3.1函数定义
- 3.3.2.算法接口实现
- 3.3.3.测试代码实现
- 3.3.4.测试展示
一.交换排序
1.基本思想
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
2.冒泡排序
2.1.算法讲解
以下只讲解冒泡排序代码的简单实现 ,想要更详细的了解冒泡排序的可以前往我之前的博客冒泡排序及其优化 (点击即可跳转),里面详细讲解了冒泡排序及其优化。
冒泡排序的特性总结:
- 冒泡排序是一种非常容易理解的排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:稳定
2.2.代码实现
2.2.1.函数定义
Sort.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
#include<time.h>
//打印
void PrintArray(int* a, int n);
//冒泡排序
void BublleSort(int* a, int n);
2.2.2.算法接口实现
Sort.c
#include"Sort.h"
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//冒泡排序
void BublleSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
int flag = 1;
for (int j = 0; j < n - i - 1; j++)
{
if (a[j + 1] < a[j])
{
Swap(&a[j + 1], &a[j]);
flag = 0;
}
}
if (flag == 1)
break;
}
}
2.2.3.测试代码实现
test.c
#include"Sort.h"
void TestBublleSort()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
printf("冒泡排序:");
BublleSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
TestBublleSort();
return 0;
}
2.2.4.测试展示
3.快速排序
3.1.算法讲解
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。
将区间按照基准值划分为左右两半部分的常见方式有:
1. hoare版本
2.hoare改进版
1. 三数取中法选key
2. 递归到小的子区间时,可以考虑使用插入排序
3.挖坑法
4.前后指针版本
5.非递归版
非递归需要借助栈来实现,将大区间划分成子区间的递归过程,需要循环实现,如果有小伙伴不懂栈的实现和使用,可以前往我之前的博客数据结构—— 栈的实现(数组栈)(点击即可跳转)了解栈的相关只是。
快速排序的特性总结:
- 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(logN)
- 稳定性:不稳定
3.2.各大算法分别单独实现
3.2.1快速排序hoare版本
Sort.h
//快速排序hoare版本
void QuickSortHoare(int* a, int begin, int end);
Sort.c
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//快速排序hoare版本
void QuickSortHoare(int* a, int begin, int end)
{
if (begin >= end)
return;
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
while (left < right && a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
QuickSortHoare(a, begin, keyi - 1);
QuickSortHoare(a, keyi + 1, end);
}
3.2.2.快速排序hoare改进版三数取中选key法
Sort.h
//1.快速排序hoare改进版三数取中选key法
void QuickSortMid(int* a, int begin, int end);
Sort.c
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//三数取中
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
if (a[begin] < a[midi])
{
if (a[end] > a[midi])
return midi;
else if (a[end] < a[midi])
return a[begin] > a[end] ? begin : end;
}
else if (a[begin] > a[midi])
{
if (a[end] < a[midi])
return midi;
else if (a[end] > a[midi])
return a[begin] < a[end] ? begin : end;
}
return midi;
}
//1.快速排序hoare版本改进版三数取中选key法
void QuickSortMid(int* a, int begin, int end)
{
if (begin >= end)
return;
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
while (left < right && a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
QuickSortMid(a,begin,keyi-1);
QuickSortMid(a, keyi + 1, end);
}
3.2.3.快速排序hoare版本改进版小区间优化法
Sort.h
//2.快速排序hoare改进版小区间优化法
void QuickSortSmall(int* a, int begin, int end);
Sort.c
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//三数取中
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
if (a[begin] < a[midi])
{
if (a[end] > a[midi])
return midi;
else if (a[end] < a[midi])
return a[begin] > a[end] ? begin : end;
}
else if (a[begin] > a[midi])
{
if (a[end] < a[midi])
return midi;
else if (a[end] > a[midi])
return a[begin] < a[end] ? begin : end;
}
return midi;
}
//2.快速排序hoare版本改进版小区间优化法
void QuickSortSmall(int* a, int begin, int end)
{
if (begin >= end)
return;
//递归到小的子区间时,可以考虑使用插入排序
//小区间可以随意取,一般取为10
if (end - begin + 1 <= 10)
{
InsertSort(a + begin, end - begin + 1);
}
else
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
// 右边找小
while (left < right && a[right] >= a[keyi])
{
--right;
}
// 左边找大
while (left < right && a[left] <= a[keyi])
{
++left;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
// [begin, keyi-1] keyi [keyi+1, end]
QuickSortSmall(a, begin, keyi - 1);
QuickSortSmall(a, keyi + 1, end);
}
}
3.2.4.快速排序挖坑法
Sort.h
//快速排序挖坑法
void QuickSort1(int* a, int begin, int end);
Sort.c
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//三数取中
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
if (a[begin] < a[midi])
{
if (a[end] > a[midi])
return midi;
else if (a[end] < a[midi])
return a[begin] > a[end] ? begin : end;
}
else if (a[begin] > a[midi])
{
if (a[end] < a[midi])
return midi;
else if (a[end] > a[midi])
return a[begin] < a[end] ? begin : end;
}
return midi;
}
//挖坑法
int PartSort1(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int key = a[begin];
int hole = begin;
while (begin < end)
{
//右边找小,填到左边坑
while (begin < end && a[end] >= key)
{
end--;
}
a[hole] = a[end];
hole = end;
while (begin < end && a[begin] <= key)
{
begin++;
}
a[hole] = a[begin];
hole = begin;
}
a[hole] = key;
return hole;
}
//快速排序挖坑版
void QuickSort1(int* a, int begin, int end)
{
if (begin >= end)
return;
int keyi = PartSort1(a, begin, end);
QuickSort1(a, begin, keyi - 1);
QuickSort1(a, keyi + 1, end);
}
3.2.5.快速排序双指针法
Sort.h
//快速排序双指针法
void QuickSort2(int* a, int begin, int end);
Sort.c
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//三数取中
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
if (a[begin] < a[midi])
{
if (a[end] > a[midi])
return midi;
else if (a[end] < a[midi])
return a[begin] > a[end] ? begin : end;
}
else if (a[begin] > a[midi])
{
if (a[end] < a[midi])
return midi;
else if (a[end] > a[midi])
return a[begin] < a[end] ? begin : end;
}
return midi;
}
//双指针法
int PartSort2(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int key = begin;
int prev = begin;
int cur = prev + 1;
while (cur <= end)
{
if (a[cur] < a[key] && ++prev != cur)
Swap(&a[cur], &a[prev]);
cur++;
}
Swap(&a[key], &a[prev]);
key = prev;
return key;
}
//快速排序双指针版
void QuickSort2(int* a, int begin, int end)
{
if (begin >= end)
return;
int keyi = PartSort2(a, begin, end);
QuickSort2(a, begin, keyi - 1);
QuickSort2(a, keyi + 1, end);
}
3.2.6.快速排序非递归版
Strck.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Strck
{
STDataType* a;
int top;
int capacity;
}ST;
//初始化/销毁
void STInit(ST* pst);
void STDestroy(ST* pst);
//压栈/出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
//获取栈顶元素
STDataType STTop(ST* pst);
//判空
bool STEmpty(ST* pst);
//统计栈内元素个数
int STSize(ST* pst);
Strck.c
#include"Strck.h"
//初始化/销毁
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
//压栈/出栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
//获取栈顶元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
//判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
//统计栈内元素个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
Sort.h
//快速排序非递归版
void QuickSortNonR(int* a, int begin, int end);
Sort.c
//快速排序非递归版
void QuickSortNonR(int* a, int begin, int end)
{
ST s;
STInit(&s);
STPush(&s, end);
STPush(&s, begin);
while (!STEmpty(&s))
{
int left = STTop(&s);
STPop(&s);
int right = STTop(&s);
STPop(&s);
int keyi = PartSort2(a, left, right);
//[left,keyi-1] keyi [keyi+1,right]
if (left < keyi - 1)
{
STPush(&s, keyi - 1);
STPush(&s, left);
}
if (keyi + 1 < right)
{
STPush(&s, right);
STPush(&s, keyi + 1);
}
}
STDestroy(&s);
}
3.3.算法完整源码分享
3.3.1函数定义
Strck.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Strck
{
STDataType* a;
int top;
int capacity;
}ST;
//初始化/销毁
void STInit(ST* pst);
void STDestroy(ST* pst);
//压栈/出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
//获取栈顶元素
STDataType STTop(ST* pst);
//判空
bool STEmpty(ST* pst);
//统计栈内元素个数
int STSize(ST* pst);
Strck.c
#include"Strck.h"
//初始化/销毁
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
//压栈/出栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
//获取栈顶元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
//判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
//统计栈内元素个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
Sort.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
#include<time.h>
//快速排序hoare版本
void QuickSortHoare(int* a, int begin, int end);
//1.快速排序hoare改进版三数取中选key法
void QuickSortMid(int* a, int begin, int end);
//2.快速排序hoare改进版小区间优化法
void QuickSortSmall(int* a, int begin, int end);
//快速排序挖坑法
void QuickSort1(int* a, int begin, int end);
//快速排序双指针法
void QuickSort2(int* a, int begin, int end);
//快速排序非递归版
void QuickSortNonR(int* a, int begin, int end);
3.3.2.算法接口实现
Sort.c
#include"Sort.h"
#include"Strck.h"
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//快速排序hoare版本
void QuickSortHoare(int* a, int begin, int end)
{
if (begin >= end)
return;
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
while (left < right && a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
QuickSortHoare(a, begin, keyi - 1);
QuickSortHoare(a, keyi + 1, end);
}
//三数取中
int GetMidi(int* a, int begin, int end)
{
int midi = (begin + end) / 2;
if (a[begin] < a[midi])
{
if (a[end] > a[midi])
return midi;
else if (a[end] < a[midi])
return a[begin] > a[end] ? begin : end;
}
else if (a[begin] > a[midi])
{
if (a[end] < a[midi])
return midi;
else if (a[end] > a[midi])
return a[begin] < a[end] ? begin : end;
}
return midi;
}
//1.快速排序hoare版本改进版三数取中选key法
void QuickSortMid(int* a, int begin, int end)
{
if (begin >= end)
return;
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
while (left < right && a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
QuickSortMid(a,begin,keyi-1);
QuickSortMid(a, keyi + 1, end);
}
//2.快速排序hoare版本改进版小区间优化法
void QuickSortSmall(int* a, int begin, int end)
{
if (begin >= end)
return;
//递归到小的子区间时,可以考虑使用插入排序
//小区间可以随意取,一般取为10
if (end - begin + 1 <= 10)
{
InsertSort(a + begin, end - begin + 1);
}
else
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int left = begin, right = end;
int keyi = begin;
while (left < right)
{
// 右边找小
while (left < right && a[right] >= a[keyi])
{
--right;
}
// 左边找大
while (left < right && a[left] <= a[keyi])
{
++left;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
// [begin, keyi-1] keyi [keyi+1, end]
QuickSortSmall(a, begin, keyi - 1);
QuickSortSmall(a, keyi + 1, end);
}
}
//挖坑法
int PartSort1(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int key = a[begin];
int hole = begin;
while (begin < end)
{
//右边找小,填到左边坑
while (begin < end && a[end] >= key)
{
end--;
}
a[hole] = a[end];
hole = end;
while (begin < end && a[begin] <= key)
{
begin++;
}
a[hole] = a[begin];
hole = begin;
}
a[hole] = key;
return hole;
}
//快速排序挖坑版
void QuickSort1(int* a, int begin, int end)
{
if (begin >= end)
return;
int keyi = PartSort1(a, begin, end);
QuickSort1(a, begin, keyi - 1);
QuickSort1(a, keyi + 1, end);
}
//双指针法
int PartSort2(int* a, int begin, int end)
{
int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);
int key = begin;
int prev = begin;
int cur = prev + 1;
while (cur <= end)
{
if (a[cur] < a[key] && ++prev != cur)
Swap(&a[cur], &a[prev]);
cur++;
}
Swap(&a[key], &a[prev]);
key = prev;
return key;
}
//快速排序双指针版
void QuickSort2(int* a, int begin, int end)
{
if (begin >= end)
return;
int keyi = PartSort2(a, begin, end);
QuickSort2(a, begin, keyi - 1);
QuickSort2(a, keyi + 1, end);
}
//快速排序非递归版
void QuickSortNonR(int* a, int begin, int end)
{
ST s;
STInit(&s);
STPush(&s, end);
STPush(&s, begin);
while (!STEmpty(&s))
{
int left = STTop(&s);
STPop(&s);
int right = STTop(&s);
STPop(&s);
int keyi = PartSort2(a, left, right);
//[left,keyi-1] keyi [keyi+1,right]
if (left < keyi - 1)
{
STPush(&s, keyi - 1);
STPush(&s, left);
}
if (keyi + 1 < right)
{
STPush(&s, right);
STPush(&s, keyi + 1);
}
}
STDestroy(&s);
}
3.3.3.测试代码实现
test.c
#include"Sort.h"
void TestQuickSortHoare()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序hoaer版:");
QuickSortHoare(a, 0,sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
void TestQuickSortMid()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序hoare改进版三数取中选key法:");
QuickSortMid(a, 0, sizeof(a) / sizeof(int) - 1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
void TestQuickSortSmall()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序hoare改进版小区间优化法:");
QuickSortSmall(a, 0, sizeof(a) / sizeof(int) - 1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
void TestQuickSort1()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序挖坑版:");
QuickSort1(a, 0, sizeof(a) / sizeof(int) - 1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
void TestQuickSort2()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序双指针版:");
QuickSort2(a, 0, sizeof(a) / sizeof(int) - 1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
void TestQuickSortNonR()
{
int a[] = { 2,4,5,7,8,0,9,6,3,1 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
printf("快速排序非递归版:");
QuickSortNonR(a, 0, sizeof(a) / sizeof(int) - 1);
PrintArray(a, sizeof(a) / sizeof(int));
printf("\n");
}
int main()
{
TestQuickSortHoare();
TestQuickSortMid();
TestQuickSortSmall();
TestQuickSort1();
TestQuickSort2();
TestQuickSortNonR();
return 0;
}