leecode更新

This commit is contained in:
markilue 2022-09-25 17:09:25 +08:00
parent d6b00cd3b8
commit 0b8049f176
2 changed files with 408 additions and 0 deletions

View File

@ -0,0 +1,276 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-25 09:58
* @Description: TODO 力扣106题 从中序与后序遍历序列构造二叉树
* 给定两个整数数组 inorder postorder 其中 inorder 是二叉树的中序遍历 postorder 是同一棵树的后序遍历请你构造并返回这颗二叉树
* @Version: 1.0
*/
public class BuildTree {
@Test
public void test(){
int[] inorder = {9, 3, 15, 20, 7}, postorder = {9, 15, 7, 20, 3};
TreeNode root = buildTree(inorder, postorder);
System.out.println(levelOrder(root));
}
@Test
public void test1(){
int[] inorder = {2,1}, postorder = {2,1};
TreeNode root = buildTree(inorder, postorder);
System.out.println(levelOrder(root));
}
@Test
public void test2(){
int[] inorder = {15,9,10,3,20,5,7,8,4}, postorder = {15,10,9,5,4,8,7,20,3};
TreeNode root = buildTree2(inorder, postorder);
System.out.println(levelOrder(root));
}
/**
* 对于这题本人没有任何思路代码随想录思路:本质上就是寻找中间节点来划分左右树而后序遍历的最后一个节点一定是中间节点:
* TODO 以后序数组的最后一个元素作为切割点先切割中序数组然后根据中序数组反过来切割后序数组一层一层切下去每次后序数组额最后一个元素就是节点元素
* 递归法:
* 速度超过5.22%内存超过5.01%
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree(int[] inorder, int[] postorder) {
TreeNode root = new TreeNode();
if (inorder.length == 0) {
return root;
}
build1(inorder, postorder, root);
return root;
}
public void build(int[] inorder, int[] postorder, TreeNode node) {
node.val = postorder[postorder.length - 1];
int length=postorder.length;
if (length == 1) {
return;
}
//分别分割左右节点的inorder和postorder
int[] newIn1 = new int[length];
int[] newPost1 = new int[length];
int[] newIn2 = new int[length];
int[] newPost2 = new int[length];
int size = 0;
boolean flag = true;
for (int i = 0,j=0; i < length; i++) {
if(node.val==inorder[i]){
flag=false;
continue;
}
if (flag) {
newIn1[i] = inorder[i];
newPost1[i]=postorder[i];
size+=1;
}else {
//这里-1因为root节点需要被跳过但是post不用
newIn2[j] = inorder[i];
newPost2[j]=postorder[i-1];
j++;
}
}
//这里需要判断是否大于0再做递归
if(size>0){
newIn1 = Arrays.copyOf(newIn1, size);
newPost1=Arrays.copyOf(newPost1,size);
node.left=new TreeNode();
build(newIn1,newPost1,node.left);
}
if(length-size-1>0){
newIn2=Arrays.copyOf(newIn2,length-size-1);
newPost2=Arrays.copyOf(newPost2,length-size-1);
node.right=new TreeNode();
build(newIn2,newPost2,node.right);
}
}
//可以改进只需要找到索引避免一直复制速度击败9.2%内存击败5.01%
public void build1(int[] inorder, int[] postorder, TreeNode node) {
node.val = postorder[postorder.length - 1];
int length=postorder.length;
if (length == 1) {
return;
}
//分别分割左右节点的inorder和postorder
int size = 0;
for (; size < length; size++) {
if(inorder[size]==node.val){
break;
}
}
//这里需要判断是否大于0再做递归
if(size>0){
node.left=new TreeNode();
//copyOfRange是左闭右开
build(Arrays.copyOfRange(inorder,0, size),Arrays.copyOfRange(postorder,0,size),node.left);
}
if(length-size-1>0){
node.right=new TreeNode();
build(Arrays.copyOfRange(inorder,size+1,length),Arrays.copyOfRange(postorder,size,length-1),node.right);
}
}
int post_idx;
int[] postorder;
int[] inorder;
Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
/**
* 官方的递归法,只用传索引不传一个数组避免了数组的一直赋值操作加快进度
* 速度超过99.46%内存超过34.84%
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree1(int[] inorder, int[] postorder) {
this.postorder = postorder;
this.inorder = inorder;
// 从后序遍历的最后一个元素开始
post_idx = postorder.length - 1;
// 建立元素下标键值对的哈希表
int idx = 0;
for (Integer val : inorder) {
idx_map.put(val, idx++);
}
return helper(0, inorder.length - 1);
}
public TreeNode helper(int in_left, int in_right) {
// 如果这里没有节点构造二叉树了就结束
if (in_left > in_right) {
return null;
}
// 选择 post_idx 位置的元素作为当前子树根节点
int root_val = postorder[post_idx];
TreeNode root = new TreeNode(root_val);
// 根据 root 所在位置分成左右两棵子树
int index = idx_map.get(root_val);
// 下标减一
post_idx--;
// 构造右子树
root.right = helper(index + 1, in_right);
// 构造左子树
root.left = helper(in_left, index - 1);
return root;
}
/**
* 官方迭代法:
* 1)我们用一个栈和一个指针辅助进行二叉树的构造初始时栈中存放了根节点后序遍历的最后一个节点指针指向中序遍历的最后一个节点
* 2)我们依次枚举后序遍历中除了第一个节点以外的每个节点如果 index 恰好指向栈顶节点
* 那么我们不断地弹出栈顶节点并向左移动 index并将当前节点作为最后一个弹出的节点的左儿子
* 如果 index 和栈顶节点不同我们将当前节点作为栈顶节点的右儿子
* 3)无论是哪一种情况我们最后都将当前的节点入栈
*
* 最后得到的二叉树即为答案
*
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree2(int[] inorder, int[] postorder) {
if (postorder == null || postorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(postorder[postorder.length - 1]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = inorder.length - 1;
for (int i = postorder.length - 2; i >= 0; i--) {
int postorderVal = postorder[i];
TreeNode node = stack.peek();
//前面的如果不等,那么就是其右节点
if (node.val != inorder[inorderIndex]) {
node.right = new TreeNode(postorderVal);
stack.push(node.right);
} else {
//右节点处理完毕处理其左节点;如果出栈就证明遇上了需要处理的左节点
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
//同步处理stack和inorder
node = stack.pop();
inorderIndex--;
}
node.left = new TreeNode(postorderVal);
stack.push(node.left);
}
}
return root;
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//使用队列完成
Deque<TreeNode> deque = new LinkedList<TreeNode>();
if (root != null) {
deque.addLast(root);
}
while (deque.size() > 0) {
List<Integer> mid = new ArrayList<>();
int size = deque.size();
//注意这里必须使用size,而不是deque.size因为deque的size一直在发生变化
for (int i = 0; i < size; i++) {
TreeNode poll = deque.poll();
mid.add(poll.val);
if (poll.left != null) deque.offer(poll.left);
if (poll.right != null) deque.offer(poll.right);
}
result.add(mid);
}
return result;
}
}

View File

@ -0,0 +1,132 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-25 12:45
* @Description: TODO 力扣105题 从前序与中序遍历序列构造二叉树:
* 给定两个整数数组preorder inorder其中preorder 是二叉树的先序遍历 inorder是同一棵树的中序遍历请构造二叉树并返回其根节点
* @Version: 1.0
*/
public class BuildTreeII {
@Test
public void test() {
int[] preorder = {3, 9, 20, 15, 7}, inorder = {9, 3, 15, 20, 7};
TreeNode node = buildTree(preorder, inorder);
System.out.println(levelOrder(node));
}
/**
* 核心其实一致即中序遍历可以确定左右节点前后序遍历可以确定根节点;因此根据前后序一直找根节点即可
* 这里运用递归法:
* 速度超过99.46%内存超过60.36%
*
* @param preorder
* @param inorder
* @return
*/
int[] preorder;
//<val,index>
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int wanted=0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return build(0, inorder.length-1);
}
public TreeNode build(int leftIndex, int rightIndex) {
if(leftIndex>rightIndex){
return null;
}
TreeNode node = new TreeNode(preorder[wanted]);
Integer index = map.get(preorder[wanted++]);
node.left=build(leftIndex,index-1);
node.right=build(index+1,rightIndex);
return node;
}
/**
* 官方迭代法:
* 1)我们用一个栈和一个指针辅助进行二叉树的构造初始时栈中存放了根节点前序遍历的第一个节点指针指向中序遍历的第一个节点
* 2)我们依次枚举前序遍历中除了第一个节点以外的每个节点
* 如果 index 恰好指向栈顶节点那么我们不断地弹出栈顶节点并向右移动 index并将当前节点作为最后一个弹出的节点的右儿子
* 如果 index 和栈顶节点不同我们将当前节点作为栈顶节点的左儿子
* 3)无论是哪一种情况我们最后都将当前的节点入栈
*
* @param preorder
* @param inorder
* @return
*/
public TreeNode buildTree1(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < preorder.length; i++) {
int preorderVal = preorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.left = new TreeNode(preorderVal);
stack.push(node.left);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//使用队列完成
Deque<TreeNode> deque = new LinkedList<TreeNode>();
if (root != null) {
deque.addLast(root);
}
while (deque.size() > 0) {
List<Integer> mid = new ArrayList<>();
int size = deque.size();
//注意这里必须使用size,而不是deque.size因为deque的size一直在发生变化
for (int i = 0; i < size; i++) {
TreeNode poll = deque.poll();
mid.add(poll.val);
if (poll.left != null) deque.offer(poll.left);
if (poll.right != null) deque.offer(poll.right);
}
result.add(mid);
}
return result;
}
}