比赛配对问题
在昨天刷题过程中看到了此题目,题目给了具体的场景,对于我这种算法初学者还是比较好分析的,在读到最后一句话之前,我的思路依然是非常混乱的,但是最后一句提到了 小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循环,每轮执行的操作固定,执行完即返回结果。运行效率更高,且无需额外的资源开销。
- 只需一个
总结:
从这个题目可以看出我确实刷的题目太少了,应该多刷下算法题,拓宽知识面。