排序算法

一、插入排序

1.直接插入排序


基本思想:

        将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。

        即先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。

void InsertSort(int *array, int len)
{
	for (int i = 1; i < len; i++) {
		int val = array[i];
		int j = i - 1;
		while (j >= 0 && array[j] > val) {
			array[j + 1] = array[j];
			j--;
		}
		array[j + 1] = val;
	}
}

2.二分插入排序

二分插入排序只是减少了比较的次数,时间复杂度没变化。

void BinSort(int *array, int len)
{
	for (int i = 1; i < len; i++) {
		int val = array[i];
		int left = 0;
		int right = i - 1;
		while (left <= right) {
			int mid = left + (right - left) / 2;
			if (array[mid] > val)
				right = mid - 1;
			else
				left = mid + 1;
		}

		for (int j = i - 1; j >= left; j--)
			array[j + 1] = array[j];
		
		array[left] = val;
	}
}

3.希尔排序

希尔排序又叫缩小增量排序。

基本思想:

        先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

void ShellSort(int *array, int len, int d)
{
	for(int inc = d; inc > 0; inc /= 2) {
		for(int i = inc; i < len; i++) {
			int val = array[i];
			int j = i - inc;
			while(j >= 0 && array[j] > val) {
				array[j + inc] = array[j];
				j -= inc;
			}
			array[j + inc] = temp;
		}
	}
}
参数d为增量因子,增量因子序列可以有各种取法,有取奇数的,也有取质数的,但增量因子最后要缩小为1。


二、选择排序

1.直接选择排序


基本思想:

        在待排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;

        然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。

void SelectSort(int *array, int len)
{
	for (int i = 0; i < len - 1; i++) {
		int min = array[i];
		int min_index = i;
		for (int j = i + 1; j < len; j++) {
			if (array[j] < min) {
				min = array[j];
				min_index = j;
			}
		}

		array[min_index] = array[i];
		array[i] = min;
	}
}

2.堆排序

二叉堆满足二个特性:

       1)父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

       2)每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

堆的存储:

        一般用数组来表示堆,i结点的父结点下标为(i–1)/2。它的左右子结点下标分别为2*i+1和2*i+2。如下标为0的结点其左右子结点下标分别为1和2。

基本思想:

        将待排序的序列看作是一棵顺序存储的二叉树,调整该二叉树成为一个堆;

        将堆顶元素与最后一个元素交换,将剩下的前n-1个元素调整成堆,依次类推,直到第1个元素和第2个元素交换为止。

void AdjustHeap(int *array, int len, int i)  
{
	int temp  = array[i];
	int child = 2 * i + 1; //左孩子结点的位置
	while (child < len) {
		if (child + 1 < len && array[child] < array[child + 1])  // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
			child++;
		
		if (array[i] < array[child]) {   // 如果较大的子结点大于父结点,交互两者的值
			array[i] = array[child];   	
			array[child] = temp;
			i = child;               // 重新调整父节点的位置
			child = 2 * i + 1;
		} else {                         // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
			break;  
		}
	}
}
 
void BuildHeap(int *array, int len)  
{
	//最后一个有子节点的位置应满足 2i + 1 = len - 1
	for (int i = (len - 2) / 2; i >= 0; i--)
		AdjustHeap(array, len, i);  
}  

void HeapSort(int *array, int len)  
{
	BuildHeap(array, len);
	
	//从最后一个元素开始调整序列  
	for (int i = len - 1; i > 0; i--) {  
		//交换堆顶元素和堆中最后一个元素  
		int temp = array[i]; 
		array[i] = array[0]; 
		array[0] = temp;
		
		//每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
		AdjustHeap(array, i, 0);
	}
}


三、交换排序

1.冒泡排序

基本思想:

        在待排序的一组数中,对未排序的数,自上而下对相邻的两个数依次进行比较和调整,让不满足要求的数往下沉,满足要求的数往上冒。

void BubbleSort(int *array, int len)
{
	bool no_swap = true;
	for (int i = 1; i < len; i++) {
		no_swap = true; //如果冒泡的过程发现没有交换过位置,那么序列就已经是有序的了
		for (int j = 0; j < len - i; j++) {  //注意n个数只需比较n-1次,防止越界
			if (array[j] > array[j + 1]) {
				int temp = array[j + 1];
				array[j + 1] = array[j];
				array[j] = temp;
				no_swap = false;
			}
		}
		
		if (no_swap)
			break;
	}
}

2.快速排序

基本思想:

        选择一个基准元素,通常选择第一个元素或者最后一个元素;

        通过一趟排序将待排序的元素分割成两部分,其中一部分元素均比基准元素小,另一部分元素比基准元素大;

        此时基准元素在其排好序后的正确位置;

        然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

void QuickSort(int *array, int left, int right)
{
	if (left >= right)
		return;
	
	int l = left;
	int r = right;
	int temp = array[l];
	
	while (l != r) {
		while (l < r && array[r] >= temp)
			r--;
			
		if (l < r)
			array[l++] = array[r];
		
		while (l < r && array[l] <= temp)
			l++;
		
		if (l < r)
			array[r--] = array[l];
	}
	
	array[l] = temp;
	QuickSort(array, left, l - 1);
	QuickSort(array, l + 1, right);
}

四、归并排序

基本思想:

        归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

void MergeArray(int *src, int left, int mid, int right, int *dst)
{
	int l = left;
	int r = mid + 1;
	int k = 0;

	while (l <= mid && r <= right) {
		if (src[l] <= src[r])
			dst[k++] = src[l++];
		else
			dst[k++] = src[r++];
	}

	while (l <= mid)
		dst[k++] = src[l++];

	while (r <= right)
		dst[k++] = src[r++];

	for (l = 0; l < k; l++)
		src[left + l] = dst[l];
}

void MergeSort(int *src, int left, int right, int *dst)
{
	if (left < right) {
		int mid = (left + right) / 2;
		MergeSort(src, left, mid, dst);
		MergeSort(src, mid + 1, right, dst);
		MergeArray(src, left, mid, right, dst);
	}
}

五、基数排序

        基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。

        有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。

        最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

        基数排序基于分别排序,分别收集,所以是稳定的。


总结

各种排序的稳定性,时间复杂度和空间复杂度总结:



本文参考自:

八大排序算法 http://blog.csdn.net/hguisu/article/details/7776068

堆与堆排序     http://blog.csdn.net/morewindows/article/details/6709644

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值