快速排序解惑——哨兵出发的顺序

我从这篇博客里学到了快排的基本思路,文中给出了源代码。作者提到了一点:每次必须是哨兵j先出发。也就是说,先从右往左扫描,寻找比基准数小的元素。为什么会是这样?下面说一下我的分析。

文中给出了代码,我这里摘过来一部分:

    temp = a[left]; //temp中存的就是基准数
    i = left;
    j = right;
    while(i != j) { //顺序很重要,要先从右边开始找
    	while(a[j] >= temp && i < j)
    		j--;
    	while(a[i] <= temp && i < j)//再找右边的
    		i++;       
    	if(i < j)//交换两个数在数组中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }
    //最终将基准数归位
    a[left] = a[i];
    a[i] = temp;

可以看到,一次遍历的终止条件是哨兵i和哨兵j相遇了,这个时候将基准数与i,j位置的元素进行交换。而哨兵i和哨兵j相遇会在以下两种情况下发生:

  1. 哨兵j向左扫描的过程中,主动和哨兵i相遇,即第5行那个while循环第二个条件不满足。
  2. 哨兵i向右扫描的过程中,主动和哨兵j相遇,即第7行那个while循环第二个条件不满足。

下面我们看一下在两种情况下,相遇时元素的大小:

  1. 在第一种情况下,相遇时候的元素一定小于等于基准数。因为哨兵i还没动,所以我们只要考察哨兵i所在位置的大小。而哨兵i所在位置一定是小于等于基准数的,因为一开始,哨兵i就是基准数,而在后续交换的过程中,会将哨兵j找到的小于基准数的元素换到了哨兵i的位置。因此,这时候,我们将基准数与相遇位置的元素进行交换,得到的序列一定满足基准数左边元素都小于等于基准数,右边元素都大于等于基准数。
  2. 在第二种情况下,相遇时候的元素一定小于基准数。这种情况下,哨兵j已经找到了比基准数小的元素,哨兵i右移的过程中与其相遇。因此,这时候,我们将基准数与相遇位置的元素进行交换,得到的序列也一定满足基准数左边元素都小于等于基准数,右边元素都大于等于基准数。

可以看到,让哨兵j先走是正确的。那么,如果让哨兵i先走会发生什么?

假设让哨兵i先走,则代码改成这样:

    while(i != j) { 
    	while(a[i] <= temp && i < j)
    		i++;       
    	while(a[j] >= temp && i < j)
    		j--;
    	if(i < j)//交换两个数在数组中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }

哨兵i和哨兵j相遇也会在两种情况下发生:

  1. 哨兵i向右扫描的过程中,主动和哨兵j相遇,即第2行那个while循环第二个条件不满足。
  2. 哨兵j向左扫描的过程中,主动和哨兵i相遇,即第4行那个while循环第二个条件不满足。

再看一下在两种情况下,相遇时元素的大小:

  1. 在第一种情况下,哨兵j还没动,但哨兵j所在位置要么不确定大小(一开始的时候),要么比基准数大(上次交换之后)。所以这时候不能将基准数与相遇位置的元素进行交换。
  2. 在第二种情况下,哨兵i已经找到比基准数大的数,哨兵j在左移的过程中与其相遇。这时候也不能将基准数与相遇位置的元素进行交换。

所以,让哨兵i先走的话逻辑就不对了。当然,在这种情况下,也可以添加额外的处理逻辑,比如让相遇位置前一个元素与基准数进行交换、比较相遇位置与基准数的大小。但何必使代码复杂化呢?

同理,如果我们选最右边的元素作为基准数,就应该让哨兵i先走了。

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值