最近正在找實習,發(fā)現(xiàn)自己的算法實在是不能再渣渣,在網(wǎng)上查了一下,發(fā)現(xiàn)大家都在刷leetcode的題,于是乎本渣渣也開始了刷題之路,主要用的是python寫的代碼。寫個日記記錄一下每天刷題的心路歷程,順便記錄學習到的知識。
#1.TwoSum
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].
一開始想的是兩個for循環(huán),暴力枚舉,于是寫出了如下代碼...
# coding: utf-8
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(0, len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
上面算法復雜度為O(n^2),本來沒抱太大希望,結果放上去居然AC了。然后去評論區(qū)看了一下,全是O(n)的解法。定義一個字典d{},先計算m = target - nums[i],如果字典里沒有鍵為n的值,則存入d[num[i]] = i,如果下次遇到目標數(shù),我們就可以通過字典找到另一個加數(shù)的索引值,返回[d[m], i],這樣就做到了以空間換時間。寫出下面的代碼:
# coding: utf-8
class Solution:
def twoSum(self, nums, target):
"""
:typenums: List[int]
:typetarget: int
:rtype: List[int]
"""
d = {}
for i, vin enumerate(nums):
m = target - v
if d.get(m)is not None:
return [d[m], i]
else:
d[v] = i
兩個代碼的運行時間比為5392ms:76ms...
#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.
這題想到的是直接法,遍歷兩個輸入鏈表,用字符串存起來,逆序,然后轉換為整數(shù)相加,把結果再逆序存進鏈表中,非常直接的解法了。。。時間復雜度O(n),代碼如下
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
if l1 is None or l2 is None:
return None
n1 = ""
n2 = ""
p = l1
q = l2
while p is not None:
n1 += str(p.val)
p = p.next
while q is not None:
n2 += str(q.val)
q = q.next
n1 = n1[::-1]
n2 = n2[::-1]
res = str(int(n1) + int(n2))
head = ListNode(0)
p = None
pre = None
res = res[::-1]
for i, s in enumerate(res):
if i == 0:
head.val = int(s)
p = head
else:
p = ListNode(int(s))
pre.next = p
pre = p
return head
提交之后AC了,但我覺得這肯定不是最優(yōu)解,因為這里逆序了三遍,數(shù)量大的時候還速度會很慢,于是去看了題解,意思是設定一個進位carry=0,在一個while循環(huán),如果p->val + q->val + carry > 10,那么next.val加上和除以10的商的整數(shù)部分,當前節(jié)點值為其模10后的余數(shù)。還學到了一個python的函數(shù)divmod(a, b),返回的是(a//b, a %b),即a除以b的商整數(shù)部分,a對b取模。重寫代碼如下
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
carry = 0
head = p = ListNode(0)
while l1 or l2 or carry:
v1 = v2 = 0
if l1:
v1 = l1.val
l1 = l1.next
if l2:
v2 = l2.val
l2 = l2.next
carry, val = divmod(v1+v2+carry, 10)
p.next = ListNode(val)
p = p.next
return head.next
新代碼簡短精悍!比原來的簡單多啦,但是不知道為什么runtime比原來還多一點...雖然只多了32ms,但是還是有些失望難道白優(yōu)化了??不管了,反正代碼好看很多。
#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.
最長非重復子串問題,看起來很簡單,一開始居然想錯了,想成了求兩個相同字符串之間的距離的最大值,嘩啦嘩啦寫了以下代碼:
# coding: utf-8
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if len(s) == 0:
return 0
maxium = 1
d = {}
for t in s:
d[t] = 1
for key in d.keys():
d[key] = d[key] + 1 if key != t else d[key]
maxium = d[key] if d[key] > maxium else maxium
return maxium
結果當然是嘩啦嘩啦的WA了,其實問題沒有那么復雜,是我一心想著最優(yōu)化然而想到的算法是錯的。WA了幾次之后發(fā)現(xiàn)不對勁,其實思路一開始就是錯的。從頭開始想,這題其實回歸純真、暴力就好(沒錯,我的想法就是很暴力)。定義空字符串res,從頭開始遍歷輸入的字符串,遇到res中沒有的字符就加進去,有的話就截取上一個相同的字符之后的字符串再加入新的字符,每趟循環(huán)計算一次res的長度,獲取最大值。重寫的代碼如下:
# coding: utf-8
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
maxium = 0
res = ""
for t in s:
if t not in res:
res += t
else:
i = res.index(t)
res = res[i+1:] + t
maxium = len(res) if len(res) > maxium else maxium
return maxium
放上去果然AC了,簡單,粗暴。如果所有問題都可以這么暴力就好了。時間復雜度是O(n),因為用到了python的not in和index還有字符串復制,如果換成其他語言可能更加復雜一些。
#4.Median of Two Sorted Arrays
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
left | Ci | right |
---|---|---|
a0, a1...ai-1 | i | ai, ai+2...an |
b0, b1...bj-1 | j | bj, bj+2...bm |
兩個有序數(shù)組求它們合并之后的中值,要求時間復雜度為O(log(m+n)),思來想去只能想到O((m+n)/2)的算法,也就是暴力枚舉的方法。于是出去搜索了一下,發(fā)現(xiàn)這是一個經(jīng)典的分治算法,在這里復述一下他的思路。
我們可以通過割的方法將一個有序數(shù)組分為兩個部分,比如說[1, 4, 7, 9]這個數(shù)組,我們在位置C上割一刀,割完后數(shù)組左邊有C個數(shù),那么數(shù)組左邊部分最大的數(shù)就是nums[C-1],假設C=2,那么這個數(shù)組就被分為[1, 4, / 7, 9]兩個部分。如果數(shù)組的長度為單數(shù)呢?在[2, 3, 5]上令割C=2,那么3將同時屬于左右兩部分,結果為[2, 3,/ 3, 4]。在單個數(shù)組中,要求數(shù)組的中值,我們可以在C=len(nums)/2的位置割一刀,求出中值為左邊最大的數(shù),即mid = nums[len(nums)/2 - 1]。
下面我們來看有兩個數(shù)組的情況。我們要求兩個數(shù)組的中值,就是求兩個數(shù)組合并后在C = len(nums1)+len(nums2)/2 位置上割一刀,左邊部分的最大值。其實我們不用真的合并這兩個數(shù)組,而是用二分的方法求出割的位置,怎么求呢?
left | Ci | right |
---|---|---|
a0, a1...ai-1 | i | ai, ai+2...an |
b0, b1...bj-1 | j | bj, bj+2...bm |
上表表示分別在i和j的位置割兩個數(shù)組,我們令上表中的L1 = ai-1,L2 = bj-1, R1 = ai, R2 = bj。顯然R1 > L1, R2 > L2。若L1 < R1 && L2 < R2,那么表示左邊的數(shù)全部小于右邊的數(shù),如果此時左邊共有k個數(shù),那么左邊最大的數(shù)就為我們要求的數(shù)。
為了統(tǒng)一計算數(shù)組長度為奇數(shù)或偶數(shù)的情況,我們引入“虛擬數(shù)組“的概念。用‘#’填充數(shù)組,比如[1, 4, 7, 9]填充為[#, 1, #, 4, #, 7, #, 9 #],這樣無論數(shù)組的長度為奇數(shù)還是偶數(shù),填充后數(shù)組長度必為2n+1的奇數(shù)。這樣做的好處是,當我們確定割的位置時,割的左邊對應原數(shù)組位置總為L = (C - 1)/2,右邊的位置總為C/2,比如在數(shù)組[#, 1, #, 4, #, 7, #, 9 #]中:
若割的位置為4,則L = (4 - 1) / 2 = 1,R = 4 / 2 = 2,而1和2正好是原數(shù)組中4和7的位置。
我們不用真正的在數(shù)組中添加#,只需要在二分開始的時候將高位設置為2n+1就可以。
如果我們使用虛擬數(shù)組的時候,那么合并后的數(shù)組總長度為2n+2m+2,中值位置為n+m+1。令i為nums1的割位置,j為nums2的割位置,那么當取得中值時必有i+j = n+m+1,因此我們只要找到同時符合 nums1[(i-1)/2] < nums2[j/2] && num2[(j-1)/2] < nums1[i/2]的i和j,就能找到中值。思路已經(jīng)明確了,我們可以開始寫代碼
# coding: utf-8
import sys
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
n = len(nums1)
m = len(nums2)
if n > m:
return self.findMedianSortedArrays(nums2, nums1)
L1, L2, R1, R2, low = 0, 0, 0, 0, 0
high = 2*n
while low <= high:
c1 = int((low + high)/2)
c2 = m + n - c1
L1 = nums1[int((c1-1)/2)] if not c1 == 0 else -sys.maxsize
L2 = nums2[int((c2-1)/2)] if not c2 == 0 else -sys.maxsize
R1 = nums1[int(c1/2)] if not c1 == 2*n else sys.maxsize
R2 = nums2[int(c2/2)] if not c2 == 2*m else sys.maxsize
if L1 > R2:
high = c1 - 1
elif L2 > R1:
low = c1 + 1
else:
break
return (max(L1, L2) + min(R1, R2)) / 2.0
最終AC了,真的好難想啊,代碼也不好寫,不過又學到了一些東西,也算是有一點進步吧。
#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"
最長回文子串問題,想到的是從中心開始往兩邊搜索的思路,思路分成兩部分,分別計算子串長度為奇數(shù)即aba形態(tài)和子串長度為偶數(shù)即abba形態(tài)的最長長度,取其最大值。首先用一個for循環(huán)遍歷每一個可能的中心點,然后用一個while循環(huán)從中心點出發(fā),如果兩邊的字符相同則count+2,否則跳出循環(huán),記錄count的最大值和最長子串的起始位置,最后返回子串[start:start+maxium],寫出代碼:
# coding: utf-8
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
maxium = 1
start = 0
for i in range(len(s)):
# aba
j = i - 1
k = i + 1
count = 1
while j >= 0 and k < len(s) and s[j] == s[k]:
count += 2
if count > maxium:
maxium = count
start = j
j -= 1
k += 1
for i in range(len(s)):
# abba
j = i
k = i + 1
count = 0
while j >= 0 and k < len(s) and s[j] == s[k]:
count += 2
if count > maxium:
maxium = count
start = j
j -= 1
k += 1
return s[start:start+maxium]
時間復雜度為O(n^2),提交后AC了。然后取看題解,有一種Manacher的算法,時間復雜度為O(n),看了半節(jié)課沒看懂,在這貼個鏈接以后有時間再看[Manacher算法詳解]。(https://www.cnblogs.com/z360/p/6375514.html)
#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"
.
這題找規(guī)律,首尾兩行的規(guī)律比較好找,中間的行數(shù)規(guī)律有奇偶數(shù)之分,分析了一會寫了代碼:
# coding: utf-8
class Solution:
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
n = len(s)
if numRows == 1 or n == 0:
return s
step = 2*numRows - 2
res = []
for i in range(numRows):
rs = ""
if i % (numRows-1) == 0:
# the first or the last row
index = i % numRows
while index < n:
rs += s[index]
index += step
res.append(rs)
else:
# the middle rows
index = i % numRows
count = 0
while index < n:
rs += s[index]
if count % 2 == 0:
index += step - 2 * (i % numRows)
else:
index += 2 * (i % numRows)
count += 1
res.append(rs)
result = ""
for i in range(numRows):
result += res[i]
return result
提交之后AC了,時間復雜度是O(n*m),但是覺得自己找規(guī)律的時間有點長,希望能再提高吧。
#7.Reverse Integer
Given a 32-bit signed integer, reverse digits of an integer.
Example 1:
Input: 123
Output: 321
Example 2:
Input: -123
Output: -321
Example 3:
Input: 120
Output: 21
Note:
Assume we are dealing with an environment which could only hold integers within the 32-bit signed integer range. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
翻轉整數(shù),先把輸入轉化為字符串,如果有符號,先把符號提取出來,然后將剩下的字符串翻轉后再加上符號,最后判斷是不是在32位int的范圍內,python的代碼幾行就寫好了。
# coding: utf-8
class Solution:
def reverse(self, x):
"""
:type x: int
:rtype: int
"""
s = str(x)
index = 1 if s[0] == "-" or s[0] == "+" else 0
s = s[0] + s[index:][::-1] if index else s[::-1]
res = int(s)
if -2147483648 < res < 2147483647:
return res
else:
return 0
果斷AC了,時間復雜度O(n),翻轉字符串還是比較耗時的,不過這也算簡單題,如果用其他語言應該復雜一點。
#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.
實現(xiàn)一個atoi函數(shù),題目好長,主要是考慮的情況有很多,其實也不是很難,就是要注意一些地方,還好python的isdigit()函數(shù)和try except幫了大忙,代碼寫好很快就AC了,算法復雜度是O(n)
# coding: utf-8
class Solution:
def isIntegerNumber(self, s):
"""
:type s: str
:rtype: bool
"""
if s.isdigit() or s == "-" or s == "+":
return True
else:
return False
def myAtoi(self, st):
"""
:type st: str
:rtype: int
"""
r = ""
status = False
for c in st:
if not status:
if c == " ":
continue
elif not self.isIntegerNumber(c):
break
elif self.isIntegerNumber(c):
status = True
r += c
else:
if c.isdigit():
r += c
else:
break
try:
r = int(r)
if r > 2147483647:
return 2147483647
elif r < -2147483648:
return -2147483648
else:
return r
except:
return 0
#9. Palindrome Number
Determine whether an integer is a palindrome. Do this without extra space.
題目很短!就是判斷一個數(shù)是否回文數(shù),要求不使用額外的空間。不能用額外的空間,那就不能將它轉換為字符串來做了,想了一個用除和模做的方法。首先計算它的數(shù)量級(10的多少次方),然后不斷將這個數(shù)除以其數(shù)量級,比較取整后的商和它與10的模,如果不相等表示該數(shù)的第一個數(shù)和最后一個數(shù)字不同,返回false。如果相等,則將該數(shù)減去其先前求出的商*數(shù)量級,再減去先前求出的模,然后除以10,得到去頭去尾的一個新數(shù),繼續(xù)判斷其是否為回文串。根據(jù)這個思路寫出的代碼:
# coding: utf-8
class Solution:
def isPalindrome(self, x):
"""
:type x: int
:rtype: bool
"""
if x < 0:
return False
l_size = 1
s_size = 10
while x / l_size >= 10:
l_size *= 10
while x > 0:
first = x // l_size
last = x % s_size
if last != first:
return False
x -= l_size * first
x -= last
x /= 10
l_size /= 100
return True
一次就AC了,說明這個方法還是管用的,而且沒有用到多余的空間。
#10. Regular Expression Matching
實現(xiàn)一個能匹配.和*的正則表達式引擎,太難了。。。想了一個晚上,要考慮的情況太多,先放棄這一題。
#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.
一開始理解錯了題目,以為是求梯形面積,WA一次之后發(fā)現(xiàn)題目不是這個意思,其實是求兩塊木板之間能裝多少水的面積,也就是短板的高度*x坐標的差。很快就寫了一個O(n^2)的思路,放上去果然超時了。
#include <cmath>
class Solution {
public:
int maxArea(vector<int>& height) {
int maxium = 0;
for (int i=0; i<height.size(); i++){
for (int j=i+1; j<height.size();j++){
int m = height[j] < height[i]? height[j] : height[i];
int area = m * (j - i);
if (area > maxium)maxium = area;
}
}
return maxium;
}
};
然后想了一個O(n)的思路,先定義一個一維數(shù)組,里面放的是該坐標能找到的最長的木板的左邊,然后從左到右遍歷木板,如果遇到比自己能找到最長的木板更長的木板,則遞歸更新該數(shù)組。我是這么想的,因為是從左到右的遍歷,所以如果在右邊找到更長的木板,那么面積一定比原來找到的那個木板更大(我現(xiàn)在知道哪里錯了,前提條件應該是數(shù)組存的木板比自身短)。寫出以下代碼:
#include <cmath>
class Solution {
public:
vector<int> highestPos;
int maxArea(vector<int>& height) {
highestPos.resize(height.size());
for(int i=0;i<height.size()-1;i++){
if(height[highestPos[i]] < height[i+1]){
changeHighestIndex(i, i+1);
}
}
int maxium = 0;
for(int i=0;i<height.size();i++){
int lowest = height[i] < height[highestPos[i]] ? height[i] : height[highestPos[i]];
int area = lowest * abs(i-highestPos[i]);
if(area > maxium)maxium = area;
}
return maxium;
}
void changeHighestIndex(int i, int newPos){
if(highestPos[i] != newPos){
int tmp = highestPos[i];
highestPos[i] = newPos;
changeHighestIndex(tmp, newPos);
}
}
};
放上去WA了,思考一會之后發(fā)現(xiàn)這個思路應該太復雜了,其實我可以從兩邊向中間遍歷,記錄最大值就好,這個思路清晰簡單,寫出來的代碼也很簡單。時間復雜度是O(n),一次就AC了。
#include <cmath>
class Solution {
public:
int maxArea(vector<int>& height) {
int low = 0;
int high = height.size() - 1;
int maxium = 0;
while(low < high){
int lowest = height[low] < height[high] ? height[low] : height[high];
int area = lowest * (high - low);
maxium = area > maxium ? area : maxium;
if(height[low] > height[high]){
high--;
}else{
low++;
}
}
return maxium;
}
};
#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.
將整數(shù)轉換為羅馬數(shù)字字符串,為此還學習了一波羅馬數(shù)字。挺簡單的題,結果邊界值0的時候卡住了,改了挺久,不應該啊。
#include <iostream>
#include <string>
using namespace std;
class Solution {
public:
string intToRoman(int num) {
string all[4][10] = {
{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
{"", "M", "MM", "MMM"}
};
int size = 10;
string res = "";
int count = 0;
while(num*10 / size){
int left = num % size;
res = all[count][left*10/size] + res;
num = num - left*10 / size;
size *= 10;
count += 1;
}
return res;
}
};
#13. Roman to Integer
Given a roman numeral, convert it to an integer.
Input is guaranteed to be within the range from 1 to 3999.
上一題的反向,我覺得更簡單一些,代碼很快就AC了,時間復雜度O(n)。
class Solution {
public:
int romanToInt(string s) {
int worth[26];
int a = 'A';
worth['M'-a] = 1000;
worth['D'-a] = 500;
worth['C'-a] = 100;
worth['L'-a] = 50;
worth['X'-a] = 10;
worth['V'-a] = 5;
worth['I'-a] = 1;
int i = s.length()-1;
int total = 0;
while(i>=0){
if(i==0){
total += worth[s[i]-a];
break;
}
int A = worth[s[i]-a];
int B = worth[s[i-1]-a];
if(A<=B){
total += A;
i -= 1;
}else{
total = total + A - B;
i -= 2;
}
}
return total;
}
};
#14. Longest Common Prefix
Write a function to find the longest common prefix string amongst an array of strings.
尋找給出字符串列表的最長共同前綴,思路是一開始最長的前綴是第一個字符串,然后遍歷每一個字符串,如果某個字符串在某位和當前的最長前綴不同,則最長前綴截取為前面相同的部分,寫出代碼:
# coding: utf-8
class Solution:
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
if len(strs) == 0:
return ""
prefix = strs[0]
for v in strs:
if len(prefix) == 0:
return ""
if len(v) < len(prefix):
prefix = prefix[:len(v)]
for i in range(len(prefix)):
if v[i] != prefix[i]:
prefix = prefix[:i]
break
return prefix
處理空字符串的情況后之后就AC了,時間復雜度O(n),這題不是很難。
#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.
找出給出數(shù)組中的三個數(shù),使得它們的和為0,要求結果去掉相同的情況。這題和第一題有點像,所以我想沿用第一題的思路,設置一個以差值為鍵的字典,這樣將O(n3)的問題化為O(n2)。然而這題的結果有很多,這種做法會出現(xiàn)很多重復的代碼,去重雖然能實現(xiàn)但是太復雜,于是看了別人是怎么做的。首先將數(shù)組排序,然后固定一個數(shù),用兩個指針指向該數(shù)右邊最大和最小的兩個數(shù),計算當前三個數(shù)的和,如果比0小則low指針右移,比0大則high指針左移,和0相等則記錄到答案中。記錄一次后要將low和high指針移動到下一個不相等的數(shù),避免重復記錄。寫出的代碼也很簡潔易懂:
# coding: utf-8
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
dic = {}
res = []
nums.sort()
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
low = i + 1
high = len(nums) - 1
while low < high:
s = nums[i] + nums[low] + nums[high]
if s < 0:
low += 1
elif s > 0:
high -= 1
else:
res.append([nums[i], nums[low], nums[high]])
while low < high and nums[low] == nums[low+1]:
low += 1
while low < high and nums[high] == nums[high-1]:
high -= 1
low += 1
high -= 1
return res
代碼一次AC,時間復雜度還是O(n^2)。
#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).
和上題差不多,要求變?yōu)榍蠼o出與目標數(shù)最接近的三個數(shù)的和。沿用上題的思路,很快就寫好了代碼:
# coding: utf-8
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
closest = 0
minium = -1
nums.sort()
for i in range(len(nums)):
low = i + 1
high = len(nums) - 1
while low < high:
s = nums[i] + nums[low] + nums[high]
d = abs(s - target)
if d == 0:
return target
if minium == -1 or d < minium:
minium = d
closest = s
if s < target:
low += 1
else:
high -= 1
return closest
處理好邊界問題后代碼就AC了,時間復雜度還是O(n^2)。
#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.
image
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
給出幾個數(shù)字,要求給出數(shù)字對應的字母最多能組合的字符串。這是一個經(jīng)典的backtracking(回溯算法)的題目。當一個題目,存在各種滿足條件的組合,并且需要把它們全部列出來時,就可以考慮backtracking了。一開始想得沒有思路,看了提示用隊列解決。于是設計了一個隊列,對于每個輸入的數(shù)字,當隊列中有元素的長度小于當前的處理的數(shù)字個數(shù)時,取出該元素,然后在隊尾中分別加入該元素分別和當前數(shù)字對應的所有字母組成的新串。根據(jù)這個思路寫出代碼:
# coding: utf-8
class Solution:
numbers = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
queue = [""]
if len(digits) == 0:
return []
for i, v in enumerate(digits):
while len(queue[0]) == i:
tmp = queue.pop(0)
for s in self.numbers[int(v)]:
queue.append(tmp+s)
return queue
代碼很短,時間復雜度是O(n),基本上是一個枚舉算法。