剑指 Offer 64. 求1+2+…+n
求 1+2+...+n
,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3
输出: 6
示例 2:
输入: n = 9
输出: 45
class Solution {
public:
int sumNums(int n) {
int ans = 0;
n != 0 && (ans = sumNums(n - 1) + n);
return ans;
}
};
算法思路
这题求连加,可以用递归,但考虑到递归出口要判断但又不能用if,这里的解决方法是用到了逻辑与的短路运算,就是前半部分语句为假时不执行后半段语句,这就形成了一个递归出口的判断。
限制:
1 <= n <= 10000
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr || root == p || root == q){
return root;
}
TreeNode* l = lowestCommonAncestor(root->left, p, q);
TreeNode* r = lowestCommonAncestor(root->right, p, q);
return l == nullptr? r : (r == nullptr ? l : root);
}
};
算法思路
判断一个点是不是两个节点的第一个共同先,有一个特点就是两个节点分别在两棵子树中,而且非第一个祖先结点的其它祖先结点,两个目标结点其实只分布在它的一边子树,因此可以从上往下遍历,回溯的过程中判断是不是第一个祖先结点就可以得到答案。
剑指 Offer 68 - II. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉树中。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map<TreeNode*, int> lev;
unordered_map<TreeNode*, map<int, TreeNode*>> st;
void dfs(TreeNode *root, TreeNode *pre){
if(root == nullptr)return;
lev[root] = lev[pre] + 1;
st[root][0] = pre;
for(int i = 1; i < 50; ++ i){
st[root][i] = st[st[root][i - 1]][i - 1];
}
dfs(root->left, root);
dfs(root->right, root);
}
TreeNode* lca(TreeNode *p, TreeNode *q){
if(lev[p] < lev[q]){
swap(p, q);
}
while(lev[p] > lev[q]){
p = st[p][(int)log2(lev[p] - lev[q])];
}
if(p == q){
return p;
}
for(int i = 49; i >= 0; -- i){
if(st[p][i] != st[q][i]){
p = st[p][i];
q = st[q][i];
}
}
return st[p][0];
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, nullptr);
return lca(p, q);
}
};
算法思路
这一题貌似和第二题一样,但我测了一下好像是数据规模不同,这一题规模比较大,上面的那种解法算是解决这个问题的最优方法了,但我在第三种用了一种比较特殊的算法"倍增"。很经典的一个例子比如你要到28米的地方,你可以走1米、2米、3米…28米直到28米,但实际上你只需要走1米、2米、4米、8米、16米就可以到达28,比如27=16+8+2+1,21=16+4+1…28以内的数都可以由面前五个数(1,2,4,8,16)组成。在这题中两个结点,我们可以先让低层结点往上走直到两个结点同一层,如果次数两个结点相同,则该结点就是结果;否则两结点同时向上走,直到第一次相遇,就是结果。如何向上走,走的步数多少就用到了倍增。代码还是比较麻烦的,也不是最优的方法(甚至可以算差),主要还是了解一下倍增这个算法,这个算法对其它问题还是很高效的。
【剑指Offer】系列:
【剑指Offer】栈
【剑指Offer】链表
【剑指Offer】字符串
【剑指Offer】查找算法
【剑指Offer】查找算法(1)
【剑指Offer】搜索与回溯算法
【剑指Offer】搜索与回溯算法(1)
【剑指Offer】动态规划
【剑指Offer】动态规划(1)
【剑指Offer】动态规划(2)
【剑指Offer】双指针
【剑指Offer】双指针(1)
【剑指Offer】双指针(2)
【剑指Offer】搜索与回溯算法(2)
【剑指Offer】搜素与回溯算法(3)
【剑指Offer】排序
【剑指Offer】排序(1)
【剑指Offer】搜索与回溯算法(4)