排序

198 阅读4分钟

使用之前实现的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];
            }
        }
    }