算法——快排

                                快排

        上一篇博客说到了分治策略,分治就是分而治之的策略,把大问题分解成多个独立而且与原问题相同的小问题,直到分成可直接解决为止。分治也可用于排序算法,最经典的分治排序就是快排了,快排是很多语言框架的默认排序算法,比如说Java的Arrays中使用的就是一种被称为双轴快排的算法。
       快排分为三个步骤:
              分解:以元素data[p]为基准把输入序列分成3段data[p]...data[q-1],data[q],data[q+1]...data[r],使         data[p]...data[q-  1]中的每一个值都小于等于data[q],并且data[q+1]...data[r]中的每一个值都大于等于data[q]
             递归求解:分别递归调用快排对data[p]...data[q-1]和data[q+1]...data[r]进行排序。
              合并:将排序好的data[p]...data[q-1]和data[q+1]...data[r]进行合并。
       下面是用C++实现的快排
#pragma once
#ifndef SORT_H
#define SORT_H
namespace algorithm 
{
	template<typename T>
	void Swap(T &t1,T &t2)
	{
		T temp = t1;
		t1 = t2;
		t2 = temp;
	}
	template<typename T>
	int Partition(T* &data,int p,int r,bool(*compare)(T t1,T t2))
	{
		int i = p, j = r + 1;
		T x = data[p];
		while(true)
		{
			while (compare(data[++i], x) && i < r);
			while (!compare(data[--j], x) && j > p);
			if (i >= j)break;
			Swap(data[i], data[j]);
		}
		data[p] = data[j];
		data[j] = x;
		return j;
	}

	template<typename T>
	void QuickSort(T* &data, int p, int r, bool(*compare)(T t1, T t2))
	{
		if(p<r)
		{
			int q = Partition(data, p, r, compare);
			QuickSort(data, p, q - 1, compare);
			QuickSort(data, q + 1, r, compare);

		}
	}

	template<typename T>
	void QuickSort(T* &data,const int size,bool (*compare)(T t1,T t2))
	{
		QuickSort(data, 0, size - 1, compare);
	}
}
#endif
      
// Algorithm.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "sort.h"
using namespace std;
using namespace algorithm;

bool comp(int a,int b)
{
	return a < b;
}

int main()
{
	
	int *data = new int[10];
	data[0] = 4;
	data[1] = 2;
	data[2] = 5;
	data[3] = 1;
	data[4] = 7;
	data[5] = 6;
	data[6] = 9;
	data[7] = 8;
	data[8] = 10;
	data[9] = 3;
	QuickSort(data, 10, comp);
	for (int i = 0;i < 10;i++)
	{
		cout << data[i] << ",";
	}
	cout << endl;
	system("pause");
    return 0;
}


      这里我每次选基准都是序列的第一个元素。整个算法过程如是
      首先以第一个元素x为基准,在序列中从左到右找到第一个大于x的元素的位置i,从右到左找到第一个小于x的元素的位置j,如果i<j就说明i的元素应该放在j的位置而j的元素要放在i的位置。然后j的位置就是x元素应该放的位置,把x和j位置上的元素交换。这样就保证了x左边的元素的值都小于等于x,而x右边的元素的值都大于等于x。然后把序列划分成三段,分别是data[0]...data[j-1],x,data[j+1]...data[size-1],然后再对左子序列和右子序列进行递归求解。左子序列的起始下标为p,终止坐标为r,直到p>=r为止(这时其实序列中就一个元素了,一个元素不需要排序)。当递归结束返回后整个序列就都是有序的了。
     上面的例子中待排序的序列为4,2,5,1,6,9,8,10,3
      第一次排序后为1,2,3,4,7,6,9,8,10,5,这时基准为4,左子序列为1,2,3,右子序列为7,6,9,8,10,5。
     对左子序列进行递归,这时p=0,r=2,以1为基准,从左向右找大于1的数,i=1,从右向左找小于1的数,j=0,此时j<i,dta[p] = data[j],data[j] = x;,序列还是1,2,3,再以0位置为基准,把1,2,3划分成{},{1},{2,3}很明显左子序列已经无需排序,右子序列排序也一样。
     {2,3}进行排序,以2为基准,从左到右找第一个大于2的数,也就是3,从右向左找第一个小于2的数,这时候j=2的下标,而i=3下标, 交换2和j为下标的元素,也就相当于没交换。此时标准为2,把{2,3}划分成{},{2},{3},此时左右子序列中的p和r都不满足p<r了,递归结束。
     再对7,6,9,8,10,5进行排序。以7为基准,从左向右找第一个大于7的位置,也就是9的位置,从右向左找第一个小于7位置,也就是5的位置,由于9的位置在5的位置的左边,所以交换9和5得7,6,5,8,10,9,再交换7和5,则得5,6,7,8,10,9这时候7是基准,把序列划分成{5,6},{7},{8,10,9}
     再对{5,6}进行排序,以5为基准,和{2,3}排序一样。
     对{8,10,9}进行排序,为8为基准,从左到右查找第一个大于8的数,就是10,从右到左查找每一个小于8的树,找不到。也就是i=10的下标,j=8的下标,以8为基准把{8,10,9}划分为{},{8},{10,9},左边的序列已经不能排序了
    对{10,9}进行排序,以10为基准,从左到右找第一个大于10的数,找不到,则i=9的下标,从右到左找第一个小于10的数,为9则j=9的下标,此时i=j,data[p]=data[j],data[j]=x,也就相当于交换10和9,{10,9}变成{9,10},再以10为基准划分为{9},{10},{}左右子序列都不需要排序了,递归结束。
    最后排序也结束了。排序后的序列变成1,2,3,4,5,6,7,8,9,10,已经是递增的了。这就是快速排序的全过程。
    通过这个过程,可以发现快排相当于一个二叉树的过程。

       然后可以发现排序的效率是和最终树的高度正相关的。可以想象,如果每次都只能从序列划分成{},{a},{b,c,d,e...},那么这棵树的高度将会达到最大值,这时的排序的效率将会最低。 如果原序列就是有序的,并且每次都选择序列的第一个元素为基准,那么树就会呈现下图这种情形

       如果去掉图中的空节点,其实这棵树就变成了一个线性链。
       根据代码中的算法,可以知道递归调用的次数就是节点中元素个数大于1的节点个数,对比两个图,很明显树的高度越大,递归调用次数越多,算法的时间复杂度也就越高。、
      而如果序列是有序的,而每次都以最中间的元素为基准呢?

可以发现这棵树基本上是平衡的,树的高度是最小的,因而此时排序的效率是最高的。这就印证了前面所说的基准的选择会影响排序的效率。那么到底应该选择什么为基准来让快排的效率尽可能的好呢?答案是没有一个标准的基准。所以经常选择用随机树来作为基准,每次递归时都用一个随机数作为基准,这样就避免了树的高度太高导致算法效率太低的情况。
       再来看一下,当每次都选择第一个数为基准,而原序列又是有序的,其实快排就退化成了选择排序了。因为每一次都是从右边选择一个最小的的数嘛,而每次都把右边的序列扫描了一遍,因此此时的时间复杂度为O(n^2),而当情况最好时,也就是树最平衡时,时间复杂度为O(nlogn),此时相当于二分+扫描,二分的时间复杂度为O(logn)。来准确计算一下,设最好情况下时间复杂度为T(n),则T(1) = O(1),而T(n) = 2T(n/2)+O(n),因为每次都把序列分成了两个长度几乎相等的子序列,因此排序时间复杂度为2T(n/2)+O(n),解这个递归方程得T(n)=O(nlogn)

猜你喜欢

转载自blog.csdn.net/qq_31728311/article/details/79260297