快速排序注意点

本文通过分析快速排序算法的常见错误,探讨了在循环中交换元素后立即移动指针导致的问题,强调了右指针必须先移动以及交换数据后不应立即更新指针的重要原则。并给出了正确的快速排序代码实现,总结了排序算法的关键注意事项。
摘要由CSDN通过智能技术生成

        今天在看了快速排序的思路后尝试自己写了一个快排实现算法,发现结果不对,调试了很久最后结果还是有点问题。

1、快速排序错误写法1 之 循环中交换元素后立即移动了左、右指针

        下面贴出我有问题的代码,你能看出来哪里有问题吗?

public static void main(String[] args) {
    int [] array = {6, 1, 2, 7, 9, 3 , 4, 5, 10, 8, 20, 12, 90, 110, 11};
    quickSort(array, 0 , array.length - 1);
    for (int i : array) {
        System.out.print(i + " ");
    }
}

public static void quickSort(int [] array, int start, int end) {
    //取下标0的元素作为基点,实现数组左边子数组元素元素全部小于基点,数组右边子数组元素全部大于基点
    if (start >= end) {
        return;
    }
    int pivot = array[start];
    int i = start;
    int j = end;
    while (i < j) {
        while(array[j] >= pivot && i < j) {
            j--;
        }
        while(array[i] <= pivot && i < j) {
            i++;
        }

        if (i < j) {
            int temp = array[j];
            array[j] = array[i];
            array[i] = temp;
            i++;
            j--;
        }
    }
    int temp = array[j];
    array[j] = pivot;
    array[start] = temp;
    quickSort(array, start, j - 1);
    quickSort(array, j + 1, end);
}

运行结果为:

1 2 3 4 5 6 7 8 9 10 11 12 110 20 90 

可以看到,结果数组中后半部分居然不是有序的。

        后来参照网上的写法,发现错误代码在if条件内部交换i,j位置的元素后,让i自增加1了,即i++,让j自减减1了,即j--。假如把i++;j--注释掉,再运行一遍,发现结果正常了。

1 2 3 4 5 6 7 8 9 10 11 12 20 90 110 

        不禁让我好奇这是为什么呢?

我们来看这么一个原始数组A = [20, 12, 90, 110, 11]

1、首先选取pivot=A[0];

2、设置两个指针,一个指针 l 指向A[0],并且方向向右;另一个指针 r 指向A[length -1],并且方向向左;

3、r 从右往左遍历,直到找到一个比pivot小的元素;等 r 达到指定位置后,让 l 从左往右遍历,直到找到一个比pivot大的元素。

        千万注意两点:

        1、必须让指针 r 先移动,r 移动完后 l 才能开始移动;

        2、两个指针移动比较时,遇到等于pivot的元素也要继续移动,因为我们要实现的是让pivot左边的子数组都小于pivot,右边的子数组都大于pivot。

4、这样目前指针 l 指向的元素是90, r指向的元素是11, 交换着两个元素内容;数组变成

[20, 12, 11, 110, 90]

5、假如在交换完之后,立马 l++; r--;  这时候 l==r了,现在 r 指向的数110, l 指向的也是110;

9、接着再循环时,循环条件不满足,就退出整个while循环了。

10、然后交换数组第一个元素和array[j]的值,就变成[110, 12, 11, 20, 90]。

11、很明显,上面出错了

        我们知道,必须先执行r指针的循环,等它结束后才能执行 l 指针的循环(下面有解释原因)。如果你在交换数据后立马执行l++; r--; 这就相当于l 和 r 指针同时移动了一步,并且没有先后顺序。

        上面会导致 r 指针在(array[j] >= pivot && i < j)条件中提前触发i>=j这个条件,导致r指针还没挪到正确位置就被迫结束了。

2、快速排序错误写法2 之 左指针先与右指针移动

         让 l 指针先移动

public static void main(String[] args) {
    int [] array = {6, 1, 2, 7, 9, 3 , 4, 5, 10, 8, 20, 12, 90, 110, 11};
    //int [] array = {3, 6, 7, 2, 4, 9, 1, 5};
    quickSort(array, 0 , array.length - 1);
    for (int i : array) {
        System.out.print(i + " ");
    }
}

public static void quickSort(int [] array, int start, int end) {
    //取下标0的元素作为基点,实现数组左边子数组元素元素全部小于基点,数组右边子数组元素全部大于基点
    if (start >= end) {
        return;
    }
    int pivot = array[start];
    int i = start;
    int j = end;
    while (i < j) {
        while(array[i] <= pivot && i < j) {
            i++;
        }
        while(array[j] >= pivot && i < j) {
            j--;
        }
        if (i < j) {
            int temp = array[j];
            array[j] = array[i];
            array[i] = temp;
            //i++;
            //j--;
        }
    }
    int temp = array[j];
    array[j] = pivot;
    array[start] = temp;
    quickSort(array, start, j - 1);
    quickSort(array, j + 1, end);
}

运行结果为

1 2 5 3 4 6 7 8 9 20 10 11 110 12 90

上面仅仅只是把l 和 r指针对应的循环调整了下顺序,运行结果就错误了。

3、为什么必须右指针r先移动?

        假如对[6, 1, 2, 7, 9]进行从小到大的排序,6作为基准数,如果从左边开始,i首先到达一个比6大的数,这个数为7,这时候再从左边走,但这时7到最后一个数之间已经没有比6小的数,所以6只能与7进行替换,我们要做的是从小到大的排序,这样把一个大于6的数排到前边,显然是错误的,所以应从右边进行,这样即使右边到达某个数后,这个数左边即使没有大于基准数的数,那也是把这个比基准数小的数替换到首位,达到基准数左边都比右边小的效果。
 

4、快速排序正确写法

package shuzu;

public class quickSort {

    public static void main(String[] args) {
        int [] array = {6, 1, 2, 7, 9, 3 , 4, 5, 10, 8, 20, 12, 90, 110, 11};
        //int [] array = {3, 6, 7, 2, 4, 9, 1, 5};
        quickSort(array, 0 , array.length - 1);
        for (int i : array) {
            System.out.print(i + " ");
        }
    }

    public static void quickSort(int [] array, int start, int end) {
        //取下标0的元素作为基点,实现数组左边子数组元素元素全部小于基点,数组右边子数组元素全部大于基点
        if (start >= end) {
            return;
        }
        int pivot = array[start];
        int i = start;
        int j = end;
        while (i < j) {
            while(array[j] >= pivot && i < j) {
                j--;
            }
            while(array[i] <= pivot && i < j) {
                i++;
            }
            if (i < j) {
                int temp = array[j];
                array[j] = array[i];
                array[i] = temp;
            }
        }
        int temp = array[j];
        array[j] = pivot;
        array[start] = temp;
        quickSort(array, start, j - 1);
        quickSort(array, j + 1, end);
    }

    public static void swap(int [] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }
}

5、总结

快速排序注意点:

        1、右指针必须比左指针先移动

        2、在循环中交换数据后,不要移动两个指针,否则不满足右指针比左指针先移动的效果;

        3、左指针移动时,保证在第一个大于pivot指向的元素大,等于都不行。同理右指针移动。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值