树:
1. 二叉搜索树的第k大节点
2. 公共祖先
- 二叉树的公共祖先
- 二叉搜索树的公共祖先
3. 翻转二叉树
4. 合并二叉树
5. 树的遍历
- 前序遍历
- 层序遍历
6. 二叉搜索树与双向链表
7. 路径问题
- 二叉树中和为某一值的路径
- 求根到叶子节点数字之和
8. 根据数组判断是否为指定二叉树
- 判断数组是否是 二叉搜索树的后序遍历
1. 二叉搜索树的第k大节点
class Solution {
int ans = 0;
int find = -999;
public int kthLargest(TreeNode root, int k) {
dfs(root, k);
return find;
}
public void dfs(TreeNode root, int k){
if(find != -999) return;
if(root != null){
dfs(root.right, k);
ans++;
if(ans == k){
find = root.val;
return;
}
dfs(root.left, k);
}
}
}
2.1 二叉树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null; // 如果树为空,直接返回null
if(root == p || root == q) return root; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
TreeNode left = lowestCommonAncestor(root.left, p, q); // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
TreeNode right = lowestCommonAncestor(root.right, p, q); // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
if(left == null) return right; // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
else if(right == null) return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
else return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
}
}
2.2 二叉搜索树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
int val = root.val;
if(val > p.val && val < q.val){
return root;
}
else if(val < p.val && val < q.val){
return lowestCommonAncestor(root.right, p, q);
}
else if(val > p.val && val > q.val){
return lowestCommonAncestor(root.left, p, q);
}
else{
return root;
}
}
}
3. 翻转二叉树
4 4
/ \ / \
2 7 -> 7 2
/ \ / \ / \ / \
1 3 6 9 9 6 3 1
-----------------------------------------------
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return root;
TreeNode temp = root.left;
root.left = invertTree(root.right);
root.right = invertTree(temp);
return root;
}
}
4. 合并二叉树
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
-------------------------------------------------------
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1 == null && t2 == null) return null;
if(t1 == null && t2 != null) return t2;
if(t1 != null && t2 == null) return t1;
t1.val = t1.val + t2.val;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
return t1;
}
}
5.1 树的前序遍历
非递归前序遍历
-------------------------------------------------------
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null) return list;
Stack<TreeNode> stack = new Stack<>();
while(root != null || stack.size() != 0){
while(root != null){
list.add(root.val);
stack.push(root);
root = root.left;
}
TreeNode temp = stack.pop();
root = temp.right;
}
return list;
}
}
5.2 层序遍历
基于栈的层序遍历
---------------------------------
class Solution {
public int[] levelOrder(TreeNode root) {
if(root == null) return new int[0];
Queue<TreeNode> queue = new LinkedList<>();
int[] ans = new int[2000];
int j = 0;
queue.offer(root);
while(queue.size() != 0){
int size = queue.size();
for(int i =0; i< size; i++){
TreeNode temp = queue.poll();
ans[j++] = temp.val;
if(temp.left != null) queue.offer(temp.left);
if(temp.right != null) queue.offer(temp.right);
}
}
return Arrays.copyOfRange(ans,0,j);
}
}
6. 二叉搜索树与双向链表
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
上图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
-------------------------------------------------------------------------------
思路:本质是前序遍历,用pre记录前一个节点
class Solution {
Node pre,head; //
public Node treeToDoublyList(Node root) {
if(root == null) return null;
dfs(root);
head.left = pre; //此时已经初始化了,head已经成为前序第一个节点
pre.right = head; //pre已经成为前序最后一个节点
return head;
}
public void dfs(Node root){
if(root == null) return;
dfs(root.left);
if(pre == null) head = root; //pre==null说明还未初始化,这是前序第一个节点
else pre.right = root; //已经初始化了,让前一个节点的right等于现在的root
root.left = pre;
pre = root;
dfs(root.right);
}
}
7.1 二叉树中和为某一值的路径
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
------------------------------------------------------
返回:
[
[5,4,11,2],
[5,8,4,5]
]
---------------------------------------------------------------------
思路: 回溯
class Solution {
List<List<Integer>> ans;
public List<List<Integer>> pathSum(TreeNode root, int sum) {
ans = new ArrayList<>();
if(root == null) return ans;
List<Integer> list = new ArrayList<>();
dfs(root, list, 0, sum);
return ans;
}
public void dfs(TreeNode root, List<Integer> list, int temp, int sum){
if(root == null) return;
if(root.left == null && root.right == null && temp+root.val == sum){
list.add(root.val);
ans.add(new ArrayList<Integer>(list));
}
else{
list.add(root.val);
dfs(root.left, list, temp + root.val, sum);
dfs(root.right, list, temp + root.val, sum);
}
list.remove(list.size() - 1);
}
}
7.2 求根到叶子节点数字之和
输入: [1,2,3]
1
/ \
2 3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
--------------------------------------------------
class Solution {
int result = 0;
public int sumNumbers(TreeNode root) {
dfs(root, 0);
return result;
}
public void dfs(TreeNode root, int presum){
if(root == null) return;
int sum = presum*10 + root.val;
if(root.left == null && root.right == null) result += sum;
else{
dfs(root.left, sum);
dfs(root.right, sum);
}
return;
}
}
8.1 判断数组是否是 二叉搜索树的后序遍历
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
输入: [1,6,3,2,5]
输出: false //不是二叉搜索树的后序遍历
输入: [1,3,2,6,5]
输出: true //是的
5
/ \
2 6
/ \
1 3
-----------------------------------------------------
思路:左右指针
当数组长度<=2,一定是
不然每次取数组最后一个数,这个数一定是root
left从左开始扫描,right从右开始扫描
(1)left扫描到了结尾,说明全部都是左子树 / right扫描到了开头,全部都是右子树
(2)left -1 = right,说明满足 左边都小,右边都大的趋势,递归 左右
(3)其他情况,返回false
class Solution {
public boolean verifyPostorder(int[] postorder) {
if(postorder.length <= 2) return true;
int len = postorder.length;
int root = postorder[len-1];
//System.out.println(root);
int left = 0;
int right = len-2;
while(left <= len-2 && postorder[left] < root){
left++;
}
while(right >= 0 && postorder[right] > root){
right--;
}
if(left == len-1 || right == -1) return verifyPostorder(Arrays.copyOfRange(postorder,0,len-1));
else if(left - 1 == right) return verifyPostorder(Arrays.copyOfRange(postorder,0,left)) && verifyPostorder(Arrays.copyOfRange(postorder,left,len-1));
else return false;
}
}