呜呜呜小菜鸡被吊打了!不熟悉数据结构真的很要命!早上刷了一题队列类的题目,但是我用的是暴力解法,结果在提交的时候超时了。所以特写此篇“纪念”一下呜呜呜呜...
我想先从队列概念开始进行题解讲解。
队列概念
队列(Queue)是java中一种常见的数据结构,遵循先进先出(FIFO)。
它在执行插入操作的时候是从队列的最后面添加元素,弹出元素时从队列的最前面。
呐,我翻出了课上的笔记。
push()被称为插入、放置、添加或排队。pop()被称为删除,删除,获取或退出队列peek()返回front位置的值size()假设队列不是空,返回队列中的总数isFull()如果队列为满,则方法返回true. Else, falseisEmpty()如果队列为空,则方法返回true. Else, false- Empty Queue: front=0, rear=-1
👆这些只是一些常用的方法,大家感兴趣可以再去搜搜,方法还是很多的。
疯狂整数的统计
进入正题。
题目
题目描述:小C发现了一类特殊的整数,他称之为“疯狂整数”。疯狂整数是指只包含数字
'1'和'2'的整数。举个例子,数字1、2和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,而不是检查所有可能的数字。
我们来具体分析一下代码。设从左到右分别对应队列的front和back:
-
初始化队列:队列中先后添加
"1"和"2"。即:初始化队列为{1, 2}之所以将队列声明为string类型,是为了方便后面生成下一个疯狂整数。根据string数据类型的特点,只需要用
+连接字符串,不需要处理整数数据类型中的个位、十位、百位等进制问题。 -
当队列不为空,进入循环。用
队列.poll()取出队列内元素并将其转化为整型,与N作比较。
-
如果小于等于
N,则符合条件,count++,并将当前弹出的数字作为最高位数在后面增添"1"且增添"2",以此生成两个新的疯狂整数,按顺序插入队列。 -
如果大于
N,则意味着从当前位置到队列最后一个元素都比N大,所以进入else分支,跳出循环。roll()方法和remote()的区别是,当队列为空时,调用 poll() 方法会返回 null,而remove()方法会抛出NoSuchElementException异常。
- return 结果,over!
AI返回给我的代码中没有第24行到26行else分支的,我觉得这样可能会更高效就自己加了。
模拟示例
就以这个 solution(50) == 6 的例子进行模拟:
- 初始化计数器和队列:
count = 0,queue={1, 2} - 进入
while循环:
- 第一次循环:num = 1,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 1,queue={2, 11, 12} - 第二次循环:num = 2,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 2,queue={11, 12, 21, 22} - 第三次循环:num = 11,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 3,queue={12, 21, 22, 111, 112} - 第四次循环:num = 12,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 4,queue={21, 22, 111, 112, 121, 122} - 第五次循环:num = 21,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 5,queue={22, 111, 112, 121, 122, 211, 212} - 第六次循环:num = 22,由于num < N,计数器加一,生成两个新的疯狂整数。此时:
count = 6,queue={111, 112, 121, 122, 211, 212, 221, 222} - 第七次循环:num = 111,由于num < N这个条件不满足,于是执行
else分支,跳出循环。此时计数器的值为6
- return 计数器的值,也就是6。OVER!
小结
其实我对栈、队列这类的数据结构比较混乱,尤其是它们的方法的名称比较疑惑。如果文章中有哪些地方写得有误,欢迎大家指正!
噢对了,大家如果有区分这类数据结构的方法,可以发在评论区中,我去学习学习!