剑指Offer_LeetCode

13.机器人的运动范围

最开始没看题解,先想到了解是一个等腰直角三角形的情况,后来看了题解发现是多个等腰直角三角形的连通性问题,大意了。
和12 题一样,DFS+剪枝

  1. 递归参数和返回值:元素在矩阵中的索引i,j,二者数位和sum_i,sum_j。返回值为(1+右方递归的可达解总数+下方递归的可达解总数),代表该单元格的可达解总数
  2. 终止条件:行列越界;数位和大于k;当前元素已访问。均放回0
  3. 递归逻辑:
  • 标记当前单元格:我这里用了一个大小m*n的二维矩阵,初始化为0,访问则置为1,表示当前单元格被访问过
  • 搜索下一单元格:计算当前元素的下、右方元素数位和,并递归到下一层
    int rows;
    int cols;
    int SucessCount(int i, int j, int sum_i, int sum_j, int k, vector<vector<int>>& matrix){
        if(i >= rows || j >= cols || (sum_i + sum_j) > k || matrix[i][j] == 1) return 0;
        matrix[i][j] = 1; //置1表示当前已被访问
        int si = (i + 1)/ 10 + (i + 1)% 10; //右方数位和
        int sj = (j + 1)/10 + (j + 1)%10;  //下方数位和
        int right_num = SucessCount(i + 1, j, si, sum_j, k, matrix);
        int down_num = SucessCount(i, j + 1, sum_i, sj, k, matrix);
        return 1 + right_num + down_num;
    }
    int movingCount(int m, int n, int k) {
        rows = m;
        cols = n;
        vector<vector<int>> matrix(m, vector<int>(n)); //int型初始值默认即0
        return SucessCount(0, 0, 0, 0, k, matrix);
    }

14.剪绳子

n<=58那一版没有问题,大数求余也太恶心了,尤其是C++就是会溢出,留个坑。

15. 二进制中1的个数

通过位运算符对n进行操作,因为C++中位运算符都是针对二进制位的。

//简单地使用位与(&)运算符和左移运算符(<<)
//&:如果两个运算对象的对应位置都是1则运算结果中该位为1,否则为0
//<<:在右侧插入值为0的二进制位
int hammingWeight(uint32_t n) {
        int count = 0;
        for(int i = 0; i < 32; i++){//依次判断第i位是否为1
            if(n & (1<<i)) count++;
        }
        return count;
    }

考虑到n & (n - 1)可以将n的最右侧即最低位1变为0,因此循环将n的最低位置为0直到所有位都变成0,可以记录做了几次置0的操作来确定1的个数。

nt hammingWeight(uint32_t n) {
        int count = 0;
        while(n){
            n = n & (n - 1);
            count++;
        }
        return count;
    }

16.数值的整数次方

  • 递归法
    n为偶数时:xn = xn/2 * xn/2
    n为奇数时:xn = x * xn -1/2 * xn-1/2
    n>0时,直接计算;n<0时,计算x-n后取倒数
    测试用例中包含n = -231,取-n = 231会溢出,因此新建long long N = n来接住数值
double quickMul(double x, long long N){
        if(N == 0) return 1.0;
        double y = quickMul(x, N / 2);
        if(N % 2 == 0) return y * y;
        else return x * y * y;
    }
    double myPow(double x, int n) {
        long long N = n;
        if(N >= 0) return quickMul(x, N);
        else return 1.0 / quickMul(x, -N);
    }
  • 迭代法
double quickMul(double x, long long N){
        double result = 1.0;
        double x_c = x;
        while(N > 0){
            if(N & 1 == 1) result = result * x_c; //二进制该位为1时,
            x_c = x_c * x_c; //二进制该位为0时,继续贡献平方
            N = N >> 1;
        }
        return result;
    }
    double myPow(double x, int n) {
        long long N = n;
        if(N >= 0) return quickMul(x, N);
        else return 1.0 / quickMul(x, -N);
    }

27.二叉树的镜像

递归分别交换左子树及右子树,节点为空时返回。
前后序递归均可,要么在根节点递归前交换,要么在之后交换。中序会使有的节点交换两次。

29.顺时针打印矩阵

注意区间要一致,注意哪个是行下标,哪个是列下标,每次转一圈

21.调整数组顺序使奇数位于偶数前面

双指针法,i指向最前面的偶数,j指向最后面的奇数(或者指向i后面的第一个奇数),每次交换ij的数值

    vector<int> exchange(vector<int>& nums) {
        if(nums.size() <= 1) return nums;
        int i = 0, j = nums.size() - 1;  //j指向最后面的奇数 
        //j = 0则指向i后面的第一个奇数
        while(i < nums.size()){
            if(nums[i] % 2 == 1) i++;
            else{
                while(j > i){
                	//j = i + 1;指向i后面的第一个奇数,从i+1开始寻找
                    if(nums[j] % 2 == 0) j--;  //这里变成j++
                    else{
                        int tmp = nums[j];
                        nums[j] = nums[i];
                        nums[i] = tmp;
                        i++;
                        break;
                    }
                }
                //if(j >= nums.size()) return nums;条件改变
                if(j <= i)return nums; //注意这里的循环停止的条件
            }
        }
        return nums;
    }

31.栈的压入、弹出序列

最开始想复杂了,可以直接建一个栈,来模拟压入与弹出的过程,循环后栈为空则是。

	bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int a = 0;
        //遍历元素压入栈
        for(int i = 0; i < pushed.size(); i++){
            st.emplace(pushed[i]);
            //栈不为空且栈顶元素是当前弹出元素时弹出
            while(!st.empty() && st.top() == popped[a]){
                st.pop();
                a++;
            }
        }
        //最后栈中元素全部弹出则成功
        if(st.empty()) return true;
        else return false;
    }

33.二叉搜索树的后序遍历

在这里插入图片描述
二叉搜索树的后序遍历中,最后一个是根节点,前面小于根节点的都是左子树,从第一个大于根节点的元素到倒数第二个元素都是右子树。

  1. 递归函数的参数及返回值:传入遍历数组及子树区间,返回是否符合后序遍历的bool变量
bool traversal(vector<int>& postorder, int left, int right)
  1. 终止条件
if(left >= right) return true;
  1. 单层逻辑
    (1) 从左向右遍历,找到第一个大于根节点的索引mid
    (2) [left, mid - 1]依次遍历,都小于根节点,且[mid, right - 1]都大于根节点,则符合
    (3) 递归判断其左右子树是否也满足
class Solution {
public:
    bool traversal(vector<int>& postorder, int left, int right){
        if(left >= right) return true;  //终止条件
        int mid = left;
        //找到对于当前根节点postorder[right]的左右子树分割点
        while(postorder[mid] < postorder[right] && mid < right) mid ++;
        
        for(int i = left; i < mid; i++){
            if(postorder[i] > postorder[right]) return false;
        }
        for(int i = mid; i < right; i++){
            if(postorder[i] < postorder[right]) return false;
        }
        //继续递归左右子树
        return traversal(postorder, left, mid - 1) && traversal(postorder, mid, right - 1);
    }
    bool verifyPostorder(vector<int>& postorder) {
        return traversal(postorder, 0 ,postorder.size() - 1);
    }
};

64.求1+2+…+n

能用的只有加减法、赋值运算、位运算符、逻辑运算符
首先简单的循环做加法,或用公式都不行,普通的递归方法:

int sumNums(int n){
	if(n == 0) return 0;
	else return n + sumNums(n - 1);
}

n=0为递归的终止条件,但题目要求不能用条件判断,因此可以借助逻辑运算符的短路性质:计算A && B时,若A表达式结果为false,则结果得到false,不会再执行表达式B;计算A || B时,若A表达式结果为true,结果得到true,同样不会再执行表达式B。
在该递归逻辑中,n为0则不再递归直接返回,n不为0才继续计算,因此可以写成:

int sumNums(int n) {
        cout<<n<<endl;
        n && (n = n + sumNums(n - 1));
        //或者写成 !n || (n = n + sumNums(n - 1));
        return n;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值