荷兰国旗问题

本文探讨了荷兰国旗问题的解决方案,目标是将一个包含R(红)、W(白)、B(蓝)字符的数组重新排列,使得R在前,W居中,B在后。提出了一种时间复杂度为O(n)的算法,通过头尾双向标记交换实现。然而,当遇到连续的'W'时,算法的时间复杂度可能会略微超过O(n)。最后,舍友提出使用计数排序的方法,虽然时间复杂度为O(2n),但更简洁且高效。
题目是:要求重新排列一个由字符R,W,B(R代表红色,W代表白色,B代表蓝色,这都是荷兰国旗的颜色)构成的数组,使得所有的R都排在最前面,W排在其次,B排在最后。为荷兰国旗问题设计一个算法,其时间复杂度为O(n)

这道题想了相当久。要求时间复杂度是O(n),肯定只能用简单排序,然后在思想上加以改进。既然只有三种值,那么很容易就能遇到最大和最小值,这种情况下考虑使用头尾双向标记的交换排序。
但是有一个问题困扰了我很久,怎么去处理‘W’??
一开使我的思路是头设置i,尾设置j,交换的时候,如果r[i] == 'B' && r[j] == 'R',直接交换并且++i,--j。如果有一方是'W',那么交换之后指向‘W’的坐标不动,另一方照常移动。
但是这个时候问题就出来了,如果同时遇到两个‘W’呢?
后来想到一种思路,设置三个坐标i, j, k,i和j标志头和尾值不正确的位置,即可以用来交换的位置,而k用来标志正在进行交换的坐标。k可以从头开始 往后遍历,当遇到j的时候循环结束。当k遇到‘R’或者‘B’的时候,与i或者j交换,如果遇到‘W’则继续往前走。

代码如下:
void HolandFlag(char s[], int n) {
    char temp;
    int i = 0, j = n - 1, k = 0;
    while (k < j) {
        ++cmp1_count;
        while (s[i] == 'R') {
            ++i;
            ++cmp1_count;
        }
        ++cmp1_count;
        while (s[j] == 'B') {
            --j;
            ++cmp1_count;
        }
        if (k < i)
            k = i;
        ++cmp1_count;
        while (s[k] == 'W') {
            ++k;
            ++cmp1_count;
        }
        if (s[k] == 'R') {
            temp = s[i];
            s[i] = s[k];
            s[k] = temp;
            ++i;
            ++exch_count;
        } else {
            temp = s[j];
            s[j] = s[k];
            s[k] = temp;
            --j;
            ++exch_count;
        }
    }
}



这个算法时间复杂度会稍大于O(n),因为当遇到一连串‘W’的时候,k将会往前走,之后i将会沿着k走过的道路往前走,耗费了无谓的比较时间。
在最好情况下,正序,所比较的次数应该为n+m(其中m是‘W’的个数,全都是k比较所耗)。交换次数是0次。
在最差情况下,各个字母都不在自己应该在的位置上,此时无谓的比较将会相当多(即while中的循环开始条件比较,但是比较均不成功不会进入循环),比较次数将在3n和4n之间。此时交换次数是n-m次(期中m是‘W’的个数)
空间复杂度只用到了一个交换空间,为O(1)。

本以为这解法已经相当完美了,不过。。。
不过晚上回来之后跟舍友商量,舍友说直接用计数排序,遍历一遍记录下三种颜色的个数,然后再遍历一遍依次给数组赋值。。。时间复杂度最多也就O(2n),空间复杂度O(3)而已。。
尼玛。。。。瞬间跪了。。不过仔细想了想这种方法似乎还真比我的“标准解法”好,简单而且完美完成题目的要求,时间性能和空间性能也足够高。
算法之路真是无止尽啊。。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值