插入排序、折半插入排序、shell排序、直接选择排序、树形排序、冒泡排序、快速排序、归并排序的C语言代码实现

之前数据结构课程学的几个重要的排序算法,现在整理了一下用于回顾知识和分享

一.各排序基本思想

  • 插入排序:基本思想为每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。
  • 折半插入排序:基本思想为将数组分为有序部分和待排序部分,每次按顺序从待排序列中选择一个元素,通过折半查找的方式,从有序数列部分找到待排元素的正确位置并插入
  • shell排序:基本思想是把记录按下标的一定量分组,对每组使用直接插入排序的算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件被分成一组,算法结束
  • 直接选择排序:基本思想是第一趟从n个元素的数据序列中选出关键字最小(或最大)的元素并放到最前(或最后)位置,下一趟再从n-1个元素中选出最小(或最大)的元素并放到次前(后)位置,以此类推,经过n-1趟完成排序
  • 树形排序:基本思想是先把待排序的n个记录的关键字两两进行比较,取出较小者。然后再在 n/2 个较小者中,采用同样的方法进行比较选出每两个中的较小者,如此反复,直至选出最小关键字记录为止
  • 冒泡排序:基本思想是将第一个元素和第二个元素进行比较,若为逆序则将两个元素交换,然后比较第二个元素和第三个元素。依次类推,直至第 n-1个元素和第 n个元素进行比较为止
  • 快速排序:基本思想是使用分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小。之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的
  • 归并排序:归并,就是把元素合并在一起,在这里我们主要是通过把数据元素分开,当数组元素细化后将它在合并起来。在归并算法中,主要用到了分治和合并的两种思想。分治:将数组元素细化,分成n多个单个元素序列。合并:一步一步将分治的数组元素合并起来,合并的同时进行顺序排序。

二.排序算法

#include <Windows.h>
#include <iostream>
using namespace std;

//插入排序
void insertSort(int a[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int p = i;
		for (int j = i - 1; j >= 0; j--)
		{
			if (tmp >= a[j]) break;
			a[p] = a[j];
			p = j;
		}
		a[p] = tmp;
	}
}

//折半插入排序
void BinaryInsertSort(int a[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int L = 0, R = i - 1;
		while (L <= R)
		{
			int m = (L + R) / 2;

			if (a[m] > tmp) R = m - 1;
			else L = m + 1;
		}
		int ans = L;
		for (int j = i; j > L; j--)
		{
			a[j] = a[j - 1];
		}
		a[L] = tmp;
	}
}

//Shell排序
void ShellSort(int A[], int n, int s)
{
	int i, j, k;
	int temp;
	for (k = s; k > 0; k >>= 1)
	{
		for (i = k; i < n; i++)
		{
			temp = A[i];
			j = i - k;
			while (j >= 0 && temp < A[j])
			{
				A[j + k] = A[j];
				j -= k;
			}
			A[j + k] = temp;
		}
	}
}

//直接选择排序
void DirectSelectSort(int a[], int n)
{
	int tmp;
	for (int i = 0; i < n - 1; i++)
	{
		int k = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] < a[k])
			{
				k = j;
			}
		}
		if (i != k)
		{
			tmp = a[k];
			a[k] = a[i];
			a[i] = tmp;
		}
	}
}

//树形排序
#define Left(i)   (2 * (i) - 2 * n)
#define Right(i)  (2 * (i) - 2 * n + 1)
#define Father(i)  (n + (i) / 2)
//#define Brother(i) ((i) & 1 ? (i) - 1 : (i) + 1)
#define Root (2 * n - 2)
#define min(a, b)  (a) < (b) ? (a) : (b)
#define INF_VALUE 0x3FFFFFFF

void TreeSort(int a[], int n)
{
	int* t = new int[2 * n];
	memcpy(t, a, sizeof(int) * n);
	for (int k = n; k <= Root; k++)
	{
		t[k] = min(t[Left(k)], t[Right(k)]);
	}
	a[0] = t[Root];
	for (int k = 1; k <= n - 1; k++)
	{
		int sel = Root;
		int left = Left(sel);
		while (left >= 0)
		{
			sel = (t[sel] == t[left]) ? left : (left + 1);
			left = Left(sel);
		}
		t[sel] = INF_VALUE;
		sel = Father(sel);
		while (sel <= Root)
		{
			t[sel] = min(t[Left(sel)], t[Right(sel)]);
			sel = Father(sel);
		}
		a[k] = t[Root];
	}
	delete[] t;
}

//冒泡排序
void BubbleSort(int a[], int n)
{
	for (int k = 0; k < n; k++)
	{
		for (int i = 0; i < n - 1; i++)
		{
			if (a[i] > a[i + 1])
			{
				int tmp = a[i];
				a[i] = a[i + 1];
				a[i + 1] = tmp;
			}
		}
	}
}

//快速排序
void QuickSort(int A[], int low, int high)
{
	int i, j;
	if (low >= high) return;
	i = low;
	j = high;
	int temp = A[i];
	while (i < j)
	{
		while (i < j && temp < A[j])
			j--;
		if (i < j)
		{
			A[i++] = A[j];
		}
		while (i < j && temp >= A[i])
			i++;
		if (i < j)
		{
			A[j--] = A[i];
		}
	}
	A[i] = temp;
	QuickSort(A, low, --j);
	QuickSort(A, ++i, high);

}

// 基数排序中的队列类
class Queue {
private:
	int* a;
	int maxSize;
	int front, rear;
public:
	Queue(int sz);
	~Queue();
	void flush();
	bool empty();
	bool full();
	void clear();
	int size();
	bool push(int x);
	void pop();
	int Front();
};

Queue::Queue(int sz = 10) {
	maxSize = sz;
	a = new int[sz];
	front = rear = 0;
}

Queue::~Queue()
{
	delete[] a;
}

void Queue::flush() {
	delete[] a;
}

bool Queue::empty() {
	return front == rear;
}

bool Queue::full() {
	return rear == maxSize;
}

void Queue::clear() {
	front = rear = 0;
}
int Queue::size() {
	return  rear - front;
}

bool Queue::push(int x) {
	if (full())
		return false;
	a[rear++] = x;
	return true;
}

void Queue::pop() {
	front++;
}

int Queue::Front() {
	return a[front];
}

void RadixSort(int a[], int n)
{
	int max = a[0];
	for (int i = 1; i < n; i++)
	{
		if (a[i] > max)
			max = a[i];
	}
	int l = 0;
	while (max != 0)//求出最大长度
	{
		max = max / 10;
		l++;
	}
	Queue list[10];
	for (int i = 1; i <= l; i++)//第i个回合
	{
		int x = 1;
		for (int j = 0; j < n; j++)
		{
			int m = a[j] / x % 10;
			list[m].push(a[j]);
		}
		x *= 10;
		int y = 0;
		for (int j = 0; j < 10; j++)
		{
			if (list[j].empty())
				continue;
			else
			{
				while (!list[j].empty())
				{
					a[y] = list[j].Front();
					list[j].pop();
					y++;
				}
			}
		}
	}
}

//归并排序
template<class T>
void TwoWayMerge(T Dst[], T Src[], int s, int e1, int e2)
//两个子文件归并为一个子文件
//源数组中[s:e1]和[e1+1:e2]归并到目的数组中[s:e2]
{
	int s1, s2;
	for (s1 = s, s2 = e1 + 1; s1 <= e1 && s2 <= e2;)
	{
		if (Src[s1] <= Src[s2])
			Dst[s++] = Src[s1++];
		else
			Dst[s++] = Src[s2++];

	}
	if (s1 <= e1)
		memcpy(&Dst[s], &Src[s1], (e1 - s1 + 1) * sizeof(T));
	else
		memcpy(&Dst[s], &Src[s2], (e2 - s2 + 1) * sizeof(T));
}

template<class T>
void OnePassMerge(T Dst[], T Src[], int Len, int n)
//一趟归并:每两个相邻子文件归并,子文件长度为Len
{
	int i;
	for (i = 0; i + 2 * Len < n; i += 2 * Len)
		TwoWayMerge(Dst, Src, i, i + Len - 1, i + 2 * Len - 1);
	if (i < n - Len)
		TwoWayMerge(Dst, Src, i, i + Len - 1, n - 1);
	else
		memcpy(&Dst[i], &Src[i], (n - i) * sizeof(T));
}

template<class T>
void MergeSort(T A[], int n)
{
	int k = 1; // 初始子文件长度
	T* B = new T[n];
	while (k < n)
	{
		OnePassMerge(B, A, k, n);
		k <<= 1;
		if (k >= n)
			memcpy(A, B, n * sizeof(T));
		else
		{
			OnePassMerge(A, B, k, n);
			k <<= 1;
		}//交叉使用A,B,效率高		
	}
}

三.算法性能测试

  • 1.性能测试代码
  int main()
{
    //生成随机数
	//排序时间
	//直接插入排序
    int x[100000];
    srand(time(0));
    for (int i = 0; i < 100000; i++)
    {
        x[i] = rand() * rand();
    }
    DWORD startTime1 = GetTickCount64();//计时开始
    insertSort(x, 100000);
    DWORD endTime1 = GetTickCount64();//计时结束
    cout << "The insertSort time is:" << endTime1 - startTime1 << "ms" << endl;
    //折半插入排序
    srand(time(0));
    for (int i = 0; i < 100000; i++)
    {
        x[i] = rand() * rand();
    }
    DWORD startTime2 = GetTickCount64();//计时开始
    BinaryInsertSort(x, 100000);
    DWORD endTime2 = GetTickCount64();//计时结束
    cout << "The BinaryInsertSort time is:" << endTime2 - startTime2 << "ms" << endl;
    //Shell排序
    srand(time(0));
    for (int i = 0; i < 100000; i++)
    {
        x[i] = rand() * rand();
    }
    DWORD startTime3 = GetTickCount64();//计时开始
    ShellSort(x, 100000,50000);
    DWORD endTime3 = GetTickCount64();//计时结束
    cout << "The ShellSort time is:" << endTime3 - startTime3 << "ms" << endl;
    //直接选择排序
    srand(time(0));
    for (int i = 0; i < 100000; i++)
    {
        x[i] = rand() * rand();
    }
    DWORD startTime4 = GetTickCount64();//计时开始
    DirectSelectSort(x, 100000);
    DWORD endTime4 = GetTickCount64();//计时结束
    cout << "The DirectSelectSort time is:" << endTime4 - startTime4 << "ms" << endl;
	//树形排序
	srand(time(0));
	for (int i = 0; i < 100000; i++)
	{
		x[i] = rand() * rand();
	}
	DWORD startTime5 = GetTickCount64();//计时开始
	TreeSort(x, 100000);
	DWORD endTime5 = GetTickCount64();//计时结束
	cout << "The TreeSort time is:" << endTime5 - startTime5 << "ms" << endl;
	//冒泡排序
	srand(time(0));
	for (int i = 0; i < 100000; i++)
	{
		x[i] = rand() * rand();
	}
	DWORD startTime6 = GetTickCount64();//计时开始
	BubbleSort(x, 100000);
	DWORD endTime6 = GetTickCount64();//计时结束
	cout << "The BubbleSort time is:" << endTime6 - startTime6 << "ms" << endl;
	//快速排序
	srand(time(0));
	for (int i = 0; i < 100000; i++)
	{
		x[i] = rand() * rand();
	}
	DWORD startTime7 = GetTickCount64();//计时开始
	QuickSort(x, 0,99999);
	DWORD endTime7 = GetTickCount64();//计时结束
	cout << "The QucikSort time is:" << endTime7 - startTime7 << "ms" << endl;
	//归并排序
	srand(time(0));
	for (int i = 0; i < 100000; i++)
	{
		x[i] = rand() * rand();
	}
	DWORD startTime8 = GetTickCount64();//计时开始
	MergeSort(x, 100000);
	DWORD endTime8 = GetTickCount64();//计时结束
	cout << "The MergeSort time is:" << endTime8 - startTime8 << "ms" << endl;
    return 0;
}
  • 2.测试结果
    在这里插入图片描述

四.分析总结

直接插入排序的时间复杂度为O( )
折半插入排序的时间复杂度为
O( )
Shell排序的时间复杂度为O( )
直接选择排序的时间复杂度为O( )
树形选择排序的时间复杂度为O( )
冒泡排序的时间复杂度为O( )
快速排序的时间复杂度为O( )
基数排序的时间复杂度为O( )
归并排序的时间复杂度为O( )

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值