使用之前实现的BST树排序
- 代码实现
public class Sort {
public viod bstSort() {
Random random = new Random();
int i = 0;
BST<Integer, String> bstTree = new BST<>();
while (i < 100) {
bstTree.add(random.nextInt(500), null);
i++
}
bstTree.inOrder();
}
}
快速排序
排序思想:将数组最左边项值作为基数,索引为基准线划分数组。把数组中每一项跟基数比较, 比基数小的放数组左边, 大的放右边(忽略掉相等的项)。 排完之后, 基准线的左边都比他小, 右边都比他大, 重复排序基准线左边的数组和右边数组。
- 代码实现
public class Sort {
// 测试方法
public static viod main() {
Random random = new Random();
int i = 0;
int[] arr = new int[100];
while (i < arr.length) {
arr[i++] = random.nextInt(500);
}
printList(arr);
System.out.println("");
division(arr, 0, arr.length - 1);
printList(arr);
for (i = 1; i < arr.length; i++) {
if (arr[i] - arr[i - 1] < 0) {
throw new IllegalArgumentException("排序错误");
}
}
}
public static void division(int[] arr, int left, int right) {
if (left < right) {
int baseIndex = quickSort(arr, left, right);
division(arr, left, baseIndex - 1);
// 这里注意一下,因为每次我们取的都是最左边项做基准, 所以比较右边数组的时候, left必须取baseIndex + 1;
division(arr, baseIndex + 1, right);
}
}
// 第一种排序实现
public static int quickSort(int[] arr, int left, int right) {
int base = arr[left];
while (left < right) {
// 必须要忽略掉和base相等的项, 不然会造成这个数组被我们扔左边又扔右边,排序陷入死循环。
while(left < right && arr[right] >= base) {
right--;
}
arr[left] = arr[right]
while(left < right && arr[left] <= base) {
left++;
}
arr[right] = arr[left];
}
arr[left] = base;
// 此时, left为基准线索引。
return left;
}
// 第二种排序实现
public static int quickSort1(int[] arr, int left, int right) {
int base = arr[left];
int i = left;
for (int j = i + 1; j < right + 1; j++) {
if (arr[j] < base) {
swap(arr, ++i, j);
}
}
swap(arr, i, left);
return i;
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void printList(int[] list) {
for (int i = 0; i < list.length; i++) {
// 左边的输出空格
System.out.print(list[i] + "\t");
}
}
}
- 上面的代码实现了两种快速排序方法,但是有一个缺点,就是我们都默认对和基准数相等的项没有处理。这样会导致第二次, 第三次.... , 会一直对这些项在进行再次比较。
- 当排序的数组中有大量重复的数的时候, 其实我们可以对排序方法进行优化: 划分三个区间, 一个区间是比基准数小的, 一个区间是相等的 , 一个区间是大的, 这就是三路排序,这样就大大提高了排序性能,因为我们每次排序都处理了相等的数,减少了大量递归次数(相对于有大量重复数据的数组)。
- 三路排序代码实现
public class Sort {
public static void division(int[] arr, int left, int right) {
if (left < right) {
int[] baseIndex = quickSort(arr, left, right);
division(arr, left, baseIndex[0] - 1);
division(arr, baseIndex[1], right);
}
}
// 具体逻辑
public static int[] quickSort(int[] arr, int left, int right) {
int base = arr[left];
int i = left;
int k = right + 1;
while (j < k) {
if (arr[j] < base) {
swap(arr, ++i, j++);
}else if(arr[j] > base) {
swap(arr, j, --k)
}else {
j++;
}
}
swap(arr, i, left);
return new int[]{i, k};
}
}
PS:如果感觉绕的话,可以用一些小的数据跟一下就容易了,6个左右就差不多。
归并排序
排序思想: 找到数组中间位置,对左右两边排序,然后把两个有序数组合并成一个有序数组。 但是对左右两边怎么排序呢? 先对左边数组找到中间位置,再次拆分成两个数组,排序之后,在合并成一个有序数组。递归下去,到极限位置是对只有两个元素的数组排序,比较大小即可。
- 代码实现
public class Sort {
public static void division(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
division(arr, left, mid);
division(arr, mid + 1, right);
mergeSort(arr, left, mid, right)
}
}
// 具体逻辑
public static void mergeSort(int[] arr, int left, int mid, int right) {
int[] tempArr = int[right - left + 1];
int index = 0;
int r = mid + 1;
int k = left;
while(k <= mid && r <= right) {
// 谁小就把谁放入定义的临时数组中
tempArr[index++] = arr[k] < arr[r] ? arr[k++] : arr[r++];
}
// 放完之后, arr可能还有一边的元素没放完, 继续放。
while(k <= mid) {
tempArr[index++] = arr[k++];
}
while(r <= right) {
tempArr[index++] = arr[r++];
}
// tempArr已经是有序的了, 将tempArr放入arr中
for (int i = 0; i < tempArr.length; i++) {
arr[left++] = temp[i];
}
}
}