leetcode面试刷题笔记--python

1.两数之和:

做题要点:字典

利用map性质,当target-nums[i]在map中时,直接返回,如果不在则将nums[i]加入到map中即可。

1 class Solution:
2     def twoSum(self, nums: List[int], target: int) -> List[int]:
3         dic = {}
4         for i in range(len(nums)):
5             if target - nums[i] in dic.keys():
6                 return([i, dic.get(target - nums[i])])
7             dic[nums[i]] = i

时间空间复杂度O(n)

 

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

做题要点:滑动窗口,字典,双指针

利用滑动窗口方法,记指针i=0,j=0.同时建立字典。循环遍历数组元素,当数组元素不包含于字典时,i++,同时将该元素添加到字典中。如果字典中包含该元素,则将字典中s[j]删除,并且将j++。最后返回字典长度最大值即可。

 1 class Solution:
 2     def lengthOfLongestSubstring(self, s: str) -> int:
 3         i = 0  # 左指针
 4         j = 0  # 右指针
 5         dic = {}
 6         maxv = 0
 7         while i < len(s):
 8             if s[i] not in dic.values():
 9                 dic[s[i]] = s[i]
10                 i += 1
11             else:
12                 dic.pop(s[j])
13                 j += 1
14             if maxv < len(dic):
15                 maxv = len(dic)
16         return maxv
View Code

 

4.两数相加:

做题要点:数学

1.暴力还原整数再加再转换成链表:先将两个链表转换成两个整数,两整数相加,将结果转换为链表

 1 class Solution:
 2     def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
 3         num1 = ""
 4         num2 = ""
 5         while l1 is not None:
 6             num1 += str(l1.val)
 7             l1 = l1.next
 8         while l2 is not None:
 9             num2 += str(l2.val)
10             l2 = l2.next
11         ans = str(int(num1[::-1]) + int(num2[::-1]))
12         length = len(ans)
13         result = ListNode(int(ans[0]))
14         for i in range(1, length):
15             result = ListNode(int(ans[i]), result)
16         return result
View Code

2.合理利用进位

用s来存储每位相加的结果,同时保存进位结果。遍历链表,当l1不为空或者l2不为空或者s不为0时,执行两链表按位相加的操作,将结果保存在s,用s的个位建立新节点,同时再将s除十取floor即可(保存进位)。

 1     def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
 2         head = p = ListNode(0)
 3         s = 0
 4         while l1 or l2 or s != 0:
 5             s += (l1.val if l1 else 0) + (l2.val if l2 else 0)
 6             p.next = ListNode(s % 10)
 7             p = p.next
 8             s //= 10  # 进位
 9             l1 = l1.next if l1 else None
10             l2 = l2.next if l2 else None
11         return head.next

 

15.三数之和:

做题要点:双指针

本题目与第一题有一定的联系,我们首先将整个数组进行排序遍历nums数组中的元素nums[i]。同时令L为i+1,R为len(nums)-1。当nums[i]=nums[i-1]时,跳过该次循环,因为此时会造成重复解。当L小于R时,我们进行循环判断nums[L]+nums[R]+nums[i]是否为0,如果为0,将其加入答案并跳过L,R相邻重复元素。如果>0则说明nums[R]过大,故将R--。如果<0则说明nums[L]过小,将L++。

 1 class Solution:
 2     def threeSum(self, nums: List[int]) -> List[List[int]]:
 3         if len(nums)<3:
 4             return []
 5         nums.sort()
 6         ans = []
 7         for i in range(len(nums)):
 8             if nums[i]>0:
 9                 return ans
10             if(i>0 and nums[i]==nums[i-1]):
11                 continue
12             L = i+1
13             R = len(nums)-1
14             while L < R:
15                 if nums[i] + nums[L] + nums[R] == 0:
16                     ans.append([nums[i],nums[L],nums[R]])
17                     # 去除重复解!!
18                     while(L<R and nums[L]==nums[L+1]):
19                         L=L+1
20                     while(L<R and nums[R]==nums[R-1]):
21                         R=R-1
22                     L=L+1
23                     R=R-1
24                 elif nums[i] + nums[L] + nums[R] > 0:
25                     R-=1
26                 elif nums[i] + nums[L] + nums[R] < 0:
27                     L+=1
28         return ans
View Code

时间复杂度O(n2),空间复杂度O(1)

 

15.K个一组翻转链表:

结合翻转链表的解法,加入count对已经反转的节点进行计数。当k=1或头节点为空或链表长度为1时表示不需要翻转,直接返回头节点。每翻转一次,将count自增一,当count%k为0时,将pre赋值为cur节点,同时,将cur节点向后挪动一个节点。并且将count自增。

 1 class Solution:
 2         def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
 3             if head is None or head.next is None or k == 1:
 4                 return head
 5             count = 1
 6             leng = 0
 7             node = head
 8             while node:
 9                 leng+=1
10                 node = node.next
11             pre = ListNode(0)
12             ans = ListNode(0)
13             pre.next = head
14             ans.next = head
15             cur = head
16             while leng - count > leng % k and cur.next:
17                 next_n = cur.next
18                 cur.next = next_n.next
19                 next_n.next = pre.next
20                 count += 1
21                 pre.next = next_n
22                 if count == k:
23                     ans.next = next_n
24                 if count % k == 0:
25                     pre = cur
26                     cur = cur.next
27                     count += 1
28             return ans.next
View Code

时间复杂度O(n),空间复杂度O(1)

 

11. 盛最多水的容器:

使用双指针法,从两侧向内收缩。注意指针变动的规律:将hi和hj中值较小的下标向内收缩!因为如果值较大的下标收缩面积必定减小!

 1 def maxArea(self, height: List[int]) -> int:
 2         i = 0
 3         j = len(height)-1
 4         max_v = 0
 5         while i < j:
 6             max_v = max((j-i)*min(height[i],height[j]),max_v)
 7             if height[i+1] > min(height[i],height[j]):
 8                 i+=1
 9             elif height[j-1] > min(height[i],height[j]):
10                 j-=1
11             else:
12                 i+=2
13                 j-=2
14         return max_v
View Code

 

33. 搜索旋转排序数组:

本题目可以结合之前做过的一个题目:找寻旋转数组的最小值。我们可以先找到旋转数组的最小值,以此最小值为分界点,对左右两个排序数组进行二分查找。

 1 class Solution:
 2     def search(self, nums: List[int], target: int) -> int:
 3         index = 0
 4         left = 0
 5         right = len(nums)-1
 6         while left != right:
 7             mid = int((right - left)/2) + left
 8             if nums[mid] > nums[right]: left = mid +1
 9             elif nums[mid] < nums[right]: right = mid
10             else: right-=1
11         index = right
12         # 对左右两侧分别进行二分查找
13         left1 = 0
14         right1 = index-1
15         while left1 < right1:
16             mid = int((right1 - left1)/2) + left1
17             if nums[mid] == target: return mid
18             elif nums[mid] > target: right1 = mid
19             else: left1 = mid+1
20         if nums[left1] == target: return left1
21         left2 = index
22         right2 = len(nums)-1
23         while left2 < right2:
24             mid = int((right2 - left2)/2) + left2
25             if nums[mid] == target: return mid
26             elif nums[mid] > target: right2 = mid
27             else: left2 = mid+1
28         if nums[left2] == target: return left2
29         return -1
View Code

时间复杂度O(nlogn),空间复杂度O(1)

 

23. 合并k个升序链表:

由本题可以联想到合并两个升序链表,结合分治算法,两两归并即可。

 1 class Solution:
 2     def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
 3         node = head = ListNode(0)
 4         while l1 and l2:
 5             if l1.val > l2.val:
 6                 node.next = l2
 7                 l2 = l2.next
 8                 node = node.next
 9             else:
10                 node.next = l1
11                 l1 = l1.next
12                 node = node.next
13         while l1:
14             node.next = l1
15             l1 = l1.next
16             node = node.next
17         while l2:
18             node.next = l2
19             l2 = l2.next
20             node = node.next
21         return head.next
22 
23     def merge(self,left,right,lists):
24         if left == right - 1:
25             # 可以两两合并
26             return self.mergeTwoLists(lists[left],lists[right])
27         if left == right:
28             # 不需要合并
29             return lists[left]
30         mid = int((right - left)/2) + left
31         l = self.merge(left,mid,lists)
32         r = self.merge(mid+1,right,lists)
33         return self.mergeTwoLists(l,r)
34 
35     def mergeKLists(self, lists: List[ListNode]) -> ListNode:
36         if len(lists) == 0:
37             return None
38         return self.merge(0,len(lists)-1,lists)

时间复杂度O(nlogk),k是链表个数,n是元素总数。空间复杂度为O(1)

 

56. 合并区间:

先将整个列表按第一个元素进行排序,再新建列表ans,将其初始化为intervals[0]。遍历intervals[1:],将元组记为x,y。判断ans[-1][1]和x的大小,如果x>ans[-1][1],说明区间没有重叠,直接将[x,y]加入ans。如果x<=ans[-1][1]。说明区间有重叠。故将ans[-1][1]赋值为y和ans[-1][1]的较大值即可。

 1 def takeFirst(ele):
 2         return ele[0]
 3 
 4 class Solution:
 5     def merge(self, intervals: List[List[int]]) -> List[List[int]]:
 6         if len(intervals) == 0:
 7             return []
 8         intervals.sort(key=takeFirst)
 9         ans = []
10         ans.append(intervals[0])
11         for x,y in intervals[1:]:
12             if x > ans[-1][1]:
13                 # 说明没有区间重叠,故直接append
14                 ans.append([x,y])
15             else:
16                 # 说明有区间重叠,此时将ans[-1][1]赋值为y和ans[-1][1]的较大值
17                 ans[-1][1] = max(y,ans[-1][1])
18         return ans
View Code

时间复杂度O(nlogn),空间复杂度O(1)

 

46. 全排列:

使用回溯算法,遍历所有可能即可!递归要熟练。

 1 class Solution:
 2     def permute(self, nums: List[int]) -> List[List[int]]:
 3         ans = []
 4         used = [0 for i in range(len(nums))]
 5         def backtrack(nums,path):
 6             if not nums:
 7                 # 此时已经满足条件
 8                 ans.append(path)
 9                 return
10             for i in range(len(nums)):
11                 # 遍历每一个可能的元素进行下一层递归!
12                 backtrack(nums[:i] + nums[i+1:],path+[nums[i]])
13             return
14         backtrack(nums,[])
15         return ans
16 
17     
View Code

时间复杂度O(n!)

 

39. 组合总和:

使用回溯算法,遍历所有可能即可。要对递归熟练。同时要合理利用约束条件降低时间复杂度!!!

 1 class Solution:
 2     def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
 3         res = []
 4         def backtrack(candidates,path):
 5             if sum(path) == target:
 6                 temp = sorted(path)
 7                 if temp not in res:
 8                     res.append(temp) 
 9                 return
10             if sum(path) > target: #说明此时已经不可能找到了
11                 return
12             for i in range(len(candidates)):
13                 # 不可被重复选取
14                 # backtrack(candidates[:i]+candidates[i+1:],path+[candidates[i]])
15                 # 可被无限制重复选取
16                 backtrack(candidates,path+[candidates[i]])
17             return
18         backtrack(candidates,[])
19         return res
View Code

 

39. 滑动窗口的最大值:

使用双端队列,维护一个元素从队首到队尾一次变小的双端队列,每次取最大值即为队首值!注意新加入元素的位置以及队首元素是否还在窗口中即可。

 1 class Solution:
 2     def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
 3         if not nums:
 4             return []
 5         ans = []
 6         L = 0
 7         R = 0
 8         deque = []
 9         # 初始化滑动窗口
10         for i in range(0,k):
11             while deque and nums[deque[-1]] < nums[i]:
12                 deque.pop(-1)
13             deque.append(i)
14             R+=1
15         # 注意窗口L-R闭区间
16         ans.append(nums[deque[0]])
17         # 窗口开始滑动
18         while R < len(nums):
19             # 找到新加入窗口的元素位置
20             while deque and nums[deque[-1]] < nums[R]:
21                 deque.pop(-1)
22             deque.append(R)
23             # 将不在窗口中的元素从队首pop,注意窗口区间一开一闭!
24             while deque and (deque[0] <= L or deque[0] > R):
25                 deque.pop(0)
26             deque.append(R)
27             ans.append(nums[deque[0]])
28             L+=1
29             R+=1
30         return ans
View Code

时间复杂度:O(n),每个元素最多进一次队列出一次队列最多操作2n次,空间复杂度O(n)。

 

38. 字符串的排列:

仍然是使用回溯法,注意如何去重:对字符串排好序之后,在遍历元素递归之前,如果当前元素和前一个元素相等,则说明前面已经对该字母进行过递归!故跳过此循环。

 1 class Solution:
 2     def permutation(self, s: str) -> List[str]:
 3         res = []
 4         # 排序后方便进行去重
 5         s = list(sorted(s))
 6         def backtrack(now,path):
 7             if not now:
 8                 res.append(path)
 9                 return
10                 # 说明此排列已经完成
11             for i in range(len(now)):
12                 # 如果当前元素和前一个元素相同,则重复!
13                 if i > 0 and now[i] == now[i-1]:
14                     continue
15                 backtrack(now[:i]+now[i+1:],path+now[i])
16         backtrack(s,"")
17         return res
View Code

时间复杂度O(n!)

 

31. 下一个排列:

针对本题目,可以设计如下算法:

1.遍历数组找到满足nums[k]<nums[k+1]的最大的k,如果没找到说明数组降序排列,此时直接数组逆序。

2.遍历数组找到满足nums[l]>nums[k]的最大的l,将nums[k]和nums[l]交换,再将nums[k+1:]逆序即可!

原理:找第一个的过程从后往前找nums[k] < nums[k+1], 意味着k+1之后是不上升的 nums[j]>=nums[j+1], j>k, 因此,后面的是逆序的,即最大字典序,找到的值是比nums[k]大的最小值,设为nums[t],意味着nums[t]>nums[k]>nums[t+1], 交换nums[k]与nums[t]后最大字典序不变,逆序之后即最小字典序。

 1 class Solution:
 2     def nextPermutation(self, nums: List[int]) -> None:
 3         """
 4         Do not return anything, modify nums in-place instead.
 5         """
 6         index1 = 0
 7         flag = 0
 8         index2 = 0
 9         for i in range(len(nums)):
10             if i+1>=len(nums):
11                 if flag == 0:
12                     index1 = -1
13                 break
14             if nums[i]<nums[i+1]:
15                 flag = 1
16                 index1 = i
17         if index1 == -1:
18             nums.reverse()
19             return
20         for i in range(len(nums)):
21             if nums[i]>nums[index1]:
22                 index2 = i
23         nums[index1],nums[index2] = nums[index2],nums[index1]
24         # 翻转nums[index1+1:]
25         nums[index1+1:] = list(reversed(nums[index1+1:]))
26         
View Code

 时间复杂度:O(n)

199. 二叉树的右视图:

对于本题目,本人采用层序遍历的方法,从左到右遍历每一层时将最后一个节点加入到ans中即可。

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def rightSideView(self, root: TreeNode) -> List[int]:
10         # 按层进行BFS,每次添加到list中的都是queue的最后一个元素即可。 
11         if not root:
12             return []
13         ans = []
14         queue = []
15         queue.append(root)
16         ans.append(root.val)
17         while queue:
18             temp = []
19             # 将下一层的节点从左向右全部加入temp中
20             for node in queue:
21                 if node.left:
22                     temp.append(node.left)
23                 if node.right:
24                     temp.append(node.right)
25             #将最靠右的节点加入ans中即为该层的右视图
26             if temp:
27                 ans.append(temp[-1].val)
28             queue = temp
29         return ans
View Code

时间复杂度:O(n)

 

215. 数组中的第k个最大元素:

老题重做,对于本题而言,利用快速排序的partition函数进行递归即可。注意左右边界的传入!

 1 class Solution:
 2     def partition(self,nums,left,right,target):
 3         # 随机化
 4         random_index = random.randint(left, right)
 5         nums[random_index],nums[right] = nums[right],nums[random_index]
 6         main = nums[right]
 7         i = left-1
 8         j = left
 9         while j<right:
10             if nums[j]>main:
11                 nums[i+1],nums[j] = nums[j],nums[i+1]
12                 i+=1
13             j+=1
14         nums[i+1],nums[right] = nums[right],nums[i+1]
15         # 数组下标从0计数,故加一
16         now = i+1+1-left
17         if now == target:
18             return nums[now-1+left]
19         elif now < target:
20             # 说明当前元素比target对应元素大,故应从右半部分递归搜索第target-now大的元素
21             return self.partition(nums,left + now,right,target-now)
22         elif now > target:
23             # 说明当前元素比target对应元素小,故应从左半部分递归
24             return self.partition(nums,left,left-1 + now-1,target)
25 
26 
27     def findKthLargest(self, nums: List[int], k: int) -> int:
28         return self.partition(nums,0,len(nums)-1,k)
View Code

时间复杂度O(n)

 

9. 回文数:

可以用栈将数字进行倒序,可以转换为字符串进行判断。也可以按如下方式:

 1 class Solution:
 2     def isPalindrome(self, x: int) -> bool:
 3         if x < 0:
 4             return False
 5         else:
 6             num = x
 7             ans = 0
 8             while num > 0:
 9                 ans += num%10
10                 num//=10
11                 if num > 0:
12                     ans *= 10
13         return ans == x
View Code

时间复杂度O(n),空间复杂度O(1)

 

88. 合并两个有序数组:

归并排序的merge操作即可,注意此处要将nums2合并到nums1中,因此我们需要倒序遍历。

 1 class Solution:
 2     def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
 3         """
 4         Do not return anything, modify nums1 in-place instead.
 5         """
 6         i = m-1
 7         j = n-1
 8         k = len(nums1)-1
 9         while i >= 0 and j >= 0:
10             if nums1[i] > nums2[j]:
11                 nums1[k] = nums1[i]
12                 k-=1
13                 i-=1
14             else:
15                 nums1[k] = nums2[j]
16                 k-=1
17                 j-=1
18         while i >= 0:
19             nums1[k] = nums1[i]
20             k-=1
21             i-=1
22         while j >= 0:
23             nums1[k] = nums2[j]
24             k-=1
25             j-=1
View Code

时间复杂度O(n),空间复杂度O(1)

 

14. 最长公共前缀

遍历即可。

 1 class Solution:
 2     def longestCommonPrefix(self, strs: List[str]) -> str:
 3         if len(strs) == 0:
 4             return ""
 5         min_len = 10086
 6         ans = ""
 7         for s in strs:
 8             if len(s)<min_len:
 9                 min_len = len(s)
10         for i in range(min_len):
11             for j in range(1,len(strs)):
12                 if strs[j][i] != strs[j-1][i]:
13                     return ans
14             ans += strs[0][i]
15         return ans
View Code

时间复杂度O(s),s为所有字符串长度和,空间复杂度O(1)

 

141. 环形链表

快慢指针即可解决。快指针每次走两个节点,慢指针每次走一个节点,如果存在环则快慢指针必然相遇。

 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.next = None
 6 
 7 class Solution:
 8     def hasCycle(self, head: ListNode) -> bool:
 9         slow = head
10         fast = head
11         while fast and fast.next:
12             fast = fast.next.next
13             slow = slow.next
14             if fast == slow:
15                 return True
16         return False
View Code

时间复杂度O(n),空间复杂度O(1)

 

112. 路径总和

对左右子树递归dfs即可。

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def hasPathSum(self, root: TreeNode, sum: int) -> bool:
10         if not root:
11             return False
12         left = False
13         right = False
14         if root.left:
15            left = self.hasPathSum(root.left,sum-root.val)
16         if root.right:
17            right = self.hasPathSum(root.right,sum-root.val)
18         if root.val == sum and not root.left and not root.right :
19             # 说明此时root为叶子结点
20             return True
21         # 说明此时不为叶子结点或者是不满足和为sum
22         return left or right
23         
View Code

时间复杂度O(n),空间复杂度O(n)

 

160. 相交链表

设置两个node,node1和node2,令其next分别为headA和headB。让node1和node2同时向后走一个节点。如果node走到了链表末尾也即为null,将其重置为另一个链表的head即可。当node1等于node2时,则为相交链表的第一个相交节点。

 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.next = None
 6 
 7 class Solution:
 8     def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
 9         # 双指针
10         node1 = ListNode(0)
11         node2 = ListNode(0)
12         node1.next = headA
13         node2.next = headB
14         while True:
15             if node1:
16                 node1 = node1.next
17             elif not node1:
18                 # 此时将node1重置到headB
19                 node1 = headB
20             if node2:
21                 node2 = node2.next
22             elif not node2:
23                 # 此时将node2重置到headA
24                 node2 = headA
25             if node2 == node1:
26                 return node1
View Code

时间复杂度O(n),空间复杂度O(1)

 

176. 第二高的薪水

sql嵌套,先找最高薪水,再找小于最高薪水的最高薪水即可。

# Write your MySQL query statement below
select max(salary) SecondHighestSalary from employee where salary < (select max(salary) from employee)

101. 对称二叉树

递归即可,注意对null情况进行完备地判断!

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def isSymmetric(self, root: TreeNode) -> bool:
10         def symmetric(node1,node2):
11             if not node1 and not node2:
12                 # 两节点都为null
13                 return True
14             if node1 and not node2 or node2 and not node1:
15                 # 一个null一个有值false
16                 return False
17             if node1.val!=node2.val:
18                 return False
19             if not node1.left and not node1.right and not node2.left and not node2.right and node1.val==node2.val:
20                 # 叶子节点必对称
21                 return True
22             return symmetric(node1.left,node2.right) and symmetric(node1.right,node2.left)
23         if not root or not root.right and not root.left:
24             return True
25         return symmetric(root.left,root.right)
26             
27 
28         
29         
View Code

时间复杂度O(n),空间复杂度O(n)

 

175. 组合两个表

person左连接address即可保留address所有属性,注意平时的限制关键词where应该改成on!

# Write your MySQL query statement below
select FirstName,LastName,City,State from Person left join Address on Person.PersonId = Address.PersonId

543. 二叉树的直径:

对于此题目,对二叉树进行后序遍历即可:当前节点为null时,说明此时直径为0,如果当前节点不为null,求其左子树直径和右子树直径。如果左右子树直径之和大于当前保存的最大直径,则将最大值替换为左右子树直径之和。并将左右子树直径较大值+1返回上一层。

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def diameterOfBinaryTree(self, root: TreeNode) -> int:
10         path = []
11         # 后序遍历即可
12         def dfs(node):
13             if not node:
14                 return 0
15             left = dfs(node.left)
16             right = dfs(node.right)
17             # 1代表当前节点!
18             path.append(left + right)
19             return max(left+1,right+1)
20         dfs(root)
21         #print(path)
22         if not path:
23             return 0
24         return max(path)
View Code

时间复杂度O(n),空间复杂度O(n)

 

103. 二叉树的锯齿形层次遍历:

对于此题目,在二叉树的层序遍历基础上新加一个层数判断即可。

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
10         if not root:
11             return []
12         ans = []
13         queue = []
14         queue.append(root)
15         ans.append([root.val])
16         count = 1
17         while queue:
18             temp = []
19             for i in queue:
20                 if i.left:
21                     temp.append(i.left)
22                 if i.right:
23                     temp.append(i.right)
24             if temp:
25                 if count %2 != 0:
26                     # 说明此时需要逆序遍历
27                     ans_t = []
28                     for i in temp[::-1]:
29                         ans_t.append(i.val)    
30                     ans.append(ans_t)
31                 else:
32                     ans_t = []
33                     for i in temp:
34                         ans_t.append(i.val)    
35                     ans.append(ans_t)
36             count+=1
37             queue = temp
38         return ans
View Code

时间复杂度O(n),空间复杂度O(n)

 

19. 删除链表的倒数第N个节点:

双指针法:定义node1和node2,两节点均初始化为头结点,先让node2走n+1个节点,同时如果node2为空同时还要继续前进时说明要删除头结点,此时直接返回第二个节点即可。待node2遍历完成后,让node1和node2同时前进,当node2为null时,说明node1.next为待删除节点,删除即可。

 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, val=0, next=None):
 4 #         self.val = val
 5 #         self.next = next
 6 class Solution:
 7     def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
 8         node1 = head
 9         node2 = head
10         ans = head
11         for i in range(n+1):
12             # node2提前走n个节点
13             if node2:
14                 node2 = node2.next
15             else:
16                 return ans.next
17         while node2:
18             node1 = node1.next
19             node2 = node2.next
20         node1.next = node1.next.next
21         return ans
22             
23         
View Code

时间复杂度O(n),空间复杂度O(n)

 

92. 反转链表II:

此题目在反转链表的基础上指定了要反转的链表范围,可以先记node1为pre,pre.next为head,让node1前进m个节点,则此时node1即为待反转部分起点的前一个节点。此时我们可以添加一个变量count记录已经反转的节点个数,每翻转一次count++,当count>=n-m时,停止反转操作。对于返回值进行特判:如果m=1说明从起点开始反转,此时返回pre1.next。如果m>1说明从中间开始反转,直接返回head即可。

 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.next = None
 6 
 7 class Solution:
 8     def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
 9         pre = ListNode(0)
10         pre.next = head
11         node1 = pre
12         for i in range(m-1):
13             node1 = node1.next
14         # 得到开始反转的节点的前一个节点为node1
15         count = 0
16         pre = node1
17         pre_ret = node1
18         node = node1.next
19         while count < n-m:
20             n_next = node.next
21             node.next = n_next.next
22             n_next.next = pre.next
23             pre.next = n_next
24             count+=1
25         if m > 1:
26             return head
27         return pre_ret.next
View Code

时间复杂度O(n),空间复杂度O(1)

 

102. 二叉树的层序遍历:

用队列遍历即可

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, x):
 4 #         self.val = x
 5 #         self.left = None
 6 #         self.right = None
 7 
 8 class Solution:
 9     def levelOrder(self, root: TreeNode) -> List[List[int]]:
10         if not root:
11             return []
12         queue = []
13         queue.append(root)
14         ans = [[root.val]]
15         while queue:
16             temp = []
17             ans_temp = []
18             for node in queue:
19                 if node.left:
20                     ans_temp.append(node.left.val)
21                     temp.append(node.left)
22                 if node.right:
23                     ans_temp.append(node.right.val)
24                     temp.append(node.right)
25             if ans_temp:
26                 ans.append(ans_temp)
27             queue = temp
28         return ans
View Code

时间复杂度O(n),空间复杂度O(n)

 

 

102. 两两交换链表中的节点:

对于本题而言,在反转链表的基础上添加了两两反转的限制。在每次反转之后,将pre设置为node,并将node赋为node.next即可。注意节点数为奇数时最后一个节点不反转!关键代码:

# 两个一组对应操作
pre = node
node = node.next
 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, val=0, next=None):
 4 #         self.val = val
 5 #         self.next = next
 6 class Solution:
 7     def swapPairs(self, head: ListNode) -> ListNode:
 8         if not head or not head.next:
 9             return head
10         pre = ListNode(0)
11         pre.next = head
12         node = head
13         ans = head.next
14         while node:
15             if not node.next:
16                 # 对于余下一个节点的情况的处理
17                 break
18             node_next = node.next
19             node.next = node_next.next
20             node_next.next = pre.next
21             pre.next = node_next
22             # 两个一组对应操作
23             pre = node
24             node = node.next
25         return ans
26         
View Code

时间复杂度O(n),空间复杂度O(1)

 

22.括号生成

对于本题目而言,对于输入的数字n,先初始化n对括号的字符串,然后类似于全排列算法,求得括号组合的全排列,在递归的过程中进行剪枝(对于本体而言因为括号只有左右括号,因此当左右括号都被处理过之后可以跳过循环。)

 1 class Solution:
 2     def generateParenthesis(self, n: int) -> List[str]:
 3         # 遍历所有情况,再筛选所有有效的括号组合
 4         ans = []
 5         str_in = ""
 6         for i in range(n):
 7             str_in+="()" #initialize input
 8         def judge(brack_str):
 9             stack = []
10             for i in brack_str:
11                 if i == "(":
12                     stack.append(i)
13                 elif i == ")" and stack:
14                     if stack.pop() != "(":
15                         return False
16                 elif not stack:
17                     return False
18             return True
19         def backtrack(str_input,path):
20             flag_left = 0
21             flag_right = 0
22             if not str_input:
23                 if judge(path) and path not in ans:
24                     ans.append(path)
25                 path = ""
26                 return
27             for i in range(len(str_input)):
28                 if str_input[i] == "(" and flag_left == 1:
29                     continue
30                 if str_input[i]  == ")" and flag_right == 1:
31                     continue
32                 if str_input[i]  == "(" and flag_left == 0:
33                     flag_left = 1
34                 if str_input[i]  == ")" and flag_right == 0:
35                     flag_right = 1
36                 backtrack(str_input[:i]+str_input[i+1:],path+str_input[i])
37         backtrack(str_in,"")
38         return ans
View Code

时间复杂度O(n!)

 

解法2:可以记左括号剩余数为left,右括号剩余数为right,用dfs即可。终止条件为left与right均为0.同时如果right<left,则不合法。

 1 class Solution:
 2     def generateParenthesis(self, n: int) -> List[str]:
 3         # 遍历所有情况,再筛选所有有效的括号组合
 4         ans = []
 5         str_in = ""
 6         def backtrack(left,right,path):
 7             if left == 0 and right == 0:
 8                 ans.append(path)
 9             if right < left:
10                 # 说明此时右括号用得多,不合法
11                 return
12             if left > 0:
13                 backtrack(left-1,right,path+"(")
14             if right > 0:
15                 backtrack(left,right-1,path+")")
16         backtrack(n,n,"")
17         return ans
View Code

时间复杂度O(n!)

 

22.螺旋矩阵

对于此题目而言,边界条件的限定十分重要,记上下左右边界分别为:up,down,left,right,依次从边界行数或列数进行模拟遍历即可。

 1 class Solution:
 2     def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
 3         up = 0
 4         down = len(matrix)-1
 5         left = 0
 6         right = len(matrix[0])-1
 7         ans = []
 8         while True:
 9             i = left
10             while i <= right:
11                 ans.append(matrix[up][i])
12                 i+=1
13             up+=1
14             if up > down:
15                 break
16             
17             i = up
18             while i <= down:
19                 ans.append(matrix[i][right])
20                 i+=1
21             right-=1
22             if left > right:
23                 break
24             
25             i = right
26             while i >= left:
27                 ans.append(matrix[down][i])
28                 i-=1
29             down-=1
30             if up > down:
31                 break
32             
33             i = down
34             while i >= up:
35                 ans.append(matrix[i][left])
36                 i-=1
37             left+=1
38             if left > right:
39                 break
40         return ans
View Code

时间复杂度O(m*n)

 

148.排序链表

对于此题目要求而言,其要求空间复杂度为O(1),时间复杂度为O(nlogn),想到归并排序,但是限于空间复杂度不能使用递归。因此采用自底向上的迭代计算。

主要函数有两个:

cut(node,step):将node链表的前step个节点截取下来,并返回cut完后的头节点

merge(l1,l2):将l1和l2两个链表进行合并,最后返回链表头节点。

先两两进行归并,再四四进行归并,依次类推。代码如下:

 1 # Definition for singly-linked list.
 2 # class ListNode:
 3 #     def __init__(self, val=0, next=None):
 4 #         self.val = val
 5 #         self.next = next
 6 class Solution:
 7     def sortList(self, head: ListNode) -> ListNode:
 8         def cut(node,step):
 9             #切下node链表的前step个节点并返回切完后的头节点
10             #若节点不够,则能切几个是几个
11             for i in range(step-1):
12                 if node:
13                     node = node.next
14             if node:
15                 ans = node.next
16                 node.next = None
17                 return ans
18             else:
19                 return None
20         def merge(l1,l2):
21             pre = ListNode(0)
22             head = pre
23             while l1 and l2:
24                 if l1.val>l2.val:
25                     pre.next = l2
26                     pre = pre.next
27                     l2 = l2.next
28                 else:
29                     pre.next = l1
30                     pre = pre.next
31                     l1 = l1.next
32             while l1:
33                 pre.next = l1
34                 pre = pre.next
35                 l1 = l1.next
36             while l2:
37                 pre.next = l2
38                 pre = pre.next
39                 l2 = l2.next
40             return head.next
41         if not head:
42             return None
43         node = head
44         length = 0
45         pre = ListNode(0)
46         pre.next = head
47         current = pre.next
48         tail = pre
49         while node:
50             node = node.next
51             length+=1
52         for i in range(int(log(length,2))+1):# 归并操作次数
53             # 最后一次操作将整个链表的两半进行一次merge!
54             step = pow(2,i)
55             current = pre.next
56             tail = pre
57             while current:
58                 left = current
59                 right = cut(current,step)
60                 current = cut(right,step)#下一次循环的left
61                 tail.next = merge(left,right) #merge获取到合并的头节点,将已经合并的链表的尾部与该合并链表连接!
62                 while tail.next:
63                     tail = tail.next
64         return pre.next
View Code

时间复杂度O(nlogn),空间复杂度为O(1)

 

94.二叉树的中序遍历

本题目要求非递归,故使用栈对递归过程进行模拟。

根节点入栈,进入循环

如果当前节点有左节点,此时将其左节点进栈。

如果不存在左节点但存在右节点,此时将根节点出栈,将根节点值加入list中,并将根节点的右节点进栈。

如果当前节点为叶子节点,则直接将当前节点出栈并将值加入list中,同时当前节点的根节点的左子节点设为空(防止重入造成死循环)。

执行如上循环即可。

 1 # Definition for a binary tree node.
 2 # class TreeNode:
 3 #     def __init__(self, val=0, left=None, right=None):
 4 #         self.val = val
 5 #         self.left = left
 6 #         self.right = right
 7 class Solution:
 8     def inorderTraversal(self, root: TreeNode) -> List[int]:
 9         if not root:
10             return []
11         stack = []
12         ans = []
13         stack.append(root)
14         while stack:
15             if stack[-1].left:
16                 #当前节点有左节点
17                 stack.append(stack[-1].left)
18             elif stack[-1].right:
19                 #当前节点无左节点,有右节点,此时应该pop出根节点
20                 root_temp = stack.pop()
21                 ans.append(root_temp.val)
22                 stack.append(root_temp.right)
23             else:
24                 # 当前节点为叶子节点,pop出当前节点并且将当前节点值加入ans,同时将其设置为不可重入(设为null)
25                 node = stack.pop()
26                 ans.append(node.val)
27                 if len(stack)>0:#最后一个节点pop出之后stack为空,故需要判断
28                     stack[-1].left = None
29         return ans
30                 
31                 
View Code

时间复杂度O(n),空间复杂度O(1)

 

322.零钱兑换

对于此题目有两种解法:

dfs+剪枝:将coins数组逆序,然后对答案进行dfs搜索,当target值为零时说明搜索到一个可行解,此时将min(ans,count)赋值给ans即可。

同时注意剪枝!

1.coins[j]>target时说明硬币面值太大,此时跳过此重循环

2.(self.ans-count)*coins[j] < target

说明此时最大面值硬币都无法凑到target,因此不能得到比当前最优解更优的解,故直接break循环

 1 class Solution:
 2     def coinChange(self, coins: List[int], amount: int) -> int:
 3         self.ans = pow(2,31) - 1
 4         coins.sort(reverse=True)
 5         def backtrack(i,target,count):
 6             if target==0:
 7                 self.ans = min(self.ans,count)
 8                 return
 9             for j in range(i,len(coins)):
10                 if coins[j] > target:
11                     # 说明硬币面值太大,需要跳过此次循环
12                     continue
13                 if (self.ans-count)*coins[j] < target:
14                     # 说明此种情况下不可能有比self.ans更优的解
15                     break
16                 backtrack(j,target-coins[j],count+1)
17         for i in range(len(coins)):
18             backtrack(i,amount,0)
19         return self.ans if self.ans!=pow(2,31) - 1 else -1
View Code

时间复杂度O(N*amount)

动态规划:

记dp[i]为总金额i所需要的最少硬币数,则dp[i] = min(dp[i],dp[i-coin[j]]+1),遍历即可

 1 class Solution:
 2     def coinChange(self, coins: List[int], amount: int) -> int:
 3         #dp[i] 金额为i所需要的最少coin个数
 4         num = amount+1
 5         dp = [num for i in range(amount+1)]
 6         dp[0] = 0
 7         for i in range(1,amount+1):
 8             for j in range(len(coins)):
 9                 if i >= coins[j] and dp[i-coins[j]] != num:
10                     dp[i] = min(dp[i],dp[i-coins[j]]+1)
11         return dp[-1] if dp[-1] != amount+1 else -1
View Code

时间复杂度O(N*amount)

 

93.复原ip地址

对于本题目而言,使用回溯法+剪枝进行求解

终止条件:

当前字符串为空,说明已经全部加入,此时将路径加入结果并返回

剪枝条件:

当前字符串大于count*3或者小于count,此时不可能构成有效的ip地址,直接返回(结合题目要求得到这个条件)

去除前导0:

判断待加入的字符串第一位是否为0并且其长度是否大于1,如果均满足说明有前导0,直接退出循环即可。

代码如下:

 1 class Solution:
 2     def restoreIpAddresses(self, s: str) -> List[str]:
 3         ans = []
 4         def backtrack(num_str,path,count):
 5             if len(num_str) < count or len(num_str)>count*3:
 6                 return
 7             if not num_str:
 8                 #去掉最后一个点
 9                 ans.append(path[:len(path)-1])
10             for i in range(len(num_str)):
11                 p,q = num_str[:i+1],num_str[i+1:]
12                 if int(p[0]) == 0 and len(p) > 1:
13                     #过滤前导零
14                     break
15                 if 0 <= int(p) and int(p)<=255:
16                     backtrack(q,path+p+".",count-1)
17         backtrack(s,"",4)
18         return ans
19                 
View Code

200.岛屿数量

对于本题本人采用bfs的方式,添加一个visit数组记录该元素是否被访问过,循环遍历每个元素,当当前元素没有被访问过同时还是陆地时,利用队列进行宽度优先搜索,当完全搜索不到时说明该片陆地已经被完全遍历。此时count+1即可。注意边界条件的处理!

 代码如下:

 1 class Solution:
 2     def numIslands(self, grid: List[List[str]]) -> int:
 3         visit = [[False for i in range(len(grid[0]))] for i in range(len(grid))]
 4         # bfs遍历图
 5         queue = []
 6         line = len(grid)
 7         if line == 0:
 8             return 0
 9         col = len(grid[0])
10         if col == 0:
11             return 0
12         count = 0 #岛屿数量
13         for i in range(line):
14             for j in range(col):
15                 if grid[i][j] == "1" and not visit[i][j]: #尚未访问过此节点
16                     visit[i][j] = True
17                     queue.append([i,j])
18                     #进行bfs
19                     while queue:
20                         i1,j1 = queue.pop()
21                         # 将该点上下左右的所有没有被visit过的岛屿加入
22                         if i1<line-1 and not visit[i1+1][j1] and grid[i1+1][j1] == "1":
23                             queue.append([i1+1,j1])
24                             visit[i1+1][j1] = True
25                         if i1>0 and not visit[i1-1][j1] and grid[i1-1][j1] == "1":
26                             queue.append([i1-1,j1])
27                             visit[i1-1][j1] = True
28                         if j1<col-1 and not visit[i1][j1+1] and grid[i1][j1+1] == "1":
29                             queue.append([i1,j1+1])
30                             visit[i1][j1+1] = True
31                         if j1>0 and not visit[i1][j1-1] and grid[i1][j1-1] == "1":
32                             queue.append([i1,j1-1])
33                             visit[i1][j1-1] = True
34                     count+=1
35         return count
36                         
View Code

时间复杂度不便于分析。

 

79.单词搜索

本题采用dfs进行搜索,注意递归前后visit数组的状态赋值为true和false,同时当找到一个可行解的时候记得要直接返回!否则会超时。

 1 class Solution:
 2     def exist(self, board: List[List[str]], word: str) -> bool:
 3         #dfs求解
 4         line = len(board)
 5         col = len(board[0])
 6         ans = []
 7         visit = [[False for i in range(col)] for i in range(line)]
 8         def dfs(rem,i,j):
 9             if not rem:#说明此时已经没有剩余字符
10                 ans.append(True)
11                 return
12             if i>0 and board[i-1][j] == str(rem[0]) and not visit[i-1][j]:
13                 visit[i-1][j] = True
14                 dfs(rem[1:],i-1,j)
15                 if len(ans) > 0:
16                     return
17                 visit[i-1][j] = False
18             if i<line-1 and board[i+1][j] == str(rem[0]) and not visit[i+1][j]:
19                 visit[i+1][j] = True
20                 dfs(rem[1:],i+1,j)
21                 if len(ans) > 0:
22                     return
23                 visit[i+1][j] = False
24             if j>0 and board[i][j-1] == str(rem[0]) and not visit[i][j-1]:
25                 visit[i][j-1] = True
26                 dfs(rem[1:],i,j-1)
27                 if len(ans) > 0:
28                     return
29                 visit[i][j-1] = False
30             if j<col-1 and board[i][j+1] == str(rem[0]) and not visit[i][j+1]:
31                 visit[i][j+1] = True
32                 dfs(rem[1:],i,j+1)
33                 if len(ans) > 0:
34                     return
35                 visit[i][j+1] = False
36         for i in range(line):
37             for j in range(col):
38                 if board[i][j] == str(word[0]) and not visit[i][j]:
39                     visit[i][j] = True
40                     dfs(word[1:],i,j)
41                     visit[i][j] = False
42                     if len(ans)>0:
43                         return True
44         return False
View Code

 

posted @ 2020-11-04 23:02  Yang_Xi  阅读(157)  评论(0编辑  收藏  举报