【剑指offer第二版】JAVA刷题总结-ch4

27.二叉树的镜像
在这里插入图片描述
思路:左右子节点交换

class Solution {
    public void mirror(TreeNode root) {
        if(root == null) return;
        if(root.left!=null || root.right!=null){
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
            mirror(root.left);
            mirror(root.right);
        }
    }
}

28.对称的二叉树
在这里插入图片描述
思路:根左右=根右左

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return isSymmetric(root, root);//镜像左==右
    }
    public boolean isSymmetric(TreeNode root1, TreeNode root2){
        if(root1 == null && root2 == null) return true; //都空相等
        if(root1 == null || root2 == null) return false; //存在一个空,不相等
        if(root1.val == root2.val){//根节点相等,判断左右节点是否镜像
            return isSymmetric(root1.left, root2.right) && isSymmetric(root1.right, root2.left);
        }
        return false;
    }
}

29.顺时针打印链表
在这里插入图片描述

思路:定义四个方向对应的矩阵行列的加减,每次碰壁转弯90度。

class Solution {
    public int[] printMatrix(int[][] matrix) {
        if(matrix.length == 0 || matrix[0].length == 0) return new int[0];
        int m = matrix.length;
        int n = matrix[0].length;
        int[] printArray = new int[m*n];//注意写法
        boolean[][] visited = new boolean[m][n];
        for(int i=0; i<m; i++)
            for(int j=0; j<n; j++)
                visited[i][j]=false;
        int[] dx = {0, 1, 0, -1}, dy= {1, 0, -1, 0};//注意方向,与坐标轴无关,判断矩阵!!!
        int x=0, y=0, d=0;
        for(int i=0; i<m*n; i++){
            printArray[i] = matrix[x][y];
            visited[x][y] = true;
            int a = x+dx[d], b = y+dy[d];
            if(a<0 || a>=m || b<0 || b>=n || visited[a][b]==true){
                d = (d+1)%4;
                a = x+dx[d]; 
                b = y+dy[d];
            }
            x=a;
            y=b;
        }
        return printArray;
    }
}

30.包含min函数的栈
在这里插入图片描述
思路:最小值栈

class MinStack {
    Stack<Integer> numStack = new Stack<Integer>();
    Stack<Integer> min = new Stack<Integer>(); 
    /** initialize your data structure here. */
    public MinStack() {
    }
    
    public void push(int x) {
        numStack.push(x);
        if(min.isEmpty()) min.push(x);
        else{
            if(x<min.peek()) min.push(x);
            else min.push(min.peek());
        }
    }
    
    public void pop() {
        if(!numStack.isEmpty()){
            numStack.pop();
            min.pop();
        }
    }
    
    public int top() {
        if(!numStack.isEmpty()) return numStack.peek();
        return -1;
    }
    
    public int getMin() {
        if(!numStack.isEmpty()) return min.peek();
        return -1;
    }
}

31.栈的压入弹出序列
在这里插入图片描述
思路:按照压入序列入栈,如果弹出序列当前值相等则弹出,判断最后栈是否为空。

class Solution {
    public boolean isPopOrder(int [] pushV,int [] popV) {
        Stack<Integer> stack = new Stack<Integer>();
        if(popV.length>pushV.length) return false;//弹出一定比压入短
        for(int pushId=0, popId=0; pushId<pushV.length; pushId++){
            stack.push(pushV[pushId]);//入栈
            while(popId<popV.length && !stack.isEmpty() && stack.peek()==popV[popId] ){//如果相等,并且可以弹出,一直弹出
                stack.pop();
                popId++;//下一个弹出的值得索引
            }
        }
        if(stack.isEmpty()) return true;
        return false;
    }
}

32.从上到下打印二叉树
32-1 不分行打印

在这里插入图片描述
思路:利用队列存储树节点

class Solution {
    public List<Integer> printFromTopToBottom(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<TreeNode>();//存储节点的队列
        List<Integer> printArray = new ArrayList<Integer>();//存储输出的list
        if(root==null) return printArray;
        queue.add(root);
        while(!queue.isEmpty()){//不空
            TreeNode cur = queue.poll();
            if(cur!=null){
                queue.add(cur.left);//左右放到队列里,先进先出
                queue.add(cur.right);
                printArray.add(cur.val);
            }
        }
        return printArray;
    }
}

32-2 分行打印
在这里插入图片描述
思路:记录当前队列中节点的个数,bfs

    public List<List<Integer>> printFromTopToBottom(TreeNode root) {
        List<List<Integer>> printList = new ArrayList<>();
        if(root==null) return printList;//空
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(root);
        int k = queue.size();//每次都把队列排空,类似bfs的思想,所以记录当前队列个数。
        while(!queue.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            while(k>0){
                TreeNode cur = queue.poll();
                if(cur!=null){
                    tmp.add(cur.val);
                    if(cur.left!=null) queue.add(cur.left);
                    if(cur.right!=null) queue.add(cur.right);
                }
                k--;
            }
            printList.add(tmp);
            k = queue.size();
        }
        return printList;
    }

32-3 之字形打印二叉树:
在这里插入图片描述

思路:reverse

class Solution {
    public List<List<Integer>> printFromTopToBottom(TreeNode root) {
        List<List<Integer>> printList = new ArrayList<>();
        if(root==null) return printList;//空
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(root);
        int k = queue.size();//每次都把队列排空,类似bfs的思想,所以记录当前队列个数。
        int level = 1;
        while(!queue.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            while(k>0){
                TreeNode cur = queue.poll();
                if(cur!=null){
                    tmp.add(cur.val);
                    if(cur.left!=null) queue.add(cur.left);
                    if(cur.right!=null) queue.add(cur.right);
                }
                k--;
            }
            if((level&1)==0) Collections.reverse(tmp);//reverse
            printList.add(tmp);
            k = queue.size();
            level++;
        }
        return printList;
    }
}

33. 二叉搜索树的后序遍历序列
在这里插入图片描述
思路:后序遍历最后一个为根节点,前面一部分都比根小,后面一部分都比根大。

class Solution {
    public boolean verifySequenceOfBST(int [] sequence) {
        if(sequence==null) return false;
        if(sequence.length==0) return true;
        int root= sequence[sequence.length-1];
        int i = 0;
        for(i=0; i<sequence.length-1;i++){
            if(sequence[i]>root)//找到第一个大于根节点的节点,左边是左子树,右边是右子树
                break;
        }
        int[] left = new int[i];
        for(int k=0;k<i;k++){
            left[k]=sequence[k];
        }
        int j=i;
        for(j=i; j<sequence.length-1;j++){
            if(sequence[j]<root)
                return false;//重点,如果遍历不到倒数第二个,直接returnfalse
        }
        int[] right = new int[j-i];
        for(int k=i;k<j;k++){
            right[k-i]=sequence[k];
        }

        boolean leftbool = true;
        if(i>0) leftbool=verifySequenceOfBST(left);
        boolean rightbool = true;
        if(j>0) rightbool = verifySequenceOfBST(right);
        return leftbool && rightbool;
    }
}

34.二叉树中和为某一值的路径
在这里插入图片描述
思路:开list记录path,dfs左子节点,dfs右子节点。

class Solution {
    public List<List<Integer>> ans= new ArrayList<>();
    List<Integer> path = new ArrayList<>();     
    public List<List<Integer>> findPath(TreeNode root, int sum) {
        dfs(root,sum);
        return ans;
    }
    public void dfs(TreeNode root, int sum){
        if(root==null) return;
        path.add(root.val);
        sum = sum-root.val;
        if(root.left==null && root.right==null && sum==0) ans.add(new ArrayList<>(path));//如果相等,ans里面加上path答案(new一个)
        dfs(root.left, sum);//左子树
        dfs(root.right, sum);//右子树
        path.remove(path.size()-1);//算完当前节点移除

    }
}

35.复杂链表的复刻
在这里插入图片描述
思路:原链表的下一个节点为复制节点。注意new复制的节点。并在最后,复原原链表。

class Solution {
    public ListNode copyRandomList(ListNode head) {
        ListNode p = head;
        while(p!=null){//插入
            ListNode tmp = p.next;
            ListNode copyNode = new ListNode(p.val);
            p.next = copyNode;
            copyNode.next = tmp;
            p = tmp;
        }
        p=head;
        while(p!=null){//random域
            if(p.random!=null){
                p.next.random = p.random.next;
            }
            p = p.next.next;
        }
        ListNode dummy = new ListNode(-1);//虚拟头结点
        ListNode cur = dummy;
        p = head;
        while(p!=null){//拆出复制的链表,复原原来的链表
            cur.next = p.next;
            p.next = p.next.next;
            cur = cur.next;
            p = p.next;
        }
        return dummy.next;
    }
}

36.二叉搜索树与双向链表
在这里插入图片描述
思路:在中序递归的基础上改,用一个pre指针保存中序遍历的前一个结点。因为是中序遍历,遍历顺序就是双线链表的建立顺序;每一个结点访问时它的左子树肯定被访问过了,所以放心大胆的改它的left指针,不怕树断掉;同理,pre指向的结点保存的数肯定小于当前结点,所以其左右子树肯定都访问过了,所以其right指针也可以直接改。最后需要一直向左找到双向链表的头结点。**

class Solution {
    TreeNode pre = null;
    public TreeNode convert(TreeNode root) {
        dfs(root);
        while(root!=null && root.left!=null) root = root.left;//返回root最左边
        return root;
    }
    public void dfs(TreeNode root){
        while(root==null) return;
        dfs(root.left);//递归左侧
        root.left=pre;//root的上一个为pre
        if(pre!=null) pre.right = root;//pre的下一个为root
        pre = root;
        dfs(root.right);//递归右边
    }
}

37. 序列化二叉树
在这里插入图片描述
思路:序列化主要是对二叉树的广度优先遍历,可以每序列化一个节点,就把他的节点按照先左后右的顺序压入队列,然后从队列前面取出,再将其子树的左右子树压入队列。空为#,各个节点之间用!来分开反序列化将序列化的字符串按!分割即可。然后放进队列,重建即可。

class Solution {

    // Encodes a tree to a single string.
    String serialize(TreeNode root) {
        if(root==null)
            return "#!";

        String res = root.val+"!";
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            root = q.poll();
            if(root.left!=null){
                res+=root.left.val+"!";
                q.offer(root.left);
            }else{
                res+="#!";
            }

            if(root.right!=null){
                res+=root.right.val+"!";
                q.offer(root.right);
            }else{
                res+="#!";
            }
        }
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode deserialize(String data) {
        String[] values = data.split("!");
        int index = 0;
        TreeNode root = generateNodeByString(values[index++]);
        Queue<TreeNode> q = new LinkedList<>();
        if(root != null){
            q.offer(root);
        }

        TreeNode node = null;
        while(!q.isEmpty()){
            node = q.poll();
            node.left = generateNodeByString(values[index++]);
            node.right = generateNodeByString(values[index++]);
            if(node.left != null){
                q.offer(node.left);
            }
            if(node.right != null){
                q.offer(node.right);
            }
        }
        return root;
    }

    public TreeNode generateNodeByString(String val){
        if(val.equals("#"))
            return null;

        return new TreeNode(Integer.valueOf(val));
    }
}

38. 字符串的排列
在这里插入图片描述
思路:O(n!)
由于有重复元素的存在,先将所有数从小到大排序,这样相同的数会排在一起;从左到右依次枚举每个数,每次将它放在一个空位上;对于相同数,我们人为定序,就可以避免重复计算:我们在dfs时记录一个额外的状态,记录上一个相同数存放的位置 start,我们在枚举当前数时,只枚举start+1,start+2…,nstart+1,start+2,…,n 这些位置。不要忘记递归前和回溯时,对状态进行更新。
时间复杂度分析:搜索树中最后一层共 n! 个节点,前面所有层加一块的节点数量相比于最后一层节点数是无穷小量,可以忽略。且最后一层节点记录方案的计算量是O(n),所以总时间复杂度是 O(n×n!)。

class Solution {
public List<List<Integer>> permutation(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
    if(tempList.size() == nums.length){
        list.add(new ArrayList<>(tempList));
    } else{
        for(int i = 0; i < nums.length; i++){
            if(used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i - 1])) continue;
            used[i] = true; 
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, used);
            used[i] = false; 
            tempList.remove(tempList.size() - 1);
        }
    }
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值