基本流程:

a.选取第一个元素作为pivot
b0.先处理R处的元素
b1.R处的元素与pivot比较,如果比pivot元素大,元素覆盖R位置,R左移,直到与L重合
b2.R处的元素与pivot比较,如果比pivot元素小,元素覆盖L位置,L右移,直到与L重合
b3.如果处理L和R处的元素没有变化时,继续前移,如果发生了元素覆盖,继续处理另一个方向(R和L)的元素
c1.用同样的逻辑处理pivot左侧的(经过b处理过的左侧)子序列(递归)
c2.用同样的逻辑处理pivot右侧的(经过b处理过的右侧)子序列(递归)

这里用一副图来展示整个过程:

 

思考过程中总结的要点:
1.这个方法是void返回,所以传入的int[] array就是我们处理的对象,返回的也是这个对象,只不过是排序完成的对象。如果说是传入一个局部处理的int[] subArray,使用0,subArray.length-1进行处理的话,其实就完全没必要传入L,R了,但如此一来我们需要对Array进行不停地拼装,且在递归中,这种拼装消耗性能,且处理麻烦;
2.L,R都是在变化的,在方法体中,排序完成后,需要对左子序列和右子序列的头尾坐标进行标记,并进行后续的处理;
3.处理的边界就是L与R相等了,此时说明其中只有一个元素,不需要排序了;

 

代码与一些说明:

public class QuickSort {

    /**
     * 这是一个void方法,不用返回处理完的子序列,所有的操作完成了对子序列的处理与值的覆盖后,整个array自然排序完成
     *
     * @param array 待处理的数组,所有的内部递归调用的都是这个数组本身,不同的只是处理的下标区间
     * @param low   下标低位
     * @param high  下标高位
     */
    public static void quickSort(int[] array, int low, int high) {
        //如果两者相等,说明只有一个元素了。尽可能在外部判断好
        if (low >= high) {
            return;
        }

        //选取标定值,一般就选取左边位置的标定值
        int pivotValue = array[low];

        //之所以要进行赋值,是因为left与right是动态变化的,而low和high要在最后作为递归的确定边界,不能被覆盖
        int left = low;
        int right = high;

        //这里的的left==right其实就是循环的边界条件。达到了相等说明这一轮的处理结束
        while (left < right) {
            //如果右侧的值大于等于标定值,移动R,边界条件是L==R
            while (array[right] >= pivotValue && left < right) {
                right--;
            }

            //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了
            //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环
            if (array[right] < pivotValue) {
                array[left] = array[right];
            }

            while (array[left] <= pivotValue && left < right) {
                left++;
            }
            if (array[left] > pivotValue) {
                array[right] = array[left];
            }

            //如果此时L==R,那么pivot就赋给array[left]或者array[right];

            //如果此时L<R,那么说明还有一些中间元素需要处理。即没有与pivot比较的元素还存在,
            // 那么上一步已经把大于pivot的元素放到了右侧,这里的位置将会在下一轮被右侧传入的值覆盖,所以设置什么值也无所谓了。
            if (left == right) {
                array[left] = pivotValue;
            }
        }

        quickSort(array, low, left - 1);
        quickSort(array, left + 1, high);
    }

    public static void main(String[] args) {
        int[] array = {1, 3, 2, 44, 33, 12, 222, 32, 11, 22, 33, 12, 3, 2, 1};
        quickSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }
}

 加入了排序的方向:

public class QuickSort {

    private Order order;

    /**
     * 排序方向
     */
    enum Order {
        /**
         * 默认,顺序
         */
        ASC,
        /**
         * 可选,逆序
         */
        DES;

        Order() {
        }
    }

    public QuickSort(Order order) {
        this.order = order;
    }

    /**
     * 可设置排序方向
     *
     * @param order
     */
    public void setOrder(Order order) {
        this.order = order;
    }

    /**
     * 这是一个void方法,不用返回处理完的子序列,所有的操作完成了对子序列的处理与值的覆盖后,整个array自然排序完成
     *
     * @param array 待处理的数组,所有的内部递归调用的都是这个数组本身,不同的只是处理的下标区间
     * @param low   下标低位
     * @param high  下标高位
     */
    public void quickSort(int[] array, int low, int high) {
        //如果两者相等,说明只有一个元素了。尽可能在外部判断好
        if (low >= high) {
            return;
        }

        //选取标定值,一般就选取左边位置的标定值
        int pivotValue = array[low];

        //之所以要进行赋值,是因为left与right是动态变化的,而low和high要在最后作为递归的确定边界,不能被覆盖
        int left = low;
        int right = high;

        //这里的的相等其实就是循环的边界条件。达到了相等说明这一轮的处理结束
        while (left < right) {
            //如果右侧的值大于等于标定值,移动R,边界条件是L==R
            right = processRights(array, pivotValue, left, right);
            left = processLeft(array, pivotValue, left, right);

            //如果此时L==R,那么pivot就赋给array[left]或者array[right];
            //如果此时L<R,那么说明还有一些中间元素需要处理。即没有与pivot比较的元素还存在,
            // 那么上一步已经把大于pivot的元素放到了右侧,这里的位置将会在下一轮被右侧传入的值覆盖,所以设置什么值也无所谓了。
            setPivotIfEqual(array, pivotValue, left, right);
        }

        if (low < left - 1) {
            quickSort(array, low, left - 1);
        }
        if (left + 1 < high) {
            quickSort(array, left + 1, high);
        }
    }

    private void setPivotIfEqual(int[] array, int pivotValue, int left, int right) {
        if (left == right) {
            array[left] = pivotValue;
        }
    }

    private int processLeft(int[] array, int pivotValue, int left, int right) {
        if (order == Order.ASC) {
            while (array[left] <= pivotValue && left < right) {
                left++;
            }
            if (array[left] > pivotValue) {
                array[right] = array[left];
            }
            return left;
        } else {
            while (array[left] >= pivotValue && left < right) {
                left++;
            }
            if (array[left] < pivotValue) {
                array[right] = array[left];
            }
            return left;
        }
    }

    private int processRights(int[] array, int pivotValue, int left, int right) {
        if (order == Order.ASC) {
            while (array[right] >= pivotValue && left < right) {
                right--;
            }

            //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了
            //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环
            if (array[right] < pivotValue) {
                array[left] = array[right];
            }
            return right;
        } else {
            while (array[right] <= pivotValue && left < right) {
                right--;
            }

            //到了这一步有两个条件,一个是右值比标定值小了,需要进行值覆盖;另一个是L==R了
            //这里将右侧的这个值付给了左侧,那么需要left++吗?其实不需要,因为天然地这个小于pivot的关系符合下一个循环
            if (array[right] > pivotValue) {
                array[left] = array[right];
            }
            return right;
        }
    }


    public static void main(String[] args) {
        int[] array = {1, 3, 2, 44, 33, 12, 222, 32, 11, 22, 33, 12, 3, 2, 1};
        new QuickSort(Order.DES).quickSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }
}

 

 

 

posted on 2021-09-12 22:54  长江同学  阅读(53)  评论(0编辑  收藏  举报