快速排序的基础思想就是分而治之,通过选择主元将数组分成两段,依次将比主元小的数放在主元左边,比主元大的数放主元右边,当一次主元比较结束时,主元所在的位置就是它的最终位置。如下图,然后递归调用。
什么时候是快速排序的最好情况?
每次正好中分 ——> T(N)=O(N logN)
怎么样选主元是很重要的:
这里我们取头、中、尾三数的中位数来做主元;
需要注意的是,当我们取到主元后,将主元与数组的最后一个元素换位置,这样以便于我们接下来的比较。
子集划分
如上图,我们将主元放在数组末尾,设置两个指针(i=0,j=length-1):
i所指的元素从起始位置依次与主元比较,当比主元小时指针往后移,否则停下来等;
j所指的元素从起始位置依次与主元比较,当比主元大时指针往前移,否则停下来等;
当两个指针都停下时,交换他们所指的元素。
注意:当所指的元素与主元相等时,应该停下来(考虑其效率)。
什么时候指针停下来呢?
当i - j大于等于0 时,将主元与i指针所指的元素换位置(此时正好是主元的最终位置)
然后递归调用该方法。
代码实现如下:
public class Quicksort {
public int pivot(int[] array, int left, int right) {
int mid = (left + right) / 2;
if (array[left] > array[mid]) {
swap(array, left, mid);
}
if (array[left] > array[right]) {
swap(array, left, right);
}
if (array[mid] > array[right]) {
swap(array, mid, right);
}
swap(array, mid, right);//将主元放到最右边
int tamp = array[right];
return tamp;
}
public void quickSort(int a[]) {
int start = 0;
int end = a.length - 1;
quicksort(a, start, end);
}
public void swap(int array[], int i, int j) {
int a = array[i];
int b = array[j];
array[i] = b;
array[j] = a;
}
public void quicksort(int[] array, int left, int right) {
if (left >= right) {
return;
}
int temp = pivot(array, left, right);
int i = left;
int j = right - 1;
while (i < j) {
//当此时元素大于主元时停下来做交换
//当i 和 j相等时(i和j所指的是同一个位置)且符合往前的条件 i 应该继续往前走
while (array[i] <= temp && i <= j) {
i++;
}
while (array[j] >= temp && i < j) {
j--;
}
if (i < j) {
swap(array, i, j);
} else
break;
}
swap(array, i, right);//将主元放到正确的位置
quicksort(array, left, i - 1);//递归
quicksort(array, i + 1, right);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
int[] array = { 12, 20, 5, 16,12,12, 15, 1, 30, 45, 23, 9 };
Quicksort test = new Quicksort();
test.quickSort(array);
for (int i : array) {
System.out.print(i + " ");
}
}
}
快速排序的问题
当小规模数据(例如N不到100)排序时,可能还不如用简单排序快(例如插入排序等)。