链表中倒数第k个节点

剑指 Offer 22. 链表中倒数第k个节点 - 力扣(Leetcode)

题目要求

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

例子:

1
2
3
给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

思路分析

求出链表长度size,那么倒数第k个值的前一个值,使用for循环的条件为i<size-k,找到k的前一个值的位置,让temp=temp.next输出,即为我们要找的倒数第k个值。

代码实现

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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
// 判断链表是否为空
if(head.next==null){
return head;
}
// 定义辅助变量
ListNode temp = head;
// 求出链表长度
int size = 0;
while(head!=null){
size++;
head = head.next;
}
// 判断k是否小于等于0或大于链表长度
if(k<=0 || k>size){
return null;
}
// 链表大小size-k就是我们要找倒数第k个值的前一个值
for(int i=0;i<size-k;i++){
// 条件满足向后移
temp = temp.next;
}
return temp;
}
}

反转链表

剑指 Offer 24. 反转链表 - 力扣(Leetcode)

题目要求

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

例子:

1
2
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

提示:

0 <= 节点个数 <= 5000

思路分析

递归

  1. 定义辅助变量,递归链表head.next
  2. head指向的节点指向head本身。
  3. head指向null。

原地反转

  1. 先定义一个节点 ListNode() reverseNode = new ListNode();
  2. 从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表的最前端;
  3. 最后返回reverseNode.next即可;

代码实现

递归 空间:O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
// 判断链表是否为空或者只有一个节点
if(head == null ||head.next == null){
return head;
}
// 递归链表head.next
ListNode temp = reverseList(head.next);
// 让head指向的节点指向head本身
head.next.next = head;
// head指向null
head.next = null;
return temp;
}
}

原地反转 空间:O(1)

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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
// 判断链表是否为空或者只有一个节点
if(head == null ||head.next == null){
return head;
}
// 定义一个临时指针
ListNode temp = head;
// 定义一个指针,指向temp的下一个节点
ListNode next = temp.next;
// 定义一个新链表,用来反转
ListNode reverseNode = new ListNode(0);
// 遍历链表
while(temp!=null){
// next保存当前temp节点
next = temp.next;
// 将temp.next指向新链表reverseNode的最前端
temp.next = reverseNode.next;
// 将新链表reverseNode指向temp
reverseNode.next = temp;
// 让temp后移
temp = next;
}
// 最后返回reverseNode.next
return reverseNode.next;
}
}

合并两个排序的链表

剑指 Offer 25. 合并两个排序的链表 - 力扣(Leetcode)

题目要求

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

例子:

1
2
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

提示:

0 <= 链表长度 <= 1000

思路分析

定义一个新的链表merge和一个辅助指针ListNode temp=merge,不断比较链表l1l2的值的大小,将较小的放入temp中,直到l1或者l2任意一个指向null,那就将temp.next指向另一个不为null的,最后将merge.next返回即可;

代码实现

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
39
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
// 创建一个新的链表
ListNode merge = new ListNode(0);
// 定义辅助变量
ListNode temp = merge;
// 循环遍历链表
while(l1!=null && l2!=null){
// 如果l1大于等于l2
if(l1.val>=l2.val){
temp.next = l2;
// 将l2后移
l2 = l2.next;
}else{
temp.next = l1;
// 将l1后移
l1 = l1.next;
}
// 将temp后移
temp = temp.next;
}
// 判断哪个指针没有指向null
if(l1 == null){
temp.next = l2;
}else{
temp.next = l1;
}
// 返回 merge.next
return merge.next;
}
}

树的子结构

剑指 Offer 26. 树的子结构 - 力扣(Leetcode)

题目要求

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
给定的树 A:
3

/ \

4 5

/ \

1 2
给定的树 B:
4

/

1

返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

例子:

1
2
3
4
5
输入:A = [1,2,3], B = [3,1]
输出:false

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

提示:

0 <= 节点个数 <= 10000

思路分析

递归进行以下步骤:

  1. 固定树A的头节点,判断树B是否为以A的子结构:
    • 如果成立就返回true;
    • 如果不成立继续步骤2;
  2. 判断树B是否为树A的左子树的子结构:
    • 如果成立返回true;
    • 如果不成立继续步骤3;
  3. 判断树B是否为树A的右子树的子结构:
    • 如果成立返回true;
    • 如果不成立继续步骤4;
  4. 直到递归比对树A所有节点,且以上条件都不成立,那么返回false;

代码实现

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
39
40
41
42
43
44
45
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
// 判断AB是否为空
if(A == null || B == null){
return false;
}
// 判断B是否为A的子结构
if(isSubTree(A,B)){
return true;
}
// 递归判断B是否为A的左右子树的子结构
if(isSubStructure(A.left,B) || (isSubStructure(A.right,B))){
return true;
}
return false;
}
// 判断B是否为A的子结构的方法
public boolean isSubTree(TreeNode TA,TreeNode TB){
// 判断TB是否遍历完毕
if(TB==null){
return true;
}
// 判断TA是否遍历完毕
if(TA==null){
// 这时TB没有遍历完毕,说明B不是A的子结构
return false;
}
// 判断AB是否不相等
if(TA.val != TB.val){
return false;
}
// 递归判断AB左右子树是否相等
return isSubTree(TA.left,TB.left) &&
isSubTree(TA.right,TB.right);
}
}

二叉树的镜像

剑指 Offer 27. 二叉树的镜像 - 力扣(Leetcode)

题目要求

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
例如输入:
4

/ \

2 7

/ \ / \

1 3 6 9
镜像输出:
4

/ \

7 2

/ \ / \

9 6 3 1

例子:

1
2
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

提示:

0 <= 节点个数 <= 1000

思路分析

通过递归获取左右子树,同时将left指向右子树,将right指向左子树;

代码实现

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
// 判断树是否为空或只有一个节点
if(root == null || (root.left==null && root.right==null)){
return root;
}
// 递归获取左右子树
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.right);

// 对左右子树进行镜像操作
root.left = right;
root.right = left;
return root;
}
}