基本排序算法总结

排序算法很多,这里做个总结,最重要的是记住算法思想,而不是记住程序怎么写。程序过一段时间就会忘了,算法原理熟悉了现场写出来应该是很容易的。

时间复杂度为O(n^2)的三种排序算法:插入排序、选择排序、冒泡排序。

插入排序:插入排序是选定元素找位置。对于位置为p上的元素,假定0~p-1的元素已经排序,如果p比它前面的元素小,那么p上的元素应该向前移动,直到p前面所有的元素都比它小为止。

void InsertSort(int* a, int length)
{
	if(a == NULL || length < 1)
		return;
	for(int i = 1; i < length; ++i)
	{
		int tmp = a[i];
		int j;
		for(j = i; a[j-1] > tmp && j > 0; --j)
			a[j] = a[j-1];
		a[j] = tmp;
	}
}

选择排序:选择排序是选定位置找元素。第一个位置上应该是最小的元素,那么先把最小的元素找出来放在第一个位置上。然后再考虑第二个位置,它应该放上第二小的元素,所以遍历剩下的元素找出最小的放在第二个位置上,直到所有位置都放上了合适的元素。

void SelectSort(int* a, int length)
{
	if(a == NULL || length < 1)
		return;
	for(int i = 0; i < length; ++i)
	{
		int min = a[i];
		int indexOfMin = i;
		for(int j = i + 1; j < length; ++j)
		{
			if(a[j] < min)
			{
				min = a[j];
				indexOfMin = j;
			}
		}
		a[indexOfMin] = a[i];
		a[i] = min;
	}
}

冒泡排序:冒泡排序也是选定位置找元素。冒泡排序的实现过程是两两交换,每次从最右边开始的元素开始比较,如果右边的元素比它左边一位的元素小,那么交换两个元素。例如3,2,1交换一次变成3,1,2,再交换一次变成1,3,2。这一过程总是使最小的元素移到最左边,最左边定义为0的时候最小的元素交换到位置0上,最左边定义为1时剩下的元素中最小的元素(也就是第二小)交换到位置1上,以此类推。

冒泡排序和选择排序过程很相似,但是冒泡排序的交换过程具有盲目性。冒泡排序只要右边元素比左边小都会发生交换,而选择排序只有当前元素小于最小值时记录最小值得位置(实际上等同于交换),内层循环结束后才发生一次交换。选择排序的交换次数比冒泡排序小,性能略优于冒泡排序。

void BubbleSort(int* a, int length)
{
	if(a == NULL || length < 1)
		return;
		
	for(int i = 0; i < length; ++i)
	{
		for(int j = length - 1; j > i; --j)
		{
			if(a[j] < a[j-1])
			{
				int tmp = a[j];
				a[j] = a[j-1];
				a[j-1] = tmp;
			}
		}
	}
}

时间复杂度为O(nlogn)的四种排序算法:希尔排序、堆排序、归并排序、快速排序。

希尔排序:希尔排序只需要记住一条:它是变间隔的插入排序!插入排序那里比较的元素都是相邻的两个元素,间隔为1。希尔排序通过设置比较间隔,然后间隔逐渐缩小直到为1,所以希尔排序的最后一趟,也就是间隔为1的时候完全等同于插入排序了。间隔序列h1,h2,h3…hn叫做增量序列,增量序列的选取也会对排序算法的性能有影响,由于希尔排序很少使用就不讨论了。

void ShellSort(int* a, int length)
{
	if(a == NULL || length < 1)
		return;
		
	for(int increment = length / 2; increment > 0; increment /= 2)    //希尔建议的增量序列,但是并不好
	{
		for(int i = increment; i < length; ++i)   //<span style="color:#ff0000;">注意这里i的增量是1,而不是increment</span>
		{
			int tmp = a[i];
			int j;
			for(j = i; j >= increment && a[j-increment] > tmp; j -= increment)
				a[j] = a[j-increment];
			a[j] = tmp;
		}
	}
}

堆排序:堆排序根据最大堆的堆顶总是堆上最大数的性质,每次交换堆顶和堆最后一个元素,这样堆顶元素放在了正确的位置,而由于堆顶更换了新的元素,需要下滤维护堆序。下滤完成以后可以看成比第一部大小减一的新的堆,继续交换堆顶和最后一个元素,这样第二大的数也摆在正确的位置上,重复这个过程直到堆的大小为0,即所有元素都已经摆放到合适的位置上。

#define LeftChild(i) 2*i+1

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

//最大堆下滤过程
void PercDown(int* a, int n, int i)
{
	int tmp, child;
	for(tmp = a[i]; LeftChild(i) < n; i = child)
	{
		child = LeftChild(i);
		if(child != n-1 && a[child] < a[child+1])  //最小堆的时候改为“>"
			++child;
		if(tmp < a[child])                         //最小堆的时候改为“>"
			a[i] = a[child];
		else
			break;
	}
	a[i] = tmp;
}

void HeapSort(int* a, int n)
{
	//建堆过程
	for(int i = n/2 - 1; i >= 0; --i)
		PercDown(a,n,i);
		
	for(int i = n-1; i > 0; --i)
	{
		Swap(a[0], a[i]);    //DeleteMax
		PercDown(a, i, 0);
	}
}

归并排序:归并排序假设存在两个已经排序的子数组A和B,申请一个额外的数组C,大小为n,每次从数组中A或B中取较小的数放入C中,直到其中一个子数组取完,另外一个子数组剩下的元素直接拷贝到数组C中。对于要排序的数组array,可以将array划分成两半,调用上述合并的过程,而对于各自部分的排序,递归的调用合并排序的函数。归并排序需要额外的大小为n的辅助空间,时间复杂度为O(nlogn)。

void Merge(int* array, int* TmpArray, int Lpos, int Rpos, int RposEnd)
{
	int IndexL, IndexR, index, numOfElement;
	IndexL = index = Lpos;
	IndexR = Rpos;
	numOfElement = RposEnd - Lpos + 1;
	while(IndexL < Rpos && IndexR <= RposEnd)
	{
		if(array[IndexL] < array[IndexR])
			TmpArray[index++] = array[IndexL++];
		else
			TmpArray[index++] = array[IndexR++];
	}
	
	while(IndexL < Rpos)
		TmpArray[index++] = array[IndexL++];
	while(IndexR <= RposEnd)
		TmpArray[index++] = array[IndexR++];
		
	for(int i = 0; i < numOfElement; ++i)
		array[i+Lpos] = TmpArray[i+Lpos];
}

void MergeSortCore(int* array, int* TmpArray, int start, int end)
{
	if(start < end)
	{
		int center = (start + end) / 2;
		MergeSortCore(array, TmpArray, start, center);
		MergeSortCore(array, TmpArray, center+1, end);
		Merge(array, TmpArray, start, center+1, end);
	}
}

void MergeSort(int* array, int length)
{
	if(array == NULL || length <= 1)
		return;
		
	int* TmpArray = (int*) malloc(length*sizeof(int));
	if(TmpArray != NULL)
	{
		MergeSortCore(array, TmpArray, 0, length-1);
		free(TmpArray);
	}
	else
	{
		cout << "Out of memory!" << endl;
		return;
	}
}
快速排序:快速排序是实践中已知的最快的排序算法。快速排序的过程是一个分组的过程,选定基准值,从数组的前后两头同时开始遍历数组,如果左边的数小于基准值,滑过指针,如果右边的值大于或等于基准值,也滑过指针。当左边的值大于基准值而右边的值小于基准值时,交换两个数,直到两个指针交叉。这一过程使得数组中小于基准值的数都位于基准值的左边,大于基准值的数都位于基准值右边,基准值的位置已经确定不再改变,在分别排序基准值的左边和右边。

void Swap(int& p1, int& p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}

void Qsort(int* array, int start, int end)
{
	if(start < end)
	{
		int pivot = array[start];
		int pLeft = start + 1;
		int pRight = end;
	
		while(pLeft <= pRight)
		{
			while(pLeft <= pRight && array[pRight] >= pivot)
				--pRight;
			while(pLeft <= pRight && array[pLeft] < pivot)
				++pLeft;
			if(pLeft < pRight)
				Swap(array[pRight], array[pLeft]);
		}
		Swap(array[start], array[pRight]);

		Qsort(array, start, pRight - 1);
		Qsort(array, pRight + 1, end);
	}
}

void QuickSort(int* array, int length)
{
	if(array == NULL || length <= 0)
		return;
	Qsort(array, 0, length - 1);
}
排序算法性能分析:http://blog.csdn.net/cjf_iceking/article/details/7953637


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值