CONTENTS
- LeetCode 16. 最接近的三数之和(中等)
 - LeetCode 17. 电话号码的字母组合(中等)
 - LeetCode 18. 四数之和(中等)
 - LeetCode 19. 删除链表的倒数第N个节点(中等)
 - LeetCode 20. 有效的括号(简单)
 
LeetCode 16. 最接近的三数之和(中等)
【题目描述】
给你一个长度为 n 的整数数组 nums 和一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
 返回这三个数的和。
 假定每组输入只存在恰好一个解。
【示例1】
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
 
【示例2】
输入:nums = [0,0,0], target = 1
输出:0
 
【提示】
 
     
      
       
       
         3 
        
       
         ≤ 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         . 
        
       
         l 
        
       
         e 
        
       
         n 
        
       
         g 
        
       
         t 
        
       
         h 
        
       
         ≤ 
        
       
         1000 
        
       
      
        3\le nums.length\le 1000 
       
      
    3≤nums.length≤1000
  
     
      
       
       
         − 
        
       
         1000 
        
       
         ≤ 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         ≤ 
        
       
         1000 
        
       
      
        -1000\le nums[i]\le 1000 
       
      
    −1000≤nums[i]≤1000
  
     
      
       
       
         − 
        
       
         1 
        
        
        
          0 
         
        
          4 
         
        
       
         ≤ 
        
       
         t 
        
       
         a 
        
       
         r 
        
       
         g 
        
       
         e 
        
       
         t 
        
       
         ≤ 
        
       
         1 
        
        
        
          0 
         
        
          4 
         
        
       
      
        -10^4\le target\le 10^4 
       
      
    −104≤target≤104
【分析】
和第15题相似,和 target 最接近的数可能是大于等于 target 的最小的数,也可能是小于等于 target 的最大的数。前者和第15题一样,枚举指针  
     
      
       
       
         i 
        
       
      
        i 
       
      
    i,然后用双指针  
     
      
       
       
         j 
        
       
      
        j 
       
      
    j 和  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 求出大于等于 target 的最小的数。由于我们之前的实现方式是将  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k 从右向左移动,找到 nums[i] + nums[j] + nums[k] >= target 的最小的  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k,因此我们可以得出 nums[i] + nums[j] + nums[k - 1] < target 且  
     
      
       
       
         k 
        
       
         − 
        
       
         1 
        
       
      
        k-1 
       
      
    k−1 是满足该式的最大的  
     
      
       
       
         k 
        
       
      
        k 
       
      
    k(需要注意保证  
     
      
       
       
         k 
        
       
         − 
        
       
         1 
        
       
         ≠ 
        
       
         j 
        
       
      
        k-1\ne j 
       
      
    k−1=j)。
【代码】
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        pair<int, int> res(INT_MAX, 0);
        for (int i = 0; i < nums.size(); i++)
        {
            for (int j = i + 1, k = nums.size() - 1; j < k; j++)
            {
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= target) k--;
                int s1 = nums[i] + nums[j] + nums[k], s2 = nums[i] + nums[j] + nums[k - 1];
                res = min(res, make_pair(abs(s1 - target), s1));  // 不一定会大于等于0,因此要用abs
                if (j < k - 1)  // 确保j和k-1不是一样的才能使用nums[k-1]
                    res = min(res, make_pair(target - s2, s2));  // 一定小于0
            }
        }
        return res.second;
    }
};
 
LeetCode 17. 电话号码的字母组合(中等)
【题目描述】
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。
 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

【示例1】
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
 
【示例2】
输入:digits = ""
输出:[]
 
【示例3】
输入:digits = "2"
输出:["a","b","c"]
 
【提示】
 
     
      
       
       
         0 
        
       
         ≤ 
        
       
         d 
        
       
         i 
        
       
         g 
        
       
         i 
        
       
         t 
        
       
         s 
        
       
         . 
        
       
         l 
        
       
         e 
        
       
         n 
        
       
         g 
        
       
         t 
        
       
         h 
        
       
         ≤ 
        
       
         4 
        
       
      
        0\le digits.length\le 4 
       
      
    0≤digits.length≤4
 digits[i] 是范围 ['2', '9'] 的一个数字。
【分析】
直接 DFS 爆搜即可。
【Python代码】
class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        d2str = ['', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
        res = []
    
        def dfs(digits: str, u: int, now: str):
            if u == len(digits):
                res.append(now)
                return
            for c in d2str[int(digits[u])]:
                dfs(digits, u + 1, now + c)
        dfs(digits, 0, '')
        return [] if not digits else res
 
LeetCode 18. 四数之和(中等)
【题目描述】
给你一个由 n 个整数组成的数组 nums,和一个目标值 target。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < na、b、c和d互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按任意顺序返回答案 。
【示例1】
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
 
【示例2】
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
 
【提示】
 
     
      
       
       
         1 
        
       
         ≤ 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         . 
        
       
         l 
        
       
         e 
        
       
         n 
        
       
         g 
        
       
         t 
        
       
         h 
        
       
         ≤ 
        
       
         200 
        
       
      
        1\le nums.length\le 200 
       
      
    1≤nums.length≤200
  
     
      
       
       
         − 
        
       
         1 
        
        
        
          0 
         
        
          9 
         
        
       
         ≤ 
        
       
         n 
        
       
         u 
        
       
         m 
        
       
         s 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         ≤ 
        
       
         1 
        
        
        
          0 
         
        
          9 
         
        
       
      
        -10^9\le nums[i]\le 10^9 
       
      
    −109≤nums[i]≤109
  
     
      
       
       
         − 
        
       
         1 
        
        
        
          0 
         
        
          9 
         
        
       
         ≤ 
        
       
         t 
        
       
         a 
        
       
         r 
        
       
         g 
        
       
         e 
        
       
         t 
        
       
         ≤ 
        
       
         1 
        
        
        
          0 
         
        
          9 
         
        
       
      
        -10^9\le target\le 10^9 
       
      
    −109≤target≤109
【分析】
和第15题一样,暴力枚举四个数时间复杂度为 O ( n 4 ) O(n^4) O(n4),使用双指针算法可以优化成 O ( n 3 ) O(n^3) O(n3)。需要注意本题四个数相加可能会溢出。
【代码】
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        for (int i = 0; i < nums.size(); i++)
            if (i && nums[i] == nums[i - 1]) continue;
            else for (int j = i + 1; j < nums.size(); j++)
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                else for (int k = j + 1, u = nums.size() - 1; k < u; k++)
                {
                    if (k > j + 1 && nums[k] == nums[k - 1]) continue;
                    while (k < u - 1 && (long long)nums[i] + nums[j] + nums[k] + nums[u - 1] >= target) u--;
                    if ((long long)nums[i] + nums[j] + nums[k] + nums[u] == target)
                        res.push_back({ nums[i], nums[j], nums[k], nums[u] });
                }
        return res;
    }
};
 
LeetCode 19. 删除链表的倒数第N个节点(中等)
【题目描述】
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
【示例1】

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
 
【示例2】
输入:head = [1], n = 1
输出:[]
 
【示例3】
输入:head = [1,2], n = 1
输出:[1]
 
【提示】
 
     
      
       
       
         1 
        
       
         ≤ 
        
       
         结点数目 
        
       
         ≤ 
        
       
         30 
        
       
      
        1\le 结点数目\le 30 
       
      
    1≤结点数目≤30
  
     
      
       
       
         0 
        
       
         ≤ 
        
       
         N 
        
       
         o 
        
       
         d 
        
       
         e 
        
       
         . 
        
       
         v 
        
       
         a 
        
       
         l 
        
       
         ≤ 
        
       
         100 
        
       
      
        0\le Node.val\le 100 
       
      
    0≤Node.val≤100
  
     
      
       
       
         1 
        
       
         ≤ 
        
       
         n 
        
       
         ≤ 
        
       
         结点数目 
        
       
      
        1\le n\le 结点数目 
       
      
    1≤n≤结点数目
【分析】
先扫描一遍链表,求出链表长度  
     
      
       
       
         l 
        
       
         e 
        
       
         n 
        
       
      
        len 
       
      
    len,那么我们再扫描一遍找到倒数第  
     
      
       
       
         n 
        
       
         + 
        
       
         1 
        
       
      
        n+1 
       
      
    n+1 个结点,也就是正数第  
     
      
       
       
         l 
        
       
         e 
        
       
         n 
        
       
         − 
        
       
         n 
        
       
      
        len-n 
       
      
    len−n 个结点(如果使用了虚拟头结点则为  
     
      
       
       
         l 
        
       
         e 
        
       
         n 
        
       
         − 
        
       
         1 
        
       
         − 
        
       
         n 
        
       
      
        len-1-n 
       
      
    len−1−n),将其 next 指针指向 next->next 即可。
本题还可以使用快慢指针,如下图所示,我们设置两个间隔为  
     
      
       
       
         n 
        
       
      
        n 
       
      
    n 的指针,在前面的记作 fast,后面的记作 slow,将两个指针一起移动,当 fast 的下一个结点为空时,slow 刚好走到倒数第  
     
      
       
       
         n 
        
       
         + 
        
       
         1 
        
       
      
        n+1 
       
      
    n+1 个结点:

【代码】
【遍历求长度方法】
/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(-1);  // 凡是可能影响头结点的操作都先建一个虚拟头结点
        dummy->next = head;
        int len = 0;  // 链表总长度,包括虚拟头结点
        for (ListNode* p = dummy; p; p = p->next) len++;
        // 需要找到倒数第n-1个结点,由于有虚拟头结点,也就是正数第len-n-1个结点
        ListNode* cur = dummy;
        for (int i = 0; i < len - n - 1; i++) cur = cur->next;
        cur->next = cur->next->next;
        return dummy->next;
    }
};
 
【快慢指针方法】
/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(-1);  // 凡是可能影响头结点的操作都先建一个虚拟头结点
        dummy->next = head;
        ListNode* fast = dummy;
        ListNode* slow = dummy;
        while (n--) fast = fast->next;
        while (fast->next) slow = slow->next, fast = fast->next;
        slow->next = slow->next->next;
        return dummy->next;
    }
};
 
LeetCode 20. 有效的括号(简单)
【题目描述】
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
 - 左括号必须以正确的顺序闭合。
 - 每个右括号都有一个对应的相同类型的左括号。
 
【示例1】
输入:s = "()"
输出:true
 
【示例2】
输入:s = "()[]{}"
输出:true
 
【示例3】
输入:s = "(]"
输出:false
 
【提示】
 
     
      
       
       
         1 
        
       
         ≤ 
        
       
         s 
        
       
         . 
        
       
         l 
        
       
         e 
        
       
         n 
        
       
         g 
        
       
         t 
        
       
         h 
        
       
         ≤ 
        
       
         1 
        
        
        
          0 
         
        
          4 
         
        
       
      
        1\le s.length\le 10^4 
       
      
    1≤s.length≤104
 s 仅由括号 '()[]{}' 组成
【分析】
经典括号问题,使用栈即可轻松解决,遇到左括号时入栈,遇到右括号时判断和栈顶括号是否匹配,匹配则将栈顶括号弹出。判断左右括号是否匹配可以用 ASCII 码来判断,这三对括号的左右括号相减之差最多为2。
【代码】
class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for (char c: s)
            if (c == '(' || c == '[' || c == '{') stk.push(c);
            else
                if (stk.size() && abs(stk.top() - c) < 5) stk.pop();  // 左右括号的ASCII码差值不超过2
                else return false;
        return stk.empty();  // 如果最后栈为空说明全部匹配,否则说明还有左括号为匹配
    }
};
                ![java八股文面试[多线程]——Happens-Before规则](https://img-blog.csdnimg.cn/2be4216643714b4cb722d2949349742d.png)

















