剑指offer题解(16 - 20)

简介: 剑指offer题解(16 - 20)

16.合并两个排序链表



题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。


思路


法1:递归实现,注意递归终止条件和单层递归逻辑。


  • 单层逻辑,比较节点值大小,最小值链接下一层递归的最小值;
  • 终止条件:节点为null,返回另一条链表的节点。


技巧:递归函数最重要的是明白功能(返回两个链表的较小值),其次是功能实现等。


法2:迭代实现,虚拟节点dummy,初始化cur指向新链表的头dummy。


  • l1.val <= l2.val,l1指向的节点链接到cur.next,l1指向下一个节点
  • 否则,l2指向的节点链接到cur.next,l2指向下一个节点。
  • 重复上述两个过程,直到一个单链表为null
  • 剩下的链接到cur后边


技巧:一般创建单链表,都会设置有一个虚拟节点,也叫哨兵,这样每个节点都有一个前驱结点。


代码

public class Solution {
    // 法1.递归实现
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        if (list1.val <= list2.val) {
            list1.next = Merge(list1.next, list2);
            return list1;
        } else {
            list2.next = Merge(list1, list2.next);
            return list2;
        } 
    }
    //法2:迭代实现
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                cur.next = list1;
                list1 = list1.next;
            } else {
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        cur.next = list1 != null ? list1 : list2;
        return dummy.next;
    }
}


17.树的子结构



题目描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)


思路:树问题先考虑递归,这里使用两层递归。


  • 外层递归:遍历大树,当前两头结点进内层递归,外层继续递归大树的左右子节点;
  • 内层递归:传入两棵树当前头结点,依次比较(核心逻辑:结构和节点值比较)。


代码

public class Solution {
    // 外层递归:依次遍历大树的每个节点与小树比较
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if (root1 == null || root2 == null) return false;
        return judge(root1, root2) || 
            HasSubtree(root1.left, root2) || 
            HasSubtree(root1.right, root2);
    }
    // 内层递归,进行结构和节点值的判断
    public boolean judge(TreeNode root1, TreeNode root2) {
        if (root2 == null) return true;
        if (root1 == null) return false;
        if (root1.val != root2.val) return false;
        return judge(root1.left, root2.left) && judge(root1.right, root2.right);
    }
}


18.二叉树的镜像



题目描述:操作给定的二叉树,将其变换为源二叉树的镜像。

比如:    源二叉树 
         8
        /  \
       6   10
      / \  / \
     5  7 9 11
     镜像二叉树
         8
        /  \
       10   6
      / \  / \
     11 9 7  5


思路:递归实现,递归函数:实现二叉树的镜像,先检查root节点,然后递归镜像左右子树


  • 终止条件:节点值为null返回;
  • 递归逻辑:左右子节点互换;


注意点:先记录一下其中一个节点,防止递归另一个节点被改变(这里记右节点)。


代码

public class Solution {
    // 递归实现
    public TreeNode Mirror (TreeNode pRoot) {
        if (pRoot == null) return null;
        TreeNode tmp = pRoot.right;
        pRoot.right = Mirror(pRoot.left);
        pRoot.left = Mirror(tmp);
        return pRoot;
    }
}


19.顺时针打印矩阵



题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字


思路:官方提供的转圈打印:不断缩小矩阵的范围,我们可以使用矩阵的左上角和右下角唯一表示一个矩阵。设左上角坐标为(lx, ly), 右下角坐标为(rx, ry),缩小一圈就是lx+1, ly+1, rx-1, ry-1


代码

public class Solution {
    ArrayList<Integer> ans = new ArrayList<>();
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int row = matrix.length, col = matrix[0].length;
        if (matrix == null || row == 0 || col == 0) return ans;
        int lx = 0, ly = 0;
        int rx = row - 1, ry = col - 1;
        // 遍历矩阵的每一圈
        while (lx <= rx && ly <= ry) {
            print(matrix, lx++, ly++, rx--, ry--);
        }
        return ans;
    }
    // 转圈打印,参数:该圈的标识位置(左上角和右下角坐标)
    public void print(int[][] matrix, int lx, int ly, int rx, int ry) {
        for (int j = ly; j <= ry; ++j) {
            ans.add(matrix[lx][j]);
        }
        for (int i = lx + 1; i <= rx; ++i) {
            ans.add(matrix[i][ry]);
        }
        int h = rx - lx + 1;
        // 若只有一行或者一列可以直接跳过第3、4边
        if (h > 1) {
            for (int rj = ry - 1; rj >= ly; --rj) {
                ans.add(matrix[rx][rj]);
            }
        }
        int w = ry - ly + 1;
        if (w > 1) {
            for (int ri = rx - 1; ri >= lx + 1; --ri) {
                ans.add(matrix[ri][ly]);
            }
        }
    }
}


20.实现最小栈



题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。


思路:栈结构的push和pop操作时间复杂度都是O(1),但是返回最小值需要遍历整个栈,时间复杂度O(n)。若使用一个变量min来记录最小值,那么在栈进行pop操作时候,如何更新min值就比较麻烦。


解决此问题我们可以使用空间换时间,定义两个栈,辅助栈用来记录当前操作(加入新元素后)的最小值(最小栈)。两个栈同时进行压栈操作,但是最小栈在进行压栈操作时需要判断压入的数值大小:


  • 若当前值>最小栈栈顶元素,压入最小栈栈顶元素;
  • 若当前值<=最小栈栈顶元素,压入当前值。


代码

public class Solution {
    Stack<Integer> normal = new Stack<>();
    Stack<Integer> minval = new Stack<>();
    public void push(int node) {
        normal.push(node);
        if (minval.isEmpty()) {
            minval.push(node);
        } else {
            if (node <= minval.peek()) {
                minval.push(node);
            } else {
                minval.push(minval.peek());
            }
        }
    }
    public void pop() {
        normal.pop();
        minval.pop();
    }
    // 最小栈栈顶元素
    public int top() {
        return minval.peek();
    }
    public int min() {
        return minval.peek();
    }
}


相关文章
剑指offer题目汇总(五)
剑指offer题目汇总(五)
|
存储 中间件
剑指offer(04-06)题解
剑指offer(04-06)题解
剑指offer(04-06)题解
|
机器人
剑指offer(61-67)题解
剑指offer(61-67)题解
剑指offer(61-67)题解
剑指offer(25-30)题解
剑指offer(25-30)题解
剑指offer(25-30)题解
剑指offer(19-24)题解
剑指offer(19-24)题解
剑指offer(19-24)题解