目录
2.3.3 快速排序(也算是老朋友了,qsort大家应该用挺多的哈) 🚀
Stack.h 🚀
Stack.c 🚀
1. 排序的概念及其运用 🚀
1.1 排序的概念 🚀
1.2 排序运用 🚀
1.3 常见的排序算法 🚀
2. 常见排序算法的实现 🚀
2.1 插入排序 🚀
插入和希尔排序的实现 🚀
2.2 选择排序 🚀
直接选择和堆排序的实现 🚀
2.3 交换排序 🚀
2.4 归并排序 🚀
3.排序算法复杂度及稳定性分析 🚀
4.源代码 🚀
Sort.h 🚀
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include"Stack.h"
void Print(int* arr, int sz);
//2
void InsertSort(int* arr, int sz);
//3
void ShellSort(int* arr, int sz);
//1
void BubbleSort(int* arr, int sz);
//4
void SelectSort(int* arr, int sz);
//6
void HeapSort(int* arr, int sz);
//5
void QuickSort(int* arr, int begin, int end);
void QuickSortNonR(int* arr, int begin, int end);
//7
void MergeSort(int* arr, int begin, int end);
void MergeSortNonR(int* arr, int n);
Stack.h 🚀
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct Stack {
STDateType* arr;
int top;
int capacity;
}Stack;
//初始化
void InitStack(Stack* pst);
//入栈
void PushStack(Stack* pst, STDateType x);
//出栈
void PopStack(Stack* pst);
//取栈顶
STDateType StackTop(Stack* pst);
//判空
bool EmptyStack(Stack* pst);
//求数据个数
int StackSize(Stack* pst);
//销毁
void DestoryStack(Stack* pst);
Stack.c 🚀
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
//初始化
void InitStack(Stack* pst)
{
//int newcapacity = pst->capacity = 0 ? 4 : pst->capacity * 2;
Stack* new = (Stack*)malloc(sizeof(Stack));
if (new == NULL)
{
perror("InitStack:");
exit(-1);
}
pst->arr = new;
pst->capacity = 1;
pst->top = 0;
}
//入栈
void PushStack(Stack* pst, STDateType x)
{
assert(pst);
if (pst->capacity == pst->top)
{
int newcapacity = pst->capacity * 2;
STDateType* new = (STDateType*)realloc(pst->arr,sizeof(STDateType) * newcapacity);
if (new == NULL)
{
perror("InitStack:");
exit(-1);
}
pst->arr = new;
pst->capacity = newcapacity;
}
pst->arr[pst->top] = x;
pst->top++;
}
//出栈
void PopStack(Stack* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
//取栈顶
STDateType StackTop(Stack* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->arr[pst->top - 1];
}
//判空
bool EmptyStack(Stack* pst)
{
assert(pst);
return pst->top == 0;
}
//求数据个数
int StackSize(Stack* pst)
{
assert(pst);
return pst->top;
}
//销毁
void DestoryStack(Stack* pst)
{
assert(pst);
free(pst->arr);
pst->capacity = 0;
pst->top = 0;
}
Sort.c 🚀
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"
//后面用的多,直接写成函数方便一点
void Swap(int* p, int* q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//思想:冒泡其实没啥说的,注意第二个循环的-1,因为我们
//这里用的是arr[j]和arr[j+1]小心越界的问题
void BubbleSort(int* arr,int sz)
{
int i = 0;
int j = 0;
for (i = 0; i < sz; i++)
{
for (j = 0; j < sz-i-1; j++)
{
if (arr[j] > arr[j + 1])
Swap(&arr[j], &arr[j + 1]);
}
}
}
//思想:开始让第一个数有序,然后从后一个数插入,
//如果后一个数小于前面的数,前面的数就往后挪动
//注意不管是哪种情况退出,最后都是把数据放到
//end+1,所有干脆我们就放在外面
void InsertSort(int* arr, int sz)
{
for(int i = 0; i < sz - 1;i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end--;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
//思想:先对数组进行预排序,预排序是为了
//直接插入更快,越有序,直接插入就越快,这
//也是希尔快的原因,预排序完了就直接插入排序
//预排序:分组(离相同gap)为一组进行插入排序
void ShellSort(int* arr, int sz)
{
// 1、gap > 1 预排序
// 2、gap == 1 直接插入排序
int gap = sz;
while (gap>1)
{
//保证最后为直接插入排序
gap = gap / 3 + 1;
//这里i++是使多组预排序同时进行
for (int i = 0; i < sz - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
//思路:选两个值的下标,开始都在左边,然后用一个记录下标的cur去遍历数组,
// 一个记录最大(maxi),一个记录最小(mini),然后遍历数组把最小的放
// 在左边,最大的,放在右边,直到left,right相遇
void SelectSort(int* arr, int sz)
{
int left = 0;
int right = sz - 1;
while (left<right)
{
int cur = left+1;
int mini = left;
int maxi = left;
//这里注意要等于哈,不然最后一个值就比掉了
//作者开始写的时候就画图搞了半天QAQ
while (cur <= right)
{
if (arr[cur] > arr[maxi])
{
maxi = cur;
}
if (arr[cur] < arr[mini])
{
mini = cur;
}
cur++;
}
Swap(&arr[left], &arr[mini]);
//如果left==maxi left就会
//和mini交换,要更新maxi
if (left == maxi)
{
maxi = mini;
}
Swap(&arr[right], &arr[maxi]);
left++;
right--;
}
}
//hoare
//思路:左边为key,右边先走,找小,左边再走,找大,然后左右交换
//一直循环直到相遇,因为是右边先走,左边已经是找小
//停住了,所有能保证相遇位置大于等于a[jeyi],然后
//交换a[keyi]和相遇位置的值,这样a[keyi]左边值就
//比他小,右边值就比他大
int PartSort1(int* arr, int left, int right)
{
int keyi = left;
while (left < right)
{
//右找小 注意:keyi在左边就右边先走
//要注意left>right的条件,不然可能越界
while (left < right && arr[right] >= arr[keyi])
{
right--;
}
//左找大
while (left < right && arr[left] <= arr[keyi])
{
left++;
}
Swap(&arr[left], &arr[right]);
}
Swap(&arr[left], &arr[keyi]);
return left;
}
//挖坑法
//思路:左边为key,先存起来,变成坑,左右两边一个变量
//右边先走,找小,然后交换,左边找大,然后交换
//最后把key的值给相遇位置
int PartSort2(int* a, int left, int right)
{
int pi = left;
int qi = right;
int key = a[left];
while (pi < qi)
{
//右找小
while (a[qi] > key && pi < qi)
{
qi--;
}
a[pi] = a[qi];
//左找大
while (a[pi] < key && pi < qi)
{
pi++;
}
a[qi] = a[pi];
}
a[qi] = key;
return qi;
}
int GetMidIndex(int* a, int left, int right)
{
//int mid = (left + right) / 2;
int mid = left + (right - left) / 2;
// left mid right
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] > a[right])
{
return left;
}
else
{
return right;
}
}
else // a[left] > a[mid]
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return left;
}
else
{
return right;
}
}
}
//前后指针法
//左边为keyi,用两个变量存储left和left+1,cur去找小,
//找到了prev++再交换然后cur++,没有找到就cur++,
//这样可以保证让prev到cur的值都大于等于a[keyi]
//最后交换a[keyi]和a[prev]
int PartSort3(int* a, int left, int right)
{
//yysy,自己实现不加也行
//下面这两句也是优化,处理了有序O(N^2)的情况
int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);
int cur = left + 1;
int prev = left;
int keyi = left;
while (cur <= right)
{
//这一步很巧哈,可以多理解一下
//注意是前置++
if (a[cur] < a[keyi] && a[++prev] != a[cur])
{
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[keyi], &a[prev]);
return prev;
}
//递归
void QuickSort(int* arr, int begin, int end)
{
// 子区间相等只有一个值或者不存在那么就是递归结束的子问题
if (begin >= end)
return;
//小区间优化
// 小区间直接插入排序控制有序
//yysy,自己实现不加也行
if (end - begin + 1 <= 10)
{
InsertSort(arr + begin, end - begin + 1);
}
else
{
int keyi = PartSort3(arr, begin, end);
// [begin, keyi-1]keyi[keyi+1, end]
QuickSort(arr, begin, keyi - 1);
QuickSort(arr, keyi + 1, end);
}
}
//非递归
void QuickSortNonR(int* arr, int begin, int end)
{
Stack st;
InitStack(&st);
PushStack(&st, begin);
PushStack(&st, end);
while (!EmptyStack(&st))
{
int right = StackTop(&st);
PopStack(&st);
int left = StackTop(&st);
PopStack(&st);
//left keyi-1 keyi+1 right
int keyi = PartSort1(arr, left, right);
if (left < keyi-1)
{
PushStack(&st, left);
PushStack(&st, keyi-1);
}
if (keyi + 1 < right)
{
PushStack(&st, keyi + 1);
PushStack(&st, right);
}
}
}
//升序建大堆
先找左右小的孩子
//再和root的比 比root小就交换
//root等于那个要交换的孩子 孩子再选 然后迭代
void AdjustDown(int* arr,int root,int sz)
{
//默认左孩子
int child = root * 2 + 1;
int parent = root;
while (child<sz)
{
1、选出左右孩子中大的那个
if (child + 1 < sz && arr[child + 1] > arr[child])
child++;
//2、如果孩子大于父亲,则交换,并继续往下调整
if (arr[child] > arr[parent])
{
Swap(&arr[parent], &arr[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//思想:用向下调整算法建大堆(升序),然后把堆顶的数据
//换到最后,然后缩小数据范围,再次向下调整,反复如此
void HeapSort(int* arr, int sz)
{
//向下调整--建大堆 O(N)
int curpos = (sz - 1 - 1) / 2;
while (curpos >= 0)
{
AdjustDown(arr, curpos, sz);
curpos--;
}
//end为最后一个元素下标
int end = sz - 1;
while (end >0)
{
Swap(&arr[0], &arr[end]);
//每次从0开始向下调
AdjustDown(arr, 0, end);
end--;
}
}
void _MergeSort(int* arr, int* tmp, int begin, int end)
{
if (begin >= end)
return;
//先分后合
//为了防止数据溢出
int mid = begin + (end - begin) / 2;
//注意这里我们分成 begin - mid 和 mid+1 - end
//分成 begin - mid-1 和 mid - end 有bug (1,2)会死循环
int begin1 = begin; int end1 = mid;
int begin2 = mid+1; int end2 = end;
_MergeSort(arr, tmp, begin1, end1);
_MergeSort(arr, tmp, begin2, end2);
//printf("begin1:%d end1:%d \nbegin2:%d end2:%d\n", begin1, end1, begin2, end2);
//开始归
int i = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] < arr[begin2])
tmp[i++] = arr[begin1++];
else
tmp[i++] = arr[begin2++];
}
while(begin1 <= end1)
tmp[i++] = arr[begin1++];
while (begin2 <= end2)
tmp[i++] = arr[begin2++];
//注意第三个参数是字节大小.....开始还以为是上面错了,找半天
//注意要加上begin 因为并不是每次都是初始位置
memcpy(arr + begin, tmp + begin, (end - begin + 1)*sizeof(int));
}
//思想:先分再合
//注意我们要先把排号的数据先放进tmp数组,
//然后把数组的数据拷回arr
void MergeSort(int* arr, int begin, int end)
{
int* tmp = (int*)malloc((end-begin+1) * sizeof(int));
assert(tmp);
//避免重复开辟空间就用副本函数
_MergeSort(arr, tmp, begin, end);
free(tmp);
}
//思想:不分,直接和,注意和快排转非递归不一样,快排是前序,
//所以转非递归用栈和队列都可以,但是后续不好那么搞,
//我们直接暴力求
void MergeSortNonR(int* arr, int n)
{
int* tmp = (int*)malloc(n * sizeof(int));
assert(tmp);
int gap = 1;
int begin1 = 0; int end1 = 0;
int begin2 = 0; int end2 = 0;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
begin1 = i; end1 = i + gap - 1;
begin2 = i + gap; end2 = i + 2 * gap - 1;
//如果 end1 越界
if (end1 >= n)
end1 = n - 1;
//如果 begin2 越界 end2 越界
if (begin2 >= n && end2 >= n)
{
begin2 = n;
end2 = n - 1;
}
//如果 end2 越界
if (begin2 < n && end2 >= n)
end2 = n - 1;
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] < arr[begin2])
tmp[index++] = arr[begin1++];
else
tmp[index++] = arr[begin2++];
}
while (begin1 <= end1)
tmp[index++] = arr[begin1++];
while (begin2 <= end2)
tmp[index++] = arr[begin2++];
}
memcpy(arr,tmp,n*sizeof(int));
gap *= 2;
}
free(tmp);
}
test.c 🚀
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"
void TestOP()
{
srand((unsigned int)time(0));
const int N = 10000000;
int* a1 = (int*)malloc(sizeof(int) * N);
assert(a1);
int* a2 = (int*)malloc(sizeof(int) * N);
assert(a2);
int* a3 = (int*)malloc(sizeof(int) * N);
assert(a3);
int* a4 = (int*)malloc(sizeof(int) * N);
assert(a4);
int* a5 = (int*)malloc(sizeof(int) * N);
assert(a5);
int* a6 = (int*)malloc(sizeof(int) * N);
assert(a6);
int* a7 = (int*)malloc(sizeof(int) * N);
assert(a7);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
}
int begin1 = clock();
//InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
int begin3 = clock();
//SelectSort(a3, N);
int end3 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
int begin5 = clock();
QuickSort(a5, 0, N - 1);
int end5 = clock();
int begin6 = clock();
MergeSort(a6, 0, N-1);
int end6 = clock();
int begin7 = clock();
//BubbleSort(a7, N);
int end7 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
printf("BublleSort:%d\n", end7 - begin7);
printf("SelectSort:%d\n", end3 - begin3);
printf("HeapSort:%d\n", end4 - begin4);
printf("QuickSort:%d\n", end5 - begin5);
printf("MergeSort:%d\n", end6 - begin6);
printf("MergeSort:%d\n", end7 - begin7);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
free(a7);
}
void test1()
{
int arr[] = { 1,7,2,5,3,4,9,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
//InsertSort(arr, sz);
//BubbleSort(arr, sz); //1
//SelectSort(arr, sz);
//QuickSort(arr, 0,sz-1);
//QuickSortNonR(arr, 0, sz - 1);
//HeapSort(arr, sz);
//ShellSort(arr, sz);
//MergeSort(arr, 0, sz-1);
//MergeSortNonR(arr, sz);
Print(arr, sz);
}
int main()
{
//test1();
TestOP();
return 0;
}