分治法的设计思想:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
07.
题目:
想法(图参考k神):
先序遍历可以得到每次的根,中序遍历可以根据根节点将其划分为[左子树-根节点-右子树],通过计算依次确定1.树的根节点 2.左子树根节点 3.右子树根节点,对于左右子树,再递归地寻找其根和左右子树根节点即可.
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int[] preorder;
Map<Integer, Integer> dic = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder=preorder;
for(int i=0;i<inorder.length;i++){
// 保存中序遍历值对应的下标
// 通过先序遍历找到根节点值时,dic用于在中序遍历中定位该根节点
dic.put(inorder[i],i);
}
return recur(0,0,inorder.length-1);
}
/**
preRoot 根节点在先序遍历中的下标
inRoot 根节点在中序遍历中的下标
inL inR 本次遍历子树的左边界和右边界
*/
private TreeNode recur(int preRoot, int inL, int inR) {
// 到达叶子节点,递归终止
if(inL > inR) return null;
// 建立此时的根节点
TreeNode node = new TreeNode(preorder[preRoot]);
// 获得根节点在中序遍历中的下标
// 以划分根节点、左子树、右子树
int inRoot = dic.get(preorder[preRoot]);
// 开启左子树递归
// 左子树的根节点,在先序遍历中的下标:为preRoot + 1
node.left = recur(preRoot + 1, inL, inRoot - 1);
// 开启右子树递归
// 右子树的根节点,在先序遍历中的下标:为preRoot + (inRoot - inL) + 1
// 其中(inRoot - inL)为:通过中序遍历计算得到的此时左子树的长度,越过才能找到右子树根节点
node.right = recur(preRoot + inRoot - inL + 1, inRoot + 1, inR);
// 回溯返回根节点
return node;
}
}
结果:
16.
题目:
剑指 Offer 16. 数值的整数次方https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/
想法:看得出是个快速幂的题目,学习了一下.
代码:
//快速幂
class Solution {
public double myPow(double x, int n) {
if(x==0)
return 0;
long b=n;
double res=1.0;
if(b<0){
b=-b;
x=1/x;
}
while(b>0){
//如果这位有值,则结果乘上x
if((b&1)==1)
res*=x;
//求x的平方
x*=x;
//右移一位(判断前一位)
b>>=1;
}
return res;
}
}
结果:
33.
题目:
剑指 Offer 33. 二叉搜索树的后序遍历序列https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/
想法:根据二叉搜索树的性质可知,根的左子树的值应该都小于根,二右子树的值应该都都大于根,递归的判断每一个"左右子树",再将结果进行与运算即可.
代码:
//我写的:
class Solution {
int[] postorder;
public boolean verifyPostorder(int[] postorder) {
this.postorder=postorder;
return recur(0,postorder.length-2,postorder.length-1);
}
/**
left right 该子树的左.右边界(下标)
root 该子树的根节点(下标)
*/
private boolean recur(int left,int right,int root){
//找完啦
if(left>=right)
return true;
int i=left;
int j=right;
//左子树的值均小于根节点
while(postorder[i]<postorder[root])
i++;
//右子树的值均大于根节点
while(j>=0 && postorder[j]>postorder[root])
j--;
//找到左右子树分界线base=j(第一个小于root的下标,后续归到左子树)
int base=j;
//base==i-1则说明此时的"左右子树"确实是根的左右子树
//再分别遍历"左子树"和"右子树"
return base==i-1 && recur(left,base-1,base) && recur(base+1,right-1,right);
}
}
//k神:少传递一个root值,更巧妙
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length - 1);
}
/**
i 该子树的左边界(下标)
j-1 该子树的左.右边界(下标)
j 该子树的根节点(下标)
*/
boolean recur(int[] postorder, int i, int j) {
if(i >= j) return true;
//p用于遍历
int p = i;
while(postorder[p] < postorder[j])
p++;
//m用于记录左右子树分界线(第一个大于root的下标,后续归到右子树)
int m = p;
while(postorder[p] > postorder[j])
p++;
//p==j则说明此时的"左右子树"确实是根的左右子树
//再分别遍历"左子树"和"右子树"
return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
}
}
结果: