推荐去看JDK中的快速排序,只有几十行代码。
静下心来。
快速排序的思路:
(1)把数组分成两部分,大的一部分,小的一部分。这两部分里的数只需在内部做比较,无需去和另一部分做比较。然后递归下去。如果把划分点都记下来的话,我们就得到了一棵二叉排序树。
(2)选定了一个值value作为划分的界限,怎么把数组分成两部分呢?
如果不需要节省空间,可以这样做:建立一个一样大小的新数组,原数组里的数依次和value比较,如果这个数比value小,就丢到新数组的左边,比value大则丢到新数组的右边,最后新数组中剩下的那个位置就是value的位置。
为了节省空间,考虑数组元素交换位置的方法,比value小的就交换到数组的左边,比value大的就交换到数组的右边。一个办法就是从数组的两头开始比较,如果左端的数刚好比value小,它的位置就不用动,右端的数如果刚好比value大,也不用动。把左边碰到的比value大的数,以及右边碰到的比value小的数,交换位置。
package practice1;
public class SortHelper3 {
public static void main(String[] args) {
int[] a = new int[] { 1, 0 };
quickSort(a);
print(a);
a = new int[] { 1, 1, 1 };
quickSort(a);
print(a);
a = new int[] { 2 };
quickSort(a);
print(a);
a = new int[] { 2, 5, 1, 5, 1 };
quickSort(a);
print(a);
a = new int[] {};
quickSort(a);
print(a);
}
private static void print(int[] a) {
if (a.length == 0) {
return;
}
for (int i = 0; i < a.length - 1; i++) {
System.out.print(a[i] + ",");
}
System.out.println(a[a.length - 1]);
}
public static void quickSort(int[] a) {
quickSort1(a, 0, a.length - 1);
}
/**
* 如果把作为分界点的值本身也分到某一个子树中(不作为中间节点),可能会出现死循环,比如{1,1}一直被分到左子树或者右子树中。
*
* @param a
* @param start
* @param end
*/
private static void quickSort1(int[] a, int start, int end) {
if (start >= end) {
return;
}
// 选择一个数来划分数组
int key = start + (end - start) >> 1;
int value = a[key];
// le左边的(不包括le)都小于等于value
int le = start;
// ge右边的(不包括ge)都大于等于value
int ge = end;
// 整个区间分成了 [start,le) [le,key) key (key,ge] (ge,end]
while (le < ge) {
while (value <= a[ge] && key < ge) {
ge--;
}
while (value >= a[le] && key > le) {
le++;
}
// 将key右边的“较小数”和key左边的“较大数”交换位置
swap(a, le, ge);
if (le < key && ge > key) {
le++;
ge--;
} else if (le == key) {
// 如果le和key重合,则key交换到了ge的位置
key = ge;
le++;
} else {
key = le;
ge--;
}
}
a[key] = value;
quickSort1(a, start, key - 1);
quickSort1(a, key + 1, end);
}
private static void swap(int[] a, int le, int ge) {
int temp = a[le];
a[le] = a[ge];
a[ge] = temp;
}
}
private static void quickSort1(int[] a, int start, int end) {
if (start >= end) {
return;
}
int key = start;
int value = a[start];
// le左边的(不包括le)都<=value
int le = start;
// ge右边的(不包括ge)都>=value
int ge = end;
// 整个区间分成了 [start,le) [le,key) key (key,ge] (ge,end]
while (le < ge) {
// key和le重合,ge向左移动
while (value <= a[ge] && key < ge) {
ge--;
}
if (key != ge) {
// ge的位置上有一个比value小的数,这个数应该在key的左边,把key和ge交换一下位置
a[key] = a[ge];
key = ge;
// 此时le位置上的数就是刚才移过来的数,它已经和value比较过,比value要小
le++;
}
// key与ge重合,le向右移动
while (value >= a[le] && key > le) {
le++;
}
if (key != le) {
a[key] = a[le];
key = le;
ge--;
}
}
a[key] = value;
quickSort1(a, start, key - 1);
quickSort1(a, key + 1, end);
}
JDK中的快速排序做了很多优化,非常值得一看。
(1)如果数组长度小于7,则使用插入排序
(2)对于较长的数组,选择了一个伪中数来作为分界值,避免在这种情况下快速排序性能降低:数组已经基本排好序,如果默认选第一个数作为划分值,则总是不能比较平均地对数组进行划分。
(3)将与分界值相等的数全部调整到数组的中间,只对与分界值不相等的部分进行递归。针对有很多相同元素的数组,可以提升效率。