LeetCode Week 2:第 11 ~ 20 题

专栏——LeetCode

推荐文章

  • LeetCode Week 1:第 1 ~ 10 题
  • LeetCode Week 2:第 11 ~ 20 题

————————————————————————————————————————————————————

文章目录

    • 专栏——LeetCode
    • 推荐文章
    • 11. 盛最多水的容器
    • 12. 整数转罗马数字
    • 13. 罗马数字转整数
    • 14. 最长公共前缀
    • 15. 三数之和
    • 16. 最接近的三数之和
    • 17. 电话号码的字母组合
    • 18. 四数之和
    • 19. 删除链表的倒数第N个节点
    • 20. 有效的括号

11. 盛最多水的容器

题目描述

给你 n 个非负整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1a2...an,每个数代表坐标中的一个点 ( i , a i ) (i, a_i) (i,ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 ( i , a i ) 和 ( i , 0 ) (i, a_i) 和 (i, 0) (i,ai)(i,0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明: 你不能倾斜容器,且 n 的值至少为 2。
LeetCode Week 2:第 11 ~ 20 题_第1张图片

示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49

题解:
(指针扫描)O(n)
在初始时,左右指针分别指向数组的左右两端,此时我们需要移动一个指针。移动哪一个呢?
直觉告诉我们,应该移动对应数字较小的那个指针(即此时的左指针)。这是因为,由于容纳的水量是由两个指针指向的数字中较小值*指针之间的距离
决定的。如果我们移动数字较大的那个指针,那么「两个指针指向的数字中较小值」不会增加,「指针之间的距离」会减小,那么这个乘积会减小。
因此,我们移动数字较大的那个指针是不合理的。因此,我们移动 数字较小的那个指针。
然后不断计算两个指针指向的数字中较小值*指针之间的距离,去max即可

c++版

class Solution {
public:
    int maxArea(vector<int>& height) {
        int ans = 0;
        for(int i = 0, j = height.size() - 1; i != j;){
            ans = max(ans, min(height[i], height[j]) * (j - i));
            if(height[i] > height[j])j--;
            else i++;
        }
        return ans;
    }
};

python版

class Solution:
    def maxArea(self, height: List[int]) -> int:
        ans = 0
        i, j = 0, len(height) - 1
        while(i != j):
            ans = max(ans, min(height[i], height[j]) * (j - i))
            if(height[i] > height[j]):
                j -= 1
            else:
                i += 1
        return ans;

12. 整数转罗马数字

题目描述

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。

示例 1:

输入: 3
输出: "III"

示例 2:

输入: 4
输出: "IV"

示例 3:

输入: 9
输出: "IX"

示例 4:

输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

示例 5:

输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

题解:
我们需要先了解罗马数字的计数方法:

基本字符		I	V	X	L	C	D	M
阿拉伯数字	1	5	10	50	100	500	1000

1.相同的数字连写,所表示的数等于这些数字相加得到的数,如:III=3;
2.小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如:VIII=8, XII=12;
3.小的数字在大的数字的左边(限于 IV、IX、XL、XC、CD和CM),所表示的数等于大数减小数得到的数,如:IV=4, IX=9;
4.正常使用时,连写的数字重复不得超过三次;
我们可以将所有减法操作看做一个整体,当成一种新的单位。从大到小整理所有单位得到:

M	  CM   D    CD   C    XC  L  XL	 X	 IX  V	IV I
1000  900  500  400  100  90  50  40  10  9  5  4  1

此时我们可以将目标整数看成这些单位值的加和,且同一种单位不能使用超过3次。
所以我们尽可能优先使用值较大的单位即可。
时间复杂度分析:计算量与最终罗马数字的长度成正比,对于每一位阿拉伯数字,罗马数字最多用4个字母表示(比如VIII=8),
所以罗马数字的长度和阿拉伯数字的长度是一个数量级的,而阿拉伯数字的长度是 O(logn),因此时间复杂度是 O(logn)。

c++版

class Solution {
public:
    string intToRoman(int num) {
        int value[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string reps[] = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};

        string ans = "";
        for(int i = 0; i < 13; i++){
            while(num >= value[i]){
                num -= value[i];
                ans += reps[i];
            }
        }
        return ans;
    }
};

python版

class Solution:
    def intToRoman(self, num: int) -> str:
        value = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
        reps = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
        ans = ""
        for i in range(13):
            while num >= value[i]:
                num -= value[i]
                ans += reps[i]
        return ans

13. 罗马数字转整数

题目描述

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值

I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

示例 1:

输入: "III"
输出: 3

示例 2:

输入: "IV"
输出: 4

示例 3:

输入: "IX"
输出: 9

示例 4:

输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

题解:
1.定义映射,将单一字母映射到数字。
2.从前往后扫描,如果发现 s[i+1] 的数字比 s[i] 的字大,那么累计 s[i+1]-s[i] 差值即可,
并将 i 多向后移动一位;否则直接累计 s[i] 的值。
时间复杂度
仅遍历一次整个字符串,故时间复杂度为 O(n)。

c++版

class Solution {
public:
    int romanToInt(string s) {
        unordered_map<char, int> mp;
        mp['I'] = 1,mp['V'] = 5,mp['X'] = 10;
        mp['L'] = 50,mp['C'] = 100,mp['D'] = 500;
        mp['M'] = 1000;
        int ans = 0;
        int n = s.size();
        for(int i = 0; i < n; i++){
            if(i != n - 1 && mp[s[i + 1]] > mp[s[i]]){
                ans += (mp[s[i + 1]] - mp[s[i]]);
                i++;
            }
            else
                ans += mp[s[i]];
        }
        return ans;
    }
};

python版

class Solution:
    def romanToInt(self, s: str) -> int:
        mp = {}
        mp['I'], mp['V'], mp['X'] = 1, 5, 10
        mp['L'], mp['C'], mp['D'] = 50, 100, 500
        mp['M'] = 1000
        ans, i = 0, 0
        n = len(s)
        while(i < n):
            if i != n - 1 and mp[s[i + 1]] > mp[s[i]]:
                ans += (mp[s[i + 1]] - mp[s[i]])
                i += 1
            else:
                ans += mp[s[i]]
            i += 1
        return ans

14. 最长公共前缀

题目描述

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

说明:

所有输入只包含小写字母 a-z 。

算法
(暴力枚举) O(nm)
1.暴力枚举方法很简单:首先得到第一个字符串的长度 m,然后从长度 1 到 m 依次枚举判断是否所有字符串的前缀是否都相等。
2.注意输入可能为空数组。

c++版

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        string ans = "";

        if(strs.size() == 0) return ans;
        int n = strs[0].size();
        if(n == 0) return ans;

        for(int i = 0; i < n; i++){
            for(auto str : strs)
                if(str.size() <= i || str[i] != strs[0][i])
                    return ans;
            ans += strs[0][i];
        }
        return ans;
    }
};

python版

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        ans = ""
        if len(strs) == 0:
            return ans
        n = len(strs[0])
        if(n == 0):
            return ans
        for i in range(n):
            for str in strs:
                if len(str) <= i or str[i] != strs[0][i]:
                    return  ans
            ans += strs[0][i]
        return ans

15. 三数之和

题目描述

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

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

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

题解:
排序 + 双指针 O ( n 2 ) O(n^2) O(n2)
先把数组从小到大排序
确定三数中的第一个数a[i],然后用两个指针,分别指向这个数后面的序列的头和尾。
由于已经排好序了,如果和比0大就把右指针向左移动,如果和比0小就把左指针向右移动一位
注意:碰到重复元素需要跳过,避免重复计数

c++版

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        int n = nums.size();
        for(int i = 0; i < n; i++){
            if(i && nums[i] == nums[i - 1])continue;
            for(int j = i + 1, k = n - 1; j < k; j++){
                if(j > i + 1 && nums[j] == nums[j - 1])continue;
                while(j < k - 1 && nums[i] + nums[j] + nums[k] > 0) k--;
                if(nums[i] + nums[j] + nums[k] == 0)
                    ans.push_back({nums[i], nums[j], nums[k]});
            }
        }
        return ans;
    }
};

python版

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        l = []
        n = len(nums)
        for i in range(n):
            if i and nums[i] == nums[i - 1]:
                continue
            k = n - 1
            for j in range(i + 1, n):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                while j < k and nums[i] + nums[j] + nums[k] > 0:
                    k -= 1
                if j == k:
                    break
                if nums[i] + nums[j] + nums[k] == 0:
                    l.append([nums[i], nums[j], nums[k]])
        return l

16. 最接近的三数之和

题目描述

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

题解:
排序 + 双指针
本题目因为要计算三个数,如果靠暴力枚举的话时间复杂度会到 O(n^3)需要降低时间复杂度
首先进行数组排序,时间复杂度 O(nlogn)
在数组 nums 中,进行遍历,每遍历一个值利用其下标i,形成一个固定值 nums[i]
再使用前指针指向 start = i + 1 处,后指针指向 end = nums.length - 1 处,也就是结尾处
根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans
同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end–,
如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果
整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O(n^2)
总时间复杂度:O(nlogn) + O(n^2) = O(n^2)

c++版

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int ans = 1e9;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int i  = 0; i < n; i++){
            if(i != 0 && nums[i] == nums[i - 1]) //去掉重复元素
                continue;
            int j = i + 1;
            int k = n - 1;
            while(j < k){
                int sum = nums[i] + nums[j] + nums[k];
                if(sum == target)
                    return sum;
                if(abs(sum - target) < abs(ans - target))
                    ans = sum;
                if(sum > target)k--;
                if(sum < target)j++;
            }
        }
        return ans;
    }
};

python版

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        ans = 1e4
        nums.sort()
        n = len(nums)
        for i in range(n):
            if i != 0 and nums[i] == nums[i - 1]:
                continue
            j = i + 1
            k = n - 1
            while j < k:
                num = nums[i] + nums[j] + nums[k]

                if num == target:
                    return num
                if abs(num - target) < abs(ans - target):
                    ans = num
                if num > target:
                    k -= 1
                if num < target:
                    j += 1
        return ans

17. 电话号码的字母组合

题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

LeetCode Week 2:第 11 ~ 20 题_第2张图片

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

题解
(递归) O ( 4 l ) O(4^l) O(4l)
可以通过手工或者循环的方式预处理每个数字可以代表哪些字母。
通过递归尝试拼接一个新字母。
递归到目标长度,将当前字母串加入到答案中。
注意,有可能数字串是空串,需要特判。

c++版

class Solution {
public:
    string mp[10] = {"","","abc","def",
                   "ghi","jkl","mno",
                   "pqrs","tuv","wxyz"};
    vector<string> ans;

    vector<string> letterCombinations(string digits) {
        if(digits.empty()) return ans;
        dfs(digits, 0, "");
        return ans;
    }
    void dfs(string digits, int u, string path){
        if(u == digits.size()) ans.push_back(path);
        else
            for(auto c : mp[digits[u] - '0'])
                dfs(digits, u + 1, path + c);
    }
};

python版

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:

        mp = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
        ans = []

        def dfs(strs, u, path):
            if u == len(strs):
                ans.append(path)
                return 
            index  = int(strs[u])
            for c in mp[index]:
                dfs(strs,u + 1, path + c)
        
        if len(digits) == 0:
            return ans
        dfs(digits, 0, "")

        return ans

18. 四数之和

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

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

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
 [-1,  0, 0, 1],
 [-2, -1, 1, 2],
 [-2,  0, 0, 2]
]

题解
排序+双指针
跟15题的解题思路一样,只不过这里要多循环一层

c++版

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int i = 0; i < n - 3; i++){
            if(i != 0 && nums[i] == nums[i - 1]) continue; //去掉重复元素
            for(int j = i + 1; j < n - 2; j++){
                if(j != i + 1 && nums[j] == nums[j - 1]) continue;//去掉重复元素
                int k = j + 1;
                int u = n - 1;
                while(k < u){
                    int num = nums[i] + nums[j] + nums[k] + nums[u];
                    if(num > target){
                        while(k < u && nums[u] == nums[u - 1])u--;//去掉重复元素
                        u--;
                    }
                    else if(num < target) {
                        while(k < u && nums[k] == nums[k + 1])k++;//去掉重复元素
                        k++;
                    }
                    else{
                        ans.push_back({nums[i],nums[j],nums[k],nums[u]});
                        while(k < u && nums[u] == nums[u - 1])u--;//去掉重复元素
                        while(k < u && nums[k] == nums[k + 1])k++;//去掉重复元素
                        k++;
                        u--;
                    }
                }
            }
        }
        return ans;
    }
};

python版

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        ans = []
        nums.sort()
        n = len(nums)
        for i in range(n - 3):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, n - 2):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                k = j + 1
                u = n - 1
                while(k < u):
                    num = nums[i] + nums[j] + nums[k] + nums[u]
                    if num > target:
                        while(k < u and nums[u] == nums[u - 1]):
                            u -= 1
                        u -= 1
                    elif num < target:
                        while(k < u and nums[k] == nums[k + 1]):
                            k += 1
                        k += 1
                    else:
                        ans.append([nums[i],nums[j],nums[k],nums[u]])
                        while(k < u and nums[k] == nums[k + 1]):
                            k += 1
                        while(k < u and nums[u] == nums[u - 1]):
                            u -= 1
                        k += 1
                        u -= 1
        return ans

19. 删除链表的倒数第N个节点

题目描述

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:
给定的 n 保证是有效的。

进阶:
你能尝试使用一趟扫描实现吗?

题解
第一种做法:首先循环一遍得到长度L,知道长度后再循环一次得到答案
第二种做法:我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。
现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。
此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。

c++版

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* a = new ListNode(-1);
        a->next = head;
        ListNode* p = a;
        ListNode* q = a;
        for(int i = 0; i <= n; i++)
            p = p->next;
        while(p != NULL){
            q = q->next;
            p = p->next;
        }
        q->next = q->next->next;
        return a->next;
    }
};

python版

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        a = ListNode(-1)
        a.next = head
        p = a
        q = a
        for i in range(0, n + 1):
            p = p.next
        while(p):
            p = p.next
            q = q.next
        q.next = q.next.next
        return a.next

20. 有效的括号

题目描述

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true

示例 2:

输入: "()[]{}"
输出: true

示例 3:

输入: "(]"
输出: false

示例 4:

输入: "([)]"
输出: false

示例 5:

输入: "{[]}"
输出: true

题解:
栈的应用,遍历字符串,如果当前字符为括号的左半边’(’, ‘[’, ‘{‘就加入到队列中去;
如果当前字符为括号的右半边’)’, ‘]’, ‘}’,则判断是否与栈中的字符匹配,如果匹配的话就出栈。
最后判断栈是否为空即可

c++版

class Solution {
public:
    bool isValid(string s) {
        char a[10010];
        int t = 0;
        for(int i = 0; i < s.size(); i++){
            if( t > 0 && s[i] == '}' && a[t] == '{')
                t--;
            else if( t > 0 && s[i] == ']' && a[t] == '[')
                t--;
            else if( t > 0 && s[i] == ')' && a[t] == '(')
                t--;
            else a[++t] = s[i];
        }
        if(t == 0) return true;
        else return false;
    }
};

python版

class Solution:
    def isValid(self, s: str) -> bool:
        stack = [""]
        t = 0
        for c in s:
            if t > 0 and c == ')' and stack[t] == '(':
                t -= 1
                stack.pop()
            elif t > 0 and c == '}' and stack[t] == '{':
                t -= 1
                stack.pop()
            elif t > 0 and c == ']' and stack[t] == '[':
                t -= 1
                stack.pop()
            else:
                t += 1
                stack.append(c)
        return True if t == 0 else False

你可能感兴趣的:(LeetCode,算法,leetcode)