LeetCode题解之哈希表、字符串、栈和队列

哈希表

在这里插入图片描述

242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

class Solution {
public:
    bool isAnagram(string s, string t) {
        vector<int> res(26,0);
        for(char c : s) res[c - 'a']++;
        for(char d : t) 
        {
            if(res[d - 'a'] == 0) return false;
            res[d-'a']--;
        }
        for(int i = 0; i < res.size(); i++)
        {
            if(res[i] != 0) return false;
        }
        return true;
    }
};

383. 赎金信

给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        vector<int>res(26,0);
        for(char c : magazine) res[c-'a']++;
        for(char d : ransomNote){
            if(res[d-'a'] == 0) return false;
            res[d-'a']--;
        }
        return true;
    }
};

1002. 查找常用字符(☆)

给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。

class Solution {
public:
    vector<string> commonChars(vector<string>& A) {
         vector<string> result;
        if (A.size() == 0) return result;
        int hash[26] = {0}; // 用来统计所有字符串里字符出现的最小频率
        for (int i = 0; i < A[0].size(); i++) { // 用第一个字符串给hash初始化
            hash[A[0][i] - 'a']++;
        }

        int hashOtherStr[26] = {0}; // 统计除第一个字符串外字符的出现频率
        for (int i = 1; i < A.size(); i++) {
            memset(hashOtherStr, 0, 26 * sizeof(int));
            for (int j = 0; j < A[i].size(); j++) {
                hashOtherStr[A[i][j] - 'a']++;
            }
            // 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
            for (int k = 0; k < 26; k++) { 
                hash[k] = min(hash[k], hashOtherStr[k]);
            }
        }
        // 将hash统计的字符次数,转成输出形式
        for (int i = 0; i < 26; i++) {
            while (hash[i] != 0) { // 注意这里是while,多个重复的字符
                string s(1, i + 'a'); // char -> string
                result.push_back(s);
                hash[i]--;
            }
        }

        return result;
    }
};

349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set;
        vector<int> res;
        for(int c : nums1) set.insert(c);
        sort(nums2.begin(), nums2.end());
        for(int i = 0; i < nums2.size(); i++){
            if(i > 0 && nums2[i] == nums2[i-1]) continue;
            if(set.find(nums2[i]) != set.end()) res.push_back(nums2[i]);
        }
        return res;
    }
};

350. 两个数组的交集 II

给定两个数组,编写一个函数来计算它们的交集。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int, int> map1;
        unordered_map<int, int> map2;
        vector<int> res;
        for(int c : nums1) map1[c]++;
        for(int d : nums2) map2[d]++;
        sort(nums1.begin(), nums1.end());
        for(int  i = 0; i < nums1.size(); i++) 
        {
            if(i > 0 && nums1[i] == nums1[i-1]) continue;
            int k = min(map1[nums1[i]], map2[nums1[i]]);
            while(k != 0)
            {
                res.push_back(nums1[i]);
                k--;
            }
        }
        return res;

    }
};
class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        //利用双指针

        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(), nums2.end());

        int left = 0, right = 0;

        vector<int> res;

        while(left < nums1.size() && right < nums2.size()) {
            if(nums1[left] < nums2[right]) {
                left++;
            } else if(nums1[left] > nums2[right]) {
                right++;
            } else {
                res.push_back(nums1[left]);
                left++;
                right++;
            }
        }
        return res;
    }
};

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 true ;不是,则返回 false 。

其中对于无限循环,那么需要有一个集合来存储以前的sum如果存在则立刻退出,如果不存在就加入几何

class Solution {
public:
   // 取数值各个位上的单数之和
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++)
        {
            auto iter = map.find(target - nums[i]);
            if(iter != map.end())
            {
                return {iter->second, i};
            }
            map[nums[i]] = i;
        }
        return {};
    }
};

15. 三数之和(☆)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

双指针

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }
            // 错误去重方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // 正确去重方法
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                } else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
    }
};

18. 四数之和(☆)

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:答案中不可以包含重复的四元组。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++) {
            // 这种剪枝是错误的,这道题目target 是任意值
            // if (nums[k] > target) {
            //     return result;
            // }
            // 去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // 正确去重方法
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    if (nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    } else if (nums[k] + nums[i] + nums[left] + nums[right] < target) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // 去重逻辑应该放在找到一个四元组之后
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }

            }
        }
        return result;
    }

};

字符串

179. 最大数

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

class Solution {
public:
    static bool cmp (const int &a, const int &b) {
        //str_a+str_b > str_b+str_a
        string sa = to_string(a);
        string sb = to_string(b);
        return sa + sb > sb + sa;
    }
    string largestNumber(vector<int>& nums) {
        //重点在排序
        sort(nums.begin(), nums.end(),cmp);
        string str;
        for(int num : nums) {
            if(!(num == 0 && str[0] == '0')) {
                str += to_string(num);
            }
        }
        return str;
        
    }
};

28. 实现 strStr()

实现 strStr() 函数。

class Solution {
public:
    int strStr(string haystack, string needle) {
        //直接遍历,当然也可以利用昨天的固定滑动窗口
        if(haystack.size() < needle.size()) return -1;
        if(needle.size() == 0) return 0;
        for(int i = 0; i <= haystack.size() - needle.size(); i++) {
            if(haystack[i] == needle[0]) {
                string str = haystack.substr(i, needle.size());
                if(str == needle) return i;
            }
        }
        return -1;
    }
};

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

541. 反转字符串 II(☆)

给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。

如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

class Solution {
public:
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            s[i] ^= s[j];
            s[j] ^= s[i];
            s[i] ^= s[j];
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s, i, i + k - 1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0;
        int oldsize = s.size();
        for(int i = 0; i < s.size(); i++)
        {
            if(s[i] == ' ') count++;
        }

        
        s.resize(s.size() + 2 * count);
        for(int i = s.size() - 1, j = oldsize - 1; i > j; j--, i--)
        {
            if (s[j] != ' ') {
                s[i] = s[j];
            } else {
                s[i] = '0';
                s[i - 1] = '2';
                s[i - 2] = '%';
                i -= 2;
            }
        }
        return s;
    }
};

151. 翻转字符串里的单词(☆)

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。

class Solution {
public:
    string reverseWords(string s) {
        //翻转整个字符串
        reverse(s.begin(), s.end());

        int sz = s.size();
        int index = 0;
        for(int start = 0; start < sz; start++) {
            if(s[start] != ' ') {
                //填一个空白字符然后将index移动到下一单词的开头
                if(index!= 0) s[index++] = ' ';
                //循环遍历至单词的末尾
                int end = start;
                while(end < sz && s[end] != ' ') {
                    s[index++] = s[end++];
                }
                //反转整个单词
                reverse(s.begin() + index - (end - start), s.begin() + index);
                //更新start去寻找下一个单词
                start  =end;
            }
        }
        s.erase(s.begin()+index, s.end());
        return s;
    }
};

395. 至少有 K 个重复字符的最长子串(☆☆)

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

class Solution {
public:
    int longestSubstring(string s, int k) {
        //分治:对于一个字符串来说,如果要求子串最少出现k次,那么如果某些字母出现的次数小于k,
        //这些字母一定不会出现在最长的子串中,并且这些字母将整个字符子串分割成小段,这些小段有可能是最长的
        //但是由于被分割了,还是要检查这一小段,如果某些字母出现的次数小于k,会将小段继续分割下去,
        //比如字符串"aacbbbdc",要求最少出现2次,我们记录左右闭区间,,
        //第一轮[0,7],处理"aacbbbdc",d只出现了一次不满足,于是递归解决区间[0,5]、[7,7]
        //第二轮[0,5],处理"aacbbb",  c只出现了一次不满足,于是递归解决区间[0,1]、[3,4] 
        //第二轮[7,7],处理"c",       c只出现了一次不满足,不继续递归
        //第三轮[0,1],处理"aa",      满足出现次数>=2,ret=2
        //第三轮[3,4],处理"bbb",     满足出现次数>=2 ret=3;
        if(k <= 1) return s.size();
        if(s.empty() || s.size() < k) return 0;

        vector<int> hash(128, 0);
        for(char c : s) ++hash[c];

        int i = 0;
        while( i < s.size() && hash[s[i]] >= k) ++i;
        if(i == s.size()) return s.size();

        int left = longestSubstring(s.substr(0, i), k);
        while(i < s.size() && hash[s[i]] < k) ++i;
        int right = longestSubstring(s.substr(i), k);

        return max(left, right);
     }
};

438. 找到字符串中所有字母异位词(☆☆)

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100

567. 字符串的排列

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        //滑动窗口
        vector<int> res;
        if(s.size() < p.size()) return res;

        vector<int> hash_s(26,0), hash_p(26,0);
        int left = 0, right = 0;
        //初始化代码
        for(int i = 0; i < p.size(); i++) {
            hash_p[p[i] - 'a']++;
            hash_s[s[right++] - 'a']++;
        }
        if(hash_s == hash_p) res.push_back(left);
        //固定长度的滑动窗口
        while(right < s.size()) {
            hash_s[s[right++] - 'a']++;
            hash_s[s[left++] - 'a']--;
            if(hash_s == hash_p) res.push_back(left);
        }
        return res;
    }
};

459. 重复的子字符串(☆)

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        /*
        *KMP
        *时间复杂度
        *空间复杂度
        */
        if (s.size() == 0)  return false;
        int len = s.size();
        int next[len];
        getNext(next,s);
        if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0)   //如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。
            return true;
        return false;
    }

    void getNext (int* next, const string& s) {
        next[0] = 0;
        int j = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j])   j = next[j-1];//不相同就进行回溯
            if (s[i] == s[j])   j++;
            next[i] = j;
        }
    }
};
class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        /*
        假设母串S是由子串s重复N次而成, 则 S+S则有子串s重复2N次, 那么现在有: S=Ns, S+S=2Ns, 其中N>=2。 
        如果条件成立, S+S=2Ns, 掐头去尾破坏2个s,S+S中还包含2*(N-1)s, 又因为N>=2,
         因此S在(S+S)[1:-1]中必出现一次以上
        */
       return (s + s).find(s, 1) != s.size();
    } 
};

位运算

在这里插入图片描述
在这里插入图片描述

172. 阶乘后的零(☆)

给定一个整数 n,返回 n! 结果尾数中零的数量。

class Solution {
public:
    int trailingZeroes(int n) {
        /*
            理解题意:
            题目给定一个整数 n
            需要返回 n 的阶乘尾数有几个 0

            整体思路
                首先得知道有 2 * 5 这个因式才能得出末尾有 0 的数
                就可以把问题转换成:题目给定的 n! 中有几个 2 * 5 末尾就有几个 0,而 2 是任何偶数都有的因子,所以只要考虑 n! 中一共有多少个 5 就可以了
                那么可以分这几种情况:
                    首先是 5 的倍数,它们就包含至少一个因子 5,比如 5、10、15、20,这几个都是 5 的倍数且只包含一个因子 5
                    除了 5、10、15、20,比如 25 = 5 * 5,包含两个因子 5,所以 25 的倍数 50、75 这些都包含了两个因子 5
                    还有一个特殊点的数 125 = 5 * 5 * 5,包含了三个因子 5
            现在就拿 125! 来举例算出它的末尾有多少个 0
            先去掉 5 的倍数,125 / 5 = 25,首先得出 有 25 个数是 5 的倍数(这 25 个数包含了 5 的倍数,也说明包含着 25 的倍数,125 的倍数),那么第一轮运算可以得出提供因子 5 个数有至少 25 个
            然后去掉 25 的倍数,125 / 25 = 5,可以得出有 5 个数是 25 的倍数,由于第一轮算出的 5 的倍数的数包含了这 5 个数,所以这 5 个数每个数额外提供了 5 个因子 5
            最后计算 125 的倍数,125 / 125 = 1,同上 125 再额外提供一个因子 5
            最后可以算出三轮一共提供因子 5 的个数为 25 + 5 + 1 = 31
        */
        //其实就是含有5的个数
        int count = 0;
        while(n/5 != 0) {
            count += n / 5;
            n /= 5;
        }
        return count;
    }
};

190. 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位。

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t res;
        int i = 32;
        while(i--) {
            res <<= 1;
            res += n & 1;
            n >>= 1;
        }
        return res;
    }
};

338. 比特位计数(☆)

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

class Solution {
public:
    vector<int> countBits(int n) {
        //动态规划思想
        /*
        i & (i - 1)可以去掉i最右边的一个1(如果有),因此 i & (i - 1)是比 i 小的,而且i & (i - 1)的1的个数已经在前面算过了,所以i的1的个数就是 i & (i - 1)的1的个数加上1
        */
        vector<int> dp(n+1);
        for(int i = 1; i <= n; i++) {//注意要从1开始,0不满足
            dp[i] = dp[i & (i-1)] + 1;
        }
        return dp;
    }
};
class Solution {
public:
    vector<int> countBits(int n) {
        //动态规划思想
        /*
        i >> 1会把最低位去掉,因此i >> 1 也是比i小的,同样也是在前面的数组里算过。当 i 的最低位是0,则 i 中1的个数和i >> 1中1的个数相同;当i的最低位是1,i 中1的个数是 i >> 1中1的个数再加1
        */
        vector<int> dp(n+1);
        for(int i = 0; i <= n; i++) {
            dp[i] = dp[i >> 1] + (i & 1); //注意i&1需要加括号
        }
        return dp;
    }
};

461. 汉明距离

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

思路:首先直接进行^运算。如果位相同就是0,否则就是1,
接下来就是统计1的个数,可以利用&运算,如果相同就是1,否则0

class Solution {
public:
    int hammingDistance(int x, int y) {
        int res = 0;
        int z = x ^ y;
        while(z) {
            res += z & 1;
            z >>= 1;
        }
        return res;
    }
};

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for(int num : nums) {
            res ^= num;
        }
        return res;
    }
};

剑指 Offer 56 - I. 数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        vector<int> res;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); i++) {
            if(res.empty()) res.push_back(nums[i]);
            else if(res.back() == nums[i]) res.pop_back();
            else res.push_back(nums[i]);
        }
        return res;
    }
};

剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size()-1; i += 3) {
            if(nums[i] != nums[i+1]) return nums[i];
        }
        return nums.back();
    }
};

696. 计数二进制子串(☆)

给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。

重复出现的子串要计算它们出现的次数。

class Solution {
public:
    
    int countBinarySubstrings(string s) {
        //先统计连续的0和1分别有多少个,如:111100011000,得到4323;在4323中的任意相邻两个数字,取小的一个加起来,就是3+2+2 = 7.
        int last = 0, res = 0, cur = 1;
        for(int i = 1; i < s.size(); i++) {
            if(s[i] == s[i-1]) cur++;
            else {
                last = cur;
                cur = 1;
            }
            if(last >= cur) res++;
        } 
        return res;
    } 
};

50. Pow(x, n)(☆)

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。

class Solution {
public:
    double myPow(double x, int n) {
        /*
        使用折半计算,每次把n缩小一半,这样n最终会缩小到0,任何数的0次方都为1,这时候我们再往回乘,
        如果此时n是偶数,直接把上次递归得到的值算个平方返回即可,如果是奇数,则还需要乘上个x的值。
        还有一点需要引起我们的注意的是n有可能为负数,对于n是负数的情况,我们可以先用其绝对值计算出一个结果再取其倒数即可。
        我们让i初始化为n,然后看i是否是2的倍数,是的话x乘以自己,否则res乘以x,i每次循环缩小一半,直到为0停止循环。最后看n的正负,如果为负,返回其倒数。
        */
        double res = 1;
        for(int i = n; i != 0; i /= 2) {
            if(i % 2 != 0) res *= x;
            x *= x;
        }
        return n < 0? 1/res : res;
    }
};

栈和队列

20. 有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        for(int i = 0; i < s.size(); i++) {
            if(s[i] == '(' || s[i] == '[' || s[i] == '{') st.push(s[i]);
            else if (s[i] == ')') {
                if(st.empty() || st.top() != '(') return false;
                st.pop();
            } else if(s[i] == ']') {
                if(st.empty() || st.top() != '[') return false;
                st.pop();
            } else if(s[i] == '}') {
                if(st.empty() || st.top() != '{') return false;
                st.pop();
            } 
        }
        return st.empty(); 
    }
};

1047. 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

class Solution {
public:
    string removeDuplicates(string S) {
        string result;
        for(char s : S) {
            if(result.empty() || result.back() != s) {
                result.push_back(s);
            }
            else {
                result.pop_back();
            }
        }
        return result;
    }
};

150. 逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。

有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(string str : tokens) {
            if(str == "+" || str == "-" || str == "/" || str == "*") {
                int num1  = st.top();
                st.pop();
                int num2 = st.top();
                st.pop();
                if(str == "+") {
                    st.push(num2 + num1);
                } else if(str == "-") {
                    st.push(num2 - num1);
                } else if(str == "/") {
                    st.push(num2 / num1);
                } else if(str == "*") {
                    st.push(num2 * num1);
                }
            } else {
                st.push(stoi(str));
            }        
        }
        int res = st.top();
        st.pop();
        return res;
    }
};

227. 基本计算器 II

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

【笔记】一般需要符号栈、数据栈,两个。但是,看到网上一个写的不错的算法,只用了一个数据栈。符号栈用一个变量sign代替了,只存储上一个符号,主要思想如下:
将减法转化为加法(取相反数)

  • 由于乘除法优先级高,直接计算

  • 整数不仅一位,会>10

  • 表达式中没有括号

注意:加减乘除空格的ASCII码都小于’0’,ASCII对照表如下

class Solution {
public:
    int calculate(string s) {
        /*
        *逆波兰法
        */
        int res = 0, sum = 0;
        char sign = '+';
        stack<int> nums;
        for(int i = 0; i < s.size(); i++) {
            //判断是不是大于10
            if(s[i] >= '0') {//加减乘除和空格ASCII码都小于'0'
                sum = sum * 10 - '0'+ s[i]; //进位(先减法)
            }

            if((s[i] < '0' && s[i] != ' ') || i == s.size()-1) {
                if(sign == '+') {
                    nums.push(sum);
                } else if(sign == '-') {
                    nums.push(-sum);
                } else if(sign == '*' || sign == '/') {
                    int tmp = (sign == '*'? nums.top() * sum : nums.top() / sum);
                    nums.pop();
                    nums.push(tmp);
                }
                sign = s[i];
                sum = 0;
            }
        }

        while (!nums.empty()) {
            res += nums.top();
            nums.pop();
        }
        return res;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值