数据结构 快速排序
demo : http://download.csdn.net/detail/keen_zuxwang/9879425
快速排序
是一种非常高效的排序算法,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动次数。
它采用“分而治之”的思想,以基准把大数组拆分为小数组,小的拆分为更小的直到数组的元素为一
排序思想:
对于给定的一组记录,选择一个基准元素(通常选择第一个元素或者最后一个元素),
通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素(此时基准元素在其排好序后的正确位置),
然后再用同样的方法递归地排序划分的两部分,直到序列中的所有记录均有序为止
算法稳定性
快速排序不是稳定的, 因存在数据交换,它无法保证相等的数据按顺序被扫描到和按顺序存放。
算法适用场景
快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。
时间复杂度:
最坏时间复杂度:O(n^2)
最坏情况是指每次区间划分的结果都是基准关键字的左边(或右边)序列为空,而另一边区间中的记录仅比排序前少了一项,即选择的关键字是待排序记录的最小值或最大值。
最好时间复杂度:O(nlogn)
最好情况是指每次区间划分的结果都是基准关键字的左右两边长度相等或者相差为1,即选择的基准关键字为待排序的记录的中间值。此时进行比较次数总共为 nlogn,
平均时间复杂度:O(nlogn)
平均时间复杂度为O(nlogn)的算法中,快速排序的平均性能是最好的。
空间复杂度
快速排序的过程中需要一个栈空间来实现递归。
最好情况,递归树的深度为log2n,其空间复杂度也就是O(nlogn);
最坏情况下,需要进行 n-1次递归,其空间复杂度为O(n);
平均情况,空间复杂度为O(nlogn).
基准关键字的选取(决定快速排序算法的关键):
第一种:三者取中。将序列首、尾和中间位置上的记录进行比较,选择三者中值作为基准关键字。
第二种:取left和right之间的一个随机数这里写图片描述,用n[m]作为基准关键字。采用这种方法得到的快速排序一般称为随机的快速排序
import java.util.Arrays;
import java.util.Stack;
public class QuickSort {
private static int num=0;
private static Stack<Integer> stack = new Stack<Integer>();
public static int partition(int[] arr, int left, int right) {
int pivotKey = arr[left];
int pivotPointer = left;
num++;
System.out.println("------------------num = "+num+"------------------");
System.out.println("left = "+left +" right = "+right +" key = "+ pivotKey);
System.out.println(Arrays.toString(arr));
while (left < right) {
while (left < right && arr[right] >= pivotKey)
right--;
while (left < right && arr[left] <= pivotKey)
left++;
System.out.println("left = "+left +" right = "+right);
swap(arr, left, right);// 把大的交换到右边,把小的交换到左边。
System.out.println(Arrays.toString(arr));
}
swap(arr, pivotPointer, left);// 最后把pivot交换到中间。
System.out.println("\n"+Arrays.toString(arr));
System.out.println();
return left;
}
private static void swap(int[] arr, int left, int right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
//递归、分治
public static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int pivotPos = partition(arr, left, right);
quickSort(arr, left, pivotPos - 1);
quickSort(arr, pivotPos + 1, right);
}
}
//非递归
public static void stackSort(int[] items, int l, int r) {
int i;
push(r, l); //向栈推入r和l
while(!stackempty()) { //只要栈不空就一直循环
l = pop();
r = pop();
if(r <= l)
continue;
i = partition(items, l, r);
//较大的一侧先入栈,可以保证栈的最大深度在lgN以内
if(i-l > r-i) {
push(i-1, l);
push(r, i+1);
} else {
push(r, i+1);
push(i-1, l);
}
}
}
//依次向栈推入a和b
private static void push(int a, int b) {
stack.push(a);
stack.push(b);
}
private static boolean stackempty() {
return stack.isEmpty();
}
private static int pop() {
return stack.pop();
}
public static void quickSort1(int[] items, int l, int r) {
if(l >= r) return; //不用排序
swap(items, (l+r)/2, r-1);
compexch(items, l, r-1);
compexch(items, l, r);
compexch(items, r-1, r);
//经过以上三步就完成了对l、(l+r)/2、r三个元素的排序。((l+r)/2元素放在r-1位置上)=> 基准关键字,三者取中
//与普通的快速排序也有不同,划分的时候第l个和第r个不用考虑了
//因为第l个元素一定小于“旗帜(枢轴)元素”,第r个元素一定大于“旗帜(枢轴)元素”
//“旗帜(枢轴)元素”在r-1上。
int i = partition(items, l+1, r-1);
quickSort1(items, l, i-1); //递归排序
quickSort1(items, i+1, r); //递归排序
}
private static void compexch(int[] items, int a, int b) {
if(items[b] < items[a]) {
swap(items, a, b);
}
}
public static void main(String[] args) {
int[] a = { 49, 38, 65, 97, 76, 13, 27, 49 };
int[] b = { 49, 38, 65, 97, 76, 13, 27, 49 };
int[] c = { 49, 38, 65, 97, 76, 13, 27, 49 };
/*
System.out.println("排序之前:");
System.out.println(Arrays.toString(a));
stackSort(a, 0, a.length - 1);
System.out.println("排序之后:");
System.out.println(Arrays.toString(a));
*/
System.out.println("排序之前:");
System.out.println(Arrays.toString(b));
System.out.println();
//快速排序
quickSort1(b, 0, b.length - 1);
System.out.println();
System.out.println("排序之后:");
System.out.println(Arrays.toString(b));
/*
System.out.println("排序之前:");
System.out.println(Arrays.toString(b));
System.out.println();
//快速排序
quickSort(b, 0, b.length - 1);
System.out.println();
System.out.println("排序之后:");
System.out.println(Arrays.toString(b));
*/
}
}