1. 两数之和

用一个 unordered_map 来存储已经访问过的数字和它们的索引。对于每个数字,检查 target - nums[i] 是否在 unordered_map 中,如果存在,则返回对应的索引和当前索引;如果不存在,则将当前数字和索引存入 unordered_map 中。

时间复杂度: $O(n)$。

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

2. 两数相加

创建一个新链表储存两个链表相加后的结果。相加的时候先不进行进位,在相加完成之后再去遍历链表统一处理进位。如果进位后链表末端的值大于 9,则在末端添加一个新的节点。

时间复杂度: $O(\max(m, n))$。

/**
 * 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) {
        if (l1 == nullptr)
            return l2;
        if (l2 == nullptr)
            return l1;

        ListNode *head = new ListNode(l1->val + l2->val);
        head->next = addTwoNumbers(l1->next, l2->next);

        ListNode *tail = head;
        while (tail->next)
        {
            if (tail->val > 9)
                tail->next->val++, tail->val -= 10;
            tail = tail->next;
        }

        if (tail->val > 9)
            tail->next = new ListNode(1), tail->val -= 10;

        return head;
    }


};

3. 无重复字符的最长子串

使用两个指针 lr 来维护一个滑动窗口,初始时都指向字符串的开头。用 unordered_map 来记录当前窗口内的字符是否出现过。每次将 r 指向的字符加入窗口,如果该字符已经在窗口中出现过,则移动 l 指针直到该字符不再出现在窗口中。每次更新最长子串的长度。

时间复杂度: $O(n)$。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, bool> vis;
        int l = 0, r = 0, n = s.size(), ans = 0;

        while (r < n)
        {
            while (l < r && vis.find(s[r]) != vis.end() && vis[s[r]])
                vis[s[l]] = false, l++;
            vis[s[r]] = true;
            r++;
            ans = max(ans, r - l);
        }

        return ans;
    }
};

4. 寻找两个正序数组的中位数

通过二分查找来找到第 $k$ 小的元素。每次比较两个数组中第 $k/2$ 个元素的大小,较小的那个数组中前 $k/2$ 个元素都不可能是第 $k$ 小的元素,因此可以将其排除掉,并更新 $k$ 的值。重复这个过程直到找到第 $k$ 小的元素。

时间复杂度: $O(\log(m + n))$。

class Solution {
public:
    double binarySearch(vector<int>& nums1, vector<int>& nums2, int k)
    {
        int l1 = 0, r1 = nums1.size(), l2 = 0, r2 = nums2.size();
        while (k)
        {
            if (l1 == r1)
                return nums2[l2 + k - 1];
            if (l2 == r2)
                return nums1[l1 + k - 1];
            if (k == 1)
                return min(nums1[l1], nums2[l2]);

            int mid1 = min(r1 - 1, l1 + k / 2 - 1);
            int mid2 = min(r2 - 1, l2 + k / 2 - 1);

            if (nums1[mid1] < nums2[mid2])
                k -= (mid1 - l1 + 1), l1 = mid1 + 1;
            else
                k -= (mid2 - l2 + 1), l2 = mid2 + 1;
        }
        return 0;
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if ((m + n) % 2 == 0)
            return (binarySearch(nums1, nums2, (m + n) / 2) +
                binarySearch(nums1, nums2, (m + n) / 2 + 1)) / 2;
        else
            return binarySearch(nums1, nums2, (m + n + 1) / 2);
    }
};

5. 最长回文子串

设 $dp_{i, j}$ 为字符串 $s$ 的第 $i$ 个字符到第 $j$ 个字符是否为回文串。初始时,所有长度为 1 的子串都是回文串,因此 $dp_{i, i} = 1$。对于长度大于 $1$ 的子串,如果 $s[i]$ 和 $s[j]$ 相等,并且 $dp_{i + 1, j - 1}$ 为真,则 $dp_{i, j}$ 也为真。最后,从最长的子串开始检查,找到第一个满足条件的回文子串并返回。

时间复杂度: $O(n^2)$。

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        s = " " + s;
        vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i][j] = 1;
            }
        }
        for (int len = 2; len <= n; len++) {
            for (int l = 1; l + len - 1 <= n; l++) {
                int r = l + len - 1;
                dp[l][r] = dp[l + 1][r - 1] & (s[l] == s[r]);
            }
        }
        for (int len = n; len; len--) {
            for (int l = 1; l + len - 1 <= n; l++) {
                if (dp[l][l + len - 1]) return s.substr(l, len);
            }
        }
        return "";
    }
};

6. Z 字形变换

按照题目要求将字符串 $s$ 重新排列成 Z 字形。首先计算出每个字符在 Z 字形中的位置,然后按照行的顺序将字符连接起来形成最终的字符串。

时间复杂度: $O( s )$。
class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows == 1) return s;
        string ans = "";
        int n = s.size();
        s = " " + s;
        int t = 2 * numRows - 2;
        for (int row = 1; row <= numRows; row++) {
            for (int pos = row; pos <= n; pos += t) {
                ans.push_back(s[pos]);
                if (row > 1 && row < numRows && pos + 2 * (numRows - row) <= n)
                    ans.push_back(s[pos + 2 * (numRows - row)]);
            }
        }
        return ans;
    }
};

7. 整数反转

将整数先转换成字符串,然后反转字符串。对于正数,直接反转整个字符串;对于负数,反转除第一个字符以外的部分。最后检查反转后的字符串是否超过 32 位整数的范围,如果超过则返回 0,否则返回转换后的整数。

时间复杂度: $O(\log x )$。
class Solution {
public:
    int reverse(int x) {
        string maxm = to_string(INT_MAX), minm = to_string(INT_MIN);
        string ans = to_string(x);
        if (x > 0) std::reverse(ans.begin(), ans.end());
        else std::reverse(ans.begin() + 1, ans.end());
        if (x > 0 && ans.size() == maxm.size() && ans > maxm) return 0;
        if (x < 0 && ans.size() == minm.size() && ans > minm) return 0;
        return std:: stoi(ans);
        return 0;
    }
};

8. 字符串转换整数 (atoi)

按照题意模拟即可。首先去掉字符串开头的空格,然后检查第一个非空字符是否为正负号,如果是,则记录符号并继续处理后续字符。接下来逐个处理后续字符,如果遇到非数字字符则停止处理。最后根据记录的符号和处理得到的数字进行范围检查,确保结果在 32 位整数的范围内。

时间复杂度: $O( s )$。
class Solution {
public:
    int myAtoi(string s) {
        int pos = 0;
        while (pos < s.size() && s[pos] == ' ') pos++;
        s = s.substr(pos, s.size() - pos);
        if (s.size() == 0 || ((s[0] < '0' || s[0] > '9') && s[0] != '-' && s[0] != '+'))
            return 0;
        int n = s.size();
        s = " " + s;
        int f = 1;
        long long num = 0;
        bool flag = false;
        for (int i = 1; i <= n; i++) {
            if (i == 1 && (s[i] == '-' || s[i] == '+')) {
                f = s[i] == '-' ? -1 : 1;
                continue;
            }
            if (flag && (s[i] < '0' || s[i] > '9')) break;
            if (i > 1 && (s[i] < '0' || s[i] > '9')) break;
            num = num * 10 + s[i] - '0';
            flag = true;
            if (f > 0) num = min(num, 1LL * INT_MAX);
            if (f < 0) num = min(num, -1LL * INT_MIN);
        }
        return num * f;
    }
};

9. 回文数

将整数转换成字符串,然后检查字符串是否是回文。可以通过比较字符串的前半部分和后半部分来判断是否是回文。

时间复杂度: $O(\log x )$。
class Solution {
public:
    bool isPalindrome(int x) {
        string str = to_string(x);
        for (int i = 0; i < (int)str.size() / 2; i++)
            if (str[i] != str[str.size() - i - 1]) return false;
        return true;
    }
};

10. 正则表达式匹配

设 $dp[i][j]$ 表示字符串 $s$ 的前 $i$ 个字符和模式 $p$ 的前 $j$ 个字符是否匹配。状态转移方程为:

\[f[i][j] = \begin{cases} \text{if } p[j] \ne '*'= \begin{cases} f[i-1][j-1], & \text{if } check(s[i], p[j]) \\ \text{false}, & \text{otherwise} \end{cases}\\ \text{otherwise}= \begin{cases} f[i-1][j] \ \text{or} \ f[i][j-2], & \text{if } check(s[i], p[j-1]) \\ f[i][j-2], & \text{otherwise} \end{cases} \end{cases}\]
class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size(), n = p.size();
        s = " " + s, p = " " + p;
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        auto check = [&](int i, int j) {
            if (i == 0) return false;
            if (p[j] == '.') return true;
            return s[i] == p[j];
        };
        dp[0][0] = 1;
        for (int i = 0; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (p[j] == '*') {
                    dp[i][j] |= dp[i][j - 2];
                    if (check(i, j - 1)) dp[i][j] |= dp[i - 1][j];
                } else {
                    if (check(i, j)) dp[i][j] |= dp[i - 1][j - 1];
                }
            }
        }
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                cout << dp[i][j] << ' ';
            }
            cout << endl;
        }
        return dp[m][n];
    }
};