快速排序与归并排序Java实现版

快速排序

快速排序(Quicksort) 是一种排序算法,平均时间复杂度为:O(n log n),最坏需要 O(n²),但很少见,快速排序之所以叫快速排序,就是因为它比一般的排序算法要快。
快速排序使用了分而治之的思想,步骤如下:

  1. 选择基准值(pivot)
  2. 将数组分成两个子数组:小于基准值的元素和大于基准值的元素
  3. 对这两个子数组进行快速排序

不同的选取基数值策略对排序性能都有很大的影响。

递归实现:

public static void quickSort(int[] arr) {
   qsort(arr, 0, arr.length - 1);
}

private static void qsort(int[] arr, int low, int high) {
   System.out.println(String.format("qsort() -> low:%s, high:%s", low, high));
   if (low < high) {
       //将数组分为两部分
       int pivot = partition(arr, low, high);
       //递归排序左子数组
       qsort(arr, low, pivot - 1);
       //递归排序右子数组
       qsort(arr, pivot + 1, high);
   }
}

private static int partition(int[] arr, int low, int high) {
   //枢轴记录
   int pivot = arr[low];
   while (low < high) {
       while (low < high && arr[high] >= pivot) --high;
       //交换比枢轴小的记录到左端
       arr[low] = arr[high];
       while (low < high && arr[low] <= pivot) ++low;
       //交换比枢轴小的记录到右端
       arr[high] = arr[low];
   }
   //扫描完成,枢轴到位
   arr[low] = pivot;
   //返回的是枢轴的位置
   return low;
}

非递归实现版:

public static void quickSort(int[] array){
    if (array == null || array.length == 1) return;
    //存放开始与结束索引
    Stack s = new Stack();
    //压栈
    s.push(0);
    s.push(array.length - 1);
    //利用循环里实现
    while (!s.empty()) {
        int right = s.pop();
        int left = s.pop();
        //如果最大索引小于等于左边索引,说明结束了
        if (right <= left) continue;

        int i = partition(array, left, right);
        if (left < i - 1) {
            s.push(left);
            s.push(i - 1);
        }
        if (i + 1 < right) {
            s.push(i+1);
            s.push(right);
        }
    }
}

归并排序

归并排序(mergesort)时间复杂度:O(n log n),同样采用分治法实现。

步骤:

  1. 分割:递归地把当前序列平均分割成两半
  2. 归并:在保持元素顺序的同时将上一步得到的子序列集成到一起

递归实现:

public static void sort(int[] data) {
    doSort(data, 0, data.length - 1);
}

public static void doSort(int[] data, int left, int right) {
    if (left >= right)
        return;
    System.out.println();
    // 找出中间索引
    int center = (left + right) / 2;
    // 对左边数组进行递归
    doSort(data, left, center);
    // 对右边数组进行递归
    doSort(data, center + 1, right);
    // 合并
    merge(data, left, center, right);
}

/**
 * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
 *
 * @param data
 *            数组对象
 * @param left
 *            左数组的第一个元素的索引
 * @param center
 *            左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
 * @param right
 *            右数组最后一个元素的索引
 */
private static void merge(int[] data, int left, int center, int right) {
    System.out.println(String.format("->\nleft:%s,center:%s,right:%s\nbefore:", left, center, right));
    Util.printArray(data);
    // 临时数组
    int[] tmpArr = new int[data.length];
    // 右数组第一个元素索引
    int mid = center + 1;
    // third 记录临时数组的索引
    int third = left;
    // 缓存左数组第一个元素的索引
    int tmp = left;
    while (left <= center && mid <= right) {
        // 从两个数组中取出最小的放入临时数组
        if (data[left] <= data[mid]) {
            tmpArr[third++] = data[left++];
        } else {
            tmpArr[third++] = data[mid++];
        }
    }
    // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
    while (mid <= right) {
        tmpArr[third++] = data[mid++];
    }
    while (left <= center) {
        tmpArr[third++] = data[left++];
    }
    // 将临时数组中的内容拷贝回原数组中
    // (原left-right范围的内容被复制回原数组)
    while (tmp <= right) {
        data[tmp] = tmpArr[tmp++];
    }
    System.out.println("\nafter:");
    Util.printArray(data);
    System.out.println("\n<-\n\n");
}

非递归实现:

public static void sort(int[] arr) {
    int[] orderedArr = new int[arr.length];
    for (int i = 2; i < arr.length * 2; i *= 2) {
        for (int j = 0; j < (arr.length + i - 1) / i; j++) {
            int left = i * j;
            int mid = left + i / 2 >= arr.length ? (arr.length - 1) : (left + i / 2);
            int right = i * (j + 1) - 1 >= arr.length ? (arr.length - 1) : (i * (j + 1) - 1);
            int start = left, l = left, m = mid;
            while (l < mid && m <= right) {
                if (arr[l] < arr[m]) {
                    orderedArr[start++] = arr[l++];
                } else {
                    orderedArr[start++] = arr[m++];
                }
            }
            while (l < mid)
                orderedArr[start++] = arr[l++];
            while (m <= right)
                orderedArr[start++] = arr[m++];
            System.arraycopy(orderedArr, left, arr, left, right - left + 1);
        }
    }
}

关于它们具体的工作原理网上多的是,但真的把代码整整齐齐的写出来的还真不多,白看不如一敲,我还是直接放代码比较好。具体的详细原理介绍可以随便搜一下有助于理解,我这里就不 copy 啦。

最近在学算法,后面我还会继续把常见的几种排序法发出来。

你可能感兴趣的:(快速排序与归并排序Java实现版)