位运算实现加法、输出大数、和为s的连续正数序列、判断二进制数中1的个数、BST的最近公共结点

位运算实现加法

  • 不能 INT_MIN进行左移, INT_MAX左移一位溢出, 输出-2;
class Solution {
public:
    // 之前学的计算机中负数是以补码的形式参与运算, 与这里并不矛盾
    // 这些位运算中, 负数参与的时候已经转换成了补码, 2^1 = 3, -2^1 = -1 (如果是原码, 应该是-3)
    // 位运算的时候, 符号位也参与了运算
    int add(int a, int b) {
        int x = INT_MAX; // 0111, 对正数无影响, 对负数最高位改变
        while(b != 0)  //进位为0的时候跳出
        {
            int c = (a & b) << 1; // (INT_MIN << 会报错, , 进位操作可以始终把最高位弄成0), 需要将最高位符号位变为0
            a = a ^ b; // a作为本位求和, ^保证了最高位的符号位不变 // 先假设没有进位计算一波, 然后判断进位b是不是0, 不是的话, 再来一次
            b = c;  // 始终把b作为进位
        }
        return a;
    }
};

输出大数

  • 输出0-n为最大数, 之间所有数.
  • 无论是int, long, long long 都会有上限,大数只能通过字符串输出
    • 首字母不能是0, 并且从1开始, 就用了下面那个判断
  • 递归如果最后结果只有一个, 就带返回值
  • 如果最后结果很多个, 就不带返回值, 使用一个vector, 每次在结束递归的时候装入.

void dfs(int th, int n, string& res, vector<string>& v)
{
    if (th == n + 1)
    {
        v.push_back(res);
        return ;
    }
    for(int i = 0; i <= 9; i++)
    {
        string c = to_string(i);
         /*
        if (th == 1 && i == 0)
            c = ""; // 只去除首字母是0, 不去除单独0
        */
        res += c;
        if(res == "0")  //去除首字母是0 和 单独一个0
            res = "";
        dfs(th+1, n, res, v);
        res = res.substr(0, res.size()-1);
    }
}
void test02()
{
    int n;
    cin>>n;
    string s = "";
    vector<string> v;
    dfs(1, n, s, v);
    for(auto& i:v)
        cout<<i<<endl;
}

和为s的连续正数序列

  • 滑动窗口
  • 求和不需要每次都遍历, ±一头一尾即可
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        int l = 1, r = 2;
        int sum = 3;
        vector<vector<int>> res;
        vector<int> temp;
        while(true)
        {
            if(sum < target)
            {
                sum += (++r);
            }
            else if (sum == target)
            {
                for(int i = l; i <= r; i++)
                    temp.push_back(i);
                res.push_back(temp);
                temp.clear();
                sum -= (l++);
            }
            else
            {
                if (r - l == 1)
                    break;
                sum -= (l++);
            }
        }
        return res;
    }
};

判断二进制数中1的个数

  • C++的主要数据类型只有三种: bool``整型 (char 是长度为1字节的整型, 用来存放ASCII)浮点型
    • *_t是typedef定义的标识, 我们所看到的 uint8_t、uint16_t、uint32_t都不是新的数据类型,而是通过typedef给类型起得别名。
    • size_t 也是无符号整型, 一种用来记录大小的数据类型. sizeof的返回值, 容器的大小v.size()
      通常是size_t类型. (32位 4字节, 64位 8字节)
/* There is some amount of overlap with <sys/types.h> as known by inet code */
#ifndef __int8_t_defined
# define __int8_t_defined
typedef signed char             int8_t; 
typedef short int               int16_t;
typedef int                     int32_t;
# if __WORDSIZE == 64
typedef long int                int64_t;
# else
__extension__
typedef long long int           int64_t;
# endif
#endif
 
/* Unsigned.  */
typedef unsigned char           uint8_t;
typedef unsigned short int      uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int            uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int       uint64_t;
#else
__extension__
typedef unsigned long long int  uint64_t;
#endif

注意,uint8_t实际上就是一个char,所以输出 uint8_t类型的变量实际上输出对应的字符,而不是数值,比如:

uint8_t num=67;
cout << num << endl; //输出结果为C

  • for循环比while循环更快?
  • n&(n-1) 每次可以消除最右边的一个1 (图片来源, 力扣Krahets的回答)
    在这里插入图片描述
  • 右移和左移相当于除以2和乘2, 因此是O(log2n)的时间复杂度, 只是这里O(log2n) <= 32
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        /*
        while(n)
        {
            count += (n&1);  // O(log2n)的时间复杂度
            n = n>>1;
        }*/
        for(int i = 0; i < 32; i++)
        {
            count += ((n & (1<<i)) == 0)?0:1;
        }
        /*
        while(n)
        {
            count++;
            n &= (n-1);  // O(M)时间复杂度, M为1的个数
        }*/
        return count;
    }
};

BST的最近公共结点

  • 时间复杂度要考虑最坏情况, 因为树的最差情况是链表, 因此时间复杂度是O(N)
  • 递归中, 有多少次递归就有多少的空间复杂度
/**
 * 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:
/*
    // 所有tree的通用解法, 不一定是BST
	// 自底向上, p,q沿着路径向上, 汇合的时候, 就把汇合结点一直向上
    TreeNode* dfs(TreeNode* root, TreeNode* p, TreeNode* q)
    {
        if (root == p || root == q) return root;
        if (root == NULL) return NULL;

        TreeNode* left = dfs(root->left, p, q);
        TreeNode* right = dfs(root->right, p, q);
        if (left && right) return root;
        else if (!left && right) return right;
        else if (left && !right) return left;
        else
            return NULL;
    }
    */
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //  如果p,q在root的左右子树中, 那么root就是最近共同祖先结点
        //  否则在哪边的子树, 就继续遍历哪边的子树
        //  因为这一题是BST, 因此可以根据结点大小判断
        while(root)
        {
            if(p->val > root->val && q->val > root->val)
                root = root->right;
            else if (p->val < root->val && q->val < root->val)
                root = root->left;
            else
                break;
        }
        /* 递归
        if(p->val > root->val && q->val > root->val)
            return lowestCommonAncestor(root->right, p, q);
        else if(p->val < root->val && q->val < root->val)
            return lowestCommonAncestor(root->left, p, q);
        return root;
        */
        return root;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现这个功能,可以采用二叉搜索树的特性,即左子树中所有节点的值小于根节点的值,右子树中所有节点的值大于根节点的值。因此,在先序遍历序列中,根节点的值必定是序列的第一个元素。 具体的实现步骤如下: 1. 定义一个二叉树的结构体,包含键值和左右子节点指针。 2. 定义一个函数,输入先序遍历序列序列长度,返回根节点。 3. 从先序遍历序列中取出第一个元素作为根节点。 4. 遍历序列,将小于根节点值的元素放入左子树序列,大于根节点值的元素放入右子树序列。 5. 递归创建左右子树,并将左右子树的根节点分别挂在根节点的左右子节点上。 6. 采用递归的方式遍历二叉树,查找节点U和V的位置。 7. 如果当前节点为NULL或者等于U或V,则返回当前节点。 8. 如果U和V分别在当前节点的左右子树中,则当前节点为最近公共祖先。 9. 如果U和V在当前节点的同一子树中,则继续向下递归。 10. 最终返回最近公共祖先节点的键值即可。 下面是实现代码的示例: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int key; struct Node* left; struct Node* right; } Node; Node* create_bst(int* preorder, int len) { if (len == 0) { return NULL; } Node* root = (Node*) malloc(sizeof(Node)); root->key = preorder[0]; int i; for (i = 1; i < len; i++) { if (preorder[i] > root->key) { break; } } root->left = create_bst(preorder + 1, i - 1); root->right = create_bst(preorder + i, len - i); return root; } Node* find_lca(Node* root, int u, int v) { if (root == NULL || root->key == u || root->key == v) { return root; } if (u < root->key && v < root->key) { return find_lca(root->left, u, v); } else if (u > root->key && v > root->key) { return find_lca(root->right, u, v); } else { return root; } } int main() { int preorder[] = {6, 2, 1, 4, 3, 5, 9, 7, 10}; int len = sizeof(preorder) / sizeof(preorder[0]); Node* root = create_bst(preorder, len); int u = 3, v = 5; Node* lca = find_lca(root, u, v); printf("LCA of %d and %d is %d\n", u, v, lca->key); u = 4, v = 9; lca = find_lca(root, u, v); printf("LCA of %d and %d is %d\n", u, v, lca->key); u = 4, v = 5; lca = find_lca(root, u, v); printf("LCA of %d and %d is %d\n", u, v, lca->key); return 0; } ``` 输出结果如下: ``` LCA of 3 and 5 is 4 LCA of 4 and 9 is 6 LCA of 4 and 5 is 4 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值