力扣 2021 3月

226. 翻转二叉树(0301)

递归题,稀里糊涂的,不过抓准目标,每次递归的内容是什么;什么是结束条件。随时记着这两点,递归问题很好解决

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL)
            return NULL;
        else
        {   //交换左右子树
            TreeNode* temp = root->left;
            root->left = root->right;
            root->right = temp;
            //递归
            root->left = invertTree(root->left);
            root->right = invertTree(root->right);
        }
        return root;
    }
};

263. 丑数(0302)

我寻思这也不难啊,就是找因数

class Solution {
public:
    bool isUgly(int num) {
        if(num == 0)return false;//忘记考虑了,该死
        while(num % 5 == 0)num = num / 5;
        while(num % 3 == 0)num = num / 3;
        while(num % 2 == 0)num = num / 2;
        return num == 1;
    }
};

num为0的情况忘记了,大题就是要考虑这种特殊样例哇

283. 移动零(0303)

还是两个pointer,变化无穷多呀

在原数组上操作,就是不断地把0换到后面去,需要一个普通的i指针和一个用于寻找/记录0位置的指针

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i = 0, zp = 0;//还是双指针的思路
        int temp;
        while(i != nums.size())
        {
            if(nums[i] != 0)
            {//如果当前元素不为0
                if(zp != i)
                {//两个指针不在一块儿,就进行交换动作,否则没有必要交换
                    temp = nums[i];
                    nums[i] = nums[zp];
                    nums[zp] = temp;
                }
                zp++;//注意这里 最开始加了个else wa了,不应有else,无论如何当前只要不是0,这个pointer就得++
            }
            i++;
        }
    }
};

看到个过分的:

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        for i in range(nums.count(0)):
            nums.remove(0)
            nums.append(0)
            
        # 或者:
        nums.sort(key=bool, reverse=True)

342. 4的幂(0304)

数论题

注意小括号和优先级

class Solution {
public:
    bool isPowerOfFour(int n) {
        //先判断是不是2的幂:n & (n-1) == 0 则为2的幂
        if(n < 0 || (n & (n - 1)) != 0)
            return false;
        
        //是不是4的幂,1总在奇数位上
        return n & 0x55555555;
    }
};

349. 两个数组的交集(0305)

这是哈希表,用的是unordered_set,特点是:无序+元素唯一。其他的就没啥了,无非是c++的优美但不会用的使用方法了

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;//用来放结果
        unordered_set<int> nusm1_set(nums1.begin(), nums1.end());

        for(int num : nums2)
        {
            if(nusm1_set.find(num) != nusm1_set.end())
            {//这就是在num1_set种找到了的意思
                result_set.insert(num);//则把它放进去,随便放,不用担心重复,哈希set不允许重复的
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
        //很好,基本上是照着抄的
    }
};

像下面这些用法,可以记上一记

if(nusm1_set.find(num) != nusm1_set.end())
{//这就是在num1_set种找到了的意思
	result_set.insert(num);//则把它放进去,随便放,不用担心重复,哈希set不允许重复的
}

 unordered_set<int> nusm1_set(nums1.begin(), nums1.end());

return vector<int>(result_set.begin(), result_set.end());

371. 两整数之和(0306)

这是一道加法器的题,但是也不是完全按照位来进行运算的。是集中运用&和^来的,加法器的思想

另外编译时,AddressSanitizer对有符号数左移做了保护,但是对于进位和做异或操作来说,有无符号最终之取决怎么解读,不影响实际操作,因此先转成unsigned来做,即可绕开

class Solution {
public:
    int getSum(int a, int b) {
        while(b)
        {
            //报错了,运行时错误,评论说 编译的时候使用了 AddressSanitizer 工具,会对有符号数的左移位操作做保护,强制转成无符号数做移位可绕过
            // auto carry = (a & b) << 1;//'位'的进位

            auto carry = (unsigned int)(a & b) << 1;
            a = a ^ b;
            b = carry;
        }
        return a;
    }
};

这还有一种递归解法,更好理解一些

class Solution {
public:
    int getSum(int a, int b) {
        if (!b) return a;
        return getSum(a^b, ((unsigned int)a&b) << 1);
    }
};

401. 二进制手表(0307)

有点烦这道题,但是我很喜欢这一种解法,反向思考,虽然不是正统的回溯的解法,但是由于它本身性质限制了数据量的大小,反其道而行之也挺好的。

基本上就是穷举这12 x 60种时间搭配,然后算出对应的num,如果和输入的num相等,ze加到vector种去

class Solution {
public:
    vector<string> readBinaryWatch(int num) {
        vector<string> res;
        string s;
        //反过来想
        for(int i = 0; i < 12; ++i)
        {
            for(int j = 0; j < 60; ++j)
            {
                //数他们二进制下有多少个1
                if(bitset<32>( (i << 6) | j ).count() == num)
                {
                    s = to_string(i);
                    s.append(":");
                    s.append( j > 9 ? "" : "0" );
                    s.append(to_string(j));
                    res.push_back(s);
                }
            }
        }
        return res;
    }
};

🥑437. 路径总和 III(0308-0309)

可恶,它困住了我整整一天,而一天之后我还是没能学会前缀和的写法。

双递归,就是字面意思,并且很好理解,外面一层里面一层,甚至它再来几层还是很好理解。

class Solution {
public:
    int count = 0;
    int pathSum(TreeNode* root, int sum) {
        //偷师学艺 双重递归
        if(root == NULL)//第一层递归结束条件
            return 0;
        //深搜,从第一个结点开始
        dfs(root, sum);
        //开始递归
        pathSum(root->left, sum);
        pathSum(root->right, sum);

        return count;
    }

    void dfs(TreeNode* node, int sum)
    {
        if(node == NULL)
            return;//第二层递归结束条件
        if(sum - node->val == 0)
            count++;//找到一条成功的路了
		//开始递归
        dfs(node->left, sum - node->val);
        dfs(node->right, sum - node->val);
    }
};

455. 分发饼干(0310)

我的孩子们会打起来的,没有人会满意的,因为人的欲望不可满足,这是在世必须体会的道理…

就是贪心,具体是:排序+两个指针,每一次就只管当前匹不匹配,如果不匹配,那只能往后移动饼干的指针,因为移动孩子更不可能。直到某一数组遍历完。

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        //贪心:排序+两个指针
        int res = 0;
        //排序
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        
        //两个指针
        int i=0, j=0;
        while(i < g.size() && j < s.size())
        {
            if(g[i] <= s[j])
            {
                res++;
                i++;
                j++;
            }
            else
                j++;
        }
        return res;
    }
};

开一下脑洞的话,一个孩子可以分得多个饼干,并且最终不是数满意孩子的个数,而是他满意且满意值之和,似乎会很难了。

504. 七进制数(0311)

很好理解,无需多言,就是那个0我没考虑到,这很低级了

class Solution {
public:
    string convertToBase7(int num) {
        //处理正负号,虽然有点粗糙
        bool isneg = num < 0 ? true : false;
        num = abs(num);

        string s = "";
        //没考虑到0,所以补救一下
        if(num == 0)return "0";
        while(num)
        {
            s.append(to_string(num % 7));
            num /= 7;
        }
        s.append(isneg ? "-" : "");
        reverse(s.begin(), s.end());
        return s;
    }
};

这JAVA就…好吧

public String convertToBase7(int num) {
    return Integer.toString(num, 7); 
}

我有时候就在想,这些高级语言封装这些东西是为了什么呢,真有这些孤僻函数的用武之地吗

575. 分糖果(0312)

妹妹能得到的糖果种类的制约因素:糖果种类数

种类数 >= n / 2,则可以得到n / 2种;小于,则最多只能得到糖果种类数

class Solution {
public:
    int distributeCandies(vector<int>& candyType) {
        unordered_set<int> catset(candyType.begin(), candyType.end());

        int s = catset.size();
        if(s >= candyType.size() / 2)
            return candyType.size() / 2;
        else
        return s;
    }
};

665. 非递减数列(0314)

贪心还可以这么贪呀。

第一次做的时候单纯的判断前一个和后一个的大小比较,但这是不完全的考虑,测试点[3,4,2,3]不通过。这里有两对减序对(4,2)(4,3),想要消除减序对,最优的办法就是把nums[i]的值改为nums[i-1]的值,这样对后面的影响最小。所以基于此,完备的算法生成了。

次题得复习

class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        if(nums.size() <= 2)
            return true;

        int count = 0;
        for(int i = 1; i < nums.size(); ++i)
        {
            if(nums[i] < nums[i - 1])
            {
                count++;
                if(count >= 2)
                    return false;

                if(i >= 2 && nums[i] < nums[i-2])//这里其实我并不能理解为什么是[i]和[i-2]相比
                    nums[i] = nums[i-1];
            }
        }
        return true;
    }
};

🥝821. 字符的最短距离(0316)

双向for循环走一遍,首先是被这个思路困住了,因为我不会…其次是负无穷和正无穷两个数字我设小了,导致第一次wa了

class Solution {
public:
    vector<int> shortestToChar(string s, char c) {
        //两遍循环

        vector<int> res(s.size(), 0);
        //第一遍循环,单向的得到了第一个e出现之后,大家离它的距离
        int prev = -99999;//wc,这个数字小了居然也会出错,不过情理之中,他本本来就是要代表负无穷
        for(int i = 0; i < s.size(); ++i)
        {
            if(s[i] == c)
                prev = i;

            res[i] = i - prev;
        }
        //第二遍循环,是完全相反的方向,并且和原来res中的做大小比较,取最小值
        prev = 99999;
        for(int i = s.size() - 1; i >= 0; --i)
        {
            if(s[i] == c)
                prev = i;

            res[i] = min(prev - i, res[i]);
        }
        return res;
    }
};

昨天咕了,在准备大创中期答辩ppt

874. 模拟行走机器人(0317)

看上去比较复杂的一道题,没有难度,按部就班就行了。然后这次这个ordered_set的成员不能是pair类型的,没法哈希,所以要换成set,就可以了

class Solution {
public:
    int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
        //把障碍物放到哈希表里面去
        set<vector<int>> ob(obstacles.begin(), obstacles.end());
        int x = 0, y = 0, tempx = 0, tempy = 0;
        int dire = 0;//0123分别是东南西北
        int res = 0;
        //开始执行指令
        for(int i = 0; i < commands.size(); ++i)
        {
            //更新方向
            if(commands[i] == -1)
                dire = (dire + 1) % 4;
            else if(commands[i] == -2)
                dire = ((dire - 1) % 4 + 4) % 4;
            //坐标
            else
            {
                for(int j = 0; j < commands[i]; ++j)
                {
                    tempx = x;
                    tempy = y;

                    if(dire == 0) y++;
                    else if(dire == 1) x++;
                    else if(dire == 2) y--;
                    else x--;

                    if(ob.find({x, y}) != ob.end())
                    {//在hash集合中找到了障碍物,那么就得还原回去,并且提前终止指令
                        x = tempx;
                        y = tempy;
                        break;
                    }
                }
                res = max(res, x * x + y * y);
            }
        }
        return res;
    }
};

另外还存在一个对负数取模的问题,对于负数最好就这样写

(a % max + max) % max

就很稳

1128. 等价多米诺骨牌对的数量(0318)

本来打算用hash set做,做到一半发现不对劲,set会删除重复元素,所以它并不是一道合适的题。

学到了状态压缩+一次遍历DP+OnePass,这个题有1 <= dominoes[i]<= 9,很小,每一个书数字可以用5bit来表示,那么一个就可以用10来表示,10bit一共有1024中可能,那么开辟一个初始全为0,大小为1024的list就可以表征其全部状态,著需要遍历一一遍就可以记录下所有出现的数字。

细节是两个数字前后可以调换,所以可以比一下大小,再进行编码表示,怎么表示都无所谓,只要规则一致就行。

python真简便呀

class Solution:
    def numEquivDominoPairs(self, dominoes: List[List[int]]) -> int:
        count = [0] * 1024
        res = 0
        for a, b in dominoes:
            if a <= b:
                v = (a << 5) | b
            else:
                v = (b << 5) | a
            
            res += count[v]
            count[v] += 1
        
        return res

1260. 二维网格迁移(0319)

唉果然这种题就得看数学找规律,把这个二维网格拉抻成一位,就会发现就是k个元素从尾巴依次跑到头的事情

class Solution:
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
        m = len(grid)
        n = len(grid[0])
        
        mid = []
        for l in grid:
            mid = mid + l

        for _ in range(k):
            t = mid[-1]
            mid.pop()
            mid.insert(0, t)

        for i in range(m):
            for j in range(n):
                grid[i][j] = mid[i * n + j]

        return grid

1332. 删除回文子序列(0320)

funny🙂,回文子序列,不是回文子串。前者可以不连续,后者可以。也就是说,对于非回文字符串,就可以先删去a,再删去b,最多就只要两次。挺无聊,但是又说明读题要仔细。

class Solution:
    def removePalindromeSub(self, s: str) -> int:
        if(len(s) == 0):
            return 0
        else:
            return 1 if s == s[::-1] else 2

(0321)复习

除了有个数据结构的题我跳过去了,其余简单题好像做完了,周末了,复习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值