数据结构-各种排序算法(插入、交换、选择、归并)

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前面就是不稳定。

归位:经过一次排序后就能决定一个元素的位置(该位置到最后都不会变了)

平均时复最坏时复最好时复空间复杂度稳定性归位
直接插入O\l(n^{2})O\l(n^{2})O\l(n)O\l(1)稳定不归位
折半(二分法)插入O\l(n^{2})O\l(n^{2})O\l(n)O\l(1)稳定不归位
希尔排序O\l(n^{1.3})O\l(n^{2})O\l(n)O\l(1)不稳定归位
冒泡排序O\l(n^{2})O\l(n^{2})O\l(n)O\l(1)稳定归位
快速排序O\l( n\log_{2}n )O\l(n^{2})O\l( n\log_{2}n )O\l(\log_{2}n)不稳定归位
简单选择排序O\l(n^{2})O\l(n^{2})O\l(n^{2})O\l(1)不稳定归位
堆排序O\l( n\log_{2}n )O\l( n\log_{2}n )O\l( n\log_{2}n )O\l(1)不稳定归位
归并排序O\l( n\log_{2}n )O\l( n\log_{2}n )O\l( n\log_{2}n )O\l(n)稳定不归位

 

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内排序是指在排序过程中所有数据都能被放在内存中完成排序。常见的内排序算法包括以下几种: 1. 冒泡排序(Bubble Sort):重复走访要排序的数列,每次比较相邻两个元素,如果它们的顺序错误就把它们交换过来。 2. 选择排序(Selection Sort):每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 3. 插入排序(Insertion Sort):将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。 4. 希尔排序(Shell Sort):将待排序的数组元素按下标的一定增量分组,对每组使用插入排序算法进行排序,随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,整个数组恰被分成一组,最后进行插入排序。 5. 快速排序(Quick Sort):通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后分别对这两部分继续进行排序。 6. 归并排序(Merge Sort):将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。 7. 堆排序(Heap Sort):利用堆这种数据结构所设计的一种排序算法。堆排序的过程就是将待排序的序列构造成一个堆,根据堆的性质,最大(或最小)的元素就是堆顶的根节点,将其与堆末尾元素交换,然后将剩余的元素重新构造成一个堆,重复此步骤直至整个序列有序。 8. 计数排序(Counting Sort):根据待排序序列中每个元素在序列中出现的次数,统计出每个元素在最终有序序列中的位置,进而将待排序序列排序。 9. 桶排序(Bucket Sort):将待排序序列中的元素按一定规则分配到不同的桶中,对每个桶中的元素进行排序,最后将所有桶中的元素按顺序合并成有序序列。 10. 基数排序(Radix Sort):将待排序元素按照位数划分为不同的数字,然后按照每个数字的大小依次进行排序,直至所有位数排序完成,最终得到一个有序序列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值