排序 c++

本文详细介绍了包括插入排序、希尔排序、冒泡排序等在内的十种经典的排序算法,并提供了每种算法的具体实现代码,有助于读者深入理解各种排序算法的工作原理。

下列是十种排序的实现:

1.插入排序

void InsertSort(int a[], int n)
{ 
	int i, j;
	for (i = 1; i < n; ++i)
	{
		int t = a[i];//暂存待插码
		for (j = i - 1; t < a[j] && j >= 0; --j)//寻找插入位置
			a[j + 1] = a[j];//后移
		a[j + 1] = t;
	}
}

2.希尔排序

在插入排序基础上改进,将整个排序的序列分割为若干子序列,子序列内部排序插入排序,待整个序列基本有序时对全体插入排序。
使用的分割方法是取d<n,所有相距为d的码构成一个子序列,插入排序,然后缩小d再进行排序,直到d=1,再将全体进行插入排序。可以令d=n/2,然后每次缩小d时,令d=d/2。

void ShellSort(int a[], int n)
{
	int d, i, j;
	for (d = n / 2; d >= 1; d = d / 2)
	{
		for (i = d + 1; i <= n; ++i)
		{
			a[0] = a[i];
			for (j = i - d; a[0] < a[j]; j -= d)
				a[j + d] = a[j];
			a[j + d] = a[0];
		}
	}
}

3.冒泡排序

void BubbleSort(int a[], int n)
{
	int i, j;
	for (i = 0; i < n; ++i)
		for (j = 0; j < n - i-1;++j)
			if (a[j]>a[j+1])
			{
				int t=a[j];
				a[j] = a[j+1];
				a[j+1] = t;
			}
}

4.快速排序

以一个码为轴,将序列化为两部分,左边小于等于轴值,右边大于等于轴值,重复操作直到序列有序。
下面代码用的选取轴值策略是取待划分部分的末尾为轴,小于等于它的元素则按左到右的顺序依次与其左侧元素交换,最后再将轴值交换到最后一次交换位置的下一位置。
而取轴值位置将关系到算法的性能

int Partition(int a[], int p, int r)//取轴值并交换
{
	int i = p - 1, j;
	for (j = p; j < r;++j)
		if (a[j]<=a[r])
		{
			++i;
			int t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	int t = a[i + 1];
	a[i + 1] = a[r];
	a[r] = t;
	return i + 1;
}

void QuickSort(int a[], int p, int r)//初始QuickSort(a, 0, n-1)
{
	if (p < r)
	{
		int q = Partition(a,p,r);
		QuickSort(a,p,q-1);
		QuickSort(a,q+1,r);
	}
}

5.选择排序

void SelectSort(int a[], int n)
{
	int i, j, k;
	for (i = 0; i < n-1; ++i)
	{
		k = i;
		for (j = i+1; j < n; ++j)
			if (a[j] < a[k])
				k = j;
		if (k != i)
		{
			int t = a[j];
			a[j] = a[i];
			a[i] = t;
		}
	}

}

6.堆排序

堆为一棵完全二叉树,子女比自己小者为最大堆,子女比自己大者为最小堆。因此,最大堆
的根结点为最大元素,最小堆的根结点为最小元素,堆排序就是利用这一点,每次将堆的根
结点移除,重新调整堆,如此重复直到堆为空,这样堆中元素即按从大到小从堆中取出。

#define PARENT(i) i/2//当前结点父结点下标
#define LEFT(i) 2*i//当前结点左孩子下标
#define RIGHT(i) (2*i+1)//当前结点右孩子下标

void MaxHeapify(int a[], int k, int i)//保持最大堆性质,k是堆的长度,a下标要从1开始
{
	int l=LEFT(i), r=RIGHT(i);
	int largest=0;
	if (l <= k&&a[l] > a[i])
		largest = l;
	else
		largest = i;
	if (r <= k&&a[r] > a[largest])
		largest = r;
	if (largest != i)
	{
		int t = a[i];
		a[i] = a[largest];
		a[largest] = t;
		MaxHeapify(a, k, largest);
	}

}

void BuildMaxHeap(int a[],int n)//初始化最大堆
{
	for (int i = n / 2; i > 0; --i)
		MaxHeapify(a, n, i);
}

void HeapSort(int a[], int n)//堆排序,a[0]作为交换中间量
{
	BuildMaxHeap(a, n);
	int k = n;
	for (int i = n ; i >= 2; --i)
	{
		a[0] = a[1];
		a[1] = a[i];
		a[i] = a[0];
		--k;
		MaxHeapify(a, k, 1);
	}
}

7.归并排序

将两堆已排好序的元素合并,合并过程就是一一比较两个序列中的元素,按大小顺序重新排
成一个序列,因为两个序列均为已排好序的序列,所以合并两个总长为n的序列的过程只耗
费O(n),然后重复合并过程,直到整个序列有序。

void Merge(int a[], int x, int y, int z)//合并a[p...q]与a[q+1...r],假设其已经排好序
{
	int n1 = x - y + 1, n2 = z - y;
	int *l = new int[n1];
	int *r = new int[n2];
	for (int i = 0; i < n1; ++i)
		l[i] = a[x + i];
	for (int i = 0; i < n2; ++i)
		r[i] = a[y + i + 1];
	l[n1] = INF;//设置哨兵
	r[n2] = INF;
	int i = 0, j = 0;
	for (int k = x; k <= z; ++k)
	{
		if (l[i] < r[j])
		{
			a[k] = l[i];
			++i;
		}
		else
		{
			a[k] = r[j];
			++j;
		}
	}
}

void MergeSort(int a[], int x, int z)//初始参数MergeSort(a, 0, n)
{
	if (x < z)
	{
		int y = (x + z) / 2;
		MergeSort(a, x, y);
		MergeSort(a, y, z);
		Merge(a, x, y, z);
	}
}

8.计数排序

计数排序假设n个输入元素中的每一个均是0到k之间的数,k某整数,用一中间暂存数组存储
每个数的个数(即用每个待排数作为数组下标)来作为该数在结果数组中的位置依据,这样
序列中重合的元素也能得到很好的处理。计数排序因为并没有用比较的方法排序,所有计数
排序下界优于基于比较的排序下界O(nlogn),但要使用大量额外的空间去暂存中间数据。

void CountSort(int a[], int b[], int n, int k)//a[]为待排序列,b[]存储结果
{
	int *c = new int[k+1];
	for (int i = 0; i <= k; ++i)//初始化暂存每个数个数的数组,下标为序列中的数。
		c[i] = 0;
	for (int i = 0; i < n; ++i)//每个单元存储的内容为该数个数。
		++c[a[i]];
	for (int i = 1; i <= k; ++i)//每个单元存储小于或等于该数的元素个数,以此作为该数在序列中的位置
		c[i] += c[i - 1];
	for (int i = n - 1; i >= 0; --i)
	{
		b[c[a[i]]] = a[i];
		--c[a[i]];
	}
}

9.基数排序

基数排序利了多个关键码排序的思想对单关键码排序,将单关键码按位排序,按位排序可使
用任意排序方法,与直觉相反的是基数排序是按从低位到高位的次序排序,这样避免了从高
位到低位排序时产生的中间状态不参与排序的数,n个k进制d位数,若使用计数排序,则时间
复杂度为O(d(n+k))

void CountSort(int a[], int b[], int n, int k,int d)//用于基数排序,按a[]的第d位排序
{
	int *c = new int[k+1];
	for (int i = 0; i <= k; ++i)
		c[i] = 0;
	for (int i = 0; i < n; ++i)
	{
		int t=a[i];
		int temp = 1;
		for (int j = 0; j < d; ++j)
			temp *= 10;
		t = (t / temp)%10;
		++c[t];
	}
	for (int i = 1; i <= k; ++i)
		c[i] += c[i - 1];
	for (int i = n - 1; i >= 0; --i)
	{
		int t = a[i];
		int temp = 1;
		for (int j = 0; j < d; ++j)
			temp *= 10;
		t = (t / temp) % 10;
		b[c[t]] = a[i];
		--c[t];
	}
}

void RadixSort(int a[], int n, int d)
{
	int *b = new int[n];
	for (int i = 0; i < d; ++i)
	{
		//用一种稳定算法对序列按第d位排序,可以使用计数排序
		CountSort(a, b, n, 9, d);//对十进制序列按第d位计数排序
		for (int i = 0; i < n; ++i)
			a[i] = b[i];
	}
}

10.桶排序

桶排序的思想就是将序列元素的区间划为x等分,每一等分即为一个桶,对每个桶中的元素进
行排序,一般用链表,所以用插入排序,最后把所有元素从桶中取出即排好序。桶排序需要用
到额外存放链表(即桶)的数组,并且假设序列是均匀分布的,所有,输入序列趋于均匀分
布时,桶排序趋于线性期望时间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值