leetcodetop100题解

所有题目来源:力扣(LeetCode)
链接:

1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

  1. 解法1:暴力,双层循环,时间复杂度O(n2),空间复杂度O(1)

    class Solution {
         
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
         
            vector<int> ans;
            for(int i = 0;i<nums.size();i++){
         
                for(int j = i+1;j<nums.size();j++){
         
                    if(nums.at(i) + nums.at(j) == target) {
         
                        ans.push_back(i);
                        ans.push_back(j);
                    }
                }
            }
            return ans;
        }
    };
    
  2. 解法2:利用map是红黑树的特性,将nums中的以[值,下标]的形式存储在一个map中,再逐个检查nums所需的元素(target-nums)是否存在于map中,若存在且并非自身,即找到。

    class Solution {
         
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
         
            map<int,int> mNum;
            vector<int> vAns;
            for(int i = 0;i < nums.size();i++)
                mNum[nums.at(i)] = i;
            for(int i = 0;i < nums.size();i++){
         
                int temp = target - nums.at(i);
                if(mNum.count(temp) && mNum[temp] != i){
         
                    vAns.push_back(i);
                    vAns.push_back(mNum[temp]);
                    return vAns;
                }
            }
            return vAns;
        }
    };
    
  3. 解法3:利用unordered_map的地层是哈希表的特性,将nums中的以[值,下标]的形式存储在一个unordered_map中,再逐个检查nums所需的元素(target-nums)是否存在于unordered_map中,若存在且并非自身,即找到。(unordered_map使用的哈希表对于哈希冲突的处理是不允许冲突,从这个角度来说,unordered_map和map在使用和表现上没有任何区别,但是事实上哈希的时间会比红黑树更快,前者是O(1),后者是O(lgn))

    class Solution {
         
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
         
            unordered_map<int,int> mNum;
            vector<int> vAns;
            for(int i = 0;i < nums.size();i++)
                mNum[nums.at(i)] = i;
            for(int i = 0;i < nums.size();i++){
         
                int temp = target - nums.at(i);
                if(mNum.count(temp) && mNum[temp] != i){
         
                    vAns.push_back(i);
                    vAns.push_back(mNum[temp]);
                    return vAns;
                }
            }
            return vAns;
        }
    };
    

2. 两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

题解:顺序相加,注意进位。还有一种方式是自动为不等长的链表补齐长度计算。

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
   
	if (l1 == NULL)
		return l2;
	if (l2 == NULL)
		return l1;
	int jinwei = 0;
	ListNode* head=NULL, * cur=NULL;
	while (l1 != NULL && l2 != NULL) {
   
		int num = (l1->val + l2->val + jinwei) % 10;
		ListNode* temp = new ListNode(num);
		jinwei = (l1->val + l2->val + jinwei) / 10;
		if (head == NULL) {
   
			head = temp;
			cur = temp;
		}
		else {
   
			cur->next = temp;
			cur = temp;
		}
		l1 = l1->next;
		l2 = l2->next;
	}
	if (l1 != NULL||l2 !=NULL) {
   
		ListNode* unFinished;
		if (l1 != NULL)
			unFinished = l1;
		else
			unFinished = l2;
		while (unFinished != NULL && jinwei) {
   
			int num = (unFinished->val + jinwei) % 10;
			ListNode* temp = new ListNode(num);
			jinwei = (unFinished->val + jinwei) / 10;
			cur->next = temp;
			cur = temp;
			unFinished = unFinished->next;
		}
		if (unFinished == NULL && jinwei) {
   
			ListNode* temp = new ListNode(1);
			cur->next = temp;
		}
		else
			cur->next = unFinished;
	}
	else {
   
		if (jinwei == 1) {
   
			ListNode* temp = new ListNode(1);
			cur->next = temp;
		}
	}
	return head;
}

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

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

解法:利用滑动窗口进行求解。基本思路为如果[i,j)为无重复子串,则考虑 [i,j]是否为无重复子串,如果j与[i,j)中的J重复了,则将窗口滑动至J+1的位置,每次窗口滑动都记录是否为最大值,判断是否为重复可以采用哈希表或是红黑树。

```cpp
int lengthOfLongestSubstring(string s) {
    unordered_map<char, int> mUnRepeated;
    int iMax = 0, iLeft = 0, iCurrentMax = 0;
    for (auto i = 0; i < s.size(); i++) {
        if (mUnRepeated.count(s.at(i)) && mUnRepeated[s.at(i)] >= iLeft) {
            iLeft = mUnRepeated[s.at(i)] + 1;
        }
        iMax = max(iMax, i - iLeft + 1);
        mUnRepeated[s.at(i)] = i;
    }
    return iMax;
}
```

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

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0
示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

在不考虑时间复杂度的情况下的解法(见笑):

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
   
    int iSize = nums1.size() + nums2.size();
    double iAns = 0;
    bool isOdd = false;
    if (iSize % 2 == 0)
        isOdd = false;
    else
        isOdd = true;
    if (isOdd) {
   
        int targetIndex = iSize / 2 + 1, iIndex = 0, iIndex1 = 0, iIndex2 = 0;
        while (1){
   
            iIndex++;
            if (iIndex == targetIndex) {
   
                if (iIndex1 == nums1.size()) {
   
                    iAns = nums2.at(iIndex2);
                    break;
                }
                if (iIndex2 == nums2.size()) {
   
                    iAns = nums1.at(iIndex1);
                    break;
                }
                if (nums1.at(iIndex1) < nums2.at(iIndex2)) {
   
                    iAns = nums1.at(iIndex1);
                    break;
                }
                else{
   
                    iAns = nums2.at(iIndex2);
                    break;
                }
            }
            else {
   
                if (iIndex1 == nums1.size()) {
   
                    iIndex2++;
                    continue;
                }
                if (iIndex2 == nums2.size()) {
   
                    iIndex1++;
                    continue;
                }
                if (nums1.at(iIndex1) < nums2.at(iIndex2))
                    iIndex1++;
                else
                    iIndex2++;
            }
        }
    }
    else {
   
        int targetIndex = iSize / 2, iIndex = 0, iIndex1 = 0, iIndex2 = 0;
        while (1){
   
            iIndex++;
            if (iIndex == targetIndex) {
   
                if (iIndex1 == nums1.size()) {
   
                    iAns = nums2.at(iIndex2)+ nums2.at(iIndex2 + 1);
                    iAns /= 2;
                    break;
                }
                if (iIndex2 == nums2.size()) {
   
                    iAns = nums1.at(iIndex1) + nums1.at(iIndex1 + 1);
                    iAns /= 2;
                    break;
                }
                if (nums1.at(iIndex1) < nums2.at(iIndex2)) {
   
                    iAns += nums1.at(iIndex1);
                    iIndex1++;
                }
                else {
   
                    iAns += nums2.at(iIndex2);
                    iIndex2++;
                }
                if (iIndex1 == nums1.size()) {
   
                    iAns += nums2.at(iIndex2 );
                    iAns /= 2;
                    break;
                }
                if (iIndex2 == nums2.size()) {
   
                    iAns +=  nums1.at(iIndex1 );
                    iAns /= 2;
                    break;
                }
                if (nums1.at(iIndex1) < nums2.at(iIndex2 )) {
   
                    iAns += nums1.at(iIndex1);
                }
                else {
   
                    iAns += nums2.at(iIndex2);
                }
                iAns /= 2;
                break;
            }
            else {
   
                if (iIndex1 == nums1.size()) {
   
                    iIndex2++;
                    continue;
                }
                if (iIndex2 == nums2.size()) {
   
                    iIndex1++;
                    continue;
                }
                if (nums1.at(iIndex1) < nums2.at(iIndex2))
                    iIndex1++;
                else
                    iIndex2++;
            }
        }
    }
    return iAns;
}

5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

string longestPalindrome(string s) {
   
    int iCurrent = 0, iLeft = 0, iRight = 0, iMax = 0, iPos = 0;
    string ans;
    for (; iCurrent < s.size(); iCurrent++) {
   
        int iSLeft = 0, iSRight = 0;
        iSLeft = iSRight = iLeft = iRight = iCurrent;
        if (iRight < s.size() && iLeft >= 0){
   
            while (iRight < s.size() && iLeft >= 0 && s.at(iRight) == s.at(iCurrent)) {
   
                iSRight = iRight;
                iRight++;
            }
            while (iRight < s.size() && iLeft >= 0 &&  s.at(iLeft) == s.at(iCurrent)) {
   
                iSLeft = iLeft;
                iLeft--;
            }
            while (iRight < s.size() && iLeft >= 0 &&  s.at(iLeft) == s.at(iRight)) {
   
                iSLeft = iLeft;
                iSRight = iRight;
                iLeft--;
                iRight++;
            }
            if (iSRight - iSLeft + 1> iMax) {
   
                iMax = iSRight - iSLeft + 1;
                iPos = iSLeft;
            }
        }
    }
    return s.substr(iPos, iMax);
}

10. 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:

输入:
s = “ab”
p = “."
输出: true
解释: ".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。
示例 4:

输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:

输入:
s = “mississippi”
p = “misisp*.”
输出: false

分析:动态规划问题,因为有最优子结构(复杂的串可由前一状态推导)。分析可以知道有以下几种情况:
(1)p[j]==s[i] 此时相当于只要两者的前序正确则本项就正确,即dp[i][j]=dp[i-1][j-1]
(2)p[j]=='.' ,是相当于任何一个字符,所以和上边的情况是完全一致的。dp[i][j]=dp[i-1][j-1]
(3)p[j]=='*' ''可以表示一个或者多个前一位的字符,这意味着他必须和前一位捆绑分析。有以下几种情况:
1. p[j-1]!=s[i] 相当于是
cover了0次s[i].dp[i][j] = dp[i][j-2]
2. p[j-1]==s[i] || p[j-1]=='.'此时的情况有以下几种
(1) * cover了多次s[i] 此时dp[i][j]=dp[i-1][j](不考虑cover了几次,逐渐向前推进)
(2) * cover了一次 's[i],此时dp[i][j]=dp[i][j-1](相当于是和没有是一样的) (3) '*' cover了0次s[i],此时dp[i][j]=dp[i][j-2](相当于是两个符号都没用,这种情况可能出现在类似aa*`这样的组合当中)

bool isMatch(string s, string p) {
   
	vector<vector<bool>> dp(s.size() + 1, vector<bool>(p.size() + 1, false));
	dp[0][0] = true;
	for (int i = 0; i <= s.size(); i++) {
   
		for (int j = 1; j <= p.size(); j++) {
   
			if (p[j-1] == '*') {
   
				dp[i][j] = dp[i][j - 2] || (i && dp[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'));
			}
			else {
   
				dp[i][j] = i && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
			}
		}
	}
	return dp[s.size()][p.size()];
}

11. 盛最多水的容器

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

说明:你不能倾斜容器,且 n 的值至少为 2。

示例:

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

解法1:暴力法。通过双重循环逐项取值,计算面积值并进行比对,保留最大面积。时间复杂度O(n2)。事实上会超时。

int maxArea(vector<int>& height) {
   
    int iMaxArea = 0;
    for (auto i = 0; i < height.size(); i++) {
   
        for (auto j = i + 1; j < height.size(); j++) {
   
            int iHeight = min(height.at(i), height.at(j));
            if (iHeight*(j - i) > iMaxArea)
                iMaxArea = iHeight * (j - i);
        }
    }
    return iMaxArea;
}

解法2:双指针法。这种解法利用的是本题的特性,即在左右两边中,决定水桶盛水量的是较短的一边。因此,可以考虑如下情况,左右两边左高右低,此时要将一边向内移动,只能选择短的一边,因为即使长的变得更长,也只能带来面积的减少。时间复杂度O(n)。

int maxArea(vector<int>& height) {
   
    int iMaxArea = 0, iLeft = 0, iRight = height.<
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值