已知一棵二叉树中没有重复节点,并且给定了这棵树的中序遍历数组和先序遍历数组,返回后序遍历数组。比如给定:intl pre ={1, 2,4,5,3, 6, 7};int切in={4,2,5,1,6,3,7};返回:{4,5,2,6,7,3,1}
这题不用建树 首先 我们知道 先序遍历是 中左右 那么我们就可以找到头结点 然后在用这个头结点去中序中 找到左树元素的个数和右树元素的个数 然后去掉第一个 就能分别找到左树和右树的部分
然后再取出左树的头结点 去中序中找它的左树右树元素个数...以此类推
思路还算清晰 难顶的是coding
public static int[] preInToPos2(int[] pre, int[] in) {
if (pre == null || in == null || pre.length != in.length) {
return null;
}
int N = pre.length;
HashMap inMap = new HashMap<>();
for (int i = 0; i < N; i++) {
inMap.put(in[i], i);
}
int[] pos = new int[N];
process2(pre, 0, N - 1, in, 0, N - 1, pos, 0, N - 1, inMap);
return pos;
}
public static void process2(int[] pre, int L1, int R1, int[] in, int L2, int R2, int[] pos, int L3, int R3,
HashMap inMap) {
if (L1 > R1) {
return;
}
if (L1 == R1) {
pos[L3] = pre[L1];
return;
}
pos[R3] = pre[L1];
int mid = inMap.get(pre[L1]);
int leftSize = mid - L2;
process2(pre, L1 + 1, L1 + leftSize, in, L2, mid - 1, pos, L3, L3 + leftSize - 1, inMap);
process2(pre, L1 + leftSize + 1, R1, in, mid + 1, R2, pos, L3 + leftSize, R3 - 1, inMap);
}
这个basecase很难想
当L>R的时候 说明这个递归已经超边界了 假如说左树只有一个值的时候 L1+1 L1+leftsize(此时为0) 就是L>R了 说明已经不用再递归了
然后呢 对于每个叶节点 如果递归参数没错的话 都是左边界等于右边界 那么就把它填上
啊 那为什么basecase只看前缀数组呢? 其实看哪个数组都一样 他想表达的意思都是一致的 当左边界等于右边界 说明只剩下一个元素了 那只要填进去就可以了
HashMap的作用是 不用每一次都去中序数组里面遍历数组去找 中点对应的位置
好好好 那么我们再看一道!
给定两个整数数组,preorder
和 postorder
,其中 preorder
是一个具有 无重复 值的二叉树的前序遍历,postorder
是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。
哎~根据前序后序 找中序
来想一下思路 首先前序的第一个元素时头结点 后序最后一个是头结点 那么把中序的中间点可以填上 前序是中左右 后序是左中右 我们找到前序中左树的第一个 就是左树的中点 然后用这个左树的中点 去后序里面找 这个中点 就是左树的结尾 那剩下的就是右树 以此类推
basecase 就是每次递归都会填上中序的中点 然后到叶节点的时候直接填
class Solution {
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
int [] in = new int [preorder.length];
HashMap map = new HashMap();
for(int i = 0;i map) {
if(L1>R1) {
return null;
}
if(L1==R1) {
return new TreeNode(pre[L1]);
}
TreeNode node = new TreeNode(pre[L1]);
int leftsize = map.get(pre[L1+1])-L3+1;
node.left = Process(pre,in,post,L1+1,L1+leftsize,L2,L2+leftsize-1,L3,L3+leftsize-1,map);
node.right = Process(pre,in,post,L1+leftsize+1,R1,L2+leftsize+2,R2,L3+leftsize,R3-1,map);
return node;
}
}
最长递增子序列问题的O(N*logN)的解法
这题做的脑瓜子又抽抽了
有一个数组arr 在上面找最长递增子序列
很明显 我们可以用dp[] 但是呢 dp数组的复杂度也是o(N²) 因为这个不是子字符串的问题 他不是严格依赖前面一个格子的
我们用一个ends[]数组 加速dp[]的建立
ends[]数组 的含义是ends[i] 表示 长度为i+1的递增子序列的最小结尾
dp[i]含义是 以i位置结尾的最长递增子序列有多长
先说结论 我们遍历arr ends的0位置很好填 长度为0+1的递增子序列最小结尾在此时只能是arr[0]
然后dp的值是1
接下来 查看arr[i]的值 arr[i]如果大于end[i]的最后的位置 那么就把arr[i]加入ends的结尾
然后dp此时的值为这个被加入ends 前面包括自身的元素个数
如果小于等于当前的位置 那么就去ends中找到小于这个arr[i]的元素 然后替换掉它的下一个元素
然后找这个被替换的元素 前面包括自身的元素个数 作为dp[i]的值
说一下原理 ends的含义是ends[i] 表示 长度为i+1的递增子序列的最小结尾 那么下一个arr[i]值来了
如果小于等于这个值 就说明不能接在这个i+1的递增子序列后面 但是呢 也不会直接从零开始计算
因为是子序列 不要求一定挨着 那我之前的递增子序列里面 应该还有子序列的最小结尾比它小的吧? 那我就接在它的后面 反应在ends数组里面 就是把对应长度的最小结尾给替换掉了 因为如果我比之前的最小结尾更小的话 那么接下来的数可以直接接在我的后面来达到这个递增子序列的长度了(更小的结尾提供了更多的可能) 然后再来一个数还是同样的流程 看看可以接到多长的后面 然后形成这个位置的最长子序列长度
public int findNumberOfLIS(int[] nums) {
int size = nums.length;
int [] dp = new int [size];
int [] ends = new int [size];
dp[0] = 1;
ends[0] = nums[0];
int lastends = 0;
int max = 1;
for(int i = 1;iends[lastends]) {
ends[++lastends] = nums[i];
dp[i] = lastends+1;
}else {
int pos = findpos(nums[i],ends,lastends)+1;
ends[pos] = nums[i];
dp[i] = pos+1;
}
max = Math.max(max, dp[i]);
}
return max;
}
public int findpos(int num,int [] arr,int lastends) {
int res = -1;
for(int i = lastends;i>=0;i--) {
if(arr[i]
这个findpos函数里面的的res 初值应该-1
如果这个数组是 2 2 2 的话对吧 面对arr[1]的时候 数组中没有比它还小的值 所以我们要操作的是0位置上的值
还有这个max的初始值应该设置为1 因为子序列最小也得有1长度吧