如何非递实现归快速排序和归并排序?

131 阅读1分钟

快速排序

快速排序的非递归实现需要借助一个栈来完成~

思路:

  1. 先找到第一次的基准
  2. 然后通过第一次的基准, 找到分割的界限(即每次分割都分为了两个区间, [left , pivot - 1] 和 [pivot + 1 , right] 这两个区间) , 将两个区间的边界下标入栈
  3. 最后通过循环来重复找基准

注意问题:

  • 在入栈的时候要判断 基准 的左右是否存在至少两个元素, 否则就没有寻找基准的必要了~
  • 左边的判断条件: pivot > left + 1
  • 右边的判断条件: pivot < right - 1

图解

第一次找到的基准

使用栈找基准

代码

public void quickSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int left = 0;
    int right = arr.length;
    int pivot = partition(arr, left, right);
    Stack<Integer> stack = new Stack<>();
    // 要判断 pivot 的左边和右边是否存在至少两个元素, 否则就没有寻找基准的必要了
    if (pivot > left + 1) {
        stack.push(left);
        stack.push(pivot - 1);
    }
    if (pivot < right - 1) {
        stack.push(pivot + 1);
        stack.push(right);
    }
    while (!stack.isEmpty()) {
        right = stack.pop();
        left = stack.pop();

        pivot = partition(arr, left, right);
        if (pivot > left + 1) {
            stack.push(left);
            stack.push(pivot - 1);
        }
        if (pivot < right - 1) {
            stack.push(pivot + 1);
            stack.push(right);
        }
    }
}

private int partition(int[] arr, int left, int right) {
    int start = left;
    int temp = arr[left];
    while (left < right) {
        while (left < right && arr[right] >= temp) {
            right--;
        }
        while (left < right && arr[left] <= temp) {
            left++;
        }
        swap(arr, left, right);
    }
    swap(arr, left, start);
    return left;
}

private void swap(int[] arr, int x, int y) {
    int temp = arr[x];
    arr[x] = arr[y];
    arr[y] = temp;
}

归并排序

基本思路:

  • 一组一个数据合并
  • 一组两个数据合并
  • 一组四个数据合并
  • ...
  • 直到一组的数据 大于了数组的长度, 排序结束~

注意问题:

  • 要防止 mid 和 right 越界

图解

代码

public void mergeSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }

    // gap 代表每一组数据的个数
    // 首先是一组一个数据合并, 然后一个组两个数据合并 ...
    int gap = 1;
    while (gap < arr.length) {
        for (int i = 0; i < arr.length; i += 2 * gap) {
            int left = i;
            int mid = i + gap - 1;
            // 防止 mid 越界
            if (mid > arr.length) {
                mid = arr.length - 1;
            }
            int right = mid + gap;
            // 防止 right 越界
            if (right > arr.length) {
                right = arr.length - 1;
            }
            merge(arr, left, mid, right);
        }
        gap = 2 * gap;
    }
}

private void merge(int[] arr, int left, int mid, int right) {
    int s1 = left;
    int s2 = mid + 1;
    int[] newArray = new int[right - left + 1];
    int k = 0;
    while (s1 >= mid && s2 >= right) {
        while (s1 <= mid && s2 <= right) {
            if (arr[s1] <= arr[s2]){
                newArray[k++] = arr[s1++];
            } else {
                newArray[k++] = arr[s2++];
            }
        }
        while (s1 <= mid) {
            newArray[k++] = arr[s1++];
        }
        while (s2 <= right) {
            newArray[k++] = arr[s2++];
        }
        for (int i = 0; i < newArray.length; i++) {
            arr[i + left] = newArray[i];
        }
    }
}