剑指 Offer 64. 求1+2+…+n
题目:
求 1+2+...+n
,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
题解:
关于逻辑运算符&&
和||
:
以A&&B
为例,如果A为真则会继续执行B,如果A为假则不再执行B,直接返回false;
以A||B
为例,如果A为真则不再执行B,直接返回true,如果A为假则执行B;
1.使用&&
,当n为0时,&&
的左端为 false,将不再执行递归调用;
public int sumNums(int n) {
boolean flag = n > 0 && (n += sumNums(n - 1)) > 0;//当n为0时,跳出递归
return n;
}
2.使用||
,当n=0时,||
的左端为true,将不再执行递归调用。
public static int sumNums(int n){
boolean flag = (n == 0) || (n+=sumNums(n - 1))>0;
return n;
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
题目:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先:对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
题解:
1.getPath
函数:遍历二叉搜索树,找到从根节点root
到目的节点target
所有的节点集合;
2.将节点p的节点集合存储在list集合中,节点q的节点存储在map集合中(便于查找是否存在公共节点);
注意: 遍历list集合并在map集合中查看是否存在时,要从后往前查找,否则函数将会返回root节点。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> path1 = getPath(root,p);
Map<TreeNode,Integer> path2 = getPath2(root,q);
for (int i = path1.size()-1; i >=0 ;i--){//注意:从后往前开始查找
TreeNode res = new TreeNode();
res = path1.get(i);
if (path2.containsKey(res)){
return res;
}
}
return root;
}
public List<TreeNode> getPath(TreeNode root, TreeNode target) {
List<TreeNode> list = new ArrayList<>();
TreeNode node = root;
while (node!=target){
list.add(node);
if (node.val>target.val){
node = node.left;
}else node = node.right;
}
list.add(node);
return list;
}
public Map<TreeNode,Integer> getPath2(TreeNode root, TreeNode target) {
Map<TreeNode,Integer> map = new HashMap<>();
TreeNode node = root;
while (node!=target){
map.put(node,1);
if (node.val>target.val){
node = node.left;
}else node = node.right;
}
map.put(node,1);
return map;
}
剑指 Offer 68 - II. 二叉树的最近公共祖先
题目:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
题解:
1.采用深度遍历去寻找目标节点p和q;
2.当满足以下三个条件时终止递归:
-
root为空,即遍历到叶子节点时;
-
root为p,即已经找到p不再需要找q,原因:
- 假设q是p的子节点,则返回 p即可没有q的事,不需要继续向下递归;
- 假设q不是p的子节点,则需要遍历上一个节点的右子树去寻找q,同样不需要继续向下递归。
- root为q,同理;
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null) return right;
if(right == null) return left;
return root;
}
}
剑指 Offer 07. 重建二叉树
题目:
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
题解(分治思想):
1.recur
函数中的变量:
- root---->前序遍历数组下root的下标
- left---->中序遍历数组下子树的左边界
- right---->中序遍历数组下子树的右边界
2.左右子树递归
依照上图可以确定递归调用时需要传递的参数:
node.left = recur(root + 1, left, i - 1)
;node.right = recur(root + i - left + 1, i + 1, right)
;
3.递归结束的条件为left > right
。
class Solution {
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);
}
TreeNode recur(int root, int left, int right) {
if(left > right) return null; // 递归终止
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; // 回溯返回根节点
}
}
剑指 Offer 16. 数值的整数次方
题目:
实现 pow(x, n) ,即计算 x 的 n 次幂函数。不得使用库函数,同时不需要考虑大数问题。
题解(快速幂):
public double myPow(double x, int n) {
if(x == 0) return 0;
long b = n;//避免 n = -2147483648 时执行 n = -n 会因越界而赋值出错
double res = 1.0;
if(b < 0) {
x = 1 / x;
b = -b;
}
while(b > 0) {
if((b & 1) == 1) res *= x;//b%2==1
x *= x;
b >>= 1; //b=b/2
}
return res;
}
剑指 Offer 33. 二叉搜索树的后序遍历序列
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true
,否则返回 false
。假设输入的数组的任意两个数字都互不相同。
题解:
1.对于二叉搜索树来讲,最后一个节点为根节点,从左到右第一个比根节点大的是右子树的最左下角的节点,是右子树的开始,如图
2.recur
函数的参数:
- int[] postorder 后续遍历的数组
- i ---->树的起始下标
- j —>树的末尾下标
3.recur
函数的思想:
-
从下标i开始遍历,直到下标p增加至m时,此时当前节点值比根节点值大为止,证明
i~m-1
为左子树; -
下标p从m继续遍历,只要当前值比根节点值大则继续前进;
-
如果最后
p==j
则证明当前这棵树为二叉搜索树,此时继续向下递归它的左右子树,recur(postorder, i, m - 1)
recur(postorder, m, j - 1)
参数如下图:
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length - 1);
}
boolean recur(int[] postorder, int i, int j) {
if(i >= j) return true;
int p = i;
while(postorder[p] < postorder[j]) p++;//对右子树进行遍历,判断右子树是否都比根节点小
int m = p;
while(postorder[p] > postorder[j]) p++;//对右子树进行遍历,判断右子树是否都比根节点大
return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
}
}
平时卖米粉的阿姨今天在卖米饭,一看是我又多加了半碗,转身真的泪目了,配上姐妹一早点的奶茶,绝绝子啊!!!
平时上楼走过4楼门禁的时候要是听到后面有人,我就会摁一下开关,省得后面的小姐姐再拿钥匙。今天回宿舍走到门禁那我还没等拿钥匙门就开了,只看到了一个背影,下次我一定大喊谢谢!