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)题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
(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));
}
}