Leecode Hot100 刷题笔记本-数组(C++版)

105 阅读4分钟
  1. 15. 三数之和 中等
  2. 20. 有效的括号 简单
  3. 75. 颜色分类 中等
  4. 136. 只出现一次的数字 简单
  5. 155. 最小栈 中等
  6. 169. 多数元素 简单
  7. 283. 移动零 简单
  8. 448. 找到所有数组中消失的数字 简单
  9. 461. 汉明距离 简单

15. 三数之和

Screen Shot 2023-08-28 at 10.07.46 AM.png

解法1: 排序+双指针

Screen Shot 2023-08-28 at 10.42.58 AM.png

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()<3) return ans;
        sort(nums.begin(), nums.end());
        if(nums[0]>0) return ans;
        int i = 0;
        while(i<nums.size()){
            if(nums[i]>0) break;        // 1楼网友指正,将这个if语句放这里提前终止循环
            int left = i+1, right = nums.size()-1;
            while(left< right){
                // 转换为long long避免加法过程中溢出
                //这么做是出于谨慎加之以前没有数据范围提示。现在有范围提示,改成 int 即可。
                long long y = static_cast<long long>(nums[i]);
                long long x = static_cast<long long>(nums[left]);
                long long z = static_cast<long long>(nums[right]);
                if(x + y >0-z)
                    right--;
                else if(x + y <0-z)
                    left++;
                else{
                    ans.push_back({nums[i], nums[left], nums[right]});
                    // 相同的left和right不应该再次出现,因此跳过
                    while(left<right&&nums[left]==nums[left+1])
                        left++;
                    while(left<right&&nums[right] == nums[right-1])
                        right--;
                    left++;
                    right--;
                }
            }
            // 避免nums[i]作为第一个数重复出现
            while(i+1<nums.size()&&nums[i] == nums[i+1])
                i++;
            i++;
        }
        return ans;
    }
};

Screen Shot 2023-08-28 at 10.44.12 AM.png

20. 有效的括号

Screen Shot 2023-08-07 at 3.29.12 PM.png

解法1: 辅助栈
class Solution {
public:
    bool isValid(string s) {
        int n = s.size();
        if (n % 2 == 1) {
            return false;
        }

        unordered_map<char, char> pairs = {
            {')', '('},
            {']', '['},
            {'}', '{'}
        };
        stack<char> stk;
        for (char ch: s) {
            if (pairs.count(ch)) {
                if (stk.empty() || stk.top() != pairs[ch]) {
                    return false;
                }
                stk.pop();
            }
            else {
                stk.push(ch);
            }
        }
        return stk.empty();
    }
};
  • 时间复杂度:O(n), 其中 n 是字符串 s 的长度
  • 空间复杂度:O(n+∣Σ∣), 其中 Σ 表示字符集,本题中字符串只包含 6 种括号,∣Σ∣=6。栈中的字符数量为 O(n),而哈希表使用的空间为 O(∣Σ∣),相加即可得到总空间复杂度.

75. 颜色分类

Screen Shot 2023-08-28 at 10.05.45 AM.png

解法1: 单指针
  • 对数组进行两次遍历
  • 第一次遍历, 将数组所有的0交换到数组的头部
  • 第二次遍历, 将数组所有的1交换到头部0之后
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int ptr = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 0) {
                swap(nums[i], nums[ptr]);
                ++ptr;
            }
        }
        for (int i = ptr; i < n; ++i) {
            if (nums[i] == 1) {
                swap(nums[i], nums[ptr]);
                ++ptr;
            }
        }
    }
};
  • 时间复杂度:O(n), n为数组长度
  • 空间复杂度:O(1)
解法2: 双指针
  • 从左向右遍历数组
  • 用指针p0 交换 0, p1 交换 1, 初始值为0
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int p0 = 0, p1 = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 1) {
                swap(nums[i], nums[p1]);
                ++p1;
            } else if (nums[i] == 0) {
                swap(nums[i], nums[p0]);
                if (p0 < p1) {
                    swap(nums[i], nums[p1]);
                }
                ++p0;
                ++p1;
            }
        }
    }
};
  • 时间复杂度:O(n), n为数组长度
  • 空间复杂度:O(1)
解法3: 双指针
  • 用指针 p0 交换0,p2 交换2
  • p0初始值为0, p2初始值为n-1
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int p0 = 0, p2 = n - 1;
        for (int i = 0; i <= p2; ++i) {
            while (i <= p2 && nums[i] == 2) {
                swap(nums[i], nums[p2]);
                --p2;
            }
            if (nums[i] == 0) {
                swap(nums[i], nums[p0]);
                ++p0;
            }
        }
    }
};
  • 时间复杂度:O(n), n为数组长度
  • 空间复杂度:O(1)

461. 汉明距离

Screen Shot 2023-08-06 at 2.48.29 PM.png

解法1:内置位计数功能
class Solution {
public:
    int hammingDistance(int x, int y) {
        return __builtin_popcount(x ^ y);
    }
};
  • 时间复杂度:O(1). 不同语言的实现方法不一,我们可以近似认为其时间复杂度为 O(1)
  • 空间复杂度:O(1)
解法2:内置位计数功能

Screen Shot 2023-08-06 at 3.10.09 PM.png

class Solution {
public:
    int hammingDistance(int x, int y) {
        int s = x ^ y, ret = 0;
        while (s) {
            ret += s & 1;
            s >>= 1;
        }
        return ret;
    }
};
  • 时间复杂度:O(logC), 其中 C 是元素的数据范围,在本题中 log⁡C=log⁡231=31logC=log231=31。
  • 空间复杂度:O(1)

136. 只出现一次的数字 Screen Shot 2023-08-06 at 3.15.58 PM.png

解法1: 位运算
  • 任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a
  • 任何数和其自身做异或运算,结果是 00,即 a⊕a=0 Screen Shot 2023-08-06 at 3.28.36 PM.png
class Solution {
    public int singleNumber(int[] nums) {
        int single = 0;
        for (int num : nums) {
            single ^= num;
        }
        return single;
    }
}
  • 时间复杂度:O(n),其中 n 是数组长度。只需要对数组遍历一次
  • 空间复杂度:O(1)

155. 最小栈

Screen Shot 2023-08-07 at 11.50.59 AM.png

解法一:辅助栈

  • 定义一个辅助栈x_stack, 各一个最小栈min_stack
  • 当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中
  • 当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出
  • 在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素
class MinStack {
    stack<int> x_stack;
    stack<int> min_stack;
public:
    MinStack() {
        min_stack.push(INT_MAX);
    }
    
    void push(int x) {
        x_stack.push(x);
        min_stack.push(min(min_stack.top(), x));
    }
    
    void pop() {
        x_stack.pop();
        min_stack.pop();
    }
    
    int top() {
        return x_stack.top();
    }
    
    int getMin() {
        return min_stack.top();
    }
};
  • 时间复杂度:O(1), 因为栈的插入、删除与读取操作都是 O(1),我们定义的每个操作最多调用栈操作两次
  • 空间复杂度:O(n), 其中 n 为总操作数。最坏情况下,我们会连续插入 n 个元素,此时两个栈占用的空间为 O(n)

169. 多数元素

Screen Shot 2023-08-06 at 3.32.28 PM.png

解法1: 哈希表
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        unordered_map<int, int> counts;
        int majority = 0, cnt = 0;
        for (int num: nums) {
            ++counts[num];
            if (counts[num] > cnt) {
                majority = num;
                cnt = counts[num];
            }
        }
        return majority;
    }
};
  • 时间复杂度:O(n), 其中 n 是数组 nums 的长度。我们遍历数组 nums 一次,对于 nums 中的每一个元素,将其插入哈希表都只需要常数时间
  • 空间复杂度: Screen Shot 2023-08-06 at 4.00.49 PM.png

283. 移动零

Screen Shot 2023-08-28 at 9.31.35 AM.png

解法1: 双指针
  • 使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部
  • 右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移
  • 左指针左边均为非零数
  • 右指针左边直到左指针处均为零
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(), left = 0, right = 0;
        while (right < n) {
            if (nums[right]) {
                swap(nums[left], nums[right]);
                left++;
            }
            right++;
        }
    }
};
  • 时间复杂度:O(n), 其中 n 为序列长度。每个位置至多被遍历两次
  • 空间复杂度:O(1)

448. 找到所有数组中消失的数字

Screen Shot 2023-08-06 at 4.33.17 PM.png

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        int n = nums.size();
        for (auto& num : nums) {
            int x = (num - 1) % n;
            nums[x] += n;
        }
        vector<int> ret;
        for (int i = 0; i < n; i++) {
            if (nums[i] <= n) {
                ret.push_back(i + 1);
            }
        }
        return ret;
    }
};
  • 时间复杂度:O(n),其中 n 是数组长度。只需要对数组遍历一次
  • 空间复杂度:O(1)