# 注:下面按照算法类别由浅入深,把下面罗列的这些题刷完,并且多看这些题不同的解法(国际版mostvotes),看懂之后估计就不会有太大的问题啦~

整体框架

数据结构: 一维:基础: 数组array(string),链表linked list高级 : 栈stack,队列queue,双端队列deque,集合set,映射 map(hash or map), etc二维:基础:树tree,图graph高级:  二叉搜索树binary search tree(red-black tree,AVL),堆heap,并查集disjoint set,字典树Trie特殊:位运算Bitwise,布隆过滤器BloomFilterLRU Cache算法:递归Recursion搜索Search: 深度优先搜索Depth first search,广度优先搜索Breadth first search动态规划 Dynamic Programming二分查找 Binary Search贪心 Greedy

数组&链表部分

一、数组部分

数组的常见写法: Java,C++: int a[50] ;  Python: list = []

注:高级数据语言可以不指定类型语言

数组底层硬件实现,有一个内存管理器,每当申请一个数组时,计算机会在内存中开辟一段连续的地址,每一个地址都可以直接通过内存管理器直接访问,访问任意一个位置,时间复杂度都是一致的都是O(1)

数组的插入和删除都需要O(n)的时间复杂度

看一下Java源码,数组插入和删除的操作过程:

http://developer.classpath.org/doc/java/util/ArrayList-source.html329:   /**330:    * Appends the supplied element to the end of this list.331:    * The element, e, can be an object of any type or null.332:    *333:    * @param e the element to be appended to this list334:    * @return true, the add will always succeed335:    */336:   public boolean add(E e)337:   {338:     modCount++;339:     if (size == data.length)340:       ensureCapacity(size + 1);341:     data[size++] = e;342:     return true;343:   }数组添加一个元素e,modCount是一个计数器,ensureCapacity保证数组的容量,末尾插入一个元素e345:   /**346:    * Adds the supplied element at the specified index, shifting all347:    * elements currently at that index or higher one to the right.348:    * The element, e, can be an object of any type or null.349:    *350:    * @param index the index at which the element is being added351:    * @param e the item being added352:    * @throws IndexOutOfBoundsException if index < 0 || index > size()353:    */354:   public void add(int index, E e)355:   {356:     checkBoundInclusive(index);357:     modCount++;358:     if (size == data.length)359:       ensureCapacity(size + 1);360:     if (index != size)361:       System.arraycopy(data, index, data, index + 1, size - index);362:     data[index] = e;363:     size++;364:   }指定位置index处插入元素e, checkBoundInclusive-检查是都越界,ensureCapacity保证数组容量,arraycopy-对数组序列进行移动463:   /**464:    * Checks that the index is in the range of possible elements (inclusive).465:    *466:    * @param index the index to check467:    * @throws IndexOutOfBoundsException if index > size468:    */469:   private void checkBoundInclusive(int index)470:   {471:     // Implementation note: we do not check for negative ranges here, since472:     // use of a negative index will cause an ArrayIndexOutOfBoundsException,473:     // a subclass of the required exception, with no effort on our part.474:     if (index > size)475:       throw new IndexOutOfBoundsException("Index: " + index + ", Size: "476:                                           + size);477:   }checkBoundInclusive检查是否越界160:   /**161:    * Guarantees that this list will have at least enough capacity to162:    * hold minCapacity elements. This implementation will grow the list to163:    * max(current * 2, minCapacity) if (minCapacity > current). The JCL says164:    * explictly that "this method increases its capacity to minCap", while165:    * the JDK 1.3 online docs specify that the list will grow to at least the166:    * size specified.167:    *168:    * @param minCapacity the minimum guaranteed capacity169:    */170:   public void ensureCapacity(int minCapacity)171:   {172:     int current = data.length;173: 174:     if (minCapacity > current)175:       {176:         E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)];177:         System.arraycopy(data, 0, newData, 0, size);178:         data = newData;179:       }180:   }ensureCapacity-确保数组的容量数组的删除操作和插入操作同理,也会用到相应的函数366:   /**367:    * Removes the element at the user-supplied index.368:    *369:    * @param index the index of the element to be removed370:    * @return the removed Object371:    * @throws IndexOutOfBoundsException if index < 0 || index >= size()372:    */373:   public E remove(int index)374:   {375:     checkBoundExclusive(index);376:     E r = data[index];377:     modCount++;378:     if (index != --size)379:       System.arraycopy(data, index + 1, data, index, size - index);380:     // Aid for garbage collection by releasing this pointer.381:     data[size] = null;382:     return r;383:   } 二、链表部分(Linked List)
链表部分弥补前面数组部分的缺点(插入和删除时间复杂度过高)每一个元素都需要定义一个class,头指针用head表示,尾指针用tail表示,最后一个元素next指针指向空,因为没有next指针啦;如果tail 的next指针指向head时,为循环链表
Java-Linked List源码:
http://developer.classpath.org/doc/java/util/LinkedList-source.html
链表的插入和删除操作没有引起链表的群移操作,也不需要复制元素
操作
时间复杂度(linkedList vs Array)
prepend(前向插入)
O(1)  vs  O(1)
append(后向插入)    O(1)  vs  O(1)
lookup(查询)
O(n)  vs O(1)
insert(插入)
O(1)  vs O(n)
delete(删除)
O(1)  vs O(n)
从图中可以看出,查询操作时间复杂度为O(n),也是链表的问题所在,所以可以看出并没有一种完美的数据结构,根据不同的需求用相应的数据结构即可三、跳表常见于redis中,链表的缺点在于查询时间复杂度为O(n),对链表查询进行加速,中心思想-空间换时间原始链表-时间复杂度:O(n) 简单优化:添加头尾指针 ,多添加指针会更快一级索引加速,速度可以加快,还可以增加第二级索引,更快加快速度,通常增加log2n级索引,跳表时间复杂度为logn。例如一个链表原始长度为1024,按照原始查询方式需要查1024次,如果按照跳表进行查询,只需要查询10次。增加和删除操作,跳表需要更新一次。跳表的时间复杂度为O(logn),空间复杂度为O(n)。redis应用跳表参考链接:https://redisbook.readthedocs.io/en/latest/internal-
datastruct/skiplist.html四、实战题目题目1:Leetcode-283-移动零给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,
同时保持非零元素的相对顺序。示例:输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。解法一:双指针从前到后进行遍历,遇到不是0的元素往前移,0元素放在后面class Solution:def moveZeroes(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""j = 0for i in range(len(nums)):if nums[i]!=0:nums[j] = nums[i]if i!=j:nums[i] = 0j += 1效率:
提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  52 ms  14.5 MB  Python3解法二:遍历数组,构建一个索引,遇到不是零的元素,则添加到数组前面,
直到遍历完数组,index-至数组末尾添加0
class Solution:def moveZeroes(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""index = 0for i in range(len(nums)):if nums[i]!=0:nums[index] = nums[i]index +=1for i in range(index,len(nums)):nums[i] = 0性能:
提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  88 ms  14.4 MB  Python3Most votes:
// Shift non-zero values as far forward as possible
// Fill remaining space with zerospublic void moveZeroes(int[] nums) {if (nums == null || nums.length == 0) return;int insertPos = 0;for (int num: nums) {if (num != 0) nums[insertPos++] = num;}while (insertPos < nums.length) {nums[insertPos++] = 0;}
}# in-place
def moveZeroes(self, nums):zero = 0  # records the position of "0"for i in xrange(len(nums)):if nums[i] != 0:nums[i], nums[zero] = nums[zero], nums[i]zero += 1题目2Leetcode-11-盛最多水的容器给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。
在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,
容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49解法一、暴力解法:
class Solution:def maxArea(self, height: List[int]) -> int:max_area = 0for i in range(len(height)):for j in range(i+1,len(height)):max_area = max(max_area,min(height[j],height[i])*(j-i))return max_area性能:超时解法二、双指针
数组左侧一个指针,右侧一个指针,两个指针向中间移动,直到两者相遇;
算指针高度较小的指针的高度,指针移动。
class Solution:def maxArea(self, height: List[int]) -> int:i,j,max_area = 0,len(height)-1,0while i<j:if height[i] < height[j]:max_area = max(max_area,height[i]*(j-i))i += 1else:max_area = max(max_area,height[j]*(j-i))j -= 1return max_area提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  232 ms  14.7 MB  Python3MostVotes:
class Solution(object):def maxArea(self, height):""":type height: List[int]:rtype: int"""MAX = 0 x = len(height) - 1y = 0while x != y:if height[x] > height[y]:area = height[y] * (x - y)y += 1else:area = height[x] * (x - y)x -= 1MAX = max(MAX, area)return MAXint maxArea(vector<int>& height) {int water = 0;int i = 0, j = height.size() - 1;while (i < j) {int h = min(height[i], height[j]);water = max(water, (j - i) * h);while (height[i] <= h && i < j) i++;while (height[j] <= h && i < j) j--;}return water;
}题目3Leetcode-70-爬楼梯假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。示例 1:
输入:2
输出:2
解释:有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶
# 解法
class Solution:# 只保存最近的三个值def climbStairs(self, n: int) -> int:if n<=2:return nelse:f1,f2,f3 = 1,2,3for i in range(3,n+1):f3 = f1 + f2f1 = f2f2 = f3return f3时间复杂度O(n),空间复杂度为O(1)Time Submitted Status Runtime Memory Language
28 minutes ago  Accepted  104 ms  13 MB  python
官方给出两个比较新颖的解法:Binets方法 && 斐波那契公式,详见
https://leetcode-cn.com/problems/climbing-stairs/solution
/pa-lou-ti-by-leetcode/
题目4Leetcode-15-三数之和精选题解(https://leetcode-cn.com/problems/3sum/solution/pai-xu-shuang-zhi-zhen-zhu-xing-jie-shi-python3-by/):排序 + 双指针
本题的难点在于如何去除重复解。算法流程:
特判,对于数组长度 nn,如果数组为 nullnull 或者数组长度小于 3,返回 []。
对数组进行排序。
遍历排序后数组:
若 nums[i]>0nums[i]>0:因为已经排序好,所以后面不可能有三个数加和等于 0,直接返回结果。
对于重复元素:跳过,避免出现重复解
令左指针 L=i+1,右指针 R=n-1,当 L<R时,执行循环:
当 nums[i]+nums[L]+nums[R]==0,执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 L,R 移到下一位置,寻找新的解
若和大于 0,说明 nums[R]太大,RR 左移
若和小于 0,说明 nums[L]太小,LL 右移
复杂度分析
时间复杂度:O(n^2),数组排序O(NlogN),遍历数组O(N),双指针遍历O(n),总体O(NlogN)+O(n)+O(n)*O(n) => O(n^2)
空间复杂度: O(1)class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:n=len(nums)res=[]if(not nums or n<3):return []nums.sort()res=[]for i in range(n):if(nums[i]>0):return resif(i>0 and nums[i]==nums[i-1]):continueL=i+1R=n-1while(L<R):if(nums[i]+nums[L]+nums[R]==0):res.append([nums[i],nums[L],nums[R]])while(L<R and nums[L]==nums[L+1]):L=L+1while(L<R and nums[R]==nums[R-1]):R=R-1L=L+1R=R-1elif(nums[i]+nums[L]+nums[R]>0):R=R-1else:L=L+1return res提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  1508 ms  16.6 MB  Python3题目5Leetcode-206-反转链表反转一个单链表。示例:输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?解法一:双指针迭代
思想,申请两个指针,一个指针指向空,一个指针指向头结点,然后遍历链表,每个元素指向空指针方向
class Solution:def reverseList(self, head: ListNode) -> ListNode:pre = Nonecur = headwhile cur:item = cur.nextcur.next = prepre = curcur = itemreturn pre时间复杂度为O(n),空间复杂度为O(1)
提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  36 ms  14.2 MB  Python3递归-精选题解(https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-fan-zhuan-lian-biao-by-user74/)
class Solution(object):def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""# 递归终止条件是当前为空,或者下一个节点为空if(head==None or head.next==None):return head# 这里的cur就是最后一个节点cur = self.reverseList(head.next)# 这里请配合动画演示理解# 如果链表是 1->2->3->4->5,那么此时的cur就是5# 而head是4,head的下一个是5,下下一个是空# 所以head.next.next 就是5->4head.next.next = head# 防止链表循环,需要将head.next设置为空head.next = None# 每层递归函数都返回cur,也就是最后一个节点return cur题目6Leetcode-24-两两交换链表中的节点给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.class Solution:def swapPairs(self, head: ListNode) -> ListNode:if not head or not head.next:return headfirst_node = headsecond_node = head.nextfirst_node.next = self.swapPairs(second_node.next)second_node.next = first_nodereturn second_node
题目7Leetcode-141-环形链表给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。
解法一:ListNode放在集合里,遍历列表,如果元素出现在集合里,则表示构成环
class Solution:def hasCycle(self, head: ListNode) -> bool:set_ = set()while head:if head in set_:return Trueset_.add(head)head = head.nextreturn False提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  48 ms  16.5 MB  Python3解法二:快慢指针class Solution:def hasCycle(self, head: ListNode) -> bool:if not head or not head.next:return Falsei,j = head,head.nextwhile j and j.next:if i==j:return Truei,j = i.next,j.next.nextreturn False提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  76 ms  16.3 MB  Python3
题目8Leetcode-142-环形链表 II给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。class Solution:def detectCycle(self, head: ListNode) -> ListNode:se = set()node = headwhile node is not None:if node in se:return nodeelse:se.add(node)node = node.nextreturn node
提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  60 ms  16.8 MB  Python3
题目9Leetcode-25-K 个一组翻转链表给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明 :
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换
优选解法-参考https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/kge-yi-zu-fan-zhuan-lian-biao-by-powcai/
题目10Leetcode-26-删除排序数组中的重复项给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素class Solution:def removeDuplicates(self, nums: List[int]) -> int:if len(nums) == 0:return 0i = 0for j in range(1,len(nums)):if nums[i]!=nums[j]:i +=1nums[i] = nums[j]return i + 1提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  88 ms  14.9 MB  Python3
题目11Leetcode-189-旋转数组给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""n = len(nums)k %= nfor i in range(k):nums.insert(0,nums.pop())
题目12Leetcode-21-合并两个有序链表将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:if l1 is None:return l2elif l2 is None:return l1elif l1.val < l2.val:l1.next = self.mergeTwoLists(l1.next,l2)return l1else:l2.next = self.mergeTwoLists(l1,l2.next)return l2
题目13Leetcode-88-合并两个有序数组给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3
输出: [1,2,2,3,5,6]方法一:
class Solution:def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:"""Do not return anything, modify nums1 in-place instead."""nums1[:] = sorted(nums1[:m] + nums2[:n])时间复杂度O(n+m)log(n+m),空间复杂度O(1)方法二:
class Solution:def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:"""Do not return anything, modify nums1 in-place instead."""res = []p1,p2 = 0,0nums1[:] = nums1[:m]while p1 < m and p2 < n:if nums1[p1] < nums2[p2]:res.append(nums1[p1])p1 += 1else:res.append(nums2[p2])p2 += 1if p1 < m:res[p1+p2:] = nums1[p1:]if p2 < n:res[p1+p2:] = nums2[p2:]nums1[:] = res[:]时间复杂度O(n+m),空间复杂度O(m)方法三:
class Solution:def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:"""Do not return anything, modify nums1 in-place instead."""p1,p2,p = m-1,n-1,m+n-1while p1 >= 0 and p2 >= 0:if nums1[p1] < nums2[p2]:nums1[p] = nums2[p2]p2 -= 1else:nums1[p] = nums1[p1]p1 -= 1p -= 1nums1[:p2+1] = nums2[:p2+1]
时间复杂度O(m+n),空间复杂度O(1)
题目14Leetcode-1-两数之和给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:map_ = {}for idx,num in enumerate(nums):map_[num] = idxfor i,num in enumerate(nums):j = map_.get(target-num)if j is not None and i!=j:return [i,j]
题目15Leetcode-66-加一给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
class Solution:def plusOne(self, digits: List[int]) -> List[int]:return list(map(int, list(str(int(''.join(map(str, digits))) + 1))))

栈、队列部分

一、栈和队列的优先特性

Stack & Queue 关键点

Stack: 先入后出:添加、删除皆为O(1)

Queue: 先入先出:添加、删除皆为O(1)

查询为O(n),元素是无序的

双端队列(Deque):栈和队列的结合体,可以从最前面或者最后面push或pop出来,插入和删除是O(1),查询是O(n)

Stack、Queue、Deque的工程实现

Java Stack 源码:http://developer.classpath.org/doc/java/util/Stack-source.htmlJava Queue 源码:http://fuseyism.com/classpath/doc/java/util/Queue-source.html
1、Leetcode-20-有效的符号给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。class Solution:def isValid(self, s: str) -> bool:dic = {'(':')','{':'}','[':']','a':'a'}res = ['a']if len(s) == 0:return Trueif len(s)%2==1:return Falsefor ch in s:if ch in dic:res.append(ch)elif dic[res.pop()]!=ch:return Falsereturn len(res)==1提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  20 ms  13.2 MB  Python3Mostvotes
class Solution:# @return a booleandef isValid(self, s):stack = []dict = {"]":"[", "}":"{", ")":"("}for char in s:if char in dict.values():stack.append(char)elif char in dict.keys():if stack == [] or dict[char] != stack.pop():return Falseelse:return Falsereturn stack == []2、Leetcode-155-最小栈设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。push(x) -- 将元素 x 推入栈中。
pop() -- 删除栈顶的元素。
top() -- 获取栈顶元素。
getMin() -- 检索栈中的最小元素
class MinStack:def __init__(self):"""initialize your data structure here."""self.stack_1,self.stack_2 = [],[]def push(self, x: int) -> None:self.stack_1.append(x)if len(self.stack_2) ==0 or x<= self.stack_2[-1]:self.stack_2.append(x)else:self.stack_2.append(self.stack_2[-1])def pop(self) -> None:if self.stack_1:self.stack_2.pop()return self.stack_1.pop()def top(self) -> int:if self.stack_1:return self.stack_1[-1]def getMin(self) -> int:if self.stack_2:return self.stack_2[-1]3、Leetcode-84-柱状图中最大的矩形给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积输入: [2,1,5,6,2,3]
输出: 10精选题解:
(https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/zhao-liang-bian-di-yi-ge-xiao-yu-ta-de-zhi-by-powc/)class Solution:def largestRectangleArea(self, heights: List[int]) -> int:stack = []heights = [0] + heights + [0]res = 0for i in range(len(heights)):while stack and heights[stack[-1]] > heights[i]:tmp = stack.pop()res = max(res, (i - stack[-1] - 1) * heights[tmp])stack.append(i)return res4、Leetcode-239-滑动窗口的最大值给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值class Solution(object):def maxSlidingWindow(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""if len(nums) * k == 0:return []return [max(nums[i:i+k]) for i in range(0,len(nums)-k+1)]Mostvotes可查看:https://leetcode.com/problems/sliding-window-maximum/discuss/?currentPage=1&orderBy=most_votes&query=5、Leetcode-641-设计循环双端队列设计实现双端队列。
你的实现需要支持以下操作:MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。Mostvotes:
(https://leetcode.com/problems/design-circular-deque/discuss/154055/python3-using-list-easy-to-understand)
class MyCircularDeque:def __init__(self, k):"""Initialize your data structure here. Set the size of the deque to be k.:type k: int"""self._size = 0self._front, self._rear = 0, 0self._capacity = kself._data = [-1] * kdef insertFront(self, value):"""Adds an item at the front of Deque. Return true if the operation is successful.:type value: int:rtype: bool"""if self.isFull():return Falseif self.isEmpty():self._data[self._front] = valueelse:self._front = (self._front - 1) % self._capacityself._data[self._front] = valueself._size += 1return Truedef insertLast(self, value):"""Adds an item at the rear of Deque. Return true if the operation is successful.:type value: int:rtype: bool"""if self.isFull():return Falseif self.isEmpty():self._data[self._rear] = valueelse:self._rear = (self._rear + 1) % self._capacityself._data[self._rear] = valueself._size += 1return Truedef deleteFront(self):"""Deletes an item from the front of Deque. Return true if the operation is successful.:rtype: bool"""if self.isEmpty():return Falseself._data[self._front] = -1self._front = (self._front + 1) % self._capacityself._size -= 1if self.isEmpty():self._rear = self._frontreturn Truedef deleteLast(self):"""Deletes an item from the rear of Deque. Return true if the operation is successful.:rtype: bool"""if self.isEmpty():return Falseself._data[self._rear] = -1self._rear = (self._rear - 1) % self._capacityself._size -= 1if self.isEmpty():self._front = self._rearreturn Truedef getFront(self):"""Get the front item from the deque.:rtype: int"""return self._data[self._front]def getRear(self):"""Get the last item from the deque.:rtype: int"""return self._data[self._rear]def isEmpty(self):"""Checks whether the circular deque is empty or not.:rtype: bool"""return self._size == 0def isFull(self):"""Checks whether the circular deque is full or not.:rtype: bool"""return self._size == self._capacity6、Leetcode-42-接雨水给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
下面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)Mostvotes
https://leetcode.com/problems/trapping-rain-water/discuss/17554/Share-my-one-pass-Python-solution-with-explainationdef trap(self, bars):if not bars or len(bars) < 3:return 0volume = 0left, right = 0, len(bars) - 1l_max, r_max = bars[left], bars[right]while left < right:l_max, r_max = max(bars[left], l_max), max(bars[right], r_max)if l_max <= r_max:volume += l_max - bars[left]left += 1else:volume += r_max - bars[right]right -= 1return volume

哈希表、映射、集合部分

哈希表(Hash table),也叫散列表,是根据关键码值(key value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做哈希表(或散列表)。

哈希表复杂度分析:插入、删除、大部分情况下时间复杂度都是O(1),最坏    情况哈比表的长度比较小,导致多次哈希碰撞,时间复杂度退化成O(n)

1、Leetcode-242-有效的字母异位词给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。解法:
1、暴力,sorted,两个字符串是否相等 O(NlogN)
2、hash,map --> 统计每个字符的频次class Solution:def isAnagram(self, s: str, t: str) -> bool:dict_1 = {}dict_2 = {}for item in s:dict_1[item] = dict_1.get(item,0) + 1for item in t:dict_2[item] = dict_2.get(item,0) + 1return dict_2 == dict_1提交时间 提交结果 执行用时 内存消耗 语言
几秒前  通过  56 ms  13.5 MB  Python32、Leetcode-49-字母异位词分组给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[["ate","eat","tea"],["nat","tan"],["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:import collectionsdic = collections.defaultdict(list)for c in strs:count = [0] * 26for _ in c:count[ord(_) - ord('a')] += 1dic[tuple(count)].append(c)return dic.values()

树、二叉树、二叉搜索树部分

二维数据结构-树和图,区别是否构成环

链表是特殊化的树,树是特殊化的图

二叉树遍历 Pre-order/In-order/Post-order

1、前序(Pre-order):根-左-右

2、中序(In-order): 左-根-右

3、后序(Post-order): 左-右-根

二叉搜索树(Binary Search Tree),查询等操作时间复杂度都是log(n),最坏情况是变成链表形式复杂度变为O(n)

二叉搜索树,也称有序二叉树、排序二叉树,是指一颗空树或者具有下列性质的二叉树:

  1. 左子树所有节点的值均小于它的根节点的值

  2. 右子树所有节点的值均大于它的根节点的值

  3. 重复:左右子树也分别为二叉查找树

1、Leetcode-94-二叉树中序遍历给定一个二叉树,返回它的中序 遍历。示例:输入: [1,null,2,3]1\2/3输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
解法一:递归:
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:res = []self.helper(root,res)return resdef helper(self,root:TreeNode,res:List):if root:if root.left:self.helper(root.left,res)res.append(root.val)if root.right:self.helper(root.right,res)解法二:颜色标记
(参考:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/)
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:white,Gary = 0,1res = []stack = [(white,root)]while stack:color,node = stack.pop()if node is None:continueif color == white:stack.append((white,node.right))stack.append((Gary,node))stack.append((white,node.left))else:res.append(node.val)return res
2、Leetcode-144-二叉树前序遍历# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:res = []self.helper(root,res)return resdef helper(self,root:TreeNode,res:List):if root:res.append(root.val)if root.left:self.helper(root.left,res)if root.right:self.helper(root.right,res)
3、Leetcode-590-N叉树的后序遍历官方题解(https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/solution/ncha-shu-de-hou-xu-bian-li-by-leetcode/)
"""
# Definition for a Node.
class Node:def __init__(self, val=None, children=None):self.val = valself.children = children
"""
class Solution:def postorder(self, root: 'Node') -> List[int]:if root is None:return []stack,output = [root],[]while stack:root = stack.pop()if root:output.append(root.val)for c in root.children:stack.append(c)return output[::-1]复杂度分析:时间复杂度:时间复杂度:O(M),其中 M 是 N 叉树中的节点个数。每个节点只会入栈和出栈各一次。空间复杂度:O(M)。在最坏的情况下,这棵 N 叉树只有 2 层,所有第 2 层的节点都是根节点的孩子。将根节点推出栈后,需要将这些节点都放入栈,共有 M - 1个节点,因此栈的大小为O(M)
4、Leetcode-589-N叉树的前序遍历"""
# Definition for a Node.
class Node:def __init__(self, val=None, children=None):self.val = valself.children = children
"""
class Solution:def preorder(self, root: 'Node') -> List[int]:if root is None:return []stack,output = [root],[]while stack:root = stack.pop()output.append(root.val)stack.extend(root.children[::-1])return output时间复杂度与空间复杂度如上分析所示
5、Leetcode-429-N叉树的层次遍历"""
# Definition for a Node.
class Node:def __init__(self, val=None, children=None):self.val = valself.children = children
"""
class Solution:def levelOrder(self, root: 'Node') -> List[List[int]]:if root is None:return []result,previous_layer = [],[root]while previous_layer:current_layer = []result.append([])for node in previous_layer:result[-1].append(node.val)current_layer.extend(node.children)previous_layer = current_layerreturn result复杂度分析:
时间复杂度:O(n)。n指的是节点的数量。
空间复杂度:O(n)。

泛型递归、树的递归

递归

递归-循环

通过函数体来进行的循环

Python 代码模板def recursion(level,param1,param2,...):# 终止条件if level > MAX_LEVEL:process_resultreturn# 过程逻辑process(level,data...)# 下一层self.recursion(level+1,p1,..思维要点:
1、不要人肉递归
2、拆解可重复解决的问题(重复子问题)
3、数学归纳法思维1、Leetcode-70-爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入:2
输出:2
解释:有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶
示例 2:
输入:3
输出:3
解释:有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶class Solution:# 只保存最近的三个值def climbStairs(self, n: int) -> int:if n<=2:return nelse:f1,f2,f3 = 1,2,3for i in range(3,n+1):f3 = f1 + f2f1 = f2f2 = f3return f3
2、Leetcode-22-括号生成
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。例如,给出 n = 3,生成结果为:
["((()))","(()())","(())()","()(())","()()()"
]class Solution:def generateParenthesis(self, n: int) -> List[str]:result = []def recursion(s='',left=0,right=0):if len(s)==2*n:result.append(s)if left < n:recursion(s + '(',left+1,right)if left > right:recursion(s + ')',left,right+1)recursion()return result
3、Leetcode-226-翻转二叉树
翻转一棵二叉树。
示例:
输入:4/   \2     7/ \   / \
1   3 6   9
输出:4/   \7     2/ \   / \
9   6 3   1
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:return Noneroot.left,root.right = root.right,root.leftself.invertTree(root.left)self.invertTree(root.right)return root
4、Leetcode-98-验证二叉搜索树给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:2/ \1   3
输出: true
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def isValidBST(self, root: TreeNode) -> bool:def helper(node,lower = float("-inf"),upper = float("inf")):if not node:return Trueval  =  node.valif val <= lower or val >= upper:return Falseif not helper(node.left,lower,val):return Falseif not helper(node.right,val,upper):return Falsereturn Truereturn helper(root)时间复杂度为O(N),空间复杂度为O(N)
5、Leetcode-104-二叉树的最大深度给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
class Solution:def maxDepth(self, root: TreeNode) -> int:if not root:return 0return max(self.maxDepth(root.left) + 1, self.maxDepth(root.right)+1)
时间复杂度为O(n),空间复杂度为O(logn),logn为树的高度
6、Leetcode-111-二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],3/ \9  20/  \15   7
返回它的最小深度2方法1:
class Solution:def minDepth(self, root: TreeNode) -> int:if not root:return 0if not root.left:return self.minDepth(root.right) + 1if not root.right:return self.minDepth(root.left) + 1return min(self.minDepth(root.left),self.minDepth(root.right)) + 1方法2:
class Solution:def minDepth(self, root: TreeNode) -> int:if not root:return 0children = [root.left,root.right]if not any(children):return 1min_d = float("inf")for c in children:if c:min_d = min(self.minDepth(c),min_d)return min_d + 1时间复杂度为O(n),空间复杂度为O(logn)
7、Leetcode-297-二叉树的序列化与反序列化
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:1/ \2   3/ \4   5序列化为 "[1,2,3,null,null,4,5]"
官方题解(https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/solution/er-cha-shu-de-xu-lie-hua-yu-fan-xu-lie-hua-by-leet/)
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Codec:def serialize(self, root):"""Encodes a tree to a single string.:type root: TreeNode:rtype: str"""def rserialize(root,string):if root is None:string += "None,"else:string += str(root.val) + ","string = rserialize(root.left,string)string = rserialize(root.right,string)return stringreturn rserialize(root,"")def deserialize(self, data):"""Decodes your encoded data to tree.:type data: str:rtype: TreeNode"""def rdeserialize(l):if l[0] == "None":l.pop(0)return Noneroot = TreeNode(l[0])l.pop(0)root.left = rdeserialize(l)root.right = rdeserialize(l)return rootdata_list = data.split(',')root = rdeserialize(data_list)return root时间复杂度为O(n),空间复杂度为O(n)
# Your Codec object will be instantiated and called as such:
# codec = Codec()
# codec.deserialize(codec.serialize(root))
8、Leetcode-236-二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。官方题解(https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/solution/er-cha-shu-de-zui-jin-gong-gong-zu-xian-by-leetcod/)
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def __init__(self):self.ans = Nonedef lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':def recurse_tree(current_node):if not current_node:return Falseleft = recurse_tree(current_node.left)right = recurse_tree(current_node.right)mid = current_node == p or current_node == qif mid + left + right >= 2:self.ans = current_nodereturn mid or left or rightrecurse_tree(root)return self.ans时间复杂度O(n),空间复杂度O(n)9、Leetcode-105-从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:3/ \9  20/  \15   7class Solution:def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:if len(inorder) == 0:return Noneroot = TreeNode(preorder[0])mid = inorder.index(preorder[0])root.left = self.buildTree(preorder[1:mid+1],inorder[:mid])root.right = self.buildTree(preorder[mid+1:],inorder[mid+1:])return root
10、Leetcode-77-组合给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4],
]
class Solution:def combine(self, n: int, k: int) -> List[List[int]]:def backtrack(first = 1,curr = []):if len(curr) == k:output.append(curr[:])for i in range(first,n+1):curr.append(i)backtrack(i+1,curr)curr.pop()output = []backtrack()return output
11、Leetcode-46-全排列给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
]class Solution:def permute(self, nums: List[int]) -> List[List[int]]:def backtrack(first=0):if first == n:output.append(nums[:])for i in range(first,n):nums[first],nums[i] = nums[i],nums[first]backtrack(first+1)nums[i],nums[first] = nums[first],nums[i]n = len(nums)output = []backtrack()return output

分治、回溯部分

分治、回溯是递归的一种,本质是将重复性问题分解以及最后组合每个子问题的结果

分治代码模板:
def divide_conquer(problem,param1,param2,...):# 递归终止条件if problem is None:print_resultreturn# 准备数据data = prepare_data(problem)subproblems = split_problem(problem,data)# 分治 子问题subresult1 = self.divide_conquer(subproblems[0],p1,...)subresult2 = self.divide_conquer(subproblems[1],p1,...)subresult3 = self.divide_conquer(subproblems[2],p1,...).....  # 收集子结果产生最后的结果result = process_result(subresult1,subresult2,..)# 清除当前层的状态回溯,是简单的一层一层去试探1、Leetcode-50-Pow(x,n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100class Solution:def myPow(self, x: float, n: int) -> float:def helper(x,n):if n==0:return 1.0if n%2==0:return helper(x*x,n/2)else:return helper(x*x,(n-1)/2)*xif n<0:n = -nreturn 1/helper(x,n)return helper(x,n)
2、Leetcode-78-子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[]
]
class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:results = [[]]for num in nums:newsets = []for subset in results:new_subset = subset + [num]newsets.append(new_subset)results.extend(newsets)return resultsclass Solution:def subsets(self, nums: List[int]) -> List[List[int]]:res = [[]]for i in nums:res = res + [[i] + num for num in res]return res
3、Leetcode-169-多数元素给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。示例 1:
输入: [3,2,3]
输出: 3
分治法class Solution:def majorityElement(self, nums: List[int]) -> int:def majority_element_rec(first,end):if first == end:return nums[first]mid = (end-first)//2 + firstleft = majority_element_rec(first,mid)right = majority_element_rec(mid+1,end)if left == right:return leftleft_count = sum([1 for i in range(first,end+1) if nums[i]==left])right_count = sum([1 for i in range(first,end+1) if nums[i]==right])return left if left_count > right_count else rightreturn majority_element_rec(0,len(nums)-1)时间复杂度O(nlogn),空间复杂度O(n)
4、Leetcode-17-电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。class Solution:def letterCombinations(self, digits: str) -> List[str]:phone = {"2":["a","b","c"],"3":["d","e","f"],"4":["g","h","i"],"5":["j","k","l"],"6":["m","n","o"],"7":["p","q","r","s"],"8":["t","u","v"],"9":["w","x","y","z"]}output = []def backtrack(combinations,next_digits):if len(next_digits)==0:output.append(combinations)else:for letter in phone[next_digits[0]]:backtrack(combinations + letter,next_digits[1:])if digits:backtrack("",digits)return output
5、Leetcode-51-N皇后问题
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [[".Q..",  // 解法 1"...Q","Q...","..Q."],["..Q.",  // 解法 2"Q...","...Q",".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
官方题解(https://leetcode-cn.com/problems/n-queens/solution/nhuang-hou-by-leetcode/)
class Solution:def solveNQueens(self, n: int) -> List[List[str]]:def could_place(row, col):return not (cols[col] + hill_diagonals[row - col] + dale_diagonals[row + col])def place_queen(row, col):queens.add((row, col))cols[col] = 1hill_diagonals[row - col] = 1dale_diagonals[row + col] = 1def remove_queen(row, col):queens.remove((row, col))cols[col] = 0hill_diagonals[row - col] = 0dale_diagonals[row + col] = 0def add_solution():solution = []for _, col in sorted(queens):solution.append('.' * col + 'Q' + '.' * (n - col - 1))output.append(solution)def backtrack(row = 0):for col in range(n):if could_place(row, col):place_queen(row, col)if row + 1 == n:add_solution()else:backtrack(row + 1)remove_queen(row, col)cols = [0] * nhill_diagonals = [0] * (2 * n - 1)dale_diagonals = [0] * (2 * n - 1)queens = set()output = []backtrack()return output

深度优先搜索、广度优先搜索

遍历搜索:在树(图/状态集)中寻找特定节点

搜索-遍历:

  • 每个节点都要访问一次

  • 每个节点仅仅要访问一次

  • 对于节点的访问顺序不限:深度优先、广度优先

示例代码:
def dfs(node):if node in visited:returnvisited.add(node)dfs(node.left)dfs(node.right)深度优先搜索
DFS代码-递归写法:
visited = set()
def dfs(node,visited):if node in visited:returnvisited.add(node)for next_node in node.children():if not next_node in visited:dfs(next node,visited)DFS代码-非递归写法def DFS(self,tree):if tree.root is None:return []visited,stack = [],[tree.root]while stack:node = stack.pop()visited.add(node)process(node)nodes = generage_related_node(node)stack.push(nodes)
广度优先搜索
BFS代码-递归
visited = set()
def dfs(node,visited):visited.add(node)for next_node in node.children():if next_node not in visited:dfs(next_node,visited)BFS代码-非递归
dfs BFS(graph,start,end):queue = []queue.append([start])visited.add(start)while queue:node = queue.pop()visited.add(node)process(node)nodes = generate_related_nodes(node)queue.push(nodes)
1、Leetcode-102-二叉树的层次遍历
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。例如:
给定二叉树: [3,9,20,null,null,15,7],3/ \9  20/  \15   7
返回其层次遍历结果:[[3],[9,20],[15,7]
]解法一:class Solution:def levelOrder(self, root: TreeNode) -> List[List[int]]:ans,level = [],[root]while root and level:ans.append([node.val for node in level])LRpair = [(node.left,node.right) for node in level]level = [leaf for LR in LRpair for leaf in LR if leaf]return ans解法二:BFSclass Solution:def levelOrder(self, root: TreeNode) -> List[List[int]]:from collections import dequeif not root:return []queue,res = deque([root]),[]while queue:cur_level,size = [],len(queue)for i in range(size):node = queue.popleft()if node.left:queue.append(node.left)if node.right:queue.append(node.right)cur_level.append(node.val)res.append(cur_level)return res
2、Leetcode-433-最小基因变化
一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。
假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。
例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。
与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。
现在给定3个参数 — start, end, bank,分别代表起始基因序列,目标基因序列及基因库,请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。如果无法实现目标变化,请返回 -1。注意:
起始基因序列默认是合法的,但是它并不一定会出现在基因库中。
所有的目标基因序列必须是合法的。
假定起始基因序列与目标基因序列是不一样的。
示例 1:
start: "AACCGGTT"
end:   "AACCGGTA"
bank: ["AACCGGTA"]
返回值: 1示例 2:
start: "AACCGGTT"
end:   "AAACGGTA"
bank: ["AACCGGTA", "AACCGCTA", "AAACGGTA"]
返回值: 2示例 3:
start: "AAAAACCC"
end:   "AACCCCCC"
bank: ["AAAACCCC", "AAACCCCC", "AACCCCCC"]
返回值: 3
BFSclass Solution:def minMutation(self, start: str, end: str, bank: List[str]) -> int:bank = set(bank)if end not in bank:return -1q = [(start,0)]change = {'A':'TCG','T':'ACG','C':'ATG','G':'ATC'}while q:node,step = q.pop(0)if node == end:return stepfor i,v in enumerate(node):for j in change[v]:new = node[:i] + j + node[i+1:]if new in bank:q.append((new,step+1))bank.remove(new)return -1
3、Leetcode-22-括号生成给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
["((()))","(()())","(())()","()(())","()()()"
]
class Solution:def generateParenthesis(self, n: int) -> List[str]:res = []def dfs(leftRemain,rightRemain,path,res):if leftRemain > rightRemain or leftRemain < 0 or rightRemain < 0:returnif leftRemain == 0 and rightRemain == 0:res.append(path)returndfs(leftRemain - 1, rightRemain, path + "(", res)dfs(leftRemain, rightRemain-1, path + ")", res)dfs(n,n,"",res)return res
4、Leetcode-515-在每个树行中找到最大值您需要在二叉树的每一行中找到最大的值。示例:输入: 1/ \3   2/ \   \  5   3   9 输出: [1, 3, 9]class Solution:def largestValues(self, root: TreeNode) -> List[int]:from collections import dequequeue = deque([root])res = []if not root:return []while queue:cur_level,size = [],len(queue)for i in range(size):node = queue.popleft()if node.left:queue.append(node.left)if node.right:queue.append(node.right)cur_level.append(node.val)res.append(max(cur_level))return res
5、Leetcode-127-单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",返回它的长度 5。class Solution:def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:wordList.append(endWord)queue  = collections.deque([[beginWord,1]])while queue:word,length = queue.popleft()if word == endWord:return lengthfor i in range(len(word)):for c in 'abcdefghijklmnopqrstuvwxyz':next_word = word[:i] + c + word[i+1:]if next_word in wordList:wordList.remove(next_word)queue.append([next_word,length+1])return 0
6、Leetcode-126-单词接龙II
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]输出:
[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]
]
MostVotes(https://leetcode.com/problems/word-ladder-ii/discuss/40482/Python-simple-BFS-layer-by-layer)
class Solution:def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:wordList = set(wordList)res = []layer = {}layer[beginWord] = [[beginWord]]while layer:newlayer = collections.defaultdict(list)for w in layer:if w == endWord: res.extend(k for k in layer[w])else:for i in range(len(w)):for c in 'abcdefghijklmnopqrstuvwxyz':neww = w[:i]+c+w[i+1:]if neww in wordList:newlayer[neww]+=[j+[neww] for j in layer[w]]wordList -= set(newlayer.keys())layer = newlayerreturn res
7、Leetcode-200-岛屿数量
给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。示例 1:输入:
11110
11010
11000
00000输出: 1
示例 2:输入:
11000
11000
00100
00011输出: 3
MostVotes(https://leetcode.com/problems/number-of-islands/discuss/56340/Python-Simple-DFS-Solution)class Solution:def numIslands(self, grid: List[List[str]]) -> int:if not grid:return 0count = 0for i in range(len(grid)):for j in range(len(grid[0])):if grid[i][j] == '1':self.dfs(grid, i, j)count += 1return countdef dfs(self, grid, i, j):if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j] != '1':returngrid[i][j] = '#'self.dfs(grid, i+1, j)self.dfs(grid, i-1, j)self.dfs(grid, i, j+1)self.dfs(grid, i, j-1)
8、Leetcode-529-扫雷游戏
让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块,'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。
如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。示例 1:
输入: [['E', 'E', 'E', 'E', 'E'],['E', 'E', 'M', 'E', 'E'],['E', 'E', 'E', 'E', 'E'],['E', 'E', 'E', 'E', 'E']]Click : [3,0]
输出: [['B', '1', 'E', '1', 'B'],['B', '1', 'M', '1', 'B'],['B', '1', '1', '1', 'B'],['B', 'B', 'B', 'B', 'B']]MostVotes(https://leetcode.com/problems/minesweeper/discuss/137802/Python-DFS-solution)class Solution:def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]:if not board:return []m, n = len(board), len(board[0])i, j = click[0], click[1]# If a mine ('M') is revealed, then the game is over - change it to 'X'.if board[i][j] == 'M':board[i][j] = 'X'return board# run dfs to reveal the boardself.dfs(board, i, j)return boarddef dfs(self, board, i, j):if board[i][j] != 'E':returnm, n = len(board), len(board[0])       directions = [(-1,-1), (0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0)]mine_count = 0for d in directions:ni, nj = i + d[0], j + d[1]if 0 <= ni < m and 0 <= nj < n and board[ni][nj] == 'M':        mine_count += 1if mine_count == 0:board[i][j] = 'B'else:board[i][j] = str(mine_count)returnfor d in directions:ni, nj = i + d[0], j + d[1]if 0 <= ni < m and 0 <= nj < n:self.dfs(board, ni, nj)

二分查找部分

二分查找的前提

1、目标函数单调性(单调递增或递减)

2、存在上下界(bound)

3、能够通过索引访问(index accessible)

代码模板:
left,right = 0,len(array)-1
while left <= right:mid = (left + right)/2if array[mid] == target:# find the targetbreak or return resultelif array[mid] < target:left = mid + 1else:right = mid - 11、Leetcode-69-x的平方根
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2二分查找法
class Solution:def mySqrt(self, x: int) -> int:left,right = 0,x//2 + 1while left < right:mid = left + (right-left+1)//2if mid*mid > x:right = mid -1else:left = mid return left牛顿法
class Solution:def mySqrt(self, x: int) -> int:r = xwhile r*r > x:r = (r+x//r)//2return r2、Leetcode-367-有效的完全平方数
给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如  sqrt。
示例 1:
输入:16
输出:True
示例 2:
输入:14
输出:False
class Solution:def isPerfectSquare(self, num: int) -> bool:x = numwhile x*x>num:x = (x + num//x)//2if x*x==num:return Truereturn False3、Leetcode-33-搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
class Solution:def search(self, nums: List[int], target: int) -> int:left,right = 0,len(nums)-1while left < right:mid = left + (right - left)//2if nums[0] <= nums[mid] and (target > nums[mid] or target < nums[0]):left = mid + 1elif(target > nums[mid] and target < nums[0]):left = mid + 1else:right = midif left == right and nums[left] == target: return leftreturn -1 4、编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:输入:
matrix = [[1,   3,  5,  7],[10, 11, 16, 20],[23, 30, 34, 50]
]
target = 3
输出: true
class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:if len(matrix)==0:return Falsei,row,col = 0,len(matrix)-1,len(matrix[0])-1while i <= row and 0 <=col:if matrix[i][col]>target:col -= 1elif matrix[i][col]==target:return Trueelse:i += 1return False
5、Leetcode-153-寻找旋转数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例 1:
输入: [3,4,5,1,2]
输出: 1
示例 2:
输入: [4,5,6,7,0,1,2]
输出: 0
class Solution:def findMin(self, nums: List[int]) -> int:if len(nums) == 1:return nums[0]left,right = 0,len(nums)-1if nums[right] > nums[0]:return nums[0]while left <= right:mid = left + (right - left)//2if nums[mid] > nums[mid+1]:return nums[mid+1]if nums[mid-1] > nums[mid]:return nums[mid]if nums[mid] > nums[0]:left = mid + 1else:right = mid - 1

贪心算法部分

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法。简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解,这种子问题最优解成为最优子结构。贪心算法与动态规划的不同在于它对每个子问题的解决方案都作出选择不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。

1、Leetcode-455-分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
示例 1:
输入: [1,2,3], [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
贪心算法
class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()child = 0cookie = 0while child < len(g) and cookie < len(s):if g[child] <= s[cookie]:child += 1cookie += 1return child2、Leetcode-122-买股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。示例 1:输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。class Solution:def maxProfit(self, prices: List[int]) -> int:total = 0for i in range(len(prices)-1):if prices[i+1] > prices[i]:total += (prices[i+1]-prices[i])return total3、Leetcode-55-跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。class Solution:def canJump(self, nums: List[int]) -> bool:endLength  = len(nums) - 1for i in range(len(nums)-1,-1,-1):if nums[i] + i >= endLength:endLength = ireturn endLength == 04、Leetcode-860-柠檬水找零在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零,返回 true ,否则返回 false 。示例 1:
输入:[5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。class Solution:def lemonadeChange(self, bills: List[int]) -> bool:five,ten = 0,0for bill in bills:if bill == 5:five += 1elif bill == 10:if not five:return Falsefive -= 1ten += 1else:if five and ten:five -= 1ten -= 1elif five>=3:five -= 3else:return Falsereturn True5、Leetcode-874-模拟行走机器人机器人在一个无限大小的网格上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令:
-2:向左转 90 度
-1:向右转 90 度
1 <= x <= 9:向前移动 x 个单位长度
在网格上有一些格子被视为障碍物。
第 i 个障碍物位于网格点  (obstacles[i][0], obstacles[i][1])
如果机器人试图走到障碍物上方,那么它将停留在障碍物的前一个网格方块上,但仍然可以继续该路线的其余部分。
返回从原点到机器人的最大欧式距离的平方。
示例 1:输入: commands = [4,-1,3], obstacles = []
输出: 25
解释: 机器人将会到达 (3, 4)class Solution:def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int:dx,dy,x,y = 0,1,0,0distance = 0obs_dict = {}for obs in obstacles:obs_dict[tuple(obs)] = 0for com in commands:if com == -2:dx,dy = -dy,dxelif com == -1:dx,dy = dy,-dxelse:for j in range(com):next_x = x + dxnext_y = y + dyif (next_x,next_y) in obs_dict:breakx,y = next_x,next_ydistance = max(distance,x*x+y*y)return distance6、Leetcode-45-跳跃游戏II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。class Solution:def jump(self, nums: List[int]) -> int:if nums.count(1) == len(nums):return len(nums)-1def fun(n):if not n:return 0for k,v in enumerate(n):if v + k >= len(n):return fun(n[:k]) + 1return fun(nums[:-1])

动态规划部分

动态规划和递归或者分治没有根本上的区别(关键看有无最优的子结构)

共性:找到重复子问题

差异性:最优子结构、中途可以淘汰次优解

Bottom Up- 自底向上
F[n] = F[n-1] + F[n-2]
a[0] = 0, a[1] = 1
for i in range(2,n+1):a[i] = a[i-1] + a[i-2]a[n]
0,1,1,2,3,5,8,13状态转移方程(DP方程)
opt[i,j] = opt[i+1,j] + opt[i,j+1]
完整逻辑:
if a[i,j]='空地':opt[i,j] = opt[i+1,j] + opt[i,j+1]
else opt[i,j]=0动态规划关键点:
1、最优子结构 opt[n] = best_od(opt[n-1],opt[n-2],...)
2、存储中间状态: opt[i]
3、递推公式:
Fib: opt[i] = opt[n-1] + opt[n-2]
二维路径: opt[i,j] = opt[i+1][j] + opt[i][j+1](且判断a[i,j]是否空地)1、Leetcode-62-不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
方法:动态规划
思路:第一行 + 第一列都是在边界,所以只能为1 + 动态方程class Solution:def uniquePaths(self, m: int, n: int) -> int:dp = [[0]*n]*mfor i in range(m):dp[i][0] = 1for j in range(n):dp[0][j] = 1for i in range(1,m):for j in range(1,n):dp[i][j] = dp[i-1][j] + dp[i][j-1]return dp[-1][-1]
2、Leetcode-1143-最长公共子序列给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",它的长度为 3
class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:if not text1 or not text2:return 0m,n = len(text1),len(text2)dp = [[0]*(n+1) for _ in range(m+1)]for i in range(1,m+1):for j in range(1,n+1):if text1[i-1] == text2[j-1]:dp[i][j] = 1 + dp[i-1][j-1]else:dp[i][j] = max(dp[i-1][j],dp[i][j-1])return dp[m][n]
3、Leetcode-120-三角形最小路径和给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[[2],[3,4],[6,5,7],[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)
class Solution:def minimumTotal(self, triangle: List[List[int]]) -> int:"""dp[i][j] = triangle[i][j] + min(dp[i+1][j],dp[i+1][j+1])"""dp = trianglefor i in range(len(triangle)-2,-1,-1):for j in range(len(triangle[i])):dp[i][j] += min(dp[i+1][j],dp[i+1][j+1])return dp[0][0]
4、Leetcode-53-最大子序和给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。class Solution:def maxSubArray(self, nums: List[int]) -> int:for i in range(1,len(nums)):nums[i] = max(nums[i-1],0) + nums[i]return max(nums)
5、Leetcode-152-乘积最大子序列
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution:def maxProduct(self,nums:List[int])->int:ma,mi,res = nums[0]if nums[i]<0:ma,mi = mi,mafor i in range(len(nums)):ma = max(ma*nums[i],nums[i])mi = min(mi*nums[i],nums[i])res = max(res,ma)return res
6、Leetcode-322-零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:"""f(n) = min{f(n-k),k in [1,2,5]} + 1"""MAX = float("inf")dp = [0] + [MAX] * amountfor i in range(1,amount+1):dp[i] = min([dp[i-c] if i-c >=0 else MAX for c in coins]) + 1return [dp[amount],-1][dp[amount]==MAX]
7、Leetcode-198-打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。class Solution:def rob(self, nums: List[int]) -> int:cur,pre = 0,0for num in nums:cur,pre = max(pre + num, cur),curreturn cur
8、Leetcode-213-打家劫舍-II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。class Solution:def rob(self, nums: List[int]) -> int:def my_rob(nums):cur,pre = 0,0for num in nums:pre,cur = cur,max(pre+num,cur)return curreturn max(my_rob(nums[:-1]),my_rob(nums[1:])) if len(nums)!=1 else nums[0]
动态规划解决股票系列问题
状态转移框架;
base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -float("inf")
状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i])case-1(k=1的情况): Leetcode_121_买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。class Solution:def maxProfit(self, prices: List[int]) -> int:dp_i_0,dp_i_1 = 0,float("-inf")for i in range(len(prices)):dp_i_0 = max(dp_i_0,dp_i_1 + prices[i])dp_i_1 = max(dp_i_1,-prices[i])return dp_i_0case_2(k=正无穷的情况):Leetcode_122_买卖股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。分析: 在k=正无穷的情况下,上述状态转移方程
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i])
转化为
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k][0] - prices[i])
==>
dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i])
code:class Solution:def maxProfit(self, prices: List[int]) -> int:dp_i_0,dp_i_1 = 0,-float("inf")for i in range(len(prices)):temp = dp_i_0dp_i_0 = max(dp_i_0,dp_i_1 + prices[i])dp_i_1 = max(dp_i_1,temp - prices[i])return dp_i_0case_3(添加手续费):Leetcode_714_买卖股票最佳时机含手续费
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
示例 1:
输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.class Solution:def maxProfit(self, prices: List[int], fee: int) -> int:n = len(prices)dp_i_0,dp_i_1 = 0,-float("inf")for price in prices:item = dp_i_0dp_i_0 = max(dp_i_0,dp_i_1+price)dp_i_1 = max(dp_i_1,dp_i_0-price-fee)return dp_i_0case_4(隔一天才能购买):Leetcode_309_最佳买卖股票时机含冷冻期
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]思路:
状态转移方程:
dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1],dp[i-2][0] - prices[i])
注:i天购买时,从i-2状态转移
class Solution:def maxProfit(self, prices: List[int]) -> int:n = len(prices)dp_i_0,dp_i_1,da_pre = 0,float("-inf"),0for i in range(n):temp = dp_i_0dp_i_0 = max(dp_i_0,dp_i_1 + prices[i])dp_i_1 = max(dp_i_1,da_pre - prices[i])da_pre = tempreturn dp_i_0case_5(k=2情况下):leetcode_123_买卖股票的最佳时机III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。示例 1:
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。k = 1,2,状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i])
=>
p[i][1][0] = max(dp[i-1][1][0],dp[i-1][1][1] + prices[i])
dp[i][1][1] = max(dp[i-1][1][1],dp[i-1][0][0] - prices[i])
p[i][2][0] = max(dp[i-1][2][0],dp[i-1][2][1] + prices[i])
dp[i][2][1] = max(dp[i-1][2][1],dp[i-1][1][0] - prices[i])class Solution:def maxProfit(self, prices: List[int]) -> int:dp_i10,dp_i11,dp_i20,dp_i21 = 0,-float("inf"),0,-float('inf')for price in prices:dp_i10 = max(dp_i10,dp_i11 + price)dp_i11 = max(dp_i11,-price)dp_i20 = max(dp_i20,dp_i21 + price)dp_i21 = max(dp_i21,dp_i10 - price)return dp_i20

Leetcode-一篇帖子就够啦相关推荐

  1. Vue开发入门看这篇文章就够了

    摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...

  2. linux shell find depth,搞定 Linux Shell 文本处理工具,看完这篇集锦就够了

    原标题:搞定 Linux Shell 文本处理工具,看完这篇集锦就够了 Linux Shell是一种基本功,由于怪异的语法加之较差的可读性,通常被Python等脚本代替.既然是基本功,那就需要掌握,毕 ...

  3. Android 8.0新特性(看这篇文章就够了)

    2019独角兽企业重金招聘Python工程师标准>>> 在刚结束不久的谷歌 I/O2017开发者大会上发布的第二个Android O(安卓8.0)开发者预览,并且向普通用户开放了第二 ...

  4. MySQL事务,这篇文章就够了

    原文链接:https://blog.ouyangsihai.cn/ >> MySQL事务,这篇文章就够了 在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的系列文章,应该对你读下面的 ...

  5. 好多人都说存储过程很难?认真看这篇文章就够了

    何为存储过程? 存储过程是在数据库管理系统中保存的.预先编译的并能实现某种功能的sql程序,说直白点,java知道吧?和java的方法一样. 每遇到一个新的知识点时,我们都会看看它的优点,从而加深对它 ...

  6. java后端做教育视频网站源码_【Java并发面试点】看这一篇应该是够了

    [Java并发面试点]看这一篇应该是够了 Java并发编程是Java后端.大数据开发面试必问项目之一,求职者务必掌握! Java并发面试点List 并发概念须知:进程与线程.同步与异步.并发与并行.阻 ...

  7. 使用分层网络模型的两个优点是什么_从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了...

    从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了​mp.weixin.qq.com 前言 今天和大家一起谈谈"网络",之前写的文章可能不太通俗易懂,有人就 ...

  8. python入门书籍推荐,看这篇文章就够,请!

    python入门书籍推荐,看这篇文章就够,请! 事实上,有关python的书籍很多很多,就当当网就有50000件和python有关的书籍,我特地去了当地的新华书店,蹲了大半天,回来给大家推荐,适合想学 ...

  9. C++面试常见问答题看这三篇文章就够了(上)

    目录 1. 标识符的组成结构 2. 动态关联和静态关联的区别 3.  重载(overload)和重写(overried)的区别 4. class和struct的区别 5. 构造方法的特点 6. 面向对 ...

最新文章

  1. 阿里智能对话交互实践与创新
  2. 顺天则昌,逆天则亡(2006-03-13 21:07:17)(新浪)
  3. office2007/2010/2013/2016安装出现错误:无法安装64位版本的office,因为在您的PC上......
  4. 福利一波,赠票:2018杭州云栖大会 - 单日票(9月22日)
  5. Spring Boot 揭秘与实战(二) 数据缓存篇 - EhCache
  6. python安装包_在python官网打不开的情况下获取获取官方最新安装包
  7. JZOJ 4910. 【NOIP2017模拟12.3】子串
  8. matlabpython建模_一直在用Matlab建模,现在Python很火,用学么?
  9. linux 查看可执行文件动态链接库相关信息(转)
  10. socketserver 源码浅读
  11. 《人月神话》读后感一
  12. 禾川plc编程软件_HCP Works下载 HCP Works(禾川PLC编程软件) v2.26.01.92012 官方安装版 下载-脚本之家...
  13. 轻松复制百度文库内容
  14. 主题:免费的论文查重网站 正文:给大家推荐一个免费的论文查重网站PaperPP:http://www.paperpp.com
  15. 免费的视频格式转换器哪个最好用呢?
  16. 一步一步SharePoint 2007之十九:解决实现注册用户后,自动具备访问网站的权限的问题(1)——配置Provider...
  17. SqlServer 多服务器管理(MSSQL分布式作业管理)
  18. fedora20 grub2的主题更换
  19. 全新第 13 代英特尔酷睿处理器发布,多线程性能提升 41%
  20. 计算机运算器发展趋势,2020计算器市场发展现状及及前景分析

热门文章

  1. Uva 11922 Splay
  2. 基于用户画像的实时异步化视频推荐系统
  3. Android 插件框架机制之Small
  4. h3c telnet
  5. 他山之石:五个互联网英雄的创业启示!
  6. ST-Link VCP Ctrl驱动安装失败解决(win7 64bits)
  7. extjs树使用别的皮肤的样式_收藏!艾多美逆龄六件套的使用方法~
  8. gitlab上传文件到group_gitlab恢复备份数据
  9. rocketmq 消息 自定义_rocketMq-Topic创建过程
  10. python多分类画混淆矩阵_【AI基础】分类器评估一:混淆矩阵、准确率、精确率、召回率、灵敏度、特异度、误诊率、漏诊率...