LeetCode 数据结构基础

数组

lc.136 只出现过一次的数字

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

  • 位运算
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (auto e: nums) ret ^= e;
        return ret;
    }
};

lc.169 多数元数

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

  • 排序后返回最中间的数
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        return nums[nums.size() / 2];
    }
};

官方题解,Boyer-Moore 投票算法 有点东西。

lc.75 颜色分类

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

sort 肯定可以过。

  • 双指针
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;
            }
        }
    }
};

交换 0 ,1 。

  • 也可以交换 0 ,2 。
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;
            }
        }
    }
};

lc.56 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

  • 一定要先排序···差点用set了
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> ans;
        vector<int> tmp;
        sort(intervals.begin(),intervals.end());
        int l = intervals[0][0] , r = intervals[0][1];
        int i = 0;
        while(i < intervals.size() - 1){
            if(r >= intervals[i + 1][0]){
                r = max(r , intervals[i + 1][1]);
            }
            else{
                ans.push_back({l,r});
                l = intervals[i + 1][0];
                r = intervals[i + 1][1];
            }
            i ++;
        }
        ans.push_back({l,r});

        return ans;
    }
};

为了避免数组越界,最后一组在循环外返回。
ans.push_back({l,r}); emplace_back 只能返回一个单值,要是想返回一组只能用 push_back。

lc.706 设计哈希映射

  • 我只能说设计一窍不通。
class MyHashMap {
private:
    vector<list<pair<int, int>>> data;
    static const int base = 769;
    static int hash(int key) {
        return key % base;
    }
public:
    /** Initialize your data structure here. */
    MyHashMap(): data(base) {}
    
    /** value will always be non-negative. */
    void put(int key, int value) {
        int h = hash(key);
        for (auto it = data[h].begin(); it != data[h].end(); it++) {
            if ((*it).first == key) {
                (*it).second = value;
                return;
            }
        }
        data[h].push_back(make_pair(key, value));
    }
    
    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    int get(int key) {
        int h = hash(key);
        for (auto it = data[h].begin(); it != data[h].end(); it++) {
            if ((*it).first == key) {
                return (*it).second;
            }
        }
        return -1;
    }
    
    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    void remove(int key) {
        int h = hash(key);
        for (auto it = data[h].begin(); it != data[h].end(); it++) {
            if ((*it).first == key) {
                data[h].erase(it);
                return;
            }
        }
    }
};

lc.119 杨辉三角II

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<vector<int>> ret(rowIndex+1);
        for (int i = 0; i < rowIndex+1; ++i) {
            ret[i].resize(i + 1);
            ret[i][0] = ret[i][i] = 1;
            for (int j = 1; j < i; ++j) {
                ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];
            }
        }
        return ret[rowIndex];

    }
};

lc.48 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

class Solution {
public:
    void rnmb(int i , int j , vector<vector<int>>& matrix){
        int n = matrix.size();
        matrix[i][j] = matrix[n-1-j][i];
        return;
    }
    void change(int a , int b , vector<vector<int>>& matrix){
        int tmp = matrix[a][b];
        int n = matrix.size();
        rnmb(a , b , matrix);
        rnmb(n-1-b , a , matrix);
        rnmb(n-1-a , n-1-b , matrix);
        matrix[b][n-1-a] = tmp;
        return;
    }
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        for(int i = 0 ; i < n / 2 ; i ++){
            for(int j = i ; j < n - i - 1 ; j ++){
                change(i , j , matrix);
            }
        }
        return;
    }
};

一开始方向转反了,一气之下痛改函数名。

lc.54 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

  • 模拟
class Solution {
private:
    static constexpr int direction[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(matrix.size() == 0 || matrix[0].size() == 0) return {};
        int m = matrix.size() , n = matrix[0].size();
        vector<vector<bool>> isVisited(m , vector<bool>(n));
        int len = m * n ;
        vector<int> ans(len);
        int row = 0 , col = 0 , pre = 0;

        for(int i = 0 ; i < len ; i ++){
            ans[i] = matrix[row][col];
            isVisited[row][col] = true;
            int nextrow = row + direction[pre][0] , nextcol = col + direction[pre][1];
            if(nextrow < 0 || nextrow >= m || nextcol < 0 || nextcol >= n || isVisited[nextrow][nextcol] == true){
                pre = (pre + 1) % 4 ;
            }
            row += direction[pre][0] , col += direction[pre][1];
        }
        return ans;
    }
};

真的开了一个 bool 数组以记录是否走过,我愿称之为,唯一路径 DFS 。

lc.59 螺旋矩阵 II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

  • 同 lc.54 模拟
class Solution {
private:
    static constexpr int direction[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> ans(n , vector<int>(n));
        vector<vector<bool>> isVisited(n , vector<bool>(n));
        int row = 0 , col = 0 , i = 1 , pre = 0;
        int max = n * n ;

        while(i <= max){
            ans[row][col] = i;
            isVisited[row][col] = true;
            int nextrow = row + direction[pre][0], nextcol = col + direction[pre][1]; 
            if(nextrow < 0 || nextrow >= n || nextcol < 0 || nextcol >= n || isVisited[nextrow][nextcol]){
                pre = (pre + 1) % 4;
            }
            row += direction[pre][0] , col += direction[pre][1];
            i ++;
        }

        return ans;
    }
};

这题倒是用不着 bool

class Solution {
//private:
//   static constexpr int direction[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> ans(n , vector<int>(n));
        vector<vector<int>> direction = {{0,1},{1,0},{0,-1},{-1,0}};
        int row = 0 , col = 0 , i = 1 , pre = 0;
        int Max = n * n ;

        while(i <= Max){
            ans[row][col] = i; i ++;
            int nextrow = row + direction[pre][0], nextcol = col + direction[pre][1]; 
            if(nextrow < 0 || nextrow >= n || nextcol < 0 || nextcol >= n || ans[nextrow][nextcol] != 0){
                pre = (pre + 1) % 4;
            }
            row += direction[pre][0] , col += direction[pre][1];
            
        }

        return ans;
    }
};

lc.334 递增三元子列

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int n = nums.size();
        if (n < 3) {
            return false;
        }
        vector<int> leftMin(n);
        leftMin[0] = nums[0];
        for (int i = 1; i < n; i++) {
            leftMin[i] = min(leftMin[i - 1], nums[i]);
        }
        vector<int> rightMax(n);
        rightMax[n - 1] = nums[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            rightMax[i] = max(rightMax[i + 1], nums[i]);
        }
        for (int i = 1; i < n - 1; i++) {
            if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {
                return true;
            }
        }
        return false;
    }
};

lc.74 搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
  • 两次二分
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size() , n = matrix[0].size() ;
        int l = 0 , r = m - 1 ;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(matrix[mid][0] <= target) l = mid ;
            else r = mid - 1;
        }
        int row = l ;
        l = 0 , r = n - 1 ;
        while(l < r){
            int mid = l + r >> 1;
            if(matrix[row][mid] >= target) r = mid;
            else l = mid + 1;
        }
        if(matrix[row][l] == target) return true;
        else return false;
    }
};

lc.240 搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

如果使用二分,就得对每一行都二分一次。O(mlogn)

  • 从右上角往左下角搜
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int x = 0 , y = matrix[0].size() - 1 ;
        while(x < matrix.size() && y > -1){
            if(matrix[x][y] > target) y --;
            else if(matrix[x][y] < target) x ++;
            else return true;
        }
        return false;
    }
};

如果当前的数小了,那么所在行左侧一定都小,往下一行挪动。
如果当前的数大了,那么往左侧挪动。
O(m+n)

lc.435 无重叠区间

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:

可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
  • 贪心
class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.empty()) {
            return 0;
        }
        
        sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) {
            return u[1] < v[1];
        });

        int n = intervals.size();
        int right = intervals[0][1];
        int ans = 1;
        for (int i = 1; i < n; ++i) {
            if (intervals[i][0] >= right) {
                ++ans;
                right = intervals[i][1];
            }
        }
        return n - ans;
    }
};

对右侧值进行排序,注意到sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) { return u[1] < v[1]; });
sort 函数:sort(begin, end, cmp),cmp 是一个 bool 类型的判断函数。
lambda 匿名函数:C++11, [ capture ] ( params ) opt -> ret { body; };,其中carpture是捕获列表,params是参数,opt是选项,ret则是返回值的类型,body则是函数的具体实现。

lc.238 除自身以外数组的乘积

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

  • 前缀积 * 后缀积
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> ans(nums.size()); ans[0] = 1;
        for(int i = 1 ; i < nums.size() ; i ++){
            ans[i] = ans[i - 1] * nums[i - 1] ;
        }
        int r = nums[nums.size() - 1] ;
        for(int i = nums.size() - 2 ; i >= 0 ; i --){
            ans[i] *= r ;
            r *= nums[i] ;
        }
        return ans;
    }
};

lc.560 和为 k 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。

由于nums里的值会有负数,所以没法用滑动窗口

  • 哈希表 + 前缀和
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int,int> hm;
        hm[0] = 1 ;
        int cnt = 0 , sum = 0;
        for(auto& i : nums){
            sum += i ;
            if(hm.find(sum - k) != hm.end()){
                cnt += hm[sum - k];
            }
            hm[sum] ++ ;`在这里插入代码片`
        }
        return cnt;
    }
};

当前 i 个数的和与前 j 个数的和 的差值为k , cnt ++ 。注意hm[0] = 1 ;

字符串

lc.415 字符串相加

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.length() - 1, j = num2.length() - 1, add = 0;
        string ans = "";
        while (i >= 0 || j >= 0 || add != 0) {
            int x = i >= 0 ? num1[i] - '0' : 0;
            int y = j >= 0 ? num2[j] - '0' : 0;
            int result = x + y + add;
            ans.push_back('0' + result % 10);
            add = result / 10;
            i -= 1;
            j -= 1;
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

lc.409 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。
注意:假设字符串的长度不会超过 1010。

class Solution {
public:
    int longestPalindrome(string s) {
        int cnt = 0 ;
        unordered_map<char,int> hm;
        for(auto& i : s){
            hm[i] ++;
        }
        for(auto& i : hm){
            int x = i.second;
            cnt += x % 2;
        }

        if(cnt == 0) return s.size() ;
        else return s.size() + 1 - cnt ;

    }
};

注意unordered_map值的遍历与调用。

lc.290 单词规律

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        int size_pattern = pattern.size();
        vector<string> words;
        string temp = "";
        // Divide the string to a string vector by words.
        for (int i = 0; i <= s.size(); i++) {
            if (i < s.size() && s[i] != ' ') {
                temp += s[i];
            }
            else {
                words.push_back(temp);
                temp = "";
            }
        }
        // If the size of pattern is not equal to the number of words.
        if (size_pattern != words.size()) return false;
        // Traverse through pattern[0,size-2] with iterator "i".
        for (int i = 0; i < size_pattern - 1; i++) {
            // Search for the matched element in pattern[i+1,size-1] with iterator "j".
            for (int j = i + 1; j < size_pattern; j++) {
                // No matter matched or not, turn to words to check if it is the same.
                if (pattern[j] == pattern[i]) {
                    if (words[i] != words[j]) {
                        return false;
                    }
                    break;
                }
                else {
                    if (words[i] == words[j]) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
};

取代双hashmap

lc.763 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

  • hashmap
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> ans;
        unordered_map<char,int> hm ;
        for(int i = 0 ; i < s.size() ; i ++){
            hm[s[i]] = i ;
        }
        int l = 0 , r = 0;
        for(int i = 0 ; i < s.size() ; i ++){
            r = max(r , hm[s[i]]);
            if(i == r){
                ans.emplace_back(r - l + 1);
                l = i + 1 ;
            }
        }

        return ans;
    }
};

if(i == r) 在一组数的最后一个判断,就不用遇到新的字符就判断一次。

lc.49 字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

  • hashmap 排序后作为 first
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> mp;
        for (string& str: strs) {
            string key = str;
            sort(key.begin(), key.end());
            mp[key].emplace_back(str);
        }
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};
  • 把每个 str 各个字母出现的次数作为 first
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 自定义对 array<int, 26> 类型的哈希函数
        auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
            return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
                return (acc << 1) ^ fn(num);
            });
        };

        unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
        for (string& str: strs) {
            array<int, 26> counts{};
            int length = str.length();
            for (int i = 0; i < length; ++i) {
                counts[str[i] - 'a'] ++;
            }
            mp[counts].emplace_back(str);
        }
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

lc.43 字符串相乘

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

  • 位数相加
class Solution {
public:
    string multiply(string num1, string num2) {
        string ans = "";
        string cnt0 = "";
        if(num1 == "0" || num2 == "0") return "0";
        for(int i = num2.size() - 1 ; i >= 0 ; i --){
            string tmp = "";
            for(int j = 0 ; j < num2[i] - '0' ; j ++){
                tmp = plus(tmp , num1);
            }
            cnt0 += "0";
            tmp += cnt0;
            ans = plus(ans , tmp);
        }
        ans.pop_back();
        return ans;
    }
    string plus(string num1, string num2){
        int add = 0, i = num1.size() - 1, j = num2.size() - 1 ;
        string ans = "";
        while(i >= 0 || j >= 0 || add > 0){
            int x = i >= 0 ? num1[i] - '0' : 0 ;
            int y = j >= 0 ? num2[j] - '0' : 0 ;
            int result = x + y + add;
            ans.push_back('0' + result % 10);
            add = result / 10;
            i -- ; j -- ;
        }
        reverse(ans.begin() , ans.end());
        return ans;
    }
};

if(num1 == "0" || num2 == "0") return "0"; 边界条件:0 * anything == 0 。

链表

lc.2 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *head = nullptr, *tail = nullptr;
        int carry = 0;
        while (l1 || l2) {
            int n1 = l1 ? l1->val: 0;
            int n2 = l2 ? l2->val: 0;
            int sum = n1 + n2 + carry;
            if (!head) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail->next = new ListNode(sum % 10);
                tail = tail->next;
            }
            carry = sum / 10;
            if (l1) {
                l1 = l1->next;
            }
            if (l2) {
                l2 = l2->next;
            }
        }
        if (carry > 0) {
            tail->next = new ListNode(carry);
        }
        return head;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值