LeetCode刷题131-150

①面试题 17.12.BiNode

2024.3.18 周一
题目:
二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。
实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,
转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。

思考:
node.left = null;// 当前节点左指针置空
prev.right = node;// 前置指针右指针指向当前节点,作为链表的next指针,链表新增元素
prev = node;// 指针后移

树 链表 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
//为了返回单链表的头结点而多设的一个结点
TreeNode head = new TreeNode(-1);
//当前结点的前一个结点
TreeNode pre = null;

public TreeNode convertBiNode(TreeNode root) {
helper(root);
return head.right;
}

public void helper(TreeNode root){
if (root == null) return;

helper(root.left);

if(pre == null){ //说明是第一个结点
pre = root;
head.right = root; //记录它,将它作为单链表的表头
}else{
pre.right = root;
pre = root;
}
root.left = null; //将当前结点的左指针设置为null

helper(root.right);
}
}

②面试题 02.07.链表相交

2024.3.19 周二
题目:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构。

思路1:
使用哈希表,先将 A 中所有结点存放在表中,再遍历 B ,如果哈希表中存在某一个结点,则直接返回。

哈希表 12.72% 76.64%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();

ListNode temp = headA;
while(temp != null){
set.add(temp);
temp = temp.next;
}

temp = headB;
while(temp != null){
if(set.contains(temp)){
return temp;
}
temp = temp.next;
}
return null;
}
}

思路2:
A链表有 a 个结点
B链表有 b 个结点
他们有 c 个共同结点

那么:
A的私有结点为 a - c
B的私有结点为 b - c

指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
a+(b−c)
指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
b+(a−c)

二者相同

如果俩链表有共同结点,则两指针指向同一个结点(第一个公共结点)
如果他们无共同结点,则俩指针同时指向null

双指针 99.91% 30.24%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA;
ListNode B = headB;

while(A != B){
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}

return A;

}
}

⑦LCR 171.训练计划 V

2024.3.24 周日
题目:
某教练同时带教两位学员,分别以链表 l1、l2 记录了两套核心肌群训练计划,节点值为训练项目编号。
两套计划仅有前半部分热身项目不同,后续正式训练项目相同。请设计一个程序找出并返回第一个正式训练项目编号。
如果两个链表不存在相交节点,返回 null 。

思路:
与链表相交那道题相同

同时为 null 就停止循环了

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA;
ListNode B = headB;

while(A != B){
A = A != null? A.next : headB;
B = B != null? B.next : headA;
}
return A;
}
}

③面试题 02.06.回文链表

2024.3.20 周三
题目:
编写一个函数,检查输入的链表是否是回文的

思路1:
将链表的值复制到一个List中,
在数组中使用双指针判断是否为回文

33% 51%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> temp = new ArrayList<>();

ListNode cur = head;
while(cur != null){
temp.add(cur.val);
cur = cur.next;
}

//判断
int first = 0;
int second = temp.size() - 1;
while(first < second){
if(!temp.get(first).equals(temp.get(second))){
return false;
}
first++;
second--;
}
return true;
}
}

思路2:
使用栈
如果是回文链表,那么将其结点值全部加到栈中后再依次弹出,弹出的数字应该与原链表一一对应相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public boolean isPalindrome(ListNode head) {
Stack<Integer> stack = new Stack<>();

//入栈
ListNode cur = head;
while(cur != null){
stack.push(cur.val);
cur = cur.next;
}

//判断
while(!stack.isEmpty()){
if(stack.pop() != head.val){
return false;
}
head = head.next;
}
return true;
}
}

④面试题 02.03.删除中间节点

2024.3.21 周四
题目:
若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。
假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。
例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f

思考:
pre.next = cur.next
只给出cur结点,不能使用pre
不能直接绕过,只能通过复制的方式间接删除节点

1
2
3
4
5
6
7
8
9
10
11
class Solution {
//从后向前挪一个位置(绕过node.next结点)(无法直接绕过当前node结点)
public void deleteNode(ListNode node) {

//复制后继结点node.next的结点值到node结点
node.val = node.next.val;
//绕过node。next结点
node.next = node.next.next;

}
}

⑤面试题 02.02.返回倒数第 k个结点

2024.3.22 周五
题目:
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

思路1:
借助List存储结点值,使用索引找到倒数第二个值

100% 62.14% self

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int kthToLast(ListNode head, int k) {
List<Integer> temp = new ArrayList<>();

ListNode cur = head;
while(cur != null){
temp.add(cur.val);
cur = cur.next;
}

return temp.get(temp.size() - k);
}
}

思路2:

使用两个指针,初始时,cur和pre都指向起点位置,先让cur走k步,
此时,pre和cur相距k,再让他们同时走,直到cur走到尾结点时跳出,此时pre所指即是答案

100% 84.18% 双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode pre = head;
ListNode cur = head;

//cur先走
for(int i = 0; i < k; i++){
cur = cur.next;
}

//一起走
while(cur != null){
pre = pre.next;
cur = cur.next;
}

return pre.val;
}
}

⑥面试题 02.01.移除重复节点

2024.3.23 周六
题目:

思路:
直接改变结点指向,从而删除重复结点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ListNode removeDuplicateNodes(ListNode head) {
if(head == null){
return head;
}

Set<Integer> set = new HashSet<>();
set.add(head.val);

ListNode pre = head;

while(pre.next != null){
//判断当前结点是否该被删除
if(set.add(pre.next.val)){
//添加成功
pre = pre.next;
}else{
//添加失败,有重复值,需要删除节点
pre.next = pre.next.next;
}
}

return head;
}

⑧LCR 142.训练计划IV

2024.3.25 周一
题目:
给定两个以 有序链表 形式记录的训练计划 l1、l2,分别记录了两套核心肌群训练项目编号,请合并这两个训练计划,按训练项目编号 升序 记录于链表并返回。
注意:新链表是通过拼接给定的两个链表的所有节点组成的。

思路1:递归,先确定第一个结点是哪个,后面依次递归确定

递归 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public ListNode trainningPlan(ListNode l1, ListNode l2) {
if(l1 == null){
return l2;
}else if(l2 == null){
return l1;
}else if(l1.val < l2.val){
l1.next = trainningPlan(l1.next,l2);
return l1;
}else{
l2.next = trainningPlan(l1,l2.next);
return l2;
}
}
}

思路2:比较大小,只改变指向关系

迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public ListNode trainningPlan(ListNode l1, ListNode l2) {
ListNode res = new ListNode(-1);

ListNode pre = res;

while(l1 != null && l2 != null){
if(l1.val <= l2.val){
//先指过去
pre.next = l1;
l1 = l1.next;
}else if(l1.val > l2.val){
pre.next = l2;
l2 = l2.next;
}
//再替换
pre = pre.next;
}

//最后比较最多只有一个指向null
pre.next = l1 == null ? l2 : l1;
return res.next;
}
}

⑨LCR 141.训练计划III

2024.3.26 周二
题目:
给定一个头节点为 head 的单链表用于记录一系列核心肌群训练编号,请将该系列训练编号 倒序 记录于链表并返回。
示例:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

思路1:
只改变指针指示方向
假设链表为 1→2→3→∅ 我们想要把它改成 ∅←1←2←3。
当 head 为原链表的最后一个节点时,会将最后一个节点作为新链表的头节点返回。所以返回 newList 即可

迭代 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public ListNode trainningPlan(ListNode head) {
ListNode pre = null;
ListNode cur = head;

while(cur != null){
//需要在指针指向关系改变之前记录下一个结点是什么
ListNode next = cur.next;

cur.next = pre; //更改结点指向
pre = cur; //挪动pre指针到当前

cur = next; //当前指针挪动到下一个结点
}
return pre;
}
}

思路2:
1→2←3←4←5←6
head.next.next = head //箭头反转
head.next = null //尾部指向空

示例:
输入:head = [3,6,4,1]
输出:[1,4,6,3]

思考:
3 -> 6 -> 4 -> 1 -> null

3 -> <- 6 <- 4 <- 1

null <- 3 <- 6 <- 4 <- 1

箭头反转:head.next.next = head
链表尾部指向空:head.next = null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public ListNode trainningPlan(ListNode head) {
if(head == null || head.next == null){
return head;
}

//链表其余已经被反转
ListNode newList = trainningPlan(head.next);

//反转当前的
head.next.next = head;

//尾部指向空
head.next = null;
return newList;
}
}

⑫LCR 123.图书整理

2024.3.29 周五
题目:
类似⑨LCR 141.训练计划III
书店店员有一张链表形式的书单,每个节点代表一本书,节点中的值表示书的编号。
为更方便整理书架,店员需要将书单倒过来排列,就可以从最后一本书开始整理,逐一将书放回到书架上。请倒序返回这个书单链表。
示例:
输入:head = [3,6,4,1]
输出:[1,4,6,3]

思考:
先加入到List中再倒序取出

self 20.77%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] reverseBookList(ListNode head) {
List<Integer> res = new ArrayList<>();

ListNode cur = head;

while(cur != null){
res.add(cur.val);
cur = cur.next;
}

int[] ans = new int[res.size()];

for(int i = 0; i < res.size(); i++){
ans[i] = res.get(res.size() - 1 - i);
}
return ans;
}
}

思路2:
先反转链表再加入数组中

100% self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int[] reverseBookList(ListNode head) {

//先迭代反转链表再加入到int数组中

ListNode pre = null;
ListNode cur = head;

int count = 0;

while(cur != null){
ListNode next = cur.next;

cur.next = pre;
pre = cur;

cur = next;

count++;
}

int[] res = new int[count];
for(int i = 0; i < count; i++){
res[i] = pre.val;
pre = pre.next;
}

return res;
}
}

⑩LCR 140.训练计划 II

2024.3.27 周三
题目:

思路1:
将所有结点存储在一个List集合中,使用结合下标找到所需结点

self 100% 72.52%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public ListNode trainingPlan(ListNode head, int cnt) {
List<ListNode> res = new ArrayList<>();

ListNode cur = head;

while(cur != null){
res.add(cur);
cur = cur.next;
}

return res.get(res.size() - cnt);

}
}

思路2:
使用快慢指针
先使两个指针之间预留出给定控件,返回后者到达链表末尾时,返回前者即可。

self 100% 13.52%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public ListNode trainingPlan(ListNode head, int cnt) {
ListNode slow = head;
ListNode fast = head;

for(int i = 0; i < cnt; i++){
fast = fast.next;
}

while(fast != null){
slow = slow.next;
fast = fast.next;
}

return slow;
}
}

⑪LCR 136.删除链表的节点

2024.3.28 周四
题目:
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。

思路:
实现删除链表结点的操作:pre.next = cur.next;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head.val == val){
return head.next;
}

ListNode pre = head;
ListNode cur = head.next;

while(cur.val != val){
pre = cur;
cur = cur.next;
}

if(cur != null){
pre.next = cur.next;
}

return head;
}
}

⑬LCR 027.回文链表

2024.3.30 周六
同 ③面试题 02.06.回文链表
题目:
给定一个链表的 头节点 head ,请判断其是否为回文链表。
如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

双指针 52.54%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> temp = new ArrayList<>();

while(head != null){
temp.add(head.val);
head = head.next;
}

//用双指针
int first = 0;
int second = temp.size() - 1;
while(first < second){
if(temp.get(first) != temp.get(second)){
return false;
}
first++;
second--;
}

return true;
}
}

栈 5.25%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isPalindrome(ListNode head) {
Stack<Integer> stack = new Stack<>();

ListNode cur = head;
while(cur != null){
stack.push(cur.val);
cur = cur.next;
}

while(!stack.isEmpty()){
if(stack.pop() != head.val){
return false;
}
head = head.next;
}

return true;
}
}

⑭LCR 024.反转链表

2024.3.31 周日
同⑨LCR 141.训练计划III
题目:
给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。

递归 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}

//递归
ListNode newList = reverseList(head.next);

//反转当前结点
head.next.next = head;
head.next = null;

return newList;
}
}

3 -> 6 -> 4 -> 1 -> null

3 -> <- 6 <- 4 <- 1

null <- 3 <- 6 <- 4 <- 1

迭代 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}

//迭代

ListNode pre = null;
ListNode cur = head;

while(cur != null){
ListNode next = cur.next;

cur.next = pre;
pre = cur;

cur = next;
}
return pre;
}
}

⑮LCR 023.相交链表

2024.4.1 周一
同② ⑦
题目:
给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//哈希表
Set<ListNode> temp = new HashSet<>();

ListNode A = headA;
ListNode B = headB;

while(A != null){
temp.add(A);
A = A.next;
}

while(B != null){
if(temp.contains(B)){
return B;
}
B = B.next;
}
return null;
}
}

双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//双指针
ListNode A = headA;
ListNode B = headB;

while(A != B){
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}

return A;
}
}

⑯1290.二进制链表转整数

2024.4.2 周二
题目:
给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。

100%

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public int getDecimalValue(ListNode head) {
int res = 0;
ListNode cur = head;
while(cur != null){
res = res * 2 + cur.val;
cur = cur.next;
}
return res;
}
}

运算符 100%

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public int getDecimalValue(ListNode head) {
int res = 0;
while(head != null){
res <<= 1;
res |= head.val;
head = head.next;
}
return res;
}
}

⑰876.链表的中间结点

2024.4.3 周三
题目:
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

0 1 2 3 4
5

0 1 2 3 4 5
6

100% 25%列表 self

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public ListNode middleNode(ListNode head) {
List<ListNode> temp = new ArrayList<>();

while(head != null){
temp.add(head);
head = head.next;
}
return temp.get(temp.size()/2);
}
}

100% 59% 数组

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public ListNode middleNode(ListNode head) {
ListNode [] temp = new ListNode[100];
int count = 0;
while(head != null){
temp[count++] = head;
head = head.next;
}
return temp[count/2];
}
}

思路1:
使用单指针法,进行两次遍历,第一次遍历统计链表中元素的个数,第二次遍历时遍历到低第N/2个

100% 49% 单指针法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public ListNode middleNode(ListNode head) {
int n = 0;
ListNode cur = head;
while(cur != null){
n++;
cur = cur.next;
}

cur = head;
int k = 0;

while(k < n/2){
k++;
cur = cur.next;
}
return cur;
}
}

思路2:
快慢指针法,慢指针一次走一步,快指针一次走两步,当fast走到最后一个结点or null,慢指针指向的一定为中间节点

100% 33.11%

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}

⑲2.两数相加

数字进位
2024.4.5 周五
题目:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

思路:
逆序存储 ——> 链表同一位置的数字可以直接相加,
链表中对应位置的数字为:(n1+n2+carry) % 10 (取余)
新的进位:(n1 + n2 + carry) / 10 (取整)

如果长度不同 ——> 短的链表后面有若干个0,
链表遍历完之后,如果 carry != 0,则在答案链表的后面附加一个结点,结点值为 carry

tail 是指向结果链表的尾部结点,用来不断更新并连接新的结点。
carry 是进位值,用来记录当前位相加是否产生进位。

模拟 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = null;
ListNode tail = null;

int carry = 0;

while(l1 != null || l2 != null){
int num1 = l1 != null ? l1.val : 0;
int num2 = l2 != null ? l2.val : 0;

int sum = num1 + num2 + carry;

//更新结果链表(取余)
if(res == null){
//结果链表首结点为空
tail = new ListNode(sum % 10);
res = tail;
}else{
//不是第一个结点
tail.next = new ListNode(sum % 10);
tail = tail.next;
}

//更新进位值(取整)
carry = sum / 10;

//移动俩链表指针
if(l1 != null) l1 = l1.next;
if(l2 != null) l2 = l2.next;
}

if(carry > 0){
tail.next = new ListNode(carry);
}
return res;
}
}

⑳19. 删除链表的倒数第 N 个结点

2024.4.6 周六
题目:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

思路:控制快慢指针之间的距离,使slow.next就是要删除的结点

双指针 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head;
ListNode temp = new ListNode(0,head);
ListNode slow = temp;

for(int i = 0; i < n ; i++){
fast = fast.next;
}

while(fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return temp.next;
}
}

栈 4%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//栈
Stack<ListNode> stack = new Stack<>();

ListNode temp = new ListNode(0,head);

ListNode cur = temp;

while(cur != null){
stack.push(cur);
cur = cur.next;
}

for(int i = 0; i < n; i++){
stack.pop();
}
ListNode top = stack.peek();
top.next = top.next.next;
return temp.next;
}
}
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2025 Annie
  • Visitors: | Views:

嘿嘿 请我吃小蛋糕吧~

支付宝
微信