《剑指offer第二版》九

1. 序列化二叉树

(1)题目描述

请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
你可以将以下二叉树:

    1
   / \
  2   3
     / \
    4   5

序列化为 “[1,2,3,null,null,4,5]”

(2)题目分析

本题分为两部分来处理,首先是序列化,本题使用按层遍历的方式来处理,而二叉树的按层遍历使用队列来实现,在反序列化时同样使用一个队列来实现,在进行节点拼接时,将依次入队,然后出队即可,因为跟节点的左右节点的子树也是按每一层的顺序序列化的,因此可以根据顺序进行反序列化。

(3)代码

package swordOffer.day9;

import leetcode.week2.TreeNode;

import java.util.LinkedList;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 09:46
 * @desc
 */
public class t41 {

    public String serialize(TreeNode root) {
        if (root == null) {
            return "[]";
        }
        StringBuilder res = new StringBuilder("[");
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            if (temp != null) {
                res.append(temp.data).append(',');
                queue.add(temp.left);
                queue.add(temp.right);
            } else {
                res.append("null,");
            }
        }
        res.deleteCharAt(res.length() - 1);
        res.append(']');

        return res.toString();
    }
    
    public TreeNode deserialize(String data) {
        if ("[]".equals(data)) {
            return null;
        }
        String[] vals = data.substring(1, data.length() - 1).split(",");

        LinkedList<TreeNode> queue = new LinkedList<>();
        TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
        queue.add(root);
        int i = 1;
        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            if (!"null".equals(vals[i])) {
                temp.left = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(temp.left);
            }
            i++;
            if (!"null".equals(vals[i])) {
                temp.right = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(temp.right);
            }
            i++;
        }
        return root;
    }

    public static void main(String[] args) {
        TreeNode node11 = new TreeNode(4);
        TreeNode node12 = new TreeNode(1);
        TreeNode node13 = new TreeNode(5);
        TreeNode node14 = new TreeNode(7);
        TreeNode node15 = new TreeNode(3);
        TreeNode node16 = new TreeNode(3);
        TreeNode node17 = new TreeNode(2);
        node11.left = node12;
        node11.right = node13;
        node12.left = node14;
        node12.right = node15;
        node13.left = node16;
        node13.right = node17;

        t41 tt = new t41();
        String str = tt.serialize(node11);
        System.out.println(str);
        TreeNode root = tt.deserialize(str);
        System.out.println(tt.serialize(root));
    }
}

2. 二叉搜索树与双向循环链表

(1)题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:

图片: https://uploader.shimo.im/f/b1IoPMGrq6c3CKkQ.png

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

图片: https://uploader.shimo.im/f/PyBoZZUd0DIDMduv.png

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

(2)题目分析

由于二叉搜索树的中序遍历是有序的,因此本题选用中序遍历对二叉树进行遍历,首先设置两个变量分别表示当前节点和前一个节点,然后在每一次遍历时,对这两个节点进行连接,在连接完后对这两个变量进行更新即可。需要注意的是,在遍历结束之后,需要把链表的头尾节点进行连接。由于本题运用到了二叉树的中序遍历,在这里对二叉树的三种中序遍历方法进行了复习,分别是递归遍历、非递归遍历以及morris遍历,感兴趣的可以了解一下。

(3)代码

package swordOffer.day9;

import charpter3.Node;

import java.util.Stack;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 10:59
 * @desc
 */
public class t42 {

    Node pre = new Node(0), head = pre;

    /**
     * 二叉搜搜树转成双向循环链表
     *
     * @param root
     * @return
     */
    public Node treeToDoublyList(Node root) {
        if (root == null) {
            return null;
        }
        dfs(root);
        head = head.right;
        head.left = pre;
        pre.right = head;
        return head;
    }

    void dfs(Node cur) {
        if (cur == null) {
            return;
        }
        dfs(cur.left);
        pre.right = cur;
        cur.left = pre;
        pre = cur;
        dfs(cur.right);
    }

    /**
     * morris遍历
     * @param root
     */
    public static void morris(Node root) {
        if (root == null) {
            return;
        }
        Node cur1 = root;
        Node cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                // 左节点的右节点不等于null,同时没有指向根节点,则继续向下
                while (cur2.right != null && cur2.right != cur1) {
                    cur2 = cur2.right;
                }

                // 直到找到左子树的最右节点,将它指向根节点,并且继续根节点的左子树
                if (cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                } else {
                    // 最右节点不为空,则说明该节点指向了根节点,这时候取消指向
                    cur2.right = null;
                }
            }
            // 遍历右子树
            System.out.println(cur1.data + " ");
            cur1 = cur1.right;
        }

    }

    /**
     * 二叉树递归中序遍历
     *
     * @param root
     */
    public static void indiOrder1(Node root) {
        if (root == null) {
            return;
        }
        indiOrder1(root.left);
        System.out.println(root.data);
        indiOrder1(root.right);
    }

    /**
     * 二叉树非递归中序遍历
     *
     * @param root
     */
    public static void indiOrder2(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            if (root != null && root.left != null) {
                stack.push(root.left);
                root = root.left;
            } else {
                Node node = stack.pop();
                System.out.println(node.data);
                if (node.right != null) {
                    stack.push(node.right);
                    root = node.right;
                }

            }
        }
    }

    public static void main(String[] args) {
        Node node11 = new Node(4);
        Node node12 = new Node(2);
        Node node13 = new Node(6);
        Node node14 = new Node(1);
        Node node15 = new Node(3);
        Node node16 = new Node(5);
        Node node17 = new Node(7);
        node11.left = node12;
        node11.right = node13;
        node12.left = node14;
        node12.right = node15;
        node13.left = node16;
        node13.right = node17;
        morris(node11);
    }
}

3. 二叉搜索树的第k大节点

(1)题目描述

给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:
输入: root = [3,1,4,null,2], k = 1

   3
  / \
 1   4
  \
   2

输出: 4

(2)题目分析

本题需要寻找第k大的节点,首先想到的二叉搜索树中序遍历的递增性质,但是寻找第k大的节点需要在递减序列中获取,因此本题对中序遍历进行修改,将left->root->right,改成right->root->left,则遍历出来的序列是递减的。当遍历第k次时,即可获取结果。

(3)代码

package swordOffer.day9;

import charpter3.Node;
import leetcode.week2.TreeNode;

import java.util.Stack;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 20:26
 * @desc
 */
public class t43 {
    public static int kthLargest(TreeNode root, int k) {
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        int i = 0;
        while (!stack.isEmpty()) {
            if (root != null && root.right != null) {
                stack.push(root.right);
                root = root.right;
            } else {
                TreeNode node = stack.pop();
                i++;
                if (i == k) {
                    return node.data;
                }
                if (node.left != null) {
                    stack.push(node.left);
                    root = node.left;
                }
            }
        }
        return 0;
    }

    public static void main(String[] args) {
        TreeNode node11 = new TreeNode(4);
        TreeNode node12 = new TreeNode(2);
        TreeNode node13 = new TreeNode(6);
        TreeNode node14 = new TreeNode(1);
        TreeNode node15 = new TreeNode(3);
        TreeNode node16 = new TreeNode(5);
        TreeNode node17 = new TreeNode(7);
        node11.left = node12;
        node11.right = node13;
        node12.left = node14;
        node12.right = node15;
        node13.left = node16;
        node13.right = node17;
        System.out.println(kthLargest(node11, 2));
    }
}

4. 二叉树的深度

(1)题目描述

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

(2)题目分析

通过递归,获得每个节点的左右子树的最大深度。

(3)代码

package swordOffer.day9;

import leetcode.week2.TreeNode;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 21:02
 * @desc
 */
public class t44 {
    public static int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return Math.max(maxDepth(root.left) + 1, maxDepth(root.right) + 1);
    }

    public static void main(String[] args) {
        TreeNode node11 = new TreeNode(4);
        TreeNode node12 = new TreeNode(2);
        TreeNode node13 = new TreeNode(6);
        TreeNode node14 = new TreeNode(1);
        TreeNode node15 = new TreeNode(3);
        TreeNode node16 = new TreeNode(5);
        TreeNode node17 = new TreeNode(7);
        TreeNode node18 = new TreeNode(8);
        node11.left = node12;
        node11.right = node13;
        node12.left = node14;
        node12.right = node15;
        node13.left = node16;
        node13.right = node17;
        node17.left = node18;
        System.out.println(maxDepth(node11));
    }
}

5. 0~n-1中缺失的数字

(1)题目描述

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2

(2)题目分析

遇到有序数组的搜索,想都不用想,肯定是用二分法。但是本题还有一种变种,就是无序数组中寻找缺失的数字,这时候将0-n的总和减去数组中数字的总和即为所求。

(3)代码

package swordOffer.day9;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 21:08
 * @desc
 */
public class n45 {
    public static int missingNumber(int[] nums) {
        return getMiss(nums, 0, nums.length - 1);

    }

    public static int getMiss(int[] nums, int begin, int end) {
        if (begin > end) {
            return begin;
        }
        int mid = (begin + end) / 2;
        if (mid == nums[mid]) {
            return getMiss(nums, mid + 1, end);
        } else {
            return getMiss(nums, 0, mid - 1);
        }
    }

    public static void main(String[] args) {
        int arr[] = {0, 1, 2, 4, 5};
        System.out.println(missingNumber(arr));

    }
}

6. 平衡二叉树

(1)题目描述

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]

  3
   / \
  9  20
    /  \
   15   7

返回 true 。

(2)题目分析

本题通过一个计算节点深度的辅助方法来解决,通过递归,分别计算每个节点的左右子树是平衡的,同时左右子树的深度相差不超过1。

(3)代码

package swordOffer.day9;

import leetcode.week2.TreeNode;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-09 21:29
 * @desc
 */
public class t46 {
    public static boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isBalanced(root.left) && isBalanced(root.right) &&
                Math.abs(depth(root.left) - depth(root.right)) <= 1;
    }

    public static int depth(TreeNode node1) {
        return node1 == null ? 0 : Math.max(depth(node1.left), depth(node1.right)) + 1;
    }

    public static void main(String[] args) {
        TreeNode node11 = new TreeNode(4);
        TreeNode node12 = new TreeNode(2);
        TreeNode node13 = new TreeNode(6);
        TreeNode node14 = new TreeNode(1);
        TreeNode node15 = new TreeNode(3);
        TreeNode node16 = new TreeNode(5);
        TreeNode node17 = new TreeNode(7);
        TreeNode node18 = new TreeNode(8);
        node11.left = node12;
        node11.right = node13;
        node12.left = node14;
        node12.right = node15;
        node13.left = node16;
        node13.right = node17;
        node17.left = node18;
        System.out.println(isBalanced(node11));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值