leetcode top100 - 02
2. 两数相加
坑
- 转换成数字进行运算,最后转换成链表。可能会出现溢出的情况。
因为无论是int还是long类型表达的数字长度是有限的,而链表的长度是可以无限追加的。 - 解释是干扰你的,其实就是依次从低位到高位的进位过程
笔试思路
把链表依次填充到数组中,数组容易操作,然后逐位进行加法运算;
面试思路
使用链表的思路去解决。
-
注意考虑 链表长度不一样的情况,短链表需要一直陪跑,补充0。
只要有一个不为空,则……
while(l1 != null || l2 != null){}
-
最后一位可能出现进位,需要补1
-
n%10,n/10的玩法
- 扩展
-
队列和栈的常用方法巧记
压栈弹栈,入队拉出队
栈的常用方法:
push:推入,类似于“扑通”声。
pop:弹出,类似于“砰”的一声。
peek:窥视,类似于“皮皮”声。
isEmpty:判断是否为空,类似于“是不是空”的疑问。队列的常用方法:
offer:入队,类似于“哦菲儿”声。
poll:出队,类似于“咕噜”声。
peek:查看队首元素,类似于“皮皮”声。
isEmpty:判断是否为空,类似于“是不是空”的疑问。以下是一些将谐音词汇和栈、队列联系起来的方法:
栈的谐音词汇可以联系到“扑通”声和“砰”的一声,这是因为栈的特点是后进先出,类似于一个物体被推入栈中时发出的声音,以及从栈中弹出物体时发出的声音。
队列的谐音词汇可以联系到“哦菲儿”和“咕噜”的声音,这是因为队列的特点是先进先出,类似于一个物体被推入队列中时发出的声音,以及从队列中弹出物体时发出的声音。
栈和队列的 peek 方法可以联系到“皮皮”声,因为这个方法用于查看栈顶或队首的元素,类似于在看一张扑克牌的牌面。
栈和队列的 isEmpty 方法可以联系到“是不是空”的疑问,因为这个方法用于判断栈或队列是否为空。当栈或队列为空时,就可以想象自己在问“是不是空呢?”这样可以更容易地记忆这个方法。
通过将谐音词汇和栈、队列联系起来,可以帮助我们更加轻松地记忆栈和队列的常用方法。
-
遍历链表的方法优化
在 while 循环中使用了 l1.next 和 l2.next,导致最后一个节点没有被处理。另外,在对链表进行遍历时,应该使用 while (node != null) 循环,而不是 do-while 循环。用while循环来替代do-while循环,这样就不需要判断链表是否为空或者只有一个节点了;
// 遍历链表 while (l1 != null) { l1Stack.push(l1.val); l1 = l1.next; }
-
把队列数据压进链表的方法优化
老代码
ListNode res = new ListNode(queue1.poll()); ListNode pre = res; while (!queue1.isEmpty()) { ListNode next = new ListNode(queue1.poll()); pre.next = next; pre = next; }
gpt:
ListNode dummy = new ListNode(-1); ListNode cur = dummy; while (!queue1.isEmpty()) { ListNode node = new ListNode(queue1.poll()); cur.next = node; cur = cur.next; } return dummy.next;
dummy 是一个哑节点,也称作虚拟节点。它是一个不存储任何实际数据的节点,只是作为辅助节点来帮助构建链表。
在链表的头部添加节点时,如果直接添加到头节点之前,则需要对头节点进行特殊处理。而使用 dummy 节点可以避免这种特殊处理,只需要将新节点添加到 dummy 节点的 next 上即可。最后返回 dummy 节点的 next,即整个链表的头节点。
例如,在构建一个单向链表时,可以使用 dummy 节点作为链表的头节点,它不存储任何实际数据,只是作为一个占位符。这样在添加第一个节点时,可以直接将其添加到 dummy 节点的 next 上,而不需要进行特殊处理。
在链表的删除操作中,有时也可以使用 dummy 节点来简化代码逻辑。例如,如果需要删除链表中所有值为 x 的节点,可以使用 dummy 节点作为头节点,并将其 next 指向原链表的头节点。然后遍历整个链表,对于每个节点,如果其值为 x,则将其从链表中删除。最后返回 dummy 节点的 next,即删除 x 节点后的链表头节点。这种方法可以避免对头节点进行特殊处理。
bing:
以下代码可以优化的地方有:可以使用一个循环变量cur来代替pre,这样可以避免多余的赋值操作。
可以在循环中直接使用queue1.poll()方法来获取队列中的元素,而不需要先定义一个next变量。
可以在循环结束后将cur.next置为null,这样可以避免链表中出现多余的节点。
优化后的代码如下:ListNode res = new ListNode(queue1.poll()); ListNode cur = res; while (!queue1.isEmpty()) { cur.next = new ListNode(queue1.poll()); cur = cur.next; } cur.next = null;
解法进化周期
1.野蛮期
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 倒序遍历两个列表
Stack<Integer> stack1 = new Stack();
Stack<Integer> stack2 = new Stack();
Stack<Integer> stack3 = new Stack();
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
int ans = 0;
while(l1.next != null){
stack1.push(l1.val);
l1 = l1.next;
}
while(l2.next != null){
stack2.push(l2.val);
l2 = l2.next;
}
while(stack1.size() > 0){
sb1.append(stack1.pop());
}
while(stack2.size() > 0){
sb2.append(stack2.pop());
}
ans = Integer.parseInt(sb1.toString()) + Integer.parseInt(sb2.toString());
do{
stack3.push(ans%10);
ans = ans/10;
}while(ans/10 != 0);
ListNode res = new ListNode();
ListNode cur = res;
while(stack3.size() > 0){
cur = add(cur, stack3.pop());
}
return res;
}
public ListNode add(ListNode node, int value ){
node.value = value;
ListNode node1 = new ListNode();
node.next = node1;
return node1;
}
}
```
### 2.驯化期(笔试思路)
```java
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ArrayList<Integer> l1List = new ArrayList<>();
ArrayList<Integer> l2List = new ArrayList<>();
while(l1 != null) {
l1List.add(l1.val);
l1 = l1.next;
}
while(l2 != null) {
l2List.add(l2.val);
l2 = l2.next;
}
// 短数组填充0
int l1l2Difference = l1List.size() - l2List.size();
if(l1l2Difference != 0){
if (l1l2Difference > 0) { // l1为长链表
for (;l1l2Difference > 0; l1l2Difference--){
l2List.add(0);
}
}else { // l1为短链表
for (;l1l2Difference < 0; l1l2Difference++){
l1List.add(0);
}
}
}
// 数值从低位开始加
int length = l1List.size();
ArrayList<Integer> ansList = new ArrayList<>();
int cur = 0;
int carry = 0; //进位
for (int i = 0; i < length; i++) {
cur = l1List.get(i) + l2List.get(i) + carry;
if (cur / 10 > 0){
carry = 1;
ansList.add(cur % 10);
}else {
carry = 0;
ansList.add(cur);
}
}
if (carry == 1) {
ansList.add(1);
}
// 数组转换成链表
ListNode head = new ListNode(ansList.get(0));
ListNode curNode = head;
for(int i = 1; i < ansList.size(); i++) {
curNode.next = new ListNode(ansList.get(i)); // 只关注后面的node,可以避免追加额外的节点
curNode = curNode.next;
}
return head;
}
}
3.优雅期(面试思路)
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 结果链表
ListNode dummy = new ListNode(-1);
ListNode curNode = dummy;
int curNum = 0;
int carryTag = 0;
int l1val = 0;
int l2val = 0;
// 对齐链表 只要有一个不为空,则进入循环 **圈重点**
while (l1 != null || l2 != null) {
l1val = l1 == null ? 0 : l1.val;
l2val = l2 == null ? 0 : l2.val;
l1 = l1 == null ? new ListNode(0) : l1;
l2 = l2 == null ? new ListNode(0) : l2;
curNum = l1val + l2val + carryTag;
if (curNum / 10 > 0) {
curNode.next = new ListNode(curNum % 10);
curNode = curNode.next;
carryTag = 1;
}else {
curNode.next = new ListNode(curNum);
curNode = curNode.next;
carryTag = 0;
}
// 循环进入下一位判断
l1 = l1.next;
l2 = l2.next;
}
if (carryTag == 1){
curNode.next = new ListNode(1);
}
// **圈重点**
return dummy.next;
}
}
//leetcode submit region end(Prohibit modification and deletion)