Leetcode1
思路:看似需要两重循环计算和,实则可以在第一遍循环的同时建立哈希表(字典),key记录每个位置的数所需要配对的数(记录每个位置的数本身也行,那么循环的时候要找的就是target去减),value记录位置信息
发现:当列表计算复杂度过高时,可考虑用字典,一遍循环同时建立字典,但是空间复杂度高,当同一个key需要对应不同的value不能考虑
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
l = len(nums)
d = {}
for x in range(l):
a = target - nums[x]
if a in d:
return d[a], x
else:
d[nums[x]] = x
Leetcode2
思路:可以很复杂的倒回去读出来再相加再倒回去存进去(适用范围广),也可以很直接(结合问题本身)直接第一对节点相加存进目标节点,记录进位,计算下一对节点相加加进位。
发现:建立链表的过程就是为每一个赋好值得节点.next指向下一个新建的节点,循环过程,返回链表就是返回链表头节点
# 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
"""
t1 = ''
t2 = ''
if l1 == None:
return l2
if l2 == None:
return l1
while l1!=None:
t1=t1+str(l1.val)
l1 = l1.next
while l2!=None:
t2=t2+str(l2.val)
l2 = l2.next
t1 = t1[::-1]
t2 = t2[::-1]
t= str(int(t1) + int(t2))[::-1]
head = ListNode(t[0])
answer = head
for i in range(1,len(t)):
node = ListNode(t[i])
head.next = node
head = head.next
return answer
Leetcode 3
思路:逐个位置进行匹配
发现:用数组存储最大子串就会超时,用字符串处理就不会
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
l = len(s)
longest = 0
for i in range(l):
res = ''
for j in range(i, l):
if s[j] in res:
break
else:
res = res + s[j]
num = len(res)
if num > longest:
longest = num
return longest
Leetcode 4
思路:赖皮,利用sort()直接排序
发现://整数除法,长为n的列表,下标0-(n-1),中位数是2/n或(2/n和2/(n-1)的平均数)
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums = nums1 + nums2
nums.sort()
n = len(nums)//2
if len(nums)%2 == 1:
return nums[n]
else:
return (nums[n] + nums[n-1])/2
Leetcode 5
思路:最小回文子串,奇数回文和偶数回文算法不同,一遍循环后,奇数回文是以每个位置向左右不断延伸比较每次延伸的位置是否相同,如是就加上。偶数回文要先比较该位置与下一位是否相同,如是就以这两位为基点向两边延展。
发现:注意初始值,输入值也可以是空集,所以s[0]不一定存在,得在遍历中赋初值;注意遍历中每一次都要先初始化最小奇偶回文的值。
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
st = ''
sw = ''
longest = ''
l = len(s)
for i in range(l):
#先算奇回文
st = s[i]
if len(st)>len(longest):
longest = st
t = 1#记录每次往两边的偏移
while i-t >=0 and i+t <= l-1:
if s[i-t] == s[i+t]:
st = s[i-t] + st + s[i+t]
if len(st)>len(longest):
longest = st
t = t + 1
else:
break
#再算偶回文
t = 0
sw = ''
while i-t >=0 and i+1+t <= l-1:
if s[i-t] == s[i+1+t]:
sw = s[i-t] + sw + s[i+1+t]
if len(sw)>len(longest):
longest = sw
t = t + 1
else:
break
return longest
Leetcode 6
思路:主要是找规律,行数n,一个循环是gap=2n-2,第一行和最后一行都是只有一个主规律:每个gap出现一次,其余行有副规律:每个gap-所在行数也会出现一次。输出就一行一行输出,找到每一行上字符的下标即可。
第一行:s[0]、s[gap]、s[2*gap]….
第二行:s[1]、s[gap-1]、s[gap+1]…
第i行:s[i]、s[gap-i]、s[gap+i]…
第n行:s[n]、s[gap+n]、s[2*gap+n]..
发现:找到规律问题就解决了,注意使用多个临时变量。
class Solution:
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if numRows == 1:
return s
resultlist = ''
gap = 2*numRows - 2
for i in range(numRows):
temp1 = i
temp2 = gap -i
if i == 0 or i == numRows-1:
while temp1else:
while temp1if temp2return resultlist
Leetcode 7
思路:反转问题,python都可以赖皮,先把正负符号存起来,对绝对值做反转,如果输出大于上限,按要求返回,否则加上符号输出
发现:有[::-1]反转功能,这些问题都很赖
class Solution:
def reverse(self, x):
"""
:type x: int
:rtype: int
"""
#先判断x的符号,存在id里
if x < 0:
id = 0
else:
id =1
#取x绝对值,将绝对值转为y,y翻转
x2 = abs(x)
y = str(x2)
y = y[::-1]
#判断y,判断符号加上
if int(y) > 2147483647:
return 0
if id == 1:
return int(y)
else:
return -int(y)
Leetcode 8
思路:先寻找第一个非空值,是“+”“-”就存为符号然后数数,是“1”-“9”就直接开始数数,别的情况就返回0。开始数数即该位是“0”-“9”那么就加到结果里去即可
发现:可以用re函数,专门解决何种格式化字符串问题,回顾廖雪峰re函数讲解https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193331387014ccd1040c814dee8b2164bb4f064cff000
class Solution:
def myAtoi(self, str):
"""
:type str: str
:rtype: int
"""
import re
res = re.findall(r"^[\+\-]?\d+",str.strip())
print(res)
if res !=[]:
if int(res[0]) > (2**31-1):
return (2**31-1)
if int(res[0]) < (-2**31):
return (-2**31)
return int(res[0])
else:
return 0
Leetcode9
思路:先判断符号,小于0直接返回错误。然后就是简单的直接反转。
发现:简单赖皮[::-1]
if x < 0:
return False
s1 = str(x)
s2 = s1[::-1]
if s1 == s2:
return True
else:
return False
Leetcode11
思路:可以用两重循环把所有边界都表示出来,也可以用双指针做。最开始顶左顶右的值为初始面积,逐渐向中间移,为了得到更高的面积,需要更高的柱子,所以矮的柱子向中间靠,一样高就都要移。
发现: if a> b: b = a可以写成b = max(a,b)。很多数组上的动态问题都可以考虑双指针,逐渐寻找最优结果。
class Solution:
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
l = len(height)
ans = 0
p1 = 0
p2 = l-1
while p1if height[p1] < height[p2]:
p1 +=1
else:
p2 -=1
return ans
Leetcode12
思路:编码问题如果额外规则太多,按照判断语句写太过于复杂,不如直接写一个矩阵对应规则,一行一个规则,这样转化对应时可以通过矩阵第一个坐标定位规则,第二个坐标找到正确的编码。
发现:将现有数字进行编码都可以参考这种做法,先将各种可能出现的编码情况罗列出来,一一对应转化。要熟练提取数字个十百千万位的操作。
class Solution:
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
roman = [["", "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"]]
res = ''
res = res + (roman[3][int(num / 1000 % 10)])
res = res + (roman[2][int(num / 100 % 10)])
res = res + (roman[1][int(num / 10 % 10)])
res = res + (roman[0][int(num % 10)])
return res
Leetcode13
思路:将一种编码的数转换为实数都要建立转化对照表,用哈希(字典)表示很合适。同时根据规则,逐位读字符,如果该字符比右边大就是“加”如果该字符比右边“小”就是减。
发现:先根据规则找规律,这类转换基本都要建表建哈希或建数组。
r = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
l = len(s)
res = 0
for i in range(l-1):
if (r[s[i]] >= r[s[i+1]]):
res = res + r[s[i]]
else:
res = res - r[s[i]]
res = res + r[s[l-1]]
return res
Leetcode 14
思路:以字符串组中第一个字符串作为基准比对,逐位比,每一位逐个字符串和第一个字符串的该位比,如果发现不同就返回现有答案,都相同就加上该位进入答案。或者先找出最短的字符串,以该位作为基准比。
发现:注意下标索引,每一次判断该字符串该位是否和第一个字符串该位相等前先要判断该字符串是否有该位,通过len判断
class Solution:
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
l = len(strs)
if l <=0:
return ''
if l ==0:
return strs[0]
res = ''
for i in range(len(strs[0])):
for j in range(1, l):
if len(strs[j])1 or strs[j][i] != strs[0][i]:
return res
res = res + strs[0][i]
return res
Leetcode15
思路:如利用两数之和建哈希表方法,仍需要逐个作为target,复杂度仍为O(n2)。寻求双指针法。用双指针就要先排序,每个位置一遍循环,对每一位在它的右侧范围寻找两数和它本身加起来等于零。小的往大加,大的往小减,看和大还是小,逐步变化直到双指针相遇。
发现:碰到列表问题,可以先排个序,利用大小关系继续解决。注意防重。
nums.sort()#排序
res =[]
i = 0
for i in range(len(nums)):
if i == 0 or nums[i]>nums[i-1]:
l = i+1
r = len(nums)-1
while l < r:
s = nums[i] + nums[l] +nums[r]
if s ==0:
res.append([nums[i],nums[l],nums[r]])
l +=1
r -=1
while l < r and nums[l] == nums[l-1]:#避免相同值
l += 1
while r > l and nums[r] == nums[r+1]:
r -= 1
elif s>0:
r -=1
else :
l +=1
return res
Leetcode16
思路:一个数组里的多数之和问题都可以用双指针想法解决,先排序。判断差值的绝对值是否可以更新。
发现:
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums.sort()
length = len(nums)
min= 1000000000000000
for i in range(length):
l = i +1
r = length - 1
while lif abs(target - sum) < abs(target - min):
min = sum
if target - sum ==0:
return sum
elif target - sum > 0:
l = l +1
else:
r = r - 1
return min
Leetcode17
思路:还不会递归,就按规则来,编码问题,考虑字典,按位读数,每一位数对应几个字母,在这些字母的后面都可以加上下一位数对应的字母,那么就需要一个变量临时存放这些临时生成的字母串。在已有的字符串后面,加上该位数代表的字母可能。
发现:结果一步一步生成,后面产生的结果需要利用前面产生的结果,就是递归雏形了。
class Solution:
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return []
#按照键盘分布,下标0-9分别对应字符串
digitLis = ["0","1","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
res = [""]
for num in digits:
tempLis = []
for ch in digitLis[int(num)]:
for str in res:
tempLis.append(str + ch)
res = tempLis
return res
Leetcode 18
思路:四数之和沿用三数之和的思想,三数之和外面再套一层。
发现:会超时,需要剪枝,在外面大循环的部分做判断,如果当前最小值的数量倍>target或是当前最大值的数量倍
class Solution:
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
lent = len(nums)
res = []
nums.sort()
for i in range(lent-3):
if target < nums[i]*4 or target > nums[-1]*4:
break
if i==0 or nums[i] != nums[i-1]:
for j in range(i+1,lent-2 ):
if target - nums[i] < nums[j]*3 or target -nums[i] > nums[-1]*3:
break
if j==i+1 or nums[j]!=nums[j-1]:
l = j + 1
r = lent - 1
while l < r:
if nums[i] + nums[j] + nums[l] + nums[r] == target:
res.append([nums[i],nums[j],nums[l],nums[r]])
l = l+1
r = r-1
while r > l and nums[r] == nums[r+1]:
r = r-1
while r > l and nums[l] == nums [l-1]:
l = l + 1
elif nums[i] + nums[j] + nums[l] + nums[r] > target:
r = r - 1
else:
l = l + 1
return res
Leetcode19
思路:使用快慢指针记录抵达链表尾和倒数第n个节点信息。在链表头再创建一个节点指向原表头,这样能应对表头被删的情况。
发现:记录链表倒数第几个节点可以参照此法,双指针,让快的先跑,跑一次记一次数。
class Solution:
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
dummy = ListNode(-1)
dummy.next = head
slow = fast = dummy
while n and fast.next:
fast = fast.next
n = n - 1
while fast.next and slow.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
Leetcode21
思路:将l1和l2里的值全部提取出来放进列表,对列表排序,逐个放进链表里,全是基本操作。但是这样写效率低,没有用到两个链表都是有序这一点。
发现:.sort()函数和.append()函数都会在原处修改对象,不会有返回值,不要写成temp = temp.append(temp2)
class Solution:
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
if l1 == None and l2 ==None:
return None
temp1 = []
temp = []
while l1!= None:
temp1.append(l1.val)
l1 = l1.next
while l2!= None:
temp1.append(l2.val)
l2 = l2.next
temp1.sort()
res = ListNode(temp1[0])
head = res
for i in range(1, len(temp1)):
node = ListNode(temp1[i])
head.next = node
head = head.next
return res
Leetcode22
思路:
发现:
class Solution:
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
if n==0:
return []
res = []
self.helpler(n,n,'',res)
return res
def helpler(self,l,r,item,res):
if l>r:
return
if l==0 and r==0:
res.append(item)
if l>0:
self.helpler(l-1, r, item+'(', res)
if r>0:
self.helpler(l, r-1, item+')', res)
Leetcode23
思路:暴力全部取出来放进一个列表里,列表排序,然后将这个列表存入链表里
发现:创建答案链表时,如果用ListNode(res[0])会报错,需要自行创建一个赋值节点指向要返回的节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
if lists ==None:
return []
l = len(lists)
res = []
for i in range(l):
while lists[i]!=None:
res.append(lists[i].val)
lists[i] = lists[i].next
res.sort()
ans = ListNode(0)
head = ans
for i in range(0, len(res)):
node = ListNode(res[i])
head.next = node
head = head.next
return ans.next
Leetcode24
思路:链表转换
发现:
class Solution:
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
temp = ListNode(0)
temp.next = head
ans = temp
while temp.next and temp.next.next:
n1 = temp.next
n2 = temp.next.next
n3 = temp.next.next.next
temp.next = n2
temp.next.next = n1
temp.next.next.next = n3
temp = n1
return ans.next
Leetcode26
思路:从前到后依次读,因为已排序,所以每一个读进去的数和上一个读的数做比较,相同一次,返回值就在原长度上减一,不同的话就要修改原序列的值,那么除了循环里的原数组下标变量外,还需要一个修改后数组的下标,以及一个变量记录要比较的值。
发现: 在原数组上修改问题,一般都需要一个修改后数组的下标变量以及一个要比较的量。
class Solution:
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
res = length
if not nums:
return 0
temp = nums[0]
new = 1
for i in range(1, length):
if nums[i]!= temp:
nums[new] = nums[i]
new = new +1
temp = nums[i]
else:
res = res -1
return res
Leetcode27
思路:上一题刚总结好这类思路,这里应用,在原数组上改,需要一个变量记录改变后的下标,因为已经给了要比较的值就不需要新变量存比较值了。从左往右读,相同就返回长度减一,不同就在原数组上改。
发现:这类题要变成基本操作了哦
class Solution:
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
if not nums:
return 0
l = len(nums)
length = l
index = 0
for i in range(0, l):
if nums[i] != val:
nums[index] = nums[i]
index = index +1
else:
length -=1
return length
Leetcode28
思路:对haystack做逐位读,读到和needle第一位相同开始判断剩下是否相同,一旦位数超了或者不同就break,这里可以用一个小技巧,返回值在每次找到第一个相同元素时赋位该位置,随后做剩下位置的比较,一旦有不同就把这个值改回去(原来是用计数器,如果相同元素等于needle长度那么才改变返回值,这样有点慢)。
发现:终极赖皮:return haystack.find(needle)。另,判断语句if i+j>=len1:和if haystack[i+j] != needle[j]:如果合在一起写并判断就会超时。
class Solution:
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
if not needle and not haystack:
return 0
if not needle:
return 0
if not haystack and needle:
return -1
len1 = len(haystack)
len2 = len(needle)
if len2 > len1:
return -1
res = -1
for i in range(len1):
if haystack[i] == needle[0]:
res = i
for j in range(1, len2):
if i+j>=len1:
return -1
if haystack[i+j] != needle[j]:
res = -1
break
if res != -1:
return res
return res
Leetcode29
思路:移位当作除法
发现:
class Solution:
def divide(self, dividend, divisor):
"""
:type dividend: int
:type divisor: int
:rtype: int
"""
if dividend >= 0 and divisor >0:
flag = 1
if dividend < 0 and divisor >=0:
flag = -1
if dividend >= 0 and divisor <0:
flag = -1
if dividend < 0 and divisor <0:
flag = 1
res = 0
cnt = 1
dividend = abs(dividend)
divisor = abs(divisor)
subsum = divisor
while dividend >= divisor:
while (subsum << 1) <= dividend:
cnt = cnt << 1
subsum = subsum << 1
res = res + cnt
cnt =1
dividend -= subsum
subsum = divisor
return max(min(flag * res, 0x7fffffff), -2147483648)
Leetcode34
思路:logn直接想到二分法,二分找到该数后要向左向右匹配,同时更新下标。向左匹配和向右匹配要分开写,下标在合理范围内且等于目标值则存储并更新下标。
发现:
left = 0
right = len(A) - 1
result = [-1, -1]
while left <= right:
mid = (left + right) / 2
if A[mid] > target:
right = mid - 1
elif A[mid] < target:
left = mid + 1
else:
result[0] = mid
result[1] = mid
i = mid - 1
while i >= 0 and A[i] == target:
result[0] = i
i -= 1
i = mid + 1
while i < len(A) and A[i] == target:
result[1] = i
i += 1
break
return result
Leetcode35
思路:二分定位,这题关键是如果没有找到怎么记录它该插在哪里。不用在while里做,等全部while做完了,left和right相同时,对target进行比较,如果target比left(right)小就放左边,大就放右边。
发现:二分法的问题再细化
class Solution:
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
length = len(nums)
left = 0
right = length -1
res = 0
while left < right:
mid = int((left+right)/2)
if nums[mid] < target:
left = mid +1
elif nums[mid] > target:
right = mid -1
else:
return mid
if target>nums[left]:
return left+1
else:
return left
Leetcode36
思路:不能重复出现,可以用字典解决。想一想一共有多少组需要不能重复出现,9行,9列,9个宫格,所以需要27个字典。每一个数要判断行上是否有重复的,列上是否有重复的,所在宫格是否有重复的,所以对这27个字典分个类。字典建好之后就好办了,每个数挨个读,所在行所在列容易得到,所在宫格怎么得到呢?一行三个宫格,根据所在行列就可以计算出来在第几个宫格,注意//取整的运用。然后就是挨个判断,有一个重复就false,没有重复的就把这个数加入到对应的字典中,整个遍历完也没重复的就true。
发现:感觉可以用集合做?判断是否有重复
class Solution:
def isValidSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: bool
"""
raw = [{},{},{},{},{},{},{},{},{}]
col = [{},{},{},{},{},{},{},{},{}]
cell = [{},{},{},{},{},{},{},{},{}]
for i in range(9):
for j in range(9):
temp = board[i][j]
num = 3*(i//3) + (j//3)
if temp != '.':
if temp not in raw[i] and temp not in col[j] and temp not in cell[num]:
raw[i][temp] =1
col[j][temp] =1
cell[num][temp] =1
else:
return False
return True
Leetcode38
思路:一层循环是按项生成,对于第n个生成的项都要在第n-1个项上而来,需要二层循环读取第n-1项的各位,从第一位起,统计该数字num连续出现次数m,输出值里加上m+num。每一层都是单独生成的,所以需要一个临时变量temp2来存储,每一层是基于上一层统计的,所以需要一个临时变量res记录上一层。每一个数,判断它和左边一个是否相等,如果相等就计数器m加1,一旦不等就要在返回值res里添加了。
发现:和人脑一样,按位读,如果该位和前位不同,表明前面数字不再连续,那么输出,所以只有在不同情况才输出。最后一位判断的时候,如果和前一位不同,那么把前一位的数量和数字输出,然后还需要输出1和自己,如果和前一位相同,那么现在不能输出,还需要输出m和自己,那么在每一个二层循环之后还需要额外输出 一次,加上m和该位。
class Solution:
def countAndSay(self, n):
"""
:type n: int
:rtype: str
"""
if n ==1:
return '1'
if n ==2:
return '11'
res = '11'
for i in range(3, n+1):
temp1 = res
temp2 = ''
m = 1
for j in range(1, len(temp1)):
num = temp1[j]
if num == temp1[j-1]:
m = m + 1
else:
temp2 = temp2 + str(m) + str(temp1[j-1])
m = 1
temp2 = temp2 + str(m) + str(temp1[j])
res = temp2
return res
Leetcode41
思路:刚开始觉得挺简单的,一看好多限制条件,要O(n)的时间复杂度,而且还要常数空间。
分析一下:因为是最小的整数,
反正是从1,2,3。。。。开始的。
那就把每一个数,给到他们自己的位置,比如说【3,4,-1,1】
把所有的数都放到位置:变成【1,-1,3,4】 那就能找到 那个-1的位置了。
因为要常数空间,那估计的言外之意就是,交换swap。
1.不过这里面有坑,首先【2,3】这样的,3就不能换,也就是说大过数组长度的不要动。
3.而且小于0的也不能换,
3.还有这个【3,4,-1,1】如果只换一次。。
先换3,变成【-1,4,3,1】
然后4 变成【-1,1,3,4】
哎到头了。。。这个1怎么办。。。
所以要不停的换,换到不能换了为止:
改成:
先换3,变成【-1,4,3,1】,第一个位置是-1,不能换了
再还4,变成【-1,1,3,4】,第二个位置是1,继续换【1,-1,3,4】
后面就不换了。。。
4.还有一个坑:【1,2,3】。。。换到头了,怎么办,返回4
发现:nums[i]和nums[nums[i]-1]交换的时候要注意如用a,b = b,中间的索引并不会变
注意用temp交换时,第三段nums[temp-1]而不是nums[nums[i]-1],因为nums[i]已经变了
class Solution:
def firstMissingPositive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(len(nums)):
while (nums[i]>0 and nums[i] <= len(nums) and nums[i]!= nums[nums[i]-1]):
# nums[i],nums[nums[i]-1]=nums[nums[i]-1], nums[i]
temp = nums[i]
nums[i] = nums[nums[i]-1]
nums[temp-1] = temp
for i in range(len(nums)):
if (nums[i] != i+1):
return i+1
return len(nums)+1
Leetcode 46
思路:长度为1,0直接返回。一个n个数全排列包括除了n个数在第一位的剩余n-1个数全排列。
迭代要去除已有数据nums更新为nums[0:i]+(nums[i+1:]
发现:经常写成.append[],必须是.append()
Leetcode 47
思路:
发现:
第一段代码正常思路,一个个迭代,不考虑重复的找,每找到一各长度ok的就判断它是否在之前出现过,出现过就不加了。
self.helper(nums, res, [])
return res
def helper(self, nums, res, path):
if not nums and path not in res:
res.append(path)
else:
for i in range(len(nums)):
self.helper(nums[:i] + nums[i+1:], res, path + [nums[i]])
第二个方法:思路还是不断加,DFS中加到长度和nums相等就添加到结果中,为了避免重复,那么每次加数的时候,要判断这次加进去的数和之前一次加进去的数是否一致,如果一致就跳过这次。加进去一个数之后,剩下的数组合templist继续做DFS,剩下数组合可以用(nums[0:1]+nums[i+1])表示(奇怪为什么不会超过长度)做完要pop()
self.lennum = len(nums)
nums.sort()
self.res = []
templist =[]
self.DFS(nums, [])
return self.res
def DFS(self, nums, templist):
if len(templist) == self.lennum:
self.res.append(templist[:])
return
for i in range(len(nums)):
if i!=0 and nums[i]==nums[i-1]:
continue
else:
templist.append(nums[i])
self.DFS(nums[0:i]+(nums[i+1:]), templist)
templist.pop()
Leetcode 48
思路:
发现:可以作为基础知识:如何将一个矩阵顺时针旋转90度,先转置矩阵,然后每行反转。
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
lennum = len(matrix)
for i in range(lennum):
for j in range(i+1,lennum):
temp = matrix[i][j]
matrix[i][j] = matrix[j][i]
matrix[j][i] = temp
for i in range(lennum):
matrix[i] = matrix[i][::-1]
return matrix
Leetcode 49
思路:列表每个字符串排序,存进字典,键是拍过序的字符串,值是字母相同、顺序不同的字符串。
发现:字典的键不能为列表,所以temp1 = str(sorted(nums))
class Solution:
def groupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
"""
res = {}
for nums in strs:
temp1 = str(sorted(nums))
if temp1 not in res:
res[temp1] = []
res[temp1].append(nums)
return [res[x] for x in res]
Leetcode 50
思路:可以直接return x**n,但是没意思,可以递归,2的10次方等于2的5次方相乘,2的11次方等于2的5次方相乘再乘以2,递归下去。
发现:
class Solution(object):
def myPow(self, x, n):
"""
:type x: float
:type n: int
:rtype: float
"""
if n < 0:
return 1 / self.myPow(x,-1 * n)
if x == 0:
return 0.0
if n == 0:
return 1.0
if n == 1:
return x
tmp = self.myPow(x,n // 2)
if n % 2 == 0:
return tmp * tmp
else:
return tmp * tmp * x
Leetcode 53
思路:遍历是肯定要遍历的,每读一位怎么比较,记录什么?应该要先算以每一位结尾的最大子串是多少,下一位读进来,以下一位结尾的最大子串要么是它本身,要么是它加上以前一位结尾的最大子串(都是局部最优)。
最后,将所有,以每一位结尾的最大子串值进行比较,就得到全局最优。
class Solution:
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
lennum = len(nums)
if lennum ==1:
return nums[0]
res = -99999999
temp = -9999999
for i in range(0, lennum):
temp = max(nums[i], nums[i]+temp)
res = max(res, temp)
return res
Leetcode 54
思路:
从左上开始,按照箭头的顺序,进行4个for循环,完成一圈,一共用count = min(row,col)/2 圈。
注意最后的红色箭头,如果行数和列数中较小的数是奇数,就会出现上述情况。
这个时候咱们在最后补上就好
发现:每一轮循环下标都要注意,第i轮循环,第一次从matrix[i][i] 循环到matrix [i] [col-1-i] (最后一个不取,少一个给第二次循环开头),第二次从matrix[i] [col -1-i] 循环到matrix[row-1-i] col-1-i-1,第三次从matrix[row-1-i ][col-1-i]循环到matrix[row-1-i][i],第四轮次从matrix[row-i-1][i]循环到matrix[row-i-1][i]循环到matrix[i][i]。
class Solution:
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
if matrix == []:
return []
row = len(matrix)
column = len(matrix[0])
rou = min(row, column)//2
mod = min(row, column)%2
res = []
for i in range(rou):
for j in range(i, column-i-1):
res.append(matrix[i][j])
for k in range(i, row-i-1):
res.append(matrix[k][column-i-1])
for l in range(i, column-i-1):
res.append(matrix[row-1-i][column-l-1])
for m in range(i, row-i-1):
res.append(matrix[row-m-1][i])
if mod ==1 :
if row==column:
res.append(matrix[rou][rou])
elif row>column:
for j in range(row-column+1):
res.append(matrix[rou+j][rou])
else:
for j in range(column-row+1):
res.append(matrix[rou][rou+j])
return res
Leetcode 55
思路:记录当前位置能走的最远位置max(maxpos,i+nums[i]),如果最远位置等于该位置,则返回False,如果一直到结尾则返回Ture
发现:必定要到一个位置,同时到这个位置会被卡主。记录每个位置能到达的最远距离,如果一路访问到这个最远距离的位置还出不去,那么就返回false。
能不能到达终点问题,可以转化成每个位置能到达的最远点。
class Solution:
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
maxpoi = 0
for i in range(len(nums)-1):
maxpoi = max(maxpoi, i+nums[i])
if maxpoi == i:
return False
return True
Leetcode 56
思路:首先按照开始区间进行排序sorted,然后从第一个区间开始,如果相邻的两个区间,end小于start则合并区间,且生成新的区间,如果不小于,则放到返回区间
发现:(1)List长度为0和1时,直接返回即可:Line10-11
(2)排序要以start为参考:Line12
(3)迭代判断:Line17-21
a)end小于start则合并区间,且生成新的区间,并且赋给现在位置的list Line17-19
b)不成立,则把前一个区间添加到re Line20-21
(4)要把最后一个区间添加:Line22
# Definition for an interval.
# class Interval:
# def __init__(self, s=0, e=0):
# self.start = s
# self.end = e
class Solution:
def merge(self, intervals):
"""
:type intervals: List[Interval]
:rtype: List[Interval]
"""
lenint = len(intervals)
if lenint<2:
return intervals
intervals = sorted(intervals, key =lambda x:x.start)
re = []
for i in range(1, lenint):
last = intervals[i-1]
now = intervals[i]
if now.start<=last.end:
now.end = max(intervals[i].end, last.end)
now.start = last.start
else:
re.append(intervals[i-1])
re.append(intervals[i])
return re
Leetcode 59
思路:与之前题一样,四个方向去生成,因为长宽相同,偶数的话正好,奇数的话最后要在中间生成一个点。
发现:这种题,首先算轮数n//2,生成一个初始为0的n*n矩阵,res= [[0 for x in range(n)] for x in range(n)],还需要一个计数器计数。注意每个循环的取值,下标取值。
roun = n // 2
res = [[0 for x in range(n)] for x in range(n)]
cnt = 1
for i in range(roun):
for j in range(i, n - i - 1):
res[i][j] = cnt
cnt = cnt + 1
for k in range(i, n - i - 1):
res[k][n - 1 - i] = cnt
cnt = cnt + 1
for l in range(i, n - i - 1):
res[n - 1 - i][n - 1 - l] = cnt
cnt = cnt + 1
for m in range(i, n - i - 1):
res[n - 1 - m][i] = cnt
cnt = cnt + 1
if n % 2 == 1:
res[roun][roun] = cnt
return res
Leetcode60
思路:这种生成全排列第一反应是DFS,但是超时,只能想不用生成全部,单独去找这第k个应该是什么?
发现:
暴力DFS,把所有的都生成出来
class Solution:
def getPermutation(self, n, k):
"""
:type n: int
:type k: int
:rtype: str
"""
nums = [str(i) for i in range(1, n+1)]
res = []
self.DFS(res, [], nums)
return res [k-1]
def DFS(self, res, temp1, nums):
if len(nums) == 0:
res.append("".join(temp1))
for i in range(len(nums)):
temp1.append(nums[i])
self.DFS(res, temp1, nums[:i]+ nums[i+1:])
temp1.pop()
return res
根据一位有多少个全排列依次算
class Solution:
def getPermutation(self, n, k):
"""
:type n: int
:type k: int
:rtype: str
"""
res = ''
k = k-1
fac =1
for i in range(1,n): #第一位确定是的全排列数量
fac= fac*i
nums = [str(i) for i in range(1, n+1)]
for i in reversed(range(n)):
curr =nums[k//fac] #下标是k//fac的数字
res = res + str(curr)
nums.remove(curr) #使用过的数字移除
if i != 0:
k = k%fac
fac = fac //i #(再固定一位全排列)
return res
Leetcode 61
思路:
发现:链表问题就是要画图,找到变化前后区别,头结点在哪,哪些节点下一个改变了
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
if head == None or k ==0:
return head
dummy = ListNode(0) #创建一个虚假的头结点
dummy.next = head
#计算链表长度
cnt =0
temp = dummy
while temp.next != None:
temp = temp.next #最终p指向尾节点
cnt = cnt +1
temp.next = dummy.next #形成一个环
#找到最新的head
right = cnt - k%cnt
temp = dummy.next #指向head
for i in range(1, right): #去找最新的头结点
temp = temp.next
head = temp.next #找到头结点赋给head
temp.next = None
return head
Leetcode 62
思路:动态规划初尝试,因为f(m,n) = f(m-1,n) + f(m,n-1)建立一个m*n的数组,每个格子里存的是当前格子需要的步数。
发现:
class Solution:
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
res=[[1 for i in range(n)] for i in range(m)] #m与n的位置别弄反了,m是行,n是列,第一行,第一列初始值都赋值为1
for i in range(1,m):
for j in range(1,n):
res[i][j]=res[i-1][j]+res[i][j-1]
return res[m-1][n-1]
Leetcode 63
思路:有障碍的地方路径数为0,还是建表,初始化的时候注意第一行和第一列
发现:
m = len(obstacleGrid)
n = len(obstacleGrid[0])
if obstacleGrid[0][0] == 1:
return 0
newGrid = [[0 for i in range(n)] for i in range(m)]
#初始化第一行第一列
for i in range(m):
if obstacleGrid[i][0] !=1:
newGrid[i][0] = 1
else:
break
for j in range(n):
if obstacleGrid[0][j] !=1:
newGrid[0][j] = 1
else:
break
#赋值[1,1]~[m-1][n-1]
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j]!=1:
newGrid[i][j]= newGrid[i-1][j]+ newGrid[i][j-1]
else:
newGrid[i][j]=0
return newGrid[m-1][n-1]
Leetcode64
思路:和前两题一样,newGrid[i][j] = min(newGrid[i-1][j], newGrid[i][j-1]) + grid[i][j]即可
发现:
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
newGrid = [[0 for i in range(n)] for i in range(m)]
#给第一行第一列赋值
newGrid[0][0] = grid[0][0]
for i in range(1, n):
newGrid[0][i] = newGrid[0][i-1] + grid[0][i]
for j in range(1, m ):
newGrid[j][0] = newGrid[j-1][0] + grid[j][0]
for i in range(1, m):
for j in range(1, n):
newGrid[i][j] = min(newGrid[i-1][j], newGrid[i][j-1]) + grid[i][j]
return newGrid[-1][-1]
Leetcode67
思路:先字符串转列表进行计算,最后列表转字符串。因为a和b长度不同,下标计算方式不同,故分开讨论,从后往前分开加,该位和加进位大于2就进位,否则就不进位。最后还有进位的话,就在该列表前插一个1。
发现:列表和字符串相互转换注意,a =list(a)后,列表里元素都是字符串,如需计算,还需挨个int()。列表转字符串,如果两个元素之间不需要空格 “”.join(列表名),如果需要空格”“.join(列表名)
Leetcode 69
思路:通过二分法去查找结果,注意,left<=right
发现:
class Solution:
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
if x < 2:
return x
left = 1
right = x / 2
while left <= right:
mid = int((left + right) / 2)
if(x > mid * mid):
left = mid + 1
lastMid = mid
elif(x < mid * mid):
right = mid - 1
else:
return int(mid)
return int(lastMid)
Leetcode70
思路:明显递归问题f(n)=f(n-1) +f (n-2),但是直接写超时,所以因为问题固定,那么就把数列建起来
发现:
超时算法
class Solution:
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
if n==1:
return 1
if n==2:
return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
不超时算法
class Solution:
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
count = [1,2]
for i in range(2, n):
count.append(count[i-2]+count[i-1])
return count[n-1]
Leetcode 71
思路:看到这种来来回回,增增删删的题,一般都想到用栈。
我们把字符串按照/分割之后就得到了每个文件的目录,然后判断路径是添加还是向上层进行返回。这个题很简单了。
有一个地方犯了小错误,不能写成if route == ‘..’ and stack: stack.pop()。这样的话如果栈是空的,就把..进栈了。
发现:
class Solution:
def simplifyPath(self, path):
"""
:type path: str
:rtype: str
"""
stack = list()
dirs = path.split('/')
for route in dirs:
if not route or route =='.':
continue
if route =='..':
if stack:
stack.pop()
else:
stack.append(route)
return '/' + '/'.join(stack)
Leetcode 73
思路:1、暴力法:设定一个同样大小的矩阵,每次遇到0,然后将第二个矩阵的相应位置置0,空间复杂度为O(mn)。
2、略微优化:每次遇到0的时候,记录需0的行号和列号,空间复杂度为O(m+n)。
3、空间复杂度为1,只需要将2中0的行号列号的记录在第一行和第一列就行了。
利用第一行和第一列的元素去标记该行或该列是否在更新时要全部变成0。但是这样操作会使得第一行和第一列的原始状态丢失。因此,我们需要额外一个变量hasZeros去保存第一列(或者第一行)在更新时是否要变成0,这样就不会有问题了。
另外还有一个让代码更加精简的小窍门,就是在更新的时候从下往上(Bottom-Up),这样就不用担心会破坏第一行或第一列的数据了。
发现:
class Solution:
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
if len(matrix)==0:
return matrix
m = len(matrix)
n = len(matrix[0])
newmatrix = [[0 for i in range(n)] for i in range(m)]
for i in range(m):
for j in range(n):
newmatrix[i][j] = matrix[i][j]
for i in range(m):
for j in range(n):
if matrix[i][j]==0:
for k in range(n):
newmatrix[i][k] = 0
for l in range(m):
newmatrix[l][j] = 0
for i in range(m):
for j in range(n):
matrix[i][j] = newmatrix[i][j]
m = len(matrix)
n = len(matrix[0])
if m == 0 or n == 0:
return
hasZeros = 0
for i in range(m):#第i行
if matrix[i][0] == 0:#第零列
hasZeros = 1
for j in range(1,n):#第i行的第j列
if matrix[i][j] == 0:
matrix[i][0] = 0
matrix[0][j] = 0
for i in range(m-1,-1,-1):#i = 2,1,0
for j in range(n-1,0,-1):#j = 2,1
if matrix[i][0] == 0 ormatrix[0][j] == 0:
#第零列的第i行,或第零行的第i列任一个为零,则将第i行第j列的元素置为零
matrix[i][j] = 0
if hasZeros == 1:
matrix[i][0] = 0
return
Leetcode74
思路:[]、 [[]]等返回false,排序最快还是快排,找准下标,第x个数排进m*n的矩阵,位置在[int(x/n)][x%m]
发现:
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) == 0 or len(matrix[0]) == 0:
return False
m = len(matrix)
n = len(matrix[0])
start, end = 0, m * n - 1
while start <= end:
mid = int(start + (end - start) / 2)
if matrix[int(mid/n)][mid%n] > target:
end = mid - 1
elif matrix[int(mid/n)][mid%n] < target:
start = mid + 1
else:
return True
return False
Leetcode75
思路:0移到最左边,2移到最右边,移动后需继续判断,遇到1则跳过。因为从左边开始循环,必须换到当前位置为0,1才进入下一位,这里的1会被后面的0替换掉
发现:
class Solution:
def sortColors(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
left = 0
right = len(nums) - 1
i = 0
while i <= right:
if nums[i] == 2:
nums[i], nums[right] = nums[right], nums[i]
right -= 1
if nums[i] == 0:
nums[i], nums[left] = nums[left], nums[i]
left += 1
i += 1
continue
if nums[i] == 1:
i += 1
Leetcode 77
思路:这类全排列问题,第一反应是dfs,参数需要记录当前排到哪个数了、当前生成的矩阵、还差几个数、剩下res和n,即[res, i, n, k,temp]。
发现:
class Solution:
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = []
self.dfs(res, 0, n, k, [])
return res
def dfs(self, res, i, n, k, temp):
if k ==0:
res.append(temp)
else:
for i in range(i+1, n+1):
self.dfs(res, i, n, k-1, temp + [i])
Leetcode 78
思路:① 最外层循环逐一从 nums 数组中取出每个元素 num
② 内层循环从原来的结果集中取出每个中间结果集,并向每个中间结果集中添加该 num 元素
③往每个中间结果集中加入 num
④将新的中间结果集加入结果集中
发现:建立过程[]——[],[1]——[],[1],[2]——[], [1],[2],[1,2]——[],[1],[2],[1,2],[3]——[],[1],[2],[1,2],[3][1,3]——[],[1],[2],[1,2],[3],[1,3],[2,3]——[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]
res = [[]]
for num in nums :
for temp in res[:] :
x = temp[:]
x.append(num)
res.append(x)
return res
Leetcode79
思路:对于任何board中的一个元素,如果它上下左右的元素存在并且没有被访问到,则存在着向上,向下,向左,向右的四条搜索路径,那么将这条搜索路径定义成递归函数,这个递归函数需要知道搜索点的位置以及word中剩下的子字符串。递归的终止条件是word中剩下的子字符串为空。但是超时。
发现:
class Solution:
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
def find(board, word, i, j, m, n):
if word == '':
return True
if i < 0 or i >= m or j < 0 or j >= n:
return False
elif word[0] == board[i][j]:
board[i][j] = None #标记
res = find(board, word[1:], i+1, j, m, n)or find(board, word[1:], i-1, j, m, n)or find(board, word[1:], i, j+1, m, n)or find(board, word[1:], i, j-1, m, n)
board[i][j] = word[0] #恢复原来的值
return res
if len(word) == 0:
return True
m = len(board)
if m == 0:
return False
n = len(board[0])
for i in range(m):
for j in range(n):
if find(board, word, i, j, m, n):
return True
else:
return False
Leetcode 80
思路:遍历数组,用一个变量计数,该位如果和前位一样就加1,统计该数出现次数,如果大于2,就把该位删了,如果不同就重新计数。
发现:数组长度变化,所以用while
size = len(nums)
temp = 1
i = 1
if size == 0 or size == 1:
return size
while i < len(nums):
if nums[i] == nums[i-1]:
temp = temp + 1
if temp >2:
del nums[i]
else:
i = i+1
else:
temp = 1
i = i+1
return len(nums)
Leetcode
思路:赖皮,if target in nums:。或者先二分找极小值,这个就是分界点,比两边都大,
发现:
class Solution:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
if target in nums:
return True
else:
return False
Leetcode82
思路:遍历节点,如果相邻两个节点值一样就删掉,问题是如果连续奇数个值以上相同,最后一个数会被错误留下,所以当有连续两个数相同时,需要继续判断后面的数是否一样,如果一样一起删除,所以需要多一个指针cur。
发现:
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dummy = ListNode(0)
dummy.next = head
temp = dummy
while temp.next and temp.next.next:
if temp.next.val == temp.next.next.val:
cur = temp.next
temp.next = temp.next.next.next
while cur and cur.next and cur.val == cur.next.val:
cur = cur.next
temp.next = cur.next
else:
temp = temp.next
return dummy.next
Leetcode 83
思路:既然是链表已经是有序的了,那么只需遍历一次即可。遍历时需要考虑当前结点的值与下一结点的值,如果相等就移除下一结点,注意此时要保持当前结点不变,而下一结点变成原来的下下结点。如果值不同,那么只需把两个结点指针都后移即可。
发现:
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
temp = head
while temp and temp.next:
if temp.next.val == temp.val:
temp.next = temp.next.next
else:
temp =temp.next
return head
Leetcode 86
思路:给个list,给个值,把list分为两部分,小于x的放在前,大于等于x的放在后,两部分各自保持原有顺序
发现:
class Solution:
def partition(self, head, x):
"""
:type head: ListNode
:type x: int
:rtype: ListNode
"""
if head == None or head.next == None or x == None:
return head
p1=head1=ListNode(0)
p2=head2=ListNode(0)
p=head
while p:
if p.valelse:
p2.next=p
p2=p2.next
p=p.next
p1.next=head2.next
p2.next=None
return head1.next
Leetcode88
思路:先把nus1里初始化的0清掉,再把nums2里的元素挨个放进nums1里,最后排序nums1
发现:
class Solution:
def merge(self, nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: void Do not return anything, modify nums1 in-place instead.
"""
for i in range(len(nums1)-m):
nums1.pop()
for j in range(len(nums2)):
nums1.append(nums2[j])
nums1.sort()
Leetcode 89
思路:考虑到格雷码相邻两个二进制数只能在一位上有差异,所以我们可以用一种从最低为开始,逐位加一的方法去解决这个问题,直接看一个例子,我们以n=3为例。
我们可以令一开始的数为000,然后在最低位加一,此时所有已知格雷码为:
000
001
然后我们在上述已知的格雷码中,从下往上在次低位加一:
000
001
011
010
这里要注意加一的顺序,必须要从下往上加,因为新增加的数字如果是从上往下产生的,那么就相当于是同时在多个位上发生改变,而从下往上加可以确保只有一位发生了改变,又因为原先有的相邻格雷码之间只有一位有区别,如果在同一个位置上都加一,相邻两个之间的差异仍然只有一,因此性质还是成立的。
最后我们在最高位上加一,注意还是要从下往上:
000
001
011
010
110
111
101
100
发现:注意镜像规律才能保证相邻位只差一位,需要一个矩阵存储res[::-1]。
class Solution:
def grayCode(self, n):
"""
:type n: int
:rtype: List[int]
"""
res = [0]
i = 0
while i < n:#从2的0次方开始,
res_inv = res[::-1]#求res的反向list
res_inv = [x + pow(2,i) for x in res_inv]
res = res + res_inv
i += 1
return res
Leetcode 90
思路:集合中加入了重复元素,这样可以判断集合中是否已有当前子集,若无则将该子集加入到res中,if x not in res:res.append(x),注意先拍个序,不然会有[1,4],[4,1] 两种组合题都可以用这种添加法
发现:
沿用78题思路版本:
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = [[]]
nums.sort()
for num in nums :
for temp in res[:] :
x = temp[:] #想要判断res里的每一个数组temp加上num是否在res里过,不能直接再temp上变,需要一个临时变量x
x.append(num)
if x not in res:
res.append(x)
return res
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def dfs(depth, start, valuelist):
if valuelist not in res:
res.append(valuelist)
if depth == len(nums):
return
for i in range(start, len(nums)):
dfs(depth+1, i +1, valuelist+[nums[i]])
nums.sort()
res = []
dfs(0, 0, [])
return res
Leetcode 94
思路:递归解法,对于每一个点来说都有一个中序遍历,所以可以写一个re_inorder函数,只要当前节点存在,就对其先求左节点的中序排序,记录当前点,对右节点中序排序。
发现
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
res = []
self.re_inorder(root, res)
return res
def re_inorder(self, root, res):
if root:
self.re_inorder(root.left, res)
res.append(root.val)
self.re_inorder```````
root.right, res)
Leetcode95
思路:二叉查找树,也称为二叉搜索树,二叉排序树。它可以是一棵空树,也可以是具有下列性质的二叉树:
若二叉查找树的左子树不空,则左子树上的所有结点的值均小于它的根结点的值,若右子树不为空,则右子树上所有结点的值均大于它的根结点的值;它的左右子树也分别为二叉排序树。
这个题目难在构造出来。一般构造树都需要递归。从1–n中任意选择一个数当做根节点,所以其左边的数字构成其左子树,右边的数字当做右子树。因为要求出所有的子树,所以当左子树固定的时候,把所有可能的右子树都构成,然后再变换左子树
发现:返回的类型是List[TreeNode]意味着函数是返回树节点类型的结果。这个代码难理解的地方在于left_nodes 和 right_nodes的求法,这个一定要结合递归的终止条件去看,当选择的根节点的值i比left小的时候,那么其实左子树就是空了。如果把这个理解了,那么下面的对左右子树的遍历应该也不难理解
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def generateTrees(self, n):
"""
:type n: int
:rtype: List[TreeNode]
"""
if n == 0: return []
return self.generateTreesDFS(1, n)
def generateTreesDFS(self, left, right):
if left > right:
return [None]
res = []
for i in range(left, right + 1):
left_nodes = self.generateTreesDFS(left, i - 1)
right_nodes = self.generateTreesDFS(i + 1, right)
for left_node in left_nodes:
for right_node in right_nodes:
root = TreeNode(i)
root.left = left_node
root.right = right_node
res.append(root)
return res
Leetcode 96
思路:这里是指不同结构,结构相同的二叉搜索树只有一种分布,因为每个数之间的大小关系都很清晰。我们发现,如果数组为空,毫无疑问,只有一种 BST,即空树,f (0) = 1。
如果数组仅有一个元素 1,只有一种 BST,单个节点,f (1) = 1。
如果数组有两个元素 1,2,那么有如下两种可能
f (2) = f (0) * f (1) ,1 为根的情况
+ f (1) * f (0) , 2 为根的情况
再看一看 3 个元素的数组,可以发现 BST 的取值方式如下:
f (3) = f (0) * f (2) ,1 为根的情况
+ f (1) * f (1) ,2 为根的情况
+ f (2) * f (0) ,3 为根的情
所以,由此观察,可以得出 f 的递推公式为
发现:注意初始值,dp[0]=1,需要一个二重循环,一维动态规划问题。
class Solution:
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0 for i in range(n+1)]
dp[0] = 1
dp[1] = 1
for i in range(2, n+1):
for j in range(0, i):
dp[i] = dp[i] + dp[j]*dp[i-1-j]
return dp[n]
Leetcode 98
思路:一开始想,遍历每个节点,判断它们是否满足左节点比它小,右节点比它大即可,后来发现不对。所以迭代的时候需要时刻记录当前的节点能取的最大值和最小值。
发现:
错误思路代码如下,利用遍历,对每个节点判断,不对。
class Solution:
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
return self.valid(root)
def valid(self, root):
if root:
if (root.left != None and root.left.val >= root.val) or (root.right != None and root.right.val <= root.val):
return False
self.valid(root.left)
self.valid(root.right)
return True
Leetcode118
思路:
发现:初始化temp =[1]*(i+1)很重要,不然不好赋值。
class Solution:
def generate(self, numRows):
"""
:type numRows: int
:rtype: List[List[int]]
"""
res= []
for i in range(numRows):
temp = [1]*(i+1)
res.append(temp)
for j in range(1, i):
res[i][j] = res[i-1][j-1]+res[i-1][j]
return res
Leetcode120
思路:动态规划,每个点记录到达该点要走的最短路径,从底向上计算。
发现:
class Solution:
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
n = len(triangle)
dp = triangle[n-1]
for i in range(0, n-1)[::-1]:
for j in range(i+1):
dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]
return dp[0]
Leetcode 121
思路:第一反应找最大值和最小值,最大的早于最小的,那么就找次大和次小,这样肯定很麻烦,从这个过程已经能感受到一点动态规划的感觉。是的没错,这道题就是用动态规划,我们维持两个变量,最低买入价格和当前可达到的最高利润,从第二天开始遍历,小于最低价格那么我们更新最低价格变量,然后以这一天的价格作为卖出价格,那么利润就是卖出价格-最低价格,最次也就是0,也就是我更新了最低价格还以最低价格卖出去了,因为不能用之前的价格卖,此时利润也要相应的更新,大于保存的最大利润我们就更新,遍历完成后得到结果。
发现:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
if (len(prices) <=1):
return 0
buy_price = prices[0]
max_profit = 0
for i in range(1, len(prices)):
buy_price = min(buy_price, prices[i])
max_profit = max(max_profit, prices[i] - buy_price)
return max_profit
Leetcode122
思路:122题是说不限制买卖股票的次数,只要保证你卖的日期晚于买的日期即可。这个就适合用贪心算法,只要当前比前一天的大,那么我们就卖了股票。
发现:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
n = len(prices)
max_profit = 0
for i in range(1, n):
if prices[i]>prices[i-1]:
max_profit = max_profit +(prices[i] - prices[i-1])
return max_profit
Leetcode123
思路:将天数分开,前i天调用一次121的算法,后面的天调用一次121的算法,但是要注意如果外层循环i,里面再循环121的算法,会超时,这时我们考虑用两个数组来存储结果,pre_profit和pro_profit,其中pre_profit[i]表示i天之前的最大利润,pro_profit[i]表示i天之后的最大利润,前i天的很好理解和121一样的写法,后i天注意要从后往前动态规划。
发现:
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
n = len(prices)
if n == 0:
return 0
maxpre = [0 for i in range(n)]
maxpro = [0 for i in range(n)]
max_profit = 0
pre_min = prices[0]
for i in range(1, n):
pre_min = min(pre_min, prices[i])
maxpre[i] = max([maxpre[i-1], prices[i] - pre_min])
pro_max = prices[-1]
for i in range(1, n-1)[::-1]:
pro_max = max(pro_max, prices[i])
maxpro[i] = max(maxpro[i+1], pro_max - prices[i])
for i in range(n):
max_profit = max(max_profit, maxpre[i] + maxpro[i])
return max_profit
Leetcode 125
思路:主要是对字符串进行处理,保留数字字母并判断是否回文
发现:
class Solution:
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
string = []
for i in range(len(s)):
if s[i].isalnum():
string.append(s[i].lower())
if string == string[::-1]:
return True
else:
return False
超级简洁版本:
cleanlist = [c for c in s.lower() if c.isalnum()]
return cleanlist == cleanlist[::-1]
Leetcode 128
思路:将这些数放进一个集合,对集合里的数遍历,每个数去向上向下找下一个数在不在,找到了就长度加1,集合里去掉这个数,最后返回最长的子串
发现:
用集合:
class Solution:
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
s = set(nums)
for num in nums:
if num in s:
s.discard(num)
cnt = 1
right = num + 1
left = num - 1
while left in s:
s.discard(left)
cnt += 1
left -= 1
while right in s:
s.discard(right)
cnt += 1
right += 1
ans = max(ans, cnt)
return ans
用哈希表,键是数组值,值记录是否用过:
class Solution:
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
d ={}
for num in nums:
d[num] = 0
for num in nums:
cnt = 1
d[num] = 1
left = num - 1
right = num + 1
while left in d and d[left] == 0:
d[left] = 1
left = left - 1
cnt = cnt + 1
while right in d and d[right] == 0:
d[right] = 1
right = right +1
cnt = cnt +1
ans = max(ans, cnt)
return ans
Leetcode135
思路:结果数组是res的话,初始全是1,我一开始想着遍历一遍,两个相邻依次比,rating大的那个res里对应改为rating小的+1,正着一遍if rating[i
]>rating[i-1]:res[i] = res[i-1] +1 if rating[i]
class Solution(object):
def candy(self, ratings):
"""
:type ratings: List[int]
:rtype: int
"""
n = len(ratings)
left = [1] * n
ans = 0
for i in range(1, n):
if ratings[i] > ratings[i-1]:
left[i] = left[i-1] + 1
ans = left[-1]
for i in reversed(range(0, n - 1)):
if ratings[i] > ratings[i+1]:
left[i] = max(left[i], left[i+1] + 1)
ans += left[i]
return ans
Leetcode136
思路:想到用哈希表做
发现:取字典里值最小对应的键值res = min(temp, key=temp.get)
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
temp = {}
for i in range(len(nums)):
if nums[i] not in temp:
temp[nums[i]] = 1
else:
temp[nums[i]] = 2
res = min(temp, key=temp.get)
return res
另一种字典操作方法:
dic = {}
for num in nums:
dic[num] = dic.get(num, 0)+1
for key, val in dic.items():
if val == 1:
return key
Leetcode136
思路:可用同样方法
发现:
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
temp = {}
for i in range(len(nums)):
if nums[i] not in temp:
temp[nums[i]] = 1
else:
temp[nums[i]] = 3
return min(temp, key=temp.get)
Leetcode 139
思路:# dp[i] = true means that s[0, i -1] can be constructed by the words in wordDict. # So, dp[0] must be ture.
发现:dp解决此类问题,关键在于遍历什么,每个位置存什么,此题中,每个位置应当存的是s[0, i-1]是否可以由wordDict里的词构成。所以需要一个二重循环判定从0到i进行分。
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
n = len(s)
dp = [True]+[False]*n
for i in range(n):
for j in range(i+1):
if dp[j] and s[j:i+1] in wordDict:
dp[i+1] = True
break
return dp[n]
Leetcode151
思路:
发现:
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
rs=s[::-1] #将整个字符串反转
l=rs.split() #将反转后的字符串通过split()函数进行分割,产生的单词发在列表l中
ls=[word[::-1] for word in l] #使用列表解析,反转列表l中的每一个单词
return ' '.join(ls)
Leetcode 152
思路:本题要求连续子数组的最大乘积,思路与求连续子数组的最大和相似,都是采用动态规划,maxvalue[i]表示以a[i]为结尾的子数组中最大乘积,同时维护一个全局最大值globalmax,记录maxvalue[i]中的最大值。与求子数组的最大和不同的是,还需要维记录子数组最小乘积minvalue[i],因为可能会出现 负 × 负 = 正的情况。并且最大最小乘积只可能出现在
(maxvalue[i−1]×a[i],minvalue[i−1]×a[i],a[i])三者之间
发现:
class Solution(object):
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
maxvalue = minvalue = nums[0]
globalmax = nums[0]
for i in range(1, n):
lastmax = maxvalue
maxvalue = max(minvalue*nums[i], lastmax*nums[i], nums[i])
minvalue = min(minvalue*nums[i], lastmax*nums[i], nums[i])
globalmax = max(globalmax, maxvalue)
return globalmax
Leetcode 153
思路:对于没有重复数字的数组, 旋转之后的数组可以看做两个升序数组,而且前面数组的元素都比后面数组元素大。可以用二分查找
发现:
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
left, right = 0, len(nums)-1
while left < right:
mid = left + (right - left) // 2 # 地板除,舍去小数部分
if nums[mid] < nums[right]: # 移动右边显然是更安全的选择
right = mid
else:
left = mid + 1
return nums[left]
Leetcode154
思路:
发现:
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = nums[0]
start, end = 0, len(nums) - 1
while start + 1 < end:
mid = ( start + end ) / 2
if nums[start] < nums[mid]:
start = mid
elif nums[start] > nums[mid]:
end = mid
else:
start += 1
ans = min(ans, nums[start])
return min(ans, nums[start], nums[end])
Leetcode 155
思路:简单
发现:
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.l = []
def push(self, x):
"""
:type x: int
:rtype: void
"""
if x is None:
pass
else:
self.l.append(x)
def pop(self):
"""
:rtype: void
"""
if self.l is None:
return 'error'
else:
self.l.pop(-1)
def top(self):
"""
:rtype: int
"""
if self.l is None:
return 'error'
else:
return self.l[-1]
def getMin(self):
"""
:rtype: int
"""
if self.l is None:
return 'error'
else:
return min(self.l)
Leetcode 160
思路:
发现:集合里也可以放数据结构如链表节点
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
A_set=set()
while headA:
A_set.add(headA)
headA = headA.next
while headB:
if headB in A_set:
return headB
headB = headB.next
return None
Leetcode 162
思路:easy,比较左右值即可,需要注意最后一个元素比倒数第二个大也算峰值
发现:
class Solution(object):
def findPeakElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(1, len(nums)-1):
if nums[i]> nums[i-1] and nums[i]>nums[i+1]:
return i
if nums[len(nums)-1] > nums[len(nums)-2]:
return len(nums)-1
return 0
Leetcode167
思路:用夹逼法和哈希表法都可以
发现:
class Solution(object):
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
left = 0
right = len(numbers) -1
while(left < right):
if(numbers[left] + numbers[right] == target):
return [left+1,right+1]
elif(numbers[left] + numbers[right] < target):
left += 1
else:
right -= 1
temp = dict()
result = [-1, -1]
for i in range(len(nums)):
if target-nums[i] in temp.keys():
result[1] = i
result[0] = temp.get(target - nums[i])
break
else:
temp[nums[i]] = i
return result
Leetcode 168
思路:相当于26进制
发现:
class Solution:
def convertToTitle(self, n):
"""
:type n: int
:rtype: str
"""
result=''
while n>0:
rest=(n-1)%26
result=chr(rest+65)+result
n=int((n-1)/26)
return result
Leetcode169
思路:排序后取中间那位即可
发现:
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return sorted(nums)[int(len(nums)/2)]
Leetcode 171
思路:26进制转十进制
发现:
class Solution:
def titleToNumber(self, s):
"""
:type s: str
:rtype: int
"""
result=0
n=len(s)
for i in range(n):
result=result*26+ord(s[i])-64
return result
Leetcode172
思路:求其中2*5的个数,也就是5的个数,因为每一个偶数都含2,只要有5肯定有2,使用n除以5即可
发现:
class Solution:
def trailingZeroes(self, n):
"""
:type n: int
:rtype: int
"""
r = 0
while n >= 5:
n = n // 5
r+=n
return r
Leetcode 189
思路:
发现:可以固定为这种写法了
k = k % len(nums)
nums[:k], nums[k:] = nums[len(nums)-k:], nums[:len(nums)-k]
Leetcode191
思路:bin(int) 转化为二进制数
发现:
class Solution(object):
def hammingWeight(self, n):
"""
:type n: int
:rtype: int
"""
return bin(n).count('1')
Leetcode 202
思路:按照“happy number”的定义,直接循环计算各位平方和,观察是否收敛到1,若是则是 happy number。为了判断循环是否开始重复,要用一个字典(dict)或集合(set)来保存已经出现的数字,dict的效率更高
发现:可以对算法进行优化。比如利用10以内的happy number只有1和7,或者先求出100以内的所有happy number等。
超时算法:
num_dict = {}
while True:
num_dict[n] = True
sum = 0
while(n>0):
sum += (n%10)*(n%10)
n /= 10
if sum == 1:
return True
elif sum in num_dict:
return False
else:
n = sum
AC算法:
happySet = set([1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97])
while n>99:
n = sum([int(x) * int(x) for x in list(str(n))])
return n in happySet
Leetcode 203
思路:字面意思写
发现:
if head == None:
return head
dummy = ListNode(0)
dummy.next = head
pre = dummy
while head:
if head.val == val:
pre.next = head.next
head = pre
pre = head
head = head.next
return dummy.next
Leetcode204
思路:开辟一个辅助数组,依次标记2−√n的所有倍数。最后遍历该数组,计数素数。
发现:
if n is None or n <= 1:
return 0
tmp = [True] * n
tmp[0] = False
tmp[1] = False
i = 2
while i * i < n:
if tmp[i]:
j = i
while j * i < n:
tmp[i * j] = False
j += 1
i += 1
res = 0
for k in tmp:
if k:
res += 1
return res