比赛配对问题 | 豆包MarsCode AI刷题

133 阅读4分钟

比赛配对问题

在昨天刷题过程中看到了此题目,题目给了具体的场景,对于我这种算法初学者还是比较好分析的,在读到最后一句话之前,我的思路依然是非常混乱的,但是最后一句提到了 小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。 ,欸!这似乎有点像递归的结束条件啊!那么这题我就采用了递归的做法。

题目难度: 简单

问题描述

小R正在组织一个比赛,比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制:

  • 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,且产生 n / 2 支队伍进入下一轮。
  • 如果当前队伍数为 奇数,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行 (n - 1) / 2 场比赛,且产生 (n - 1) / 2 + 1 支队伍进入下一轮。

小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。

测试用例

样例1:

输入:n = 7
输出:6

样例2:

输入:n = 14
输出:13

样例3:

输入:n = 1
输出:0

我目前看到这道题目想到是递归,各位大佬如果有更妙的做法,欢迎在评论区留言指正。以下只是我个人的思路和做法。

题解

第一次尝试

public class Main {
    public static int count = 0;
    public static int solution(int n) {
        // write code here
        count = 0;
        recursion(n);
        return count;
    }

    public static int recursion(int n){
        if (n == 1) return n;
        if (n % 2 == 0){
            n = n / 2;
        }else {
            n = (n - 1) / 2 + 1;
        }
        count += n;
        return recursion(n);
    }

    public static void main(String[] args) {
        System.out.println(solution(7) == 6);
        System.out.println(solution(14) == 13);
        System.out.println(solution(1) == 0);
    }
}
false
false
true

第一版是有很大问题的,测试用例的前两个都是false,紧着这我对递归的慢慢的debug,我这里count代表的是配对次数n则是晋级的人数,起初我认为晋级人数/2 [+1]便可以当作配对次数,这里的分析有个错误,若人数是奇数是,配对次数 != 晋级人数/2 + 1,这1个人是直接晋级的,并没有参与比赛,所以这里要对配对次数减一来消除这个错误。

修正后的代码

public class Main {
    public static int count = 0;
    public static int solution(int n) {
        // write code here
         count = 0;
        recursion(n);
        return count;
    }

    public static int recursion(int n){
        if (n == 1) return n;
        if (n % 2 == 0){
            n = n / 2;
        }else {
            n = (n - 1) / 2 + 1;
            count--;
        }
        count += n;
        return recursion(n);
    }

    public static void main(String[] args) {
        System.out.println(solution(7) == 6);
        System.out.println(solution(14) == 13);
        System.out.println(solution(1) == 0);
    }
}

以上是我的思路,当我把代码交给ChatGPT优化时,他将我的递归修改成了while循环,似乎是个不错的解决方案

public class Main {
    public static int solution(int n) {
        int count = 0;
        while (n > 1) {
            if (n % 2 == 0) {
                count += n / 2;
                n /= 2;
            } else {
                count += (n - 1) / 2;
                n = (n - 1) / 2 + 1;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(7) == 6);   // true
        System.out.println(solution(14) == 13); // true
        System.out.println(solution(1) == 0);   // true
    }
}

对比

与我的递归实现相比,使用 while 循环的版本有以下几个优点:

1. 避免递归开销

  • 递归在每次调用时都会消耗栈空间,尤其对于较大的 n 值,可能会导致栈溢出(StackOverflowError)。
  • 迭代的 while 循环实现没有栈开销,可以处理更大的输入规模而不影响性能。

2. 简洁性和可读性

  • 使用 while 循环的版本逻辑更加直接,避免了递归函数中对 count 进行反复更新的复杂性。这样可以更直观地看到每轮比赛的处理过程。
  • 循环的逻辑将计数和队伍数更新整合在一起,避免了递归代码中多次更新 count 的地方,减少了理解难度。

3. 性能更高

  • 递归会有一定的函数调用开销,而循环则省去了这些额外的调用成本。对于较大的 n,性能提升会更明显。
  • 因为迭代只是在 count 变量上进行简单的加法操作,且直接对 n 进行更新,所以在时间和空间复杂度上都更优。

4. 局部变量的安全性

  • while 循环中使用了局部变量 count,不依赖全局变量,这样可以更好地避免数据污染和意外更新的问题,尤其在并发环境或多次调用的情况下更为安全。

代码执行比较

如果我们简单比较以下代码的执行流程:

  • 递归代码

    • 每次递归调用会更新 count,不断进入更深的递归栈,消耗系统资源,且需要在每次调用后返回上一层,增加了执行时间。
  • 迭代代码

    • 只需一个 while 循环,每轮执行的操作固定,执行完即返回结果。运行效率更高,且无需额外的资源开销。

总结:

从这个题目可以看出我确实刷的题目太少了,应该多刷下算法题,拓宽知识面。