Leetcode刷题:中级算法之数组

3sum

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

The solution set must not contain duplicate triplets.

Example:

Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

思路1:
(a + b + c) = 0 ⇒ -(a + b) = c
步骤

  • 先把nums遍历一遍存到字典里面,记录它出现的次数 ⇒ 时间复杂度:O(n)
  • 以 o(n^2) 的复杂度找到所有 (a, b) 对,然后在字典里找有没有-(a + b) ,且要防止重复 ⇒ 时间复杂度O(n ^ 2)
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        dic = {}
        for num in nums:
            if num in dic:
                dic[num] += 1
            else:
                dic[num] = 1
        
        ans = []
        visited = set()
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                a, b, c = order(nums[i], nums[j], -(nums[i]+nums[j]))
                if (a, b) in visited:
                    continue
                else:
                    visited.add((a, b))
                
                dic[nums[i]] -= 1
                dic[nums[j]] -= 1
        
                if (-(nums[i]+nums[j])) in dic and dic[(-(nums[i]+nums[j]))] > 0:
                    ans.append([a, b, c])
                
                dic[nums[i]] += 1
                dic[nums[j]] += 1
                
        return ans
                    
def order(x, y, z):
    arr = [x, y, z]
    arr.sort()
    return arr[0], arr[1], arr[2]

然鹅杯具出现了,这种方法并不能100% accepted by leetcode

由此引到了最优方法:

最优方法的参考文档

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        ans = []
        for i in range(len(nums)):
            if (i != 0 and nums[i] == nums[i-1]):
                continue
            k = len(nums) - 1
            j = i + 1
            while (j < k):
                if (nums[i] + nums[j] + nums[k]) == 0:
                    ans.append([nums[i], nums[j], nums[k]])
                    j += 1
                    while (j < k and nums[j] == nums[j-1]):
                        j += 1
                elif (nums[i] + nums[j] + nums[k]) < 0:
                    j += 1
                else:
                    k -= 1

        return ans

其思路就是利用3个指针,而且要将数据排号序,时间复杂度是O(n^2)
为什么更优?

  • 防止重复的步骤精简了好多
  • 利用了有序数组的性质

3sum相关的题目

2sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

其思路就和3sum中的第一个naive思路比较像

python
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        dic = {}
        for i in range(len(nums)):
            if target - nums[i] in dic:
                return [dic[target - nums[i]], i]
            else:
                dic[nums[i]] = i
3sum closet

Given an array nums of n integers and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

Example:

Given array nums = [-1, 2, 1, -4], and target = 1.

The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

这道题目比3sum更简单一点,因为它只需要返回最接近target的值
基本思路比较像 都是利用了三个指针
时间复杂度是 O(n^2)

class Solution(object):
    def threeSumClosest(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        nums.sort()
        minVal = 2**31 - 1
        
        for i in range(len(nums)):
            j = i + 1
            k = len(nums) - 1
            while (j < k):
                val = nums[i] + nums[k] + nums[j] - target
                if (val == 0):
                    minVal = val
                    break
                    
                if abs(val) < abs(minVal):
                    minVal = val
                    
                if val > 0:
                    k -= 1
                else:
                    j += 1
        return (minVal + target)

Group Anagrams

Given an array of strings, group anagrams together.

Example:

Input: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
Output:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
Note:

All inputs will be in lowercase.
The order of your output does not matter.

class Solution(object):
    def groupAnagrams(self, strs):
        # letterMatrix
        # record the occuring times of letter from a to z
        # [0, 0, .... , 0, 0]
        # [0, 0, .... , 0, 1]
        # [0, 0, .... , 0, 2]
        # .....
        # [0, 0, .... , 0, n]
        letterMatrix = []
        for i in range(len(strs)):
            letterMatrix.append([0] * 26 + [i])

        for i in range(len(strs)):
            for ch in strs[i]:
                letterMatrix[i][ord(ch) - ord('a')] += 1

        letterMatrix.sort() # 排序

        ans = []
        ans.append([strs[letterMatrix[0][-1]]])

        for i in range(1, len(letterMatrix)):
            curStrIdx = letterMatrix[i][-1]
            if equal(letterMatrix[i], letterMatrix[i - 1]): # 如果这两个str包含的letter和个数都相同,它们归为一组
                ans[-1].append(strs[curStrIdx])
            else: # 否则分到新的一组
                ans.append([strs[curStrIdx]])

        return ans

                
def equal(arr1, arr2):
    for i in range(25, 0, -1):
        if arr1[i] != arr2[i]:
            return False
    return True

上面的解法时间复杂度是O(NK + NlogN),N, K分别是strs的元素个数和strs里面最长的字符串长度,但是可以优化成O(NK),方法就是利用字典来存字母出现的个数,而不是用列表。

class Solution(object):
    def groupAnagrams(self, strs):
        ans = []
        dic = {}
        for str in strs:
            chars = [0] * 26
            for ch in str:
                chars[ord(ch) - ord('a')] += 1
                
            chars = tuple(chars)
            
            if chars in dic: # 如果str的anagram已经被访问过,那么这个str将会和之前的分到一组
                ans[dic[chars]].append(str)
            else: # 否则它自己新成立一组
                dic[chars] = len(ans)
                ans.append([str])
        return ans

Set Matrix Zeroes

Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place.

Example 1:

Input:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
Output:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
Example 2:

Input:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
Output:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
Follow up:

A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

class Solution(object):
    def setZeroes(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        Questions:
        - Are there more than one element equals 0?
        - What are the requirements for space complexity?
        - What if the matrix is Null?
        - data type? always int?
        """
        
        m = len(matrix)
        n = len(matrix[0])
        rows = [False] * m
        cols = [False] * n
        
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    rows[i] = True
                    cols[j] = True
        
        for i in range(len(rows)):
            if rows[i]:
                for j in range(n):
                    matrix[i][j] = 0
                    
        for j in range(len(cols)):
            if cols[j]:
                for i in range(m):
                    matrix[i][j] = 0

空间复杂度为O(m + n),空间复杂度的优化方法我没想出来,参见leetcode上的原题答,这个方法有点巧妙。

class Solution(object):
    def setZeroes(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        Questions:
        - Are there more than one element equals 0?
        - What are the requirements for space complexity?
        - What if the matrix is Null?
        - data type? always int?
        """
        m = len(matrix)
        n = len(matrix[0])
        row_flag = False
        col_flag = False
        print(matrix)
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0  # 这一行全是0
                    matrix[0][j] = 0  # 这一列全是0
                    if (i == 0):
                        row_flag = True
                    if (j == 0):
                        col_flag = True


        for i in range(1, m):
            if matrix[i][0] == 0:
                matrix[i] = [0] * n

        for j in range(1, n):
            if matrix[0][j] == 0:
                for i in range(m):
                    matrix[i][j] = 0
        #print(matrix)
        if row_flag:
            matrix[0] = [0] * n


        if col_flag:
            for i in range(m):
                matrix[i][0] = 0
        #print(matrix)

Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.
Example 2:

Input: “bbbbb”
Output: 1
Explanation: The answer is “b”, with the length of 1.
Example 3:

Input: “pwwkew”
Output: 3
Explanation: The answer is “wke”, with the length of 3.
Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        Questions:
        - all lowercase letters? if upper, how to compare?
        - if the lest is none?
        
        """
        if not s:
            return 0
        
        dic = {}
        #for char in "abcdefghijklmnopqrstuvwxyz":
            #dic[char] = -1
        
        # 初始化
        left = 0
        dic[s[0]] = 0
        right = 1
        maxInterval = 1
        while (right < len(s)):
            if s[right] in dic:
                if dic[s[right]] == -1: # 如果当前字母不重复,用字典记录此字母出现的位置
                    dic[s[right]] = right
                else: # 如果当前字母重复
                    if right - left > maxInterval:# 判断目前已经visit的这个区间是不是更大
                        maxInterval = right - left

                    tmp = dic[s[right]]
                    for i in range(left, dic[s[right]] + 1): # 从[left, s[right]重复出现的位置]都要舍去
                        dic[s[i]] = -1 

                    left = tmp + 1 # left移动到重复的字母后一位
                    dic[s[right]] = right # 字典更新重复的字母的位置
            else:
                dic[s[right]] = right
            right += 1
            
        if right - left > maxInterval:
            maxInterval = right - left
        return maxInterval

Missing Ranges

Given a sorted integer array nums, where the range of elements are in the inclusive range [lower, upper], return its missing ranges.

Example:

Input: nums = [0, 1, 3, 50, 75], lower = 0 and upper = 99,
Output: [“2”, “4->49”, “51->74”, “76->99”]

这个题目乍一看挺简单的,但是题目里面是有坑的
在写代码前需要考虑:

  • 如果nums为空怎么办?此时就直接输出[lower, upper]
  • 如果给的lower和upper是unvalid怎么办? 比如, [1,2,3] upper = 2, lower = 2;在leetcode里面这题目是没有这种unvalid输入的;但是在思考问题的时候要全面一点
class Solution(object):
    def findMissingRanges(self, nums, lower, upper):
        ans = []
        try:
            for i in range(len(nums)):
                if nums[i] > lower:
                    ans.append([lower, nums[i]-1])
                lower = nums[i] + 1

            if nums[len(nums)-1] < upper:
                ans.append([nums[len(nums)-1] + 1, upper])
        except IndexError: # nums为空的情况怎么办
            ans.append([lower, upper])
        
        # 虽然分了两种情况,但是对字符串数组的输出做统一处理
        output = []
        for a in ans:
            if a[0] == a[1]:
                output.append(str(a[0]))
            else:
                output.append(str(a[0]) + "->" + str(a[1]))
        return output

Longest Palindromic Substring

Solution
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example 2:

Input: “cbbd”
Output: “bb”

一开始的理解:
“abcda”
Output:
“aba”
Expected:
“a”
并不是的 和CSCI 570老师上课讲的完全不是一个题目 但我还是努力撸出了代码!

class Solution(object):
    """
    Question:
    - 如果没有回文呢?
    - s为空呢
    """
    def longestPalindrome(self, s):
        if not s:
            return ""
        
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        
        # 初始化
        for i in range(n):
            dp[i][i] = 1
            
        for j in range(1, n):
            i = j - 1
            if (s[j] == s[i]):
                dp[i][j] = 2
            else:
                dp[i][j] = 1
        
        # 动态规划 Bottom up
        for k in range(2, n):
            for j in range(k, n):
                i = j - k
                if s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1])
                    
        #return dp[0][n-1]
        # top down
        i = 0
        j = n - 1
        left = 0
        right = dp[0][n-1]-1
        substring = [''] * (dp[0][n-1])
        while (i <= j):
            if (s[i] == s[j]):
                substring[left] = s[i]
                substring[right] = s[j]
                left += 1
                right -= 1
                i += 1
                j -= 1
            else:
                if dp[i+1][j] > dp[i][j-1]:
                    i += 1
                else:
                    j -= 1
            
        
        return "".join(substring
                            
max = lambda x, y: x if x > y else y

当我满心欢喜的又编了一个分治法的时候,时间又超了,因为你这个时间复杂度其实是O(n^3)呀傻孩子:

class Solution(object):
    def longestPalindrome(self, s):
        if not s:
            return ""
        
        self.s = s
        return self.dc(0, len(s)-1)
        
    def dc(self, left, right):
        if (left == right):
            return self.s[left]
        
        mid = (left + right) / 2
        leftStr = self.dc(left, mid)
        rightStr = self.dc(mid+1, right)
        
        midStr = ""
        for i in range(left, mid + 1):
            for j in range(right, mid, -1):
                if self.isPalindrome(i, j):
                    midStr = self.s[i:j+1]
                    break
            if (midStr != ""):
                break
                    
        return max(max(leftStr, rightStr), midStr)
                
    def isPalindrome(self, lower, upper):
        for i in range((upper-lower)/2+1):
            if self.s[lower+i] != self.s[upper-i]:
                return False
        return True                    
max = lambda s1, s2: s1 if len(s1) > len(s2) else s2

最后还是动态规划方法靠谱

class Solution(object):
    def longestPalindrome(self, s):
        n = len(s)
        if not s:
            return ""
        
        # 初始化
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True
        for i in range(n-1):
            j = i + 1
            if s[i] == s[j]:
                dp[i][j] = True
                
        # 状态转换函数:
        # dp[i][j] = dp[i+1][j-1] == True && s[i] == s[j]       
        for k in range(2, n):
            for i in range(n-k):
                j = i + k
                if dp[i+1][j-1] == True and s[i] == s[j]:
                    dp[i][j] = True
        
        max_j = 0
        max_i = 0
        for i in range(n):
            for j in range(n-1, i, -1):
                if dp[i][j] and (j-i) > (max_j-max_i):
                    max_j = j
                    max_i = i
        return s[max_i:max_j+1]

Increasing Triplet Subsequence

Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.

Formally the function should:

Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
Note: Your algorithm should run in O(n) time complexity and O(1) space complexity.

Example 1:

Input: [1,2,3,4,5]
Output: true
Example 2:

Input: [5,4,3,2,1]
Output: false

太神奇了,这个解法
这个解法为什么可以work呢?

  • min1之前(包括它本身)一定存在一个比他更小或者相等的数
  • min2之前一定存在一个比他小的数
  • 那么当后面的数既大于min2, 且min2前面有比他小的数,不就是返回true么?
class Solution(object):
    def increasingTriplet(self, nums):
        min1 = 2**31 - 1
        min2 = 2**31 - 1
        for num in nums:
            if num < min1:
                min1 = num
            if num > min1:
                min2 = min(num, min2)
            if num > min2:
                return True
        
min = lambda x, y: x if x < y else y
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值