题目解析:比赛的赢家| 豆包MarsCode AI刷题

118 阅读5分钟

比赛的赢家

问题描述

小M正在玩一个数组比赛游戏,游戏规则如下:每回合游戏都在所有元素互不相同的数组 arr 的前两个元素 arr[0] 和 arr[1] 之间进行。较大的整数将会取得这一回合的胜利并保留在位置 0,而较小的整数则会被移至数组的末尾。比赛继续,直到某个整数连续赢得 k 次,这个整数即为比赛的赢家。

给定一个整数数组 arr 和一个整数 k,请你返回赢得比赛的整数。题目数据保证游戏中一定存在赢家。

测试样例

样例1:

输入:arr = [2, 1, 3, 5, 4, 6, 7, 9],k = 2
输出:5

样例2:

输入:arr = [3, 2, 1, 4],k = 10
输出:4

样例3:

输入:arr = [1, 9, 8, 7, 6, 5, 4, 3, 2, 11],k = 7
输出:9

数据结构选择

算法一 使用数组

思路分析
  • 每次比较数组的前两个元素,然后将较大的元素放在数组的开头,较小的元素放在数组的末尾。
  • 缺点是每次移动元素时需要遍历数组,时间复杂度较高。
代码实现

public class Main {
    public static int solution(int[] arr, int k) {
        // write code here
        int winCnt = 0;
        int formerWinNum = arr[0];
        int currentWinNum;
        int loseNum;
        while(winCnt != k){
            currentWinNum = arr[0] > arr[1] ? arr[0] : arr[1];
            loseNum = arr[0] < arr[1] ? arr[0] : arr[1];
            arr[0] = currentWinNum;
            for(int i = 1; i < arr.length - 1; i++){
                arr[i] = arr[i + 1];
            }
            arr[arr.length - 1] = loseNum;
            if(formerWinNum == currentWinNum){
                winCnt++;
            }else{
                formerWinNum = currentWinNum;
                winCnt = 1;
            }
        }
        return formerWinNum; // Placeholder return
    }

    public static void main(String[] args) {
        int[] arr1 = {2, 1, 3, 5, 4, 6, 7, 9};
        System.out.println(solution(arr1, 2) == 5);
        
        int[] arr2 = {3, 2, 1, 4};
        System.out.println(solution(arr2, 10) == 4);
        
        int[] arr3 = {1, 9, 8, 7, 6, 5, 4, 3, 2, 11};
        System.out.println(solution(arr3, 7) == 9);
    }
}

算法二 使用双端队列(Deque)

思路分析
  • 使用双端队列可以优化数组操作。每次比较队列的前两个元素,然后将较大的元素放在队列的开头,较小的元素放在队列的末尾。
  • 这种方法的时间复杂度较低,因为双端队列可以在常数时间内进行头尾元素的插入和删除操作。
具体思路
    1. 初始化阶段

    • 创建一个双端队列 deque,用于存储整数数组中的元素。遍历给定的整数数组 arr,将每个元素依次添加到双端队列的末尾。
    • 初始化变量 winCnt 为 0,表示当前连续获胜的次数;初始化变量 formerWinNum 为双端队列的第一个元素,即初始状态下的获胜数字。
    1. 循环模拟游戏过程

    • 使用 while 循环,条件是 winCnt < k,即当连续获胜次数未达到 k 时持续循环。

    • 在每次循环中:

      • 从双端队列头部取出两个元素,分别记为 first 和 second
      • 比较 first 和 second 的大小,得到当前获胜数字 currentWinNum(较大值)和失败数字 loseNum(较小值)。
      • 将当前获胜数字添加回双端队列的头部,失败数字添加到双端队列的尾部。
      • 如果当前获胜数字与上一次的获胜数字 formerWinNum 相同,则连续获胜次数 winCnt 增加;否则,更新 formerWinNum 为当前获胜数字,并且将连续获胜次数重置为 1。
代码实现

import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static int solution(int[] arr, int k) {
        // 使用双端队列来优化数组操作
        Deque<Integer> deque = new ArrayDeque<>();
        for (int num : arr) {
            deque.addLast(num);
        }

        int winCnt = 0;
        int formerWinNum = deque.peekFirst();

        while (winCnt < k) {
            int first = deque.pollFirst();
            int second = deque.pollFirst();

            int currentWinNum = Math.max(first, second);
            int loseNum = Math.min(first, second);

            deque.addFirst(currentWinNum);
            deque.addLast(loseNum);

            if (formerWinNum == currentWinNum) {
                winCnt++;
            } else {
                formerWinNum = currentWinNum;
                winCnt = 1; // 重新开始计数
            }
        }

        return formerWinNum;
    }

    public static void main(String[] args) {
        int[] arr1 = {2, 1, 3, 5, 4, 6, 7, 9};
        System.out.println(solution(arr1, 2) == 5);
        
        int[] arr2 = {3, 2, 1, 4};
        System.out.println(solution(arr2, 10) == 4);
        
        int[] arr3 = {1, 9, 8, 7, 6, 5, 4, 3, 2, 11};
        System.out.println(solution(arr3, 7) == 9);
    }
}

补充知识(Java ArrayDeque)

创建ArrayDeque

  • 为创建ArrayDeque双端队列,必须导入java.util.ArrayDeque包。
  • 创建ArrayDeque
    • ArrayDeque ArrayDeque名 = new ArrayDeque<>();

ArrayDeque方法

  • 插入
    • 使用add(),addFirst(),addLast()插入元素 //如果ArrayDeque双端队列已满,会抛出IllegalStateException异常
      • add() - 将指定元素插入ArrayDeque双端队列的队尾
      • addFirst() - 将指定元素插入ArrayDeque双端队列的队头
      • addLast() - 将指定元素插入ArrayDeque双端队列的队尾
    • 使用offer(),offerFirst(),offerLast()插入元素并返回是否成功插入元素 //如果ArrayDeque双端队列已满,返回false
      • offer() - 将指定元素插入ArrayDeque双端队列的队尾
      • offerFirst() - 将指定元素插入ArrayDeque双端队列的队头
      • offerLast() - 将指定元素插入ArrayDeque双端队列的队尾
  • 访问
    • 使用getFirst(),getLast()访问元素 // 如果ArrayDeque双端队列为空,会抛出NoSuchElementException异常
      • getFirst() - 返回ArrayDeque双端队列的队头元素
      • getLast() - 返回ArrayDeque双端队列的队尾元素
    • 使用peek(),peekFirst(),peekLast()访问元素 // 如果ArrayDeque双端队列为空,会抛出NoSuchElementException异常
      • peek() - 返回ArrayDeque双端队列的队头元素
      • peekFirst() - 返回ArrayDeque双端队列的队头元素
      • peekLast() - 返回ArrayDeque双端队列的队尾元素
  • 删除
    • 使用remove(),removeFirst(),removeLast()删除元素 // 如果ArrayDeque双端队列为空,会抛出异常
      • remove() - 返回并删除ArrayDeque双端队列的队头元素
      • removeFirst() - 返回并删除ArrayDeque双端队列的队头元素
      • removeLast() - 返回并删除ArrayDeque双端队列的队尾元素
      • remove(element) - 返回并删除ArrayDeque双端队列的指定元素element // 如果找不到元素,会抛出异常
    • 使用poll(),pollFirst(),pollLast()删除元素 // 如果ArrayDeque双端队列为空,返回null
      • poll() - 返回并删除ArrayDeque双端队列的队头元素
      • pollFirst() - 返回并删除ArrayDeque双端队列的队头元素
      • pollLast() - 返回并删除ArrayDeque双端队列的队尾元素
      • clear() - 删除ArrayDeque双端队列的所有元素