疯狂整数的统计 | 豆包MarsCode AI 刷题

181 阅读5分钟

呜呜呜小菜鸡被吊打了!不熟悉数据结构真的很要命!早上刷了一题队列类的题目,但是我用的是暴力解法,结果在提交的时候超时了。所以特写此篇“纪念”一下呜呜呜呜...

我想先从队列概念开始进行题解讲解。

队列概念

队列(Queue)是java中一种常见的数据结构,遵循先进先出(FIFO)

它在执行插入操作的时候是从队列的最后面添加元素,弹出元素时从队列的最前面。

image.png 呐,我翻出了课上的笔记。

  • push() 被称为插入、放置、添加或排队。
  • pop() 被称为删除,删除,获取或退出队列
  • peek() 返回front位置的值
  • size() 假设队列不是空,返回队列中的总数
  • isFull() 如果队列为满,则方法返回true. Else, false
  • isEmpty() 如果队列为空,则方法返回true. Else, false
  • Empty Queue: front=0, rear=-1

👆这些只是一些常用的方法,大家感兴趣可以再去搜搜,方法还是很多的。

疯狂整数的统计

进入正题。

题目

题目描述:小C发现了一类特殊的整数,他称之为“疯狂整数”。疯狂整数是指只包含数字 '1' 和 '2' 的整数。举个例子,数字 12 和 121 都是疯狂整数。现在,给定一个整数 N,你的任务是计算出所有小于或等于 N 的非负疯狂整数的数量。

作者的超时解法

乍一看还挺简单,不就是用将整数转换成string类型,然后运用string中的contains()方法一下就能做出来嘛。👈这是我刚拿到题目的想法,这里附上我的超时解法,欢迎大家吊打我(bushi

大家不要模仿这个!!!

public class Main {
    public static int solution(int N) {
        int count = 0;
        for(int i = 0; i <= N; i++){
            String s = i + "";
            if(s.contains("0") || s.contains("3") || s.contains("4") || s.contains("5") ||
                s.contains("6") || s.contains("7") || s.contains("8") || s.contains("9")){

            }else{
                count++;
            } 
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(21) == 5);
        System.out.println(solution(50) == 6);
        System.out.println(solution(5) == 2);
    }
}

👆This one 是错误解法!This one 是错误解法!!This one 是错误解法!!!

题解

那我们现在来看看究竟要怎么解才是对的。

我是借助了MarsCode自带的AI功能进行提问,它给我返回了正确的做法,用的是队列。我用它的代码对题目进行了复盘。现在附上代码:

import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static int solution(int N) {
        // 初始化计数器
        int count = 0;
        
        // 使用队列来生成所有可能的疯狂整数
        Queue<String> queue = new LinkedList<>();
        queue.add("1");
        queue.add("2");
        
        while (!queue.isEmpty()) {
            String numStr = queue.poll();
            int num = Integer.parseInt(numStr);
            
            // 如果当前数字小于或等于N,则计数
            if (num <= N) {
                count++;
                // 生成下一个疯狂整数并加入队列
                queue.add(numStr + "1");
                queue.add(numStr + "2");
            }else{
                break;
            }
        }
        
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(21) == 5);
        System.out.println(solution(50) == 6);
        System.out.println(solution(5) == 2);
    }
}

通过队列,我们可以按顺序生成所有可能的疯狂整数,并且保证数字不会重复。而相比于遍历从 0 到 N 的所有数字并检查每个数字是否是疯狂整数,使用队列生成疯狂整数的方法更加高效。因为使用队列只会生成疯狂整数的数字,我们所要做的,只是检查生成的疯狂整数的数字是否会大于题目中的最大值N,而不是检查所有可能的数字。

我们来具体分析一下代码。设从左到右分别对应队列的frontback

  1. 初始化队列:队列中先后添加"1""2"。即:初始化队列为{1, 2}

    之所以将队列声明为string类型,是为了方便后面生成下一个疯狂整数。根据string数据类型的特点,只需要用+连接字符串,不需要处理整数数据类型中的个位、十位、百位等进制问题。

  2. 当队列不为空,进入循环。用队列.poll()取出队列内元素并将其转化为整型,与N作比较。

  • 如果小于等于N,则符合条件,count++,并将当前弹出的数字作为最高位数在后面增添"1"且增添"2",以此生成两个新的疯狂整数,按顺序插入队列。

  • 如果大于N,则意味着从当前位置到队列最后一个元素都比N大,所以进入else分支,跳出循环。

    roll()方法和remote()的区别是,当队列为空时,调用 poll() 方法会返回 null,而 remove()方法会抛出NoSuchElementException异常。

  1. return 结果,over!

AI返回给我的代码中没有第24行到26行else分支的,我觉得这样可能会更高效就自己加了。

模拟示例

就以这个 solution(50) == 6 的例子进行模拟:

  1. 初始化计数器和队列:count = 0queue={1, 2}
  2. 进入while循环:
  • 第一次循环:num = 1,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 1queue={2, 11, 12}
  • 第二次循环:num = 2,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 2queue={11, 12, 21, 22}
  • 第三次循环:num = 11,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 3queue={12, 21, 22, 111, 112}
  • 第四次循环:num = 12,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 4queue={21, 22, 111, 112, 121, 122}
  • 第五次循环:num = 21,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 5queue={22, 111, 112, 121, 122, 211, 212}
  • 第六次循环:num = 22,由于num < N,计数器加一,生成两个新的疯狂整数。此时:count = 6queue={111, 112, 121, 122, 211, 212, 221, 222}
  • 第七次循环:num = 111,由于num < N这个条件不满足,于是执行else分支,跳出循环。此时计数器的值为6
  1. return 计数器的值,也就是6。OVER!

小结

其实我对栈、队列这类的数据结构比较混乱,尤其是它们的方法的名称比较疑惑。如果文章中有哪些地方写得有误,欢迎大家指正!

噢对了,大家如果有区分这类数据结构的方法,可以发在评论区中,我去学习学习!