1.函数的声明与定义
sort.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 5
typedef int KeyType;
typedef char InfoType;
typedef struct {
KeyType key; //这里的key就是我们要排序的值
InfoType data;
}RecType;
#ifndef __SORT_H__
#define __SORT_H__
//直接插入
void InsertSort(RecType R[], int n);
//折半插入排序(二分法插入排序)
void BinInsertSort(RecType R[], int n);
//希尔排序
void ShellSort(RecType R[], int n);
//冒泡排序
void BubbleSort(RecType R[], int n);
//改进冒泡排序
void BubbleSort_2(RecType R[], int n);
//快速排序
void QuickSort(RecType R[], int s, int t);
//简单选择排序
void SelectSort(RecType R[], int n);
//堆排序
void HeapSort(RecType R[], int n);
//归并排序
void MergeSort(RecType R[], int n);
#endif // !__SORT_H__
2.各种排序算法函数
sort.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "sort.h"
//用于交换的函数
void swap(RecType *a, RecType *b) {
RecType tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
/*-------------------------------------------------------1.插入排序--------------------------------------------------------------------------------------------*/
//直接插入排序
void InsertSort(RecType R[], int n) { //这里的key就是我们要排序的值
int i, j; RecType tmp; //tmp为中间变量
for (i = 1; i < n; i++){ //i=1,就是默认i=0已经插入到有序组里(因为这个算法是使用一个数组来完成直接插入的无序区到有序区的插入)
if (R[i].key < R[i - 1].key) { //反序时
tmp = R[i]; //R[i]就是这一轮要插入的值
j = i - 1;
do{
R[j + 1] = R[j];
j--;
}while(j >= 0 && R[j].key > tmp.key); //这一步是把无序区要插入的值与有序区的所有值进行比较
R[j + 1] = tmp;
}
}
}
//折半插入排序(二分法插入排序)
void BinInsertSort(RecType R[], int n) {
int i, j, low, high, mid;
RecType tmp;
for (i = 1; i < n; i++) {
if (R[i].key < R[i - 1].key) {
tmp = R[i];
low = 0;
high = i - 1;
while (low <= high) {
mid = (low + high) / 2; //先找到中间值
if (tmp.key < R[mid].key) { //判断要插入的值是否小于中间的数
high = mid - 1; //小于就将high为变为mid-1,这样接下来就在low~(mid-1)这个左边区间继续比较
}
else {
low = mid + 1; //同理
}
}
for (j = i - 1; j >= high + 1; j--) { //因为是在一个数组中插入,所以要先集中进行后移
R[j + 1] = R[j];
}
R[high + 1] = tmp; //插入
}
}
}
//希尔排序
void ShellSort(RecType R[], int n) {
int i, j, d;
RecType tmp;
d = n / 2; //d是为增量初值,自己定的数(该排序就是第0个元素和第0+d个元素比较交换,知道最后d=0)
while (d > 0) {
for (i = d; i < n; i++) {
tmp = R[i]; //tmp就是第i+d个
j = i - d; //j就是第i-d个
while (j >= 0 && tmp.key < R[j].key) { //比较两元素大小,进行交换
R[j + d] = R[j];
j = j - d;
}
R[j + d] = tmp;
}
d = d / 2;
}
}
/*-------------------------------------------------------2.交换排序--------------------------------------------------------------------------------------------*/
//冒泡排序
void BubbleSort(RecType R[], int n) {
int i, j;
for (i = 0; i < n - 1; i++) {
for (j = n - 1; j > i; j--) {
if (R[j].key < R[j - 1].key) {
swap(&R[j], &R[j - 1]);
}
}
}
}
//改进冒泡排序
void BubbleSort_2(RecType R[], int n) {
int i, j;
bool exchange;
for (i = 0; i < n - 1; i++) {
exchange = false;
for (j = n - 1; j > i; j--) {
if (R[j].key < R[j - 1].key) {
swap(&R[j], &R[j - 1]);
exchange = true; //一旦有交换就置exchange为真
}
}
if (!exchange) { //没有发生交换,就中途结束算法(因为没发生交换就已经说明排序好了,再比较后面就是浪费时间)
return;
}
}
}
//快速排序
int partition(RecType R[], int s, int t) { //一趟划分(一趟就是把比基准数小的全放在左边,大的全放在右边,再递归重复这样)
int i = s, j = t;
RecType tmp = R[i]; //从R[i]为基准
while (i < j) { //从两端交替向中间扫描,知道i=j为止
while (j > i && R[j].key >= tmp.key) {
j--; //从右向左扫描,找到一个小于tmp的R[j]
}
R[i] = R[j]; //找到这样一个R[j]放入R[i]
while (i < j && R[i].key <= tmp.key) { //从左向右扫描,找到一个大于tmp的R[i]
i++;
}
R[j] = R[i]; //找到这样一个R[i]放入R[j]
}
R[i] = tmp;
return i; //返回的i就是中间的值
}
void QuickSort(RecType R[], int s, int t) { //对s-t区间的元素进行快速排序
int i;
if (s < t) { //区间至少要有两个元素
i = partition(R, s, t); //先划分
QuickSort(R, s, i - 1); //对左区间递归排序
QuickSort(R, i+1, t); //对右区间进行递归排序
}
}
/*-------------------------------------------------------3.选择排序--------------------------------------------------------------------------------------------*/
//简单选择排序
void SelectSort(RecType R[], int n) {
int i, j, k;
for (i = 0; i < n - 1; i++) { //做第i躺排序
k = i;
for (j = i + 1; j < n; j++) { //在当前无序区R[i...n-1]中选key最小的数R[k]
if (R[j].key < R[k].key) {
k = j; //k记下目前找到最小关键字所在的位置
}
}
if (k != i) { //i和k位置的两个元素互换
swap(&R[i], &R[k]);
}
}
}
//堆排序(有分大顶堆和小顶堆,这里算法是大顶堆的方式)
//过程,有一个无序区,将无序区的元素构建成大顶堆(保证了最大的在最前面),最大的(也就是根节点)和最后一个元素互换就完成了将最大的放在最后面。
//继续构建大顶堆,继续互换,直到整个序列排列好
void sift(RecType R[], int low,int high) { //堆排序是把序列看成完全二叉树,第i个结点的左孩子为第2i个,右孩子为第2i+1个元素
int i = low, j = 2 * i; //R[j]是R[i]的左孩子
RecType tmp = R[i]; //保存好根节点
while (j <= high) {
if (j < high && R[j].key < R[j + 1].key) { //若右孩子较大,把j指向右孩子
j++;
}
if (tmp.key < R[j].key) { //若根节点小于最大孩子的关键字
R[i] = R[j]; //将R[j]调整到双亲节点位置上
i = j; //修改i和j值,以便继续向下筛选
j = 2 * i;
}
else {
break; //若根节点大于等于最大孩子关键字,筛选结束
}
}
R[i] = tmp; //被筛选结点放入最终位置上,至此大顶堆构建完成
}
void HeapSort(RecType R[], int n) {
int i;
for (i = n / 2; i >= 1; i--) { //循环建立初始堆,调用sift算法n/2次
sift(R, i, n);
}
for (i = n; i >= 2; i--) { //进行n-1趟完成堆排序,每一趟堆中元素个数减一
swap(&R[1], &R[i]); //将跟结点元素和最后一个元素互换
sift(R, 1, i - 1); //继续筛选,得到i-1个结点的堆
}
}
/*-------------------------------------------------------4.归并排序--------------------------------------------------------------------------------------------*/
//归并排序
void Merge(RecType R[], int low, int mid, int high) {
RecType* R1;
int i = low, j = mid + 1, k = 0; //k是R1的下标,i,j分别是第1,2段的下标
R1 = (RecType*)malloc((high - low + 1) * sizeof(RecType)); //动态分配空间
while (i <= mid && j <= high) { //在第一段和第二段均为扫描完成循环
if (R[i].key <= R[j].key) { //将第一段中的元素放入R1中
R1[k] = R[i];
i++;
k++;
}
else { //将第二段中的元素放入R1中
R1[k] = R[j];
j++;
k++;
}
}
while (i <= mid) { //将第一段余下的部分复制到R1
R1[k] = R[i];
i++;
k++;
}
while (j <= high) { //将第二段余下的部分复制到R1
R1[k] = R[j];
j++;
k++;
}
for (k = 0, i = low; i <= high; k++, i++) { //将R1复制到R[low...high]中
R[i] = R1[k];
}
free(R1);
}
void MergePass(RecType R[], int length, int n) { //对整个排序序列进行一趟归并
int i;
for (i = 0; i + 2 * length - 1 < n; i = i + 2 * length) { //归并length长的两相邻子表
Merge(R, i, i + length - 1, i + 2 * length - 1);
}
if (i + length - 1 < n - 1) { //余下两个子表,后者的长度小于length
Merge(R, i, i + length - 1, n - 1); //归并这两个子表
}
}
void MergeSort(RecType R[], int n) { //二路归并排序
int length;
for (length = 1; length < n; length = 2 * length) { //进行log2n(这里是以2为底数的意思)躺归并
MergePass(R, length, n);
}
}
3.测试主函数
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "sort.h"
int main() {
RecType Array[MaxSize] = { 5,'A',4,'A',3,'A',2,'A',1,'A' };
//InsertSort(Array, MaxSize);
//BinInsertSort(Array, MaxSize);
//ShellSort(Array, MaxSize);
//BubbleSort(Array, MaxSize);
//BubbleSort_2(Array, MaxSize);
//QuickSort(Array, 0, 4);
//SelectSort(Array, MaxSize);
//HeapSort(Array, MaxSize-1); //堆算法是将R[1...n]排序,我们的数组从0开始,所以传的是MaxSize-1大小,这样也就代表无法排序第0个元素
MergeSort(Array, MaxSize);
for (int i = 0; i < 5; i++) {
printf("%d ", Array[i].key);
}
return 0;
}
4.算法的复杂度
稳定性:若序列中有两个相同的数,每一次排序会不会改变这两个相同数的前后位置,不改变即为稳定,改变即为不稳定
如:{1,2(第一次出现),2(第二次出现),3},经过一次排序后,第二次出现的2排在第一次出现的2前面就是不稳定。
归位:经过一次排序后就能决定一个元素的位置(该位置到最后都不会变了)
平均时复 | 最坏时复 | 最好时复 | 空间复杂度 | 稳定性 | 归位 | |
直接插入 | 稳定 | 不归位 | ||||
折半(二分法)插入 | 稳定 | 不归位 | ||||
希尔排序 | 不稳定 | 归位 | ||||
冒泡排序 | 稳定 | 归位 | ||||
快速排序 | 不稳定 | 归位 | ||||
简单选择排序 | 不稳定 | 归位 | ||||
堆排序 | 不稳定 | 归位 | ||||
归并排序 | 稳定 | 不归位 |