剑指 Offer 57
Posted lxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 57相关的知识,希望对你有一定的参考价值。
题目链接:剑指 Offer 57 - II. 和为s的连续正数序列
方法一:同向双指针
解题思路
使用两个双指针维护一个窗口,设窗口中元素的和为\\(curSum\\)。当\\(curSum > target\\)时,左指针右移一位;当\\(curSum < target\\)时,右指针右移一位;当\\(curSum == target\\)时,记录窗口内为一个答案,然后左右指针共同右移一位。
注意:由于\\(窗口大小 >= 2\\),所以当\\(r > target / 2 + 1\\)时,窗口最小的值 \\(r + (r - 1) > target\\),此时无意义,因此只取 \\(r <= target / 2 + 1\\)即可。
代码
class Solution
public:
vector<vector<int>> findContinuousSequence(int target)
vector<vector<int>> ans;
int l = 1, r = 2, curSum = 3;
// r 最多只能取target / 2 + 1,因为必须是两个连续数字,
// 当r > target / 2 + 1时,r + r - 1 > target 没有意义,所以r取不到后面的值。
while (r <= target / 2 + 1)
while (curSum < target)
r ++ ;
curSum += r;
while (curSum > target)
curSum -= l;
l ++ ;
if (curSum == target)
vector<int> oneAns;
for (int i = l; i <= r; i ++ ) oneAns.push_back(i);
ans.push_back(oneAns);
curSum -= l;
l ++ ;
r ++ ;
curSum += r;
return ans;
;
复杂度分析
时间复杂度:\\(O(target)\\);
空间复杂度:\\(O(1)\\)。
剑指 Offer 26. 树的子结构 / 剑指 Offer 27. 二叉树的镜像 / 剑指 Offer 28. 对称的二叉树 / 剑指 Offer 29. 顺时针打印矩阵
剑指 Offer 26. 树的子结构
题目描述
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/ \\
4 5
/ \\
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
看到就想到的思路就是递归的判断,如果根节点相同,那么就往左右节点判断,然后我就写出了下面的代码
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//递归的判断,如果根节点相同,就判断左边右边结点是否也相同
if(B == null)
return true;
if(A == null)
return false;
if(A.val != B.val){
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
}else{
return isSubStructure(A.left, B.left) && isSubStructure(A.right, B.right);
}
}
}
然后不出意外,挂了,倒在了B为空的示例上,因为题目中给出B为空,是false。但是在递归的过程中,B为空必须得返回tru,因此我把这部分重写了一个方法,又得到了下面的代码
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//递归的判断,如果根节点相同,就判断左边右边结点是否也相同
if(B == null)
return false;
if(A == null)
return false;
return subtree(A, B);
}
public boolean subtree(TreeNode A, TreeNode B){
if(B == null)
return true;
if(A == null)
return false;
if(A.val != B.val){
return subtree(A.left, B) || subtree(A.right, B);
}else{
return subtree(A.left, B.left) && subtree(A.right, B.right);
}
}
}
然后又挂了,倒在了[4,2,3,4,5,6,7,8,9] [4,8,9]这个示例上,这是因为啥呢,这是因为我是在左子树上找B的左半部分,右子树上找B的右半部分,显然不对,然后又改
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//递归的判断,如果根节点相同,就判断左边右边结点是否也相同
if(B == null)
return false;
if(A == null)
return false;
return subtree(A, B);
}
public boolean subtree(TreeNode A, TreeNode B){
if(B == null)
return true;
if(A == null)
return false;
if(A.val != B.val){
return subtree(A.left, B) || subtree(A.right, B);
}else{
return (subtree(A.left, B.left) && subtree(A.right, B.right)) || subtree(A.left, B) || subtree(A.right, B);
}
}
}
还是倒在了最后一个示例上[1,0,1,-4,-3] [1,-4],这次倒下其实是预料之中,因为按照我写的代码,如果根节点相同的话,我是在左右子树找剩下的部分,而这个部分不和根节点相邻我的代码也会返回true,所以继续改,这次是改成遍历所有的节点,然后判断了,这次过了
最后想想,树的题目本身比较难,这个题其实用了先序遍历+判断子树的方法,子树是一个连续的结构,不能拆开,继续加油吧
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//递归的判断,如果根节点相同,就判断左边右边结点是否也相同
if(B == null)
return false;
if(A == null)
return false;
//需要遍历每一个节点
return subtree(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
public boolean subtree(TreeNode A, TreeNode B){
if(B == null)
return true;
if(A == null)
return false;
if(A.val != B.val){
return false;
}else{
return subtree(A.left, B.left) && subtree(A.right, B.right);
}
}
}
剑指 Offer 27. 二叉树的镜像
题目描述
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \\
2 7
/ \\ / \\
1 3 6 9
镜像输出:
4
/ \\
7 2
/ \\ / \\
9 6 3 1
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
观察可知,镜像就是将左右子树交换,层层交换就可以得到结果,然后写了这个代码
class Solution {
public TreeNode mirrorTree(TreeNode root) {
//一层一层翻转
if(root == null)
return null;
TreeNode leftTree = root.left;
root.left = root.right;
root.right = leftTree;
mirrorTree(root.left);
mirrorTree(root.right);
return root;
}
}
过了,但是这里有个问题,就是这个方法是有返回值的,但是这里面调用方法的时候,并没有返回值,为什么还行的通,然后就去查了一下,有返回值的方法,可以选择用不用,没有返回值的方法,不能用
class Solution {
public TreeNode mirrorTree(TreeNode root) {
//先将底层翻转,自底向上
if(root == null)
return null;
TreeNode leftTree = mirrorTree(root.right);
TreeNode rightTree = mirrorTree(root.left);
root.left = leftTree;
root.right = rightTree;
return root;
}
}
剑指 Offer 28. 对称的二叉树
题目描述
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \\
2 2
/ \\ / \\
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \\
2 2
\\ \\
3 3
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
因为是对称的,所以从上到下依次判断左右子树是否相同,即left和right是否相同
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null)
return true;
return isSame(root.left, root.right);
}
public boolean isSame(TreeNode A, TreeNode B){
//如果都为空,对称
if(A == null && B == null)
return true;
//单个为空,不对称
if(A == null || B == null)
return false;
//数值不同,不对称
if(A.val != B.val)
return false;
//数值相同,看左右
return isSame(A.left, B.right) && isSame(A.right, B.left);
}
}
剑指 Offer 29. 顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shun-shi-zhen-da-yin-ju-zhen-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
模拟这个过程,上右下左这样循环遍历,注意区间的开闭,如果行列较小值为奇数需要另外处理
class Solution {
public int[] spiralOrder(int[][] matrix) {
//模拟这个过程,循环处理就行了,循环n / 2次,注意循环时候区间的开闭
int m = matrix.length;
if(m == 0)
return new int[]{};
int n = matrix[0].length;
int l = m < n ? m : n;
//如果是奇数,需要在后面加一行
boolean flag = false;
if(l % 2 == 1)
flag = true;
l = l / 2;
int[] res = new int[m * n];
int index = 0;
for(int k = 0; k < l; k++){
//所有区间都是左闭右开,上面的行
for(int i = k; i < n - k - 1; i++){
res[index++] = matrix[k][i];
}
//右边的列
for(int i = k; i < m - k - 1; i++){
res[index++] = matrix[i][n - k - 1];
}
//下面的行
for(int i = n - k - 1; i > k; i--){
res[index++] = matrix[m - k - 1][i];
}
//左边的列
for(int i = m - k - 1; i > k; i--){
res[index++] = matrix[i][k];
}
}
//此时,最多只剩下一行或者一列了
if(flag){
for(int i = l; i <= m - l - 1; i++){
for(int j = l; j <= n - l - 1; j++){
res[index++] = matrix[i][j];
}
}
}
return res;
}
}
这里复制一个优雅的转圈遍历代码,几个月之前的,找不到是谁的了,不好意思,再学习一下
class Solution {
//转圈遍历的优雅代码
public List<Integer> spiralOrder(int[][] matrix) {
LinkedList<Integer> result = new LinkedList<>();
if(matrix==null||matrix.length==0) return result;
int left = 0; //左边
int right = matrix[0].length - 1; //右边
int top = 0; //上边
int bottom = matrix.length - 1; //下边
//总共的元素个数
int numEle = matrix.length * matrix[0].length;
while (numEle >= 1) {
//最上面一行,从left到right
for (int i = left; i <= right && numEle >= 1; i++) {
result.add(matrix[top][i]);
numEle--;
}
//然后top++
top++;
//最右边一列,从top到bottom
for (int i = top; i <= bottom && numEle >= 1; i++) {
result.add(matrix[i][right]);
numEle--;
}
right--;
//最下面一行,从right 到 left;注意到right已经减1了
for (int i = right; i >= left && numEle >= 1; i--) {
result.add(matrix[bottom][i]);
numEle--;
}
bottom--;
//最左面一列,bottom到top,注意到bottom也减1了
for (int i = bottom; i >= top && numEle >= 1; i--) {
result.add(matrix[i][left]);
numEle--;
}
left++;
}
return result;
}
}
还有官解中的这个直接模拟的方法,就是朝着一个方向走,如果到头了就换另一个方向,另外还需要一个矩阵来记录每个元素是否已经被访问了。感觉更不好想,而且实现起来也比较难。
以上是关于剑指 Offer 57的主要内容,如果未能解决你的问题,请参考以下文章
乱序版 ● 剑指offer每日算法题打卡题解—— 双指针(题号21,57,58)