剑指offer(四):分治算法
我是菜逼!!!!!!
题目一:重建二叉树
public class p7 {
int [] preorder;
//使用哈希表来标记索引,得到根节点在中序遍历列表中的位置
HashMap<Integer,Integer> dic = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for (int i = 0; i < inorder.length; i++) {
dic.put(inorder[i],i);//将中序遍历列表及其索引存入哈希表
}
return recur(0,0,inorder.length-1);
}
//1、递归的功能就是建立对应的二叉树
TreeNode recur(int root, int left, int right){
//2、递归的终止条件是当左边界大于右边界则结束
if (left>right)return null;
//3、递推工作
TreeNode node = new TreeNode(preorder[root]);//建立当前节点
int i = dic.get(preorder[root]);//得到当前根节点中序遍历中的索引
node.left = recur(root + 1, left, i - 1);//分开构造其左节点
node.right = recur(root+i-left+1,i+1,right);//分开构造其右节点
return node;//回溯返回根节点
}
}
题目二: 数值的整数次方
class Solution {
public double myPow(double x, int n) {
//当x为0时返回0;
if(x == 0.0f) return 0.0d;
//执行时会因越界而赋值出错,解决方案是先将n
long b = n;
double res = 1.0;
if(b < 0) {
//b小于0的话x取倒数,b取
x = 1 / x;
b = -b;
}
while(b > 0) {
if((b & 1) == 1) res *= x;
x *= x;
b >>= 1;
}
return res;
}
}
tips:
-
幂结果获取:
- 根据推导,可通过循环 x = x^2操作,每次把幂从 n降至 n//2 ,直至将幂降为 0;
- 设 res=1,初始状态 x^n = x^n*res。在循环二分时,每当 n 为奇数时,将多的一项 x 乘入 res ,则最终可化至 x^n = x^0 * res = res,返回 res 即可。
-
n&1**(与操作)**:判断n二进制最右一位是否为1;
-
n>>1**(移位操作)**:n右移一位(可理解为删除最后一位)。
题目三: 打印从 1 到最大的 n 位数
class Solution {
int[] res;
int nine = 0, count = 0, start, n;
char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
public int[] printNumbers(int n) {
this.n = n;
res = new int[(int)Math.pow(10, n) - 1];
num = new char[n];
start = n - 1;
dfs(0);
return res;
}
//1、递归功能,生成n位数的数字序列
void dfs(int x) {
//2、递归的终止条件,当添加到最后一位数字时加入表示结果的字符串队列
if(x == n) {
//2.1、分离出高位的0
String s = String.valueOf(num).substring(start);
//2.1、添加到结果序列中
if(!s.equals("0")) res[count++] = Integer.parseInt(s);
if(n - start == nine) start--;
return;
}
for(char i : loop) {
if(i == '9') nine++;
num[x] = i;
dfs(x + 1);
}
nine--;
}
}
题目四:二叉搜索树的后序遍历序列
关键点:找到序列中第一个大于根节点的数,然后以此为界限分为左右两个区间
//方法一:递归分治
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length-1);
}
//1、判断当前序列是否为后序遍历序列
boolean recur(int[] list, int i, int j){
//2、终止条件是左边界大于等于右边界,此时返回true
if (i>=j)return true;
//2.1、递归工作是首先判断左树中的元素是否都小于最后的值
int p = i;
while (list[p]<list[j])p++;
//2.2、接着判断右树中的元素是否都大于最后的值
int m = p;
while (list[p]>list[j])p++;
//3、返回值,遍历到头且左右树都是后序遍历的结果
return p==j&&recur(list,0,m-1)&&recur(list,m,j-1);
}
}
//方法二:辅助单调栈
class Solution {
public boolean verifyPostorder(int[] postorder) {
Stack<Integer> stack = new Stack<>();
int root = Integer.MAX_VALUE;
for(int i = postorder.length - 1; i >= 0; i--) {
if(postorder[i] > root) return false;
while(!stack.isEmpty() && stack.peek() > postorder[i])
root = stack.pop();
stack.add(postorder[i]);
}
return true;
}
}
题目五:数组中的逆序对
class Solution {
//和归并排序一个思想
int[] nums,tem;
public int reversePairs(int[] nums) {
this.nums = nums;
tem = new int[nums.length];
return recur(nums, 0, nums.length-1) ;
}
int res;
//1、递归功能,实现对逆序对的统计
int recur(int[] nums, int l, int r){
//2、终止条件是左边界大于右比边界的索引值
if (l>=r)return 0;
//将原区间分开
int m = (l+r)/2;
res = recur(nums, l, m)+recur(nums, m+1, r);
for (int i = l; i <= r; i++) {
tem[i] = nums[i];
}
int x =l, y=m+1;
//合并区间
for (int k = l; k <= r; k++) {
if (x==m+1)nums[k]=tem[y++];
else if (y==r+1||tem[x]<=tem[y])nums[k]= tem[x++];
else {
nums[k]=tem[y++];
res+=m-x+1;
}
}
return res;
}
}