快速排序

本文详细介绍了快速排序算法的实现原理及过程,包括递归调用、分区操作等关键步骤,并通过实例演示了排序前后数组的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package quick_sort;
import static java.lang.System.out;

public class Sort
{	
	public static<T extends Comparable<T>> void quickSort(T[] array,int p,int r) 
	{	
		if(p < r) 
		{
			int q = partition(array,p,r);
			quickSort(array,p,q-1);
			quickSort(array,q+1,r);
		}		
	}
	
	private static<T extends Comparable<T>> int partition(T[] array,int p,int r) 
	{
		T x = array[r];
		int i = p-1;
		for(int j = p;j<r; ++j) 
		{
			if(array[j].compareTo(x) <= 0) 
			{
				i++;
				swap(array,i,j);
			}
		}
		swap(array,i+1,r);
		return i+1;
	}
	
	private static<T extends Comparable<T>> void swap(T[] array,int i, int j) 
	{
		T tmp = array[i];
		array[i] = array[j];
		array[j] = tmp;
	}
	
	private static void disprint(Integer[] intArray, Character[] charArray) 
	{
		for(int a: intArray)
			out.print(a+"\t");
		    out.println();		
		for(char a: charArray)
			out.print(a+"\t");
	}
	
	public static void main(String[] args) 
	{
		Integer[] intArray = {2,3,4,1,7,45,21,12};
		Character[] charArray = {'x','s','w','o','q','a','p','c'}; 
		
		out.println("未快速排序前:");
		disprint(intArray, charArray);
		
		int r = intArray.length-1;
		quickSort(intArray,0,r);
		int r1 = charArray.length-1;
		quickSort(charArray,0,r1);
		out.println("\n快速排序后:");
		disprint(intArray, charArray);
	}


}

运行截图:


快排使用的也是分治思想,并且它与归并排序不同是一种原址排序。
分解:数组A[p,r]被划分两个可能为空的子数组A[p,q-1]和A[q+1,r],使A[p,q-1]上的每个元素都小于等于A[q],且A[q]小于等于A[q+1,r]
上的每个元素,其中A[q]作为划分整个数组的轴值(pivot).
解决:通过递归调用快速排序,直到子数组只含有0个或一个元素,对子数组A[p,q-1]和A[q+1,r]排序.
合并:因为子数组都是原址排序,所以不需要合并操作,数组A[p,r]已经有序。


伪码:r为数组元素的个数,根据编程语言的数组特性,一般默认第一调用时传递参数(A,0,A.length-1).
QUICK-SORT(A,p,r)
if p < r
q = PARITION(A,p,r)
QUICK-SORT(A,p,q-1)
QUICK-SORT(A,q+1,r)

PARTITION(A,p,r)
x = A[r]
i = p-1
for j=p to r-1
if A[j] <= x
i++
exchange(A[i],A[j])
exchanage(A[i+1],A[r])   
return i+1


验证算法正确性:
p     i       j       |r
0 0 0 0 0 0 0 0 0 0 0 |0
算法在执行过程中必须维持这四个部分的条件:
[p,i]中的元素满足<=x
[i,j]中的元素满足>=x
[j,r)中的元素为任意大小
r为主轴


对任意for循环开始迭代,任意下标k必须遵循这几个原则循环不变量原则:
1、若k<q,则A[k] <= x.
2、若i+1<= k <=j-1,则A[k] >= x
3、若k=r,则A[k]==x


总的来说分位三个步骤:
迭代前的初始化->迭代中的保持循环不变量的稳定->迭代的终止条件 
Ω Θ
最坏情况:
当划分产生的两个子问题分别包含了n-1和0个元素,不平衡的时间复杂度划分操作Θ(n),则T(n)=T(n-1)+T(O)+Θ(n)=T(n-1)+Θ(n)


最好情况划分:
两个子问题平均为n/2个元素,T(n)=2T(n/2)+Θ(n)


平衡的划分:
其实任何一种常数比例的划分都会产生深度为Θ(lgn)的递归树,其中每层的时间代价都是O(n),一次只要划分按照常数比例,算法的运行时间都是O(nlgn)--递归树可以证明


对于递归式的分解这里我们必须知道
1,代入法
2,递归树
3,主方法


这里先介绍下主方法求解递归式:
主方法为下面这种形式提供了情况下的解决方案:
  T(n) = aT(n/b) + f(n)
a>=1和b>1都是常数,F(n)是渐进正函数,上式描述的是一种这样的算法运行时间:
它将规模为n的问题分解为a个子问题,每个子问题的规模n/b,a个子问题递归的进行求解每个问题的花费时间为T(n/b),f(n)包含问题的分解和子问题的合并的代价。


下面就是主定理:
 令a>=1和b>1为常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:   T(n) = aT(n/b) + f(n)
 1,若对某个常数m>0有f(n)=O(n^logb^(a-m)),则T(n) = Θ(nlogb^a)
 2,若f(n) = Θ(nlogb^a),则T(n)= Θ(logb^a lgn)
 3,若对某个常数m>0有f(n)=Ω(n^logb^(a+m)),且对某个常数有c<1和所有足够大的n有af(n/b)<=cf(n),则T(n)=Θ(f(n))
 其实简单的理解就是当在数量级上
 有f(n)<n^logb^a时,就是情况一
 有f(n)=n^logb^a时,就是情况二
 有f(n)>n^logb^a时,就是情况三
 
 所以
T(n) = 2T(n/2) + Θ(n) 有
a=2,b=2,  f(n) =  Θ(n), n^logb^a = n.由于 f(n)=Θ(n).
所以应用情况二: T(n) = Θ(nlgn)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值