剑指offer(十二)

36. 两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

思路:将两个链表遍历至末尾分别存储到两个栈内,此时栈顶为链表尾结点。若两个链表有公共结点,则其公共结点之后的尾部结点均是公用的,因此我们从尾部开始比较,遇到不同结点,则返回上一个相同结点,该结点必为第一个公共结点。

另一种思路是,得出两个链表的长度差值,令长的先走差值步,再同时开始遍历,遍历至相同结点,则返回;这种解法,在网上有更简洁的写法,可以不统计长度,直接开始遍历两个链表,若ptr1将链表1走完,那么就开始去走链表2,ptr2将链表2走完,就去走链表1。如此若两链表长度相同,则走第一遍就能得到第一个公共结点,若长度不相等,加入则当ptr1走完链表1之后去到另一个链表的头部继续遍历,此时和ptr2之间的距离就是两个链表的长度差,那么之后再遇到相同结点,就一定是第一个公共结点了。这要比第一种方法代码上相对简洁。

解法一:

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
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.Stack;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        Stack<ListNode> s1 = new Stack<ListNode>();
        Stack<ListNode> s2 = new Stack<ListNode>();
        ListNode ptr1 = pHead1, ptr2 = pHead2;
        while(ptr1!=null){
            s1.push(ptr1);
            ptr1 = ptr1.next;
        }
        while(ptr2!=null){
            s2.push(ptr2);
            ptr2 = ptr2.next;
        }
        
        ListNode res = null;
        while(!s1.isEmpty() && !s2.isEmpty()){
            if(s1.peek() == s2.peek()){
                res = s1.pop();
                s2.pop();
            }
            else if(s1.peek() != s2.peek()) return res;
            if(s1.isEmpty() || s2.isEmpty()) return res;
        }
        return null;
    }
}

解法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.Stack;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode ptr1 = pHead1, ptr2 = pHead2;
        while(ptr1!=ptr2){
            ptr1 = (ptr1 == null)? pHead2:ptr1.next;
            ptr2 = (ptr2 == null)? pHead1:ptr2.next;
        }
        return ptr1;
    }
}

37. 第一个只出现一次的字符

统计一个数字在排序数组中出现的次数。

思路: 一般情况直接遍历肯定能找到答案,且复杂度为O(n),但是面试这样肯定过不了。这题注意是有序数组,那么肯定先想到二分,但是二分找到这个数对于统计它的次数是无益的,因而我们需要改写二分,改成找到左边界和右边界。普通情况下和普通二分一样,但是当mid==k时候,假如我们要找左侧第一个k,那么此时需要判断mid-1是否为k,若为k则左侧边界肯定在mid左侧,那么使end = end - 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
import java.util.HashMap;
public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int left = getFirst(array, k, 0, array.length - 1);
        int right = getLast(array, k, 0, array.length - 1);
        return right - left + 1;
    }
    

    public int getFirst(int[] array, int k,int start, int end){
        int mid = (start + end) / 2;
        while(start <= end){
            if(array[mid] > k) end = mid - 1;
            else if(array[mid] < k) start = mid + 1;
            else if(mid > 0 && array[mid - 1] == k) end = mid - 1;
            else return mid;
            mid = (start + end) / 2; 
        }
        return start;
    }
    
    public int getLast(int[] array, int k, int start, int end){
        int mid = (start + end) / 2;
        while(start <= end){
            if(array[mid] > k) end = mid - 1;
            else if(array[mid] < k) start = mid + 1;
            else if(mid + 1 <= end && array[mid+1] == k) start = mid + 1;
            else return mid;
            mid = (start + end) / 2; 
        }
        return end;
    }
}

38. 二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

思路: 递归遍历即可,根据题目定义的深度,即该数的深度等于最长路径,也就等于左子树或者右子树的较大者加1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    
    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
    }
}

39. 平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

思路: 根据平衡二叉树定义,根结点左右子树深度差小于1,且左右子树均为平衡二叉树。写一个递归返回子树长度,若左右子树高度差有超过1,则将flag标记为false即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Solution {
    private boolean isBalanced=true;
    public boolean IsBalanced_Solution(TreeNode root) {
        int depth = countDepth(root);
        return isBalanced;
    }
    

    public int countDepth(TreeNode root){
        if(root == null || isBalanced == false) return 0;
        int left = countDepth(root.left);
        int right = countDepth(root.right);
        if(Math.abs(left - right) > 1) isBalanced=false;
        return left > right? left + 1: right + 1;
    }
}