LeetCode基本记录【1】// BASIC NOTES AND CODES OF LEETCODE [ 1 ]
LeetCode基本记录【1】
鉴于时间紧迫,只能简单记录,并且每次十道题为一篇,仅记录代码和必要的说明。后续再整理。
Leetcode 1 contains the following problems and their solutions
- Add Two Numbers
- Longest Substring Without Repeating Characters
- Longest Palindromic Substring
- ZigZag Conversion
- String to Integer (atoi)
- Container With Most Water
- Integer to Roman
- 3Sum
- 3Sum Closest
- Letter Combinations of a Phone Number
2. Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
# 链表储存的十进制数相加,主要考虑停止的位置,即判断while的条件,只要有其中一个有val或者有carry存在,就应该继续。
# 以及,listnode的形式如果进循环体迭代的话,需要先维护一个dummy变量指向头结点用于return
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
carry = 0
dummy = summ = ListNode(0)
while l1 or l2 or carry :
v1 = v2 = 0 # 这样即便有个list已经到头,也可以当做加0计算。
if l1 :
v1 = l1.val
l1 = l1.next
if l2 :
v2 = l2.val
l2 = l2.next
summ.next = ListNode((v1 + v2 + carry) % 10)
carry = (v1 + v2 + carry) // 10
summ = summ.next
return dummy.next
3. Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given “abcabcbb”, the answer is “abc”, which the length is 3.
Given “bbbbb”, the answer is “b”, with the length of 1.
Given “pwwkew”, 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.
# 注意比较start和hashmap中s[i]的大小关系,因为我们的substring是从start开始的,因此小鱼start的不需要退回去,否则会出错,比如:
# a b c b a m 这个串,start = 0,然后 abc,再到了b,此时start修改为2,就是c所指的位置,然后到a,此时由于hashmap中的a的位置在0处,小于start
# 所以不修改,如果修改,那么start=1,指向第一个b,由于后面没有重复了,所以start在1处不会变,而这个start开始会有两个以前的重复字母,所以出错
# 此处需要注意。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
hashmap = {}
maxlen = 0
start = 0
for i in range(len(s)) :
if hashmap.has_key(s[i]) and hashmap[s[i]] >= start :
start = hashmap[s[i]] + 1
hashmap[s[i]] = i
maxlen = max( maxlen, i - start + 1 )
return maxlen
The reason is that if s[j] have a duplicate in the range [i,j) with index j’, we don’t need to increase i little by little. We can skip all the elements in the range [i,j′] and let i to be j′+1 directly.
5. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example:
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example:
Input: “cbbd”
Output: “bb”
# define 一个从中心扩张的函数来计算以某点为中心的palindrome的长度,可以指定left和right分别为i,i或者i,i+1,其中前者表示以某点i为中心,向
# 两边扩展,而后者表示以i和i+1的中点为中心,得到的是一个even的palindrome。另外,需要注意在扩展的时候不要超过边界。另外,注意边界参数的控制。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
maxi = 0
max_len = 1
for i in range(len(s)-1):
len_odd = self.ExpandCenter(s, i, i)
len_even = self.ExpandCenter(s, i, i+1)
thismaxlen = max(len_odd, len_even)
max_len = max_len if thismaxlen < max_len else thismaxlen
maxi = maxi if thismaxlen < max_len else i
start = (maxi - max_len // 2) if (max_len % 2 == 1) else (maxi - max_len // 2 + 1)
end = maxi + max_len // 2
return s[start:end+1]
def ExpandCenter(self, s, left, right):
if not s[left] == s[right]:
return 0
else:
while(1) :
if left < 0 or right > len(s) - 1 :
break
elif not s[left] == s[right]:
break
else:
left -= 1
right += 1
return right - left - 1
6. ZigZag Conversion
The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: “PAHNAPLSIIGYIR”
Write the code that will take a string and make this conversion given a number of rows:
string convert(string text, int nRows);
convert(“PAYPALISHIRING”, 3) should return “PAHNAPLSIIGYIR”.
# 首先要观察规律,找到周期性中的相关参数,以及对于一个周期中的各个点所在的行数,要维护一个dict或者list,使得每个位置的元素来了都可以
# 直接接续到它所属的某一行。最后合并。
# 注意掌握python中enumerate的用法,可以书写方便。另外用list comprehension生成list之前整理过,要经常使用,使得代码简洁。
# ''.join()可以将list中的string连接起来
class Solution(object):
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if numRows == 1:
return s
rows = ['' for i in range(numRows)]
period = 2 * (numRows -1)
# place of order within one period
place = {}
for pl in range(period):
if pl < numRows:
place[pl] = pl
else:
place[pl] = period - pl
for idx, item in enumerate(s):
rows[place[idx%period]] += item
return ''.join(rows)
8. String to Integer (atoi)
Implement atoi to convert a string to an integer.
Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.
Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.
Requirements for atoi:
The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value.
The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.
If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed.
If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned.
# 既然要实现atoi,就不应该用int函数来转换,这里使用了dict,并用了strip函数处理空格,较为简单。
class Solution(object):
def myAtoi(self, st):
"""
:type str: st
:rtype: int
"""
numdict = {'1':1, '2':2, '3': 3, '4': 4, '5':5, '6':6, '7':7, '8':8, '9':9, '0':0 }
INT_MAX = 2147483647
INT_MIN = -2147483648
st = st.strip()
if st == '':
return 0
sign = 1
number = 0
for idx, char in enumerate(st):
if char == '+' and idx == 0:
sign = 1
elif char == '-' and idx == 0:
sign = -1
elif not numdict.has_key(char):
break
else:
number = numdict[char] + 10 * number
if number*sign > INT_MAX or number*sign < INT_MIN:
return INT_MAX if number*sign > 0 else INT_MIN
return number*sign
11. Container With Most Water
Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
此题目有一个O(n)的算法,实现简单,但是说明其有效性有一定难度,因此需要特别在此记录一下:
这个题目实际上就是一个求矩形最大面积的问题,这个矩形的高是min(h(left),h(right)),而它的宽度是(right - left + 1)。这个算法的基本过程很简单:从数组的头和尾两个方向向着里面移动,每次都是现在的两个指针指向的两个数中较小的那个移动,并且记录下每次的面积用来更新maxarea,till两者相遇。
说明这个算法是正确的,实际上只需要说明,在这样的O(n)的遍历过程中,一定同时遍历到过那么最大面积的left_opt
和right_opt
,也就是说,在这一移动过程中,area = min(h(left_opt),h(right_opt))*(right-left+1)
被用来更新过maxarea,因此最后的maxarea一定和它相等。
首先,考虑生成这个maxarea的left_opt
和right_opt
具有怎样的性质。由于它们组成了最大的面积,那么显然:
- right_opt 右边的数肯定不会大于等于它
- left_opt 左边的数肯定不会大于等于它
反证法显然,如果有,那么高度至少不低于现在的高度(如果被大于的那个数是桶的短边,那么高度还会增大,如果是长边,则高度不变),但是宽度变大了,所以这两个opt围成的不是最大的,与当前假设矛盾,故归谬。
有了这个性质,就可以进行下一步的讨论:
首先,由于左右两个指针分别从头和尾向中间移动,每次一格,较小的数移动。那么,首先,它们必须相交于left_opt
和right_opt
之间,而不是这个区间的外面,这个反证法也容易得到。不失一般性,我们假设left_opt所在的是短边。那么我们考察当left在left_opt的时候,如果right不在right_opt,会产生怎样的矛盾。
首先,如果此时right在right_opt
的右边,如果这个right指向的边比left_opt
的要短,那么显然它会继续向左移动,如果还短,一直移动,直到right_opt;如果碰到一个比left所指的长的,这是不可能的,为何?因为left那边是最大容器的短边,只要右边比left长,那么最大容器就应该用这个右边计算,因为它比left长,所以area的高度不变还是h(left_opt)
,而宽度却比left_opt
和right_opt
之间的宽度要长。所以综上所述,如果right在right_opt
的右边,那么通过right的移动,right_opt
必然会在left为left_opt
的时候被指到。
其次,如果right已经过了right_opt
,那么right在之前某个时刻肯定是在left在最优位置时指到过right_opt
。如果不是,那么我们考虑在right为right_opt
的时候,left必然在left_opt
的左边,而left_opt
我们已经假设是最大容器的短边了,而它左边的又比它还小,所以一定会在right为最优位置的时候,向着中间移动,直到left_opt
。因此这种情况也证明了。
所以综上,这个算法是可靠的。
下面就是它的实现,在线性时间复杂度内完成。
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
maxarea = 0;
left, right = 0, len(height) - 1
while left < right :
maxarea = max(min(height[left],height[right]) * (right - left) , maxarea)
if height[left] < height[right]:
left += 1
else:
right -= 1
return maxarea
12. Integer to Roman
Given an integer, convert it to a roman numeral.
Input is guaranteed to be within the range from 1 to 3999.
# 只要理解罗马数字的编码规则即可写出。用字典存储可以减少判断和计算
class Solution(object):
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
conv = [{} for i in range(4)]
conv[0] = {'1':'I','2':'II','3':'III','4':'IV','5':'V','6':'VI','7':'VII','8':'VIII','9':'IX'} # unit
conv[1] = {'1':'X','2':'XX','3':'XXX','4':'XL','5':'L','6':'LX','7':'LXX','8':'LXXX','9':'XC'} # ten
conv[2] = {'1':'C','2':'CC','3':'CCC','4':'CD','5':'D','6':'DC','7':'DCC','8':'DCCC','9':'CM'} # hundred
conv[3] = {'1':'M','2':'MM','3':'MMM'} # thousand
ret = ''
radix = 0
while not num == 0 :
thispos = num % 10
if not thispos == 0:
ret = conv[radix][str(thispos)] + ret
num = num // 10
radix += 1
return ret
# 看到了discuss里面的一个simple solution,发现其实可以写的更简洁:
# 加上 0 作为dict的key就可以不用判断是否有0了,完美
class Solution(object):
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
conv = [{} for i in range(4)]
conv[0] = {1:'I',2:'II',3:'III',4:'IV',5:'V',6:'VI',7:'VII',8:'VIII',9:'IX',0:''} # unit
conv[1] = {1:'X',2:'XX',3:'XXX',4:'XL',5:'L',6:'LX',7:'LXX',8:'LXXX',9:'XC',0:''} # ten
conv[2] = {1:'C',2:'CC',3:'CCC',4:'CD',5:'D',6:'DC',7:'DCC',8:'DCCC',9:'CM',0:''} # hundred
conv[3] = {1:'M',2:'MM',3:'MMM',0:''} # thousand
return conv[3][num // 1000 % 10] + conv[2][num // 100 % 10] + conv[1][num // 10 % 10] + conv[0][num % 10]
15. 3Sum
Given an array S of n integers, are there elements a, b, c in S 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.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
k Sum 也算是一类经典问题了,一个较为普遍的降低复杂度方法就是通过hash map,通过查表而不是遍历计算,从而节省时间。另外还有其他方法,之后再整理。
# 改了好几遍才勉强AC,时间要求太苛刻,好几次TLE。。。
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if len(nums) < 3:
return []
elif len(nums) == 3:
return [sorted(nums)] if sum(nums) == 0 else []
if max(nums) == 0 and min(nums) == 0:
return [[0,0,0]]
hashmap = {}
ret = []
for i, item in enumerate(nums):
hashmap[item] = i
for idx in range(len(nums) - 2):
tar = - nums[idx]
for idy in range(idx+1,len(nums) - 1):
if hashmap.has_key(tar-nums[idy]):
if hashmap[tar-nums[idy]] > idy:
ret.append(sorted([nums[idx],nums[idy],tar-nums[idy]]))
return list(set([tuple(t) for t in ret]))
16. 3Sum Closest
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
# 这个题目是 k sum closest 类型,因此用hash map就不合适了,所以需要应用所谓【夹逼法】,也就是通过从两边对范围进行缩减,从而达到最终的目标。
# 实际上,2 sum 问题可以用夹逼法来做,而 3 sum 的话就用遍历一遍第一个加数,得到第二个和第三个加数和作为target,这样就化归成了一个2 sum问题
# k sum 的时候,k>3,则需要维护一个高维的hash map,从而以空间换时间。
class Solution(object):
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
close = 65535
ret = 0
sortnum = sorted(nums)
for ida in range(len(sortnum)-2):
l, r = ida + 1, len(sortnum) - 1
while l < r:
thissum = sortnum[l] + sortnum[r] + sortnum[ida]
res = thissum - target
ret = thissum if abs(res) < close else ret
close = min(close, abs(res))
if res > 0 :
r -= 1
elif res < 0 :
l += 1
else :
return target
return ret
夹逼法的一个困难在于如何证明这样的操作下,那些没有出现的组合一定不会等于target(这里是指在k sum 问题中,在closest问题中是不会比最终的close更小)。一个简单的理解如下:
对于某一个时刻,左边指向l,右边指向r,加入nums(l)+nums(r) < target,那么我们把l++,那么可能不可能把r–得到一个需要的结果呢?答案是不能,可以这样设想:r之所以在现在的位置,说明它在之前的某一时刻移动了,而由于它移动的条件是nums(l’)+nums(r’) > target,这里l’<= l,r’ >= r,根据这个nums是sorted过的单调的序列,那么说明在r之后的数字,和l之前的数字相加已经比target大了,而现在l在l’之后,所以如果把r右移,一定会比target更大,所以也就说明我们这种移动方法并没有错过可能的solution,因此这是一个逐步逼近的算法,也就是夹逼法。算法正确性可以保证。
17. Letter Combinations of a Phone Number
Given a digit string, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below.
Input:Digit string “23”
Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
Note:
Although the above answer is in lexicographical order, your answer could be in any order you want
# 上面的mapping from digit to letters 和所有现在的手机输入法的软键盘是一样的,因此没有放图
# 可以随意排列,不需要按照字典序。
# 考虑利用python中的list comprehension在两层循环中的应用,并且利用递归得到结果。
# 另外,考虑对于字符串用list(somestring)就可以得到所有字母组成的list,e.g. ‘bili’---> ['b','i','l','i']
class Solution(object):
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
phone = {'1':'', '2':'abc', '3':'def', '4':'ghi', '5':'jkl', '6':'mno', '7':'pqrs', '8':'tuv', '9':'wxyz', '0':' '}
if len(digits) == 0:
return []
elif len(digits) == 1:
if not phone.has_key(digits):
return []
else:
return list(phone[digits])
else:
prefix = self.letterCombinations(digits[:-1])
return [pre + this for pre in prefix for this in list(phone[digits[-1]])]
【*】以上共十道题目,第一次笔记结束
2018年03月13日23:51:45
她其实挺可怜的,没亲没故的,怕你不疼她,脾气就坏了。 —— 作家,苏童 《妻妾成群》