归并排序和快速排序


这篇博客记录一下两种比较快速的排序。

归并排序和快速排序。


归并排序(\(O(nlogn)\)

归并排序,顾名思义就是先递归后合并,这里画了一张图简单理解归并过程:

代码肯定要用到递归啦,递归到不能再细分就可以开始合并了。而合并是通过申请额外的内存空间来完成的,合并时在左右两个区间内进行比较,按照大小依次放入额外的空间中,最后复制回原数组,这样一直递归往上,就实现了排序。

代码:

void merge_sort(int arr[], int a,int b)//递归
{
	if (a >= b)
		return;
	int mid = (a + b) / 2;
	merge_sort(arr, a, mid);
	merge_sort(arr, mid + 1, b);
	merge(arr, a, mid, b);
}

void merge(int arr[], int l, int mid, int r)//合并
{
	int *temp=new int[r - l + 1];//申请额外空间
	int i = l, j = mid + 1, k = 0;
	while (i <= mid && j <= r)//比较并存放在额外的空间里
	{
		if (arr[i] <= arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	/*将剩下元素的放入额外空间*/
	while (i <= mid)
		temp[k++] = arr[i++];
	while (j <= r)
		temp[k++] = arr[j++];
	for (int i = l; i <= r; i++)//复制回原空间
		arr[i] = temp[i - l];
	delete[]temp;
}

时间复杂度推算:

归并排序的一大好处就是不管数据是什么样的,它的时间复杂度都为\(O(nlogn)\)。但它的缺点就是会用额外空间,空间复杂度为\(O(n)\)


快速排序

快速排序也是利用二分法和递归进行排序。

下面是简单的快排过程:

快排的核心就在\(partition\)这个函数,他的作用就是再区间里随机找一个点(一般找最右边哪个元素),将这个点作为中心点,将比他小的元素放在左边,比他大的元素放在右边(升序),然后不断二分最后就得到一个排列好的数组了。

void quick_sort(int arr[], int a, int b)
{
	if (a >= b)
		return;
	int p = partition(arr, a, b);
	quick_sort(arr, a, p - 1);
	quick_sort(arr, p + 1, b);//这里一定是p+1,将中心点排除在外,不然会在区间里有重复元素的时候陷入死循环!!!!
}

int partition(int arr[], int a, int b)
{
	int i = a, j;
	int p = arr[b];//记录中心点
	for (j = a; j < b; j++)
	{
		if (arr[j] < p)//找到比中心点小的元素就交换
		{
			swap(arr[i], arr[j]);
			i++;
		}
	}
	swap(arr[i], arr[b]);//将中心点交换到中间
	return i;//返回中心点下标
}

因为快排是个空间复杂度为\(O(lgn)\)的只有递归空间消耗的原地排序,所以在找中心点的时候为了不用额外空间算法设计的很巧妙。

这里将\(i\)作为一个游标,他的左边是已经处理的区间,右边为未处理区间,用\(j\)去找那些比中心点小的元素并和\(i\)交换,最后把选定的中心点和\(i\)交换就完成处理了。

值得注意的是快排是一种时间复杂度不稳定的算法,它一般情况下时间复杂度为\(O(nlogn)\),但在极端条件下会退化为\(O(n^2)\)


快速排序和归并排序的区别

快速排序和归并的区别就在于它正好和递归的排序反过来。快速排序先排序再递归细分,排序是从上到下的。归并排序先递归细分再排序,排序是从下到上的。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM