寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:
nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:
nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

解法
参考网上的题解:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/shuang-zhi-zhen-by-powcai/
首先,我们理解什么中位数:指的是该数左右个数相等。
比如:odd : [1,| 2 |,3],2 就是这个数组的中位数,左右两边都只要 1 位;
even: [1,| 2, 3 |,4],2,3 就是这个数组的中位数,左右两边 1 位;

那么,现在我们有两个数组:
num1: [a1,a2,a3,…an]
nums2: [b1,b2,b3,…bn]

[nums1[:left1],nums2[:left2] | nums1[left1:], nums2[left2:]]
只要保证左右两边 个数 相同,中位数就在 | 这个边界旁边产生。
如何找边界值,我们可以用二分法,我们先确定 num1 取 m1 个数的左半边,那么 num2 取 m2 = (m+n+1)/2 - m1 的左半边,找到合适的 m1,就用二分法找。
当 [ [a1],[b1,b2,b3] | [a2,…an],[b4,…bn] ]
我们只需要比较 b3 和 a2 的关系的大小,就可以知道这种分法是不是准确的!

  • a2 < b3:说明a2不够大,nums1应该再往左边多分一些,即m1应该再加大些
  • a2 >= b3: a2有可能太大了,所以m1可以少一些;也有可能当前的位置正好,所以right = m1

例如:我们令:
nums1 = [-1,1,3,5,7,9]
nums2 =[2,4,6,8,10,12,14,16]
当 m1 = 4,m2 = 3 ,它的中位数就是median = (num1[m1] + num2[m2])/2
时间复杂度:O(log(min(m,n)))O(log(min(m,n)))

简单排序方法:

class Solution(object):def findMedianSortedArrays(self, nums1, nums2):""":type nums1: List[int]:type nums2: List[int]:rtype: float"""nums1.extend(nums2)nums1 = sorted(nums1)len_num = len(nums1)if len_num % 2 == 0:tmp = int(len_num / 2)re = (float)(nums1[tmp-1] + nums1[tmp])return re/2else:tmp = int((len_num-1) / 2)return nums1[tmp]
class Solution:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:n1 = len(nums1)n2 = len(nums2)if n1 > n2:return self.findMedianSortedArrays(nums2,nums1)k = (n1 + n2 + 1)//2left = 0right = n1while left < right :m1 = left +(right - left)//2m2 = k - m1if nums1[m1] < nums2[m2-1]:left = m1 + 1else:right = m1m1 = leftm2 = k - m1 c1 = max(nums1[m1-1] if m1 > 0 else float("-inf"), nums2[m2-1] if m2 > 0 else float("-inf") )if (n1 + n2) % 2 == 1:return c1c2 = min(nums1[m1] if m1 < n1 else float("inf"), nums2[m2] if m2 <n2 else float("inf"))return (c1 + c2) / 2

找出第 k 小的距离对

给定一个整数数组,返回所有数对之间的第 k 个最小距离。一对 (A, B) 的距离被定义为 A 和 B 之间的绝对差值。

示例 1:

输入:
nums = [1,3,1]
k = 1
输出:0
解释:
所有数对如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
因此第 1 个最小距离的数对是 (1,1),它们之间的距离为 0。

提示:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.

解法
参考网上题解:https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/solution/hei-ming-dan-zhong-de-sui-ji-shu-by-leetcode/

由于第 k 小的距离一定在 [0, W = max(nums) - min(nums)] 中,我们在这个区间上进行二分。对于当前二分的位置 mid,统计距离小于等于 mid 的距离对数量,并根据它和 k 的关系调整区间的上下界。

具体实现的时候,我们可以使用双指针来计算出所有小于等于 mid 的距离对数目。我们维护 left 和 right,其中 right 通过循环逐渐递增,left 在每次循环中被维护,使得它满足 nums[right] - nums[left] <= mid 且最小。
这样对于 nums[right],以它为右端的满足距离小于等于 mid 的距离对数目即为 right - left。我们在循环中对这些 right - left 进行累加,就得到了所有小于等于 mid 的距离对数目。

class Solution(object):def smallestDistancePair(self, nums, k):""":type nums: List[int]:type k: int:rtype: int"""nums = sorted(nums)l, r = 0, nums[-1] - nums[0]while l < r:mid = (l + r) // 2cnt = self.count(nums, mid)if cnt < k:l = mid + 1else:r = midreturn ldef count(self, nums, k):cnt = 0l = 0for r, x in enumerate(nums):while x - nums[l] > k:l += 1cnt += r - lreturn cnt

分割数组的最大值

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

注意:
数组长度 n 满足以下条件:

1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:

输入:
nums = [7,2,5,10,8]
m = 2

输出:
18

解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

解法
首先分析题意,可以得出结论,结果必定落在 [max(nums), sum(nums)] 这个区间内,因为左端点对应每个单独的元素构成一个子数组,右端点对应所有元素构成一个子数组。
然后可以利用二分查找法每次猜测一个答案,然后模拟一下划分子数组的过程,可以得到用这个mid值会一共得到的子区间数cnt,然后比较cnt和m的关系,来更新区间范围。

class Solution(object):def splitArray(self, nums, m):""":type nums: List[int]:type m: int:rtype: int"""if len(nums) == m:return max(nums)l, r = max(nums), sum(nums)while l < r:mid = (l + r) // 2cnt, tmp = 1, 0 for x in nums:tmp += xif tmp > mid:tmp = xcnt += 1if cnt > m:l = mid + 1else:r = midreturn l 

存在重复元素 III

给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。

示例 1:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false

解法
参考:https://leetcode-cn.com/problems/contains-duplicate-iii/solution/li-yong-tong-de-yuan-li-onpython3-by-zhou-pen-chen/
首先,定义桶的大小是t+1, nums[i]//(t+1)决定放入几号桶,这样在一个桶里面的任意两个的绝对值差值都<=t
例如t=3, nums=[0 ,5, 1, 9, 3,4],那么0号桶就有[0,1,3],1号桶就有[4,5],2号桶就有[9]

先不考虑索引差值最大为K的限制,那么遍历nums每一个元素,并把他们放入相应的桶中,有两种情况会返回True

要放入的桶中已经有其他元素了,这时将nums[i]放进去满足差值<=t
可能存在前面一个桶的元素并且与nums[i]的差值<=t 或者 存在后面一个桶的元素并且与nums[i]的差值<=t
根据返回True的第一个条件,可以知道前后桶的元素最多也只能有一个。

接着考虑限制桶中的索引差最大为K,当i>=k的时候:
我们就要去删除存放着nums[i-k]的那个桶(编号为nums[i-k]//(t+1))
这样就能保证遍历到第i+1个元素时,全部桶中元素的索引最小值是i-k+1,就满足题目对索引的限制了

class Solution(object):def containsNearbyAlmostDuplicate(self, nums, k, t):""":type nums: List[int]:type k: int:type t: int:rtype: bool"""if k < 0 or t < 0 :return Falsedic = {}size = t + 1for i in range(len(nums)):x = nums[i] // sizeif x in dic:return Truedic[x] = nums[i]if x - 1 in dic and abs(dic[x - 1] - nums[i]) <= t:return Trueif x + 1 in dic and abs(dic[x + 1] - nums[i]) <= t:return Trueif i  >= k:dic.pop(nums[i - k] // size)return False

三种取值的排序

一个数组里面只有’r’,‘g’,'b’三种字符随机排序,如果用In-place的方法让这个数组排好顺序,排序后的样子是[r,r,r,g,g,g,b,b,b]

解法

  • 暴力:用哈希法存下每种元素的个数,最后直接赋值
  • 三个指针:明确在 p0 左侧维护 r, p1 右侧维护 g, p2 右侧维护 b, 然后使用 p0 朝右扫描,如果扫到 g 很容易处理,如果扫到 b 就需要把 p1, p2 各自移动一位,并在移动过程中注意维护各个区间中元素成分是否正确
def rgbSort(arr):n = len(arr)p1 = p2 = n - 1p0 = 0while p0 <= p1:if arr[p0] == 'g':arr[p0], arr[p1] = arr[p1], arr[p0]p1 -= 1elif arr[p0] == 'b':arr[p0], arr[p1] = arr[p1], arr[p0]arr[p1], arr[p2] = arr[p2], arr[p1]p2 -= 1p1 -= 1else:p0 += 1return arr

第 K 个最小的素数分数

一个已排序好的表 A,其包含 1 和其他一些素数. 当列表中的每一个 p<q 时,我们可以构造一个分数 p/q 。

那么第 k 个最小的分数是多少呢? 以整数数组的形式返回你的答案, 这里 answer[0] = p 且 answer[1] = q.

示例:
输入: A = [1, 2, 3, 5], K = 3
输出: [2, 5]
解释:
已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3.
很明显第三个最小的分数是 2/5.

输入: A = [1, 7], K = 1
输出: [1, 7]
注意:

A 长度的取值范围在 2 — 2000.
每个 A[i] 的值在 1 —30000.
K 取值范围为 1 —A.length * (A.length - 1) / 2

解法
https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/solution/di-k-ge-zui-xiao-de-su-shu-fen-shu-by-leetcode/

方法一:二分查找
under(x) 用于求解小于 x 的分数数量,这是一个关于 x 的单调增函数,因此可以使用二分查找求解。
算法:使用二分查找找出一个 x,使得小于 x 的分数恰好有 K 个,并且记录其中最大的一个分数。
我们的二分搜索与其他的二分搜索方法类似:初始有区间 [lo, hi],中心点 mi = (lo + hi) / 2.0。如果小于 mi 的分数数量小于 K,更新区间为 [mi, hi],否则更新为 [lo, mi]。
under(x) 函数有两个目的:返回小于 x 的分数数量以及小于 x 的最大分数。在 under(x) 函数中使用滑动窗口的方法:对于每个 primes[j],找出最大的 i 使得 primes[i] / primes[j] < x。随着 j (和 primes[j])的增加, i 也会随之增加。

方法二:使用一个堆记录所有以 primes[j] 为分母且未被弹出的最小分数。依次从堆中弹出 K-1 个元素,此时堆顶的分数就是结果。
算法:在 Python 中,使用 (fraction, i, j) 表示分数 fraction = primes[i] / primes[j]。如果下一个分数有效(即 i+1 < j),那么使用当前分数时,将下一个分数压入堆中。

方法一:

class Solution(object):def kthSmallestPrimeFraction(self, primes, K):from fractions import Fractiondef under(x):# Return the number of fractions below x,# and the largest such fractioncount = best = 0i = -1for j in xrange(1, len(primes)):while primes[i+1] < primes[j] * x:i += 1count += i+1if i >= 0:best = max(best, Fraction(primes[i], primes[j]))return count, best# Binary search for x such that there are K fractions# below x.lo, hi = 0.0, 1.0while hi - lo > 1e-9:mi = (lo + hi) / 2.0count, best = under(mi)if count < K:lo = mielse:ans = besthi = mireturn ans.numerator, ans.denominator
class Solution(object):#Note - this solution may TLE.def kthSmallestPrimeFraction(self, A, K):pq = [(A[0] / float(A[i]), 0, i) for i in xrange(len(A) - 1, 0, -1)]for _ in xrange(K-1):frac, i, j = heapq.heappop(pq)i += 1if i < j:heapq.heappush(pq, (A[i] / float(A[j]), i, j))return A[pq[0][1]], A[pq[0][2]]

算法题:名人问题,给出最优解法

有n个人他们之间认识与否用邻接矩阵表示(1表示认识,0表示不认识),并A认识B并不意味着B认识A,也就意味着是个有向图。如果一个人是名人,他必须满足两个条件,一个是他不认识任何人,另一个是所有人必须都认识他。

解法
如果a认识b,则a不会是名人;如果a不认识b,则b不会是名人。因此每询问一次a是否认识b,都可以排除掉一个人,所以在O(n)时间内就可以排除掉n-1个人。 最后还要检查确认,是否其他人都认识这个人,以及这个人都不认识其他人。


def findCelebrity(int n):celebrity = 0for i in range(1, n):if knows(celebrity, i):celebrity = ifor i in range(n):if (celebrity != i and knows(celebrity, i)):return -1if (celebrity != i and !knows(i, celebrity)):return -1return celebrity

丑数

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解法

  • 暴力:把每个数字逐个遍历判断是否是丑数的方式。
  • 多指针法:使用空间换时间。我们使用一个数组保存每个丑数,然后生成下一个丑数。使用了3个指针,分别指向最后一个进行×2,×3,×5操作后会大于当前最大的丑数的位置。那么需要找到下一个丑数的时候,一定会是这三个指针指向的丑数进行对应操作的结果之一。因此,每次都更新三个指针指向下一次操作就会变成最大值的位置。就能一直生成下一丑数。
# -*- coding:utf-8 -*-
class Solution:def GetUglyNumber_Solution(self, index):# write code hereif index < 1:return 0res = [1]t2 = t3 = t5 = 0nextNum = 1while nextNum < index:minNum = min(res[t2] * 2, res[t3] * 3, res[t5] * 5)res.append(minNum)if res[t2] * 2 <= minNum:t2 += 1if res[t3] * 3 <= minNum:t3 += 1if res[t5] * 5 <= minNum:t5 += 1nextNum += 1return res[nextNum - 1]

判断某个数是否为丑数:
暴力破解,思路非常简单,首先除2,直到不能整除为止,然后除5到不能整除为止,然后除3直到不能整除为止。 最终判断剩余的数字是否为1,如果是1则为丑数,否则不是丑数。

class Solution:def isUgly(self, num):""":type num: int:rtype: bool"""while num >0 and num%2==0:num /= 2while num >0 and num%3==0:num /= 3while num >0 and num%5==0:num /= 5return True if num == 1.0 else False

跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

解法
贪心算法:设置一个当前能到达的最远位置,从头开始遍历,只有当i<=maxPos时,才表示可以到达i位置,这时我们更新maxPos,判断其能否到达尾部,若不能,返回false,能则返回true。

class Solution(object):def canJump(self, nums):""":type nums: List[int]:rtype: bool"""if not nums:return FalsemaxPos = 0for i in range(len(nums)):if maxPos >= i:maxPos = max(maxPos, i + nums[i])if maxPos >= len(nums) - 1:return Truereturn False

跳跃游戏 II

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:

假设你总是可以到达数组的最后一个位置。

解法
贪心算法:我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。

class Solution:def jump(self, nums: List[int]) -> int:n = len(nums)maxPos, end, step = 0, 0, 0for i in range(n - 1):if maxPos >= i:maxPos = max(maxPos, i + nums[i])if i == end:end = maxPosstep += 1return step

跳跃游戏III

这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]。请你判断自己是否能够跳到对应元素值为 0 的 任意 下标处。注意,不管是什么情况下,你都无法跳到数组之外。
示例 1:
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
示例 2:
输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 0 -> 下标 4 -> 下标 1 -> 下标 3

解法
参考:https://blog.nowcoder.net/n/54abe7064f0c44e786081a0530647582
思路:广度优先搜索:初始时将 start 加入队列。在每一次的搜索过程中,我们取出队首的节点 u,它可以到达的位置为 u + arr[u] 和 u - arr[u]。如果某个位置落在数组的下标范围 [0, len(arr)) 内,并且没有被搜索过,则将该位置加入队尾。只要我们搜索到一个对应元素值为 0 的位置,我们就返回 True。在搜索结束后,如果仍然没有找到符合要求的位置,我们就返回 False。

class Solution {public:bool canReach(vector<int>& arr, int start) {if (arr[start] == 0) {return true;}int n = arr.size();vector<bool> used(n);queue<int> q;q.push(start);used[start] = true;while (!q.empty()) {int u = q.front();q.pop();if (u + arr[u] < n && !used[u + arr[u]]) {if (arr[u + arr[u]] == 0) {return true;}q.push(u + arr[u]);used[u + arr[u]] = true;}if (u - arr[u] >= 0 && !used[u - arr[u]]) {if (arr[u - arr[u]] == 0) {return true;}q.push(u - arr[u]);used[u - arr[u]] = true;}}return false;}
};

最大正方形

在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

示例:

输入:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

输出: 4

解法

  1. 暴力法:由于正方形的面积等于边长的平方,因此要找到最大正方形的面积,首先需要找到最大正方形的边长,然后计算最大边长的平方即可:遍历矩阵中的每个元素,每次遇到 1,则将该元素作为正方形的左上角;确定正方形的左上角后,根据左上角所在的行和列计算可能的最大正方形的边长(正方形的范围不能超出矩阵的行数和列数),在该边长范围内寻找只包含 1的最大正方形;每次在下方新增一行以及在右方新增一列,判断新增的行和列是否满足所有元素都是 1。时间复杂度:O(mn*min(m,n)^2),其中 m 和 n 是矩阵的行数和列数。
  2. 动态规划:用 dp(i, j)表示以 (i, j) 为右下角,且只包含 1 的正方形的边长最大值。如果我们能计算出所有 dp(i, j) 的值,那么其中的最大值即为矩阵中只包含 1 的正方形的边长最大值,其平方即为最大正方形的面积。对于每个位置 (i, j)(i,j),检查在矩阵中该位置的值:
    如果该位置的值是 0,则 dp(i, j) = 0,因为当前位置不可能在由 1 组成的正方形中;如果该位置的值是 1,则 dp(i, j) 的值由其上方、左方和左上方的三个相邻位置的 dpdp 值决定。具体而言,当前位置的元素值等于三个相邻位置的元素中的最小值加 1,状态转移方程如下:
    dp(i, j)=min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1))+1
    此外,还需要考虑边界条件。如果 i 和 j 中至少有一个为 0,则以位置 (i, j)为右下角的最大正方形的边长只能是 1,因此 dp(i, j) = 1。

解法一:

class Solution:def maximalSquare(self, matrix: List[List[str]]) -> int:if len(matrix) == 0 or len(matrix[0]) == 0:return 0maxSide = 0rows, columns = len(matrix), len(matrix[0])for i in range(rows):for j in range(columns):if matrix[i][j] == '1':# 遇到一个 1 作为正方形的左上角maxSide = max(maxSide, 1)# 计算可能的最大正方形边长currentMaxSide = min(rows - i, columns - j)for k in range(1, currentMaxSide):# 判断新增的一行一列是否均为 1flag = Trueif matrix[i + k][j + k] == '0':breakfor m in range(k):if matrix[i + k][j + m] == '0' or matrix[i + m][j + k] == '0':flag = Falsebreakif flag:maxSide = max(maxSide, k + 1)else:breakmaxSquare = maxSide * maxSidereturn maxSquare

解法二:

class Solution(object):def maximalSquare(self, matrix):""":type matrix: List[List[str]]:rtype: int"""if not matrix or not matrix[0]:return 0max_side, res = 0, 0 n, m = len(matrix), len(matrix[0])dp = [[0 for i in range(m)] for j in range(n)]for i in range(n):for j in range(m):if matrix[i][j] == '1':if i == 0 or j == 0:dp[i][j] = 1else:dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j -1]) + 1max_side = max(max_side, dp[i][j])res = max_side * max_sidereturn res

柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例:

输入: [2,1,5,6,2,3]
输出: 10

解法

  • 枚举「宽」,我们可以使用两重循环枚举矩形的左右边界以固定宽度 w,此时矩形的高度 h,就是所有包含在内的柱子的「最小高度」,对应的面积为 w * h。时间复杂度均为 O(N^2)
  • 枚举「高」,我们可以使用一重循环枚举某一根柱子,将其固定为矩形的高度 hh。随后我们从这跟柱子开始向两侧延伸,直到遇到高度小于 hh 的柱子,就确定了矩形的左右边界。如果左右边界之间的宽度为 ww,那么对应的面积为 w * h。时间复杂度均为 O(N^2)
  • 单调递增栈操作规则:如果新的元素比栈顶元素大,就入栈;如果新的元素较小,那就一直把栈内元素弹出来,直到栈顶比新元素小。总体思路:对于一个高度,如果能得到向左和向右的边界;那么就能对每个高度求一次面积;遍历所有高度,即可得出最大面积;使用单调栈,在出栈操作时得到前后边界并计算面积

解法一:

class Solution {public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();int ans = 0;// 枚举左边界for (int left = 0; left < n; ++left) {int minHeight = INT_MAX;// 枚举右边界for (int right = left; right < n; ++right) {// 确定高度minHeight = min(minHeight, heights[right]);// 计算面积ans = max(ans, (right - left + 1) * minHeight);}}return ans;}
};

解法二:

class Solution {public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();int ans = 0;for (int mid = 0; mid < n; ++mid) {// 枚举高int height = heights[mid];int left = mid, right = mid;// 确定左右边界while (left - 1 >= 0 && heights[left - 1] >= height) {--left;}while (right + 1 < n && heights[right + 1] >= height) {++right;}// 计算面积ans = max(ans, (right - left + 1) * height);}return ans;}
};

解法三:

class Solution:def largestRectangleArea(self, heights: List[int]) -> int:n = len(heights)left, right = [0] * n, [0] * nmono_stack = list()for i in range(n):while mono_stack and heights[mono_stack[-1]] >= heights[i]:mono_stack.pop()left[i] = mono_stack[-1] if mono_stack else -1mono_stack.append(i)mono_stack = list()for i in range(n - 1, -1, -1):while mono_stack and heights[mono_stack[-1]] >= heights[i]:mono_stack.pop()right[i] = mono_stack[-1] if mono_stack else nmono_stack.append(i)ans = max((right[i] - left[i] - 1) * heights[i] for i in range(n)) if n > 0 else 0return ans

最大矩阵

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

输入:
[
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
输出: 6

解法
动态规划 - 使用柱状图的优化:我们可以以常数时间计算出在给定的坐标结束的矩形的最大宽度。我们可以通过记录每一行中每一个方块连续的“1”的数量来实现这一点。每遍历完一行,就更新该点的最大可能宽度。
然后就类似于上一题:

求出每一层的 heights[] 然后传给上一题的函数就可以了。时间复杂度 : O(NM),空间复杂度 : O(M)

class Solution:# Get the maximum area in a histogram given its heightsdef leetcode84(self, heights):stack = [-1]maxarea = 0for i in range(len(heights)):while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:maxarea = max(maxarea, heights[stack.pop()] * (i - stack[-1] - 1))stack.append(i)while stack[-1] != -1:maxarea = max(maxarea, heights[stack.pop()] * (len(heights) - stack[-1] - 1))return maxareadef maximalRectangle(self, matrix: List[List[str]]) -> int:if not matrix: return 0maxarea = 0dp = [0] * len(matrix[0])for i in range(len(matrix)):for j in range(len(matrix[0])):# update the state of this row's histogram using the last row's histogram# by keeping track of the number of consecutive onesdp[j] = dp[j] + 1 if matrix[i][j] == '1' else 0# update maxarea with the maximum area from this row's histogrammaxarea = max(maxarea, self.leetcode84(dp))return maxarea

单词接龙 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”]
]
示例 2:

输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]

输出: []

解释: endWord “cog” 不在字典中,所以不存在符合要求的转换序列。

解法
把开始单词当作开始节点,结束单词当作结束节点,现在要解决的问题就是开始节点和结束节点是否有连接,求最短路径。

首先,如果结束单词一开始就不在单词列表里,可以直接返回,因为本身就不存在这样的节点。
其次,求最短路径,最容易想到的就是BFS解法,这个从起始节点开始遍历,如果找到了结束节点,代表找到了结果,同时这个肯定是最优解(路径最短)。
注意:此时我们并不能直接返回答案,因为要求的所有最短路径,所以我们要把这一层的所有满足结果都返回。

现在我们还有2个问题:

  • 如何通过某个节点找到它关联的节点,也就是遍历边,我们需要完成这个方法def edges(word),接收一个单词,返回它关联的所有单词
  • 遍历到了结束节点后,如何查到路径。

解决方案:

  • 直接根据题目意思来,每个字母都从a-z变换,如果在单词列表里,代表是有效节点,可以返回
  • 我们在BFS的时候实际上本来就已经知道了路径,所以完全可以把路径存在节点里面,你可以想象一下,你BFS的时候队列里是[word1,word2],原本word1,word2只存储了单词信息,那我可不可以把路径也存到节点里面,变成这样[[parentparent1, parent1,word1],[parentparent2, parent2,word2]],这样一来,我们既可以知道单词信息,同时也可以知道单词从头开始的遍历路径。

BFS的时间复杂度是O(n),n代表单词列表个数,总的时间复杂度是O(26nc)。和普通BFS也没啥区别,就是2个改动,1在节点里保存了路径,2你要写一个能遍历下个节点的方法

class Solution:def findLadders(self, beginWord, endWord, wordList):se=set(wordList)if not endWord in se:return []def edges(word):arr=list(word)for i in range(len(arr)):c=arr[i]for j in range(97,123):arr[i]=chr(j)newWord=''.join(arr)if newWord in se and not newWord in marked:yield newWordarr[i]=cres=[]marked=set()queue=[[beginWord]]while queue:temp=[]found=Falsefor words in queue:marked.add(words[-1])for words in queue:for w in edges(words[-1]):v=words+[w]if w == endWord:res.append(v)found=Truetemp.append(v)if found:          #找到就不再遍历了,即使再有endWord,路径也会更长breakqueue=tempreturn res

优化一:
我们可以把每个单词看作可以hash成c个值,c代表单词长度,比如单词’hit’,可以hash成’it’,'ht’,'hi*'这3个值,如果2个单词的hash值有重合,说明这2个单词是可以通过改1个字母变成另外一个的,所以我们的寻找边方法def edges(word)可以优化成这样,此方法将寻找边的O(26*c)优化成了O©

    hash=collections.defaultdict(list)for word in wordList:for i in range(len(word)):hash[word[:i]+"*"+word[i+1:]].append(word)        #初始化:将单词列表的所有元素全部hash保存进字典def edges(word):for i in range(len(word)):for newWord in hash[word[:i]+'*'+word[i+1:]]:     #遍历该单词的所有hash,从字典里取出关联的单词if not newWord in marked:yield newWord

优化二:双向BFS
因为已经知道了开始节点和结束节点,所以可以从2头开始遍历,哪头的节点少,就遍历哪头,如果2头的节点重合了(有交集)说明连上了,遍历结束。
如果一直没交集,任何一头走到末尾就可以结束,代表全部节点都遍历完成了。
这里为了更好的取交集,begin和end都用了set来表示,同时把路径单独存储在了path字典中,一旦出现交集,从最后一个节点,依据path的存储,直到找到开始节点,能出现交集,说明一定能找到开头。

class Solution:def findLadders(self, beginWord, endWord, wordList):if not endWord in wordList:return []hash=collections.defaultdict(list)for word in wordList:for i in range(len(word)):hash[word[:i]+"*"+word[i+1:]].append(word)def edges(word):for i in range(len(word)):for newWord in hash[word[:i]+'*'+word[i+1:]]:if not newWord in marked:yield newWorddef findPath(end):res=[]for curr in end:for parent in path[curr[0]]:res.append([parent]+curr)return resmarked=set()path=collections.defaultdict(set)begin=set([beginWord])end=set([endWord])forward=Truewhile begin and end:if len(begin)>len(end):begin,end=end,beginforward=not forwardtemp=set()for word in begin:marked.add(word)for word in begin:for w in edges(word):temp.add(w)if forward:path[w].add(word)else:path[word].add(w)begin=tempif begin&end:res=[[endWord]]while res[0][0]!=beginWord:res=findPath(res)return resreturn []

正则表达式匹配

给定s1, s2, (s1只包含正则表达式匹配中的 .* ? 和正常字符,s2只包含正常字符),判定s1 s2是否匹配。.代表任意一个字符,*代表长度,?代表前面的一个字符。*代表重复次数大于等于0,? 代表重复次数 0-1
下面是两个匹配成功的例子:
s1: abc.*abc
s2: abcabc

s1: abc?abc
s2: abcabc

解法
外面一层三个 if else 选择支在判断是不是有 * ? 这两种重复标记,每个选择支里面两个选择支判断当前字符是否匹配成功
简易版:https://blog.csdn.net/qq_20141867/article/details/80910724


def match(s, partten, i, j):if i == len(s) and j == len(partten):return Trueelif i == len(s) or j == len(partten):return Falseif j + 1 < len(partten) and partten[j + 1] == '*':if partten[j] == '.' or partten[j] == s[i]:return match(s, partten, i + 1, j) or \match(s, partten, i + 1, j + 2) or \match(s, partten, i, j + 2)else:return match(s, partten, i, j + 2)elif j + 1 < len(partten) and partten[j + 1] == '?':if partten[j] == '.' or partten[j] == s[i]:return match(s, partten, i + 1, j + 2) or match(s, partten, i, j + 2)else:return match(s, partten, i, j + 2)else:if partten[j] == '.' or partten[j] == s[i]:return match(s, partten, i + 1, j + 1)else:return Falsedef main():print(match("", "", 0, 0))print(match("a", "a", 0, 0))print(match("a", ".", 0, 0))print(match("a", ".*", 0, 0))print(match("a", "a?", 0, 0))print(match("abcabc", "abc.*abc", 0, 0))print(match("abcabc", "abc?abc", 0, 0))print(match("abxxxacc", "abc.*abc", 0, 0))print(match("abcdabc", "abc?abc", 0, 0))if __name__ == "__main__":main()

判断图是否是树

给出 n 个节点,标号分别从 0 到 n - 1 并且给出一个 无向 边的列表 (给出每条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树

样例
给出n = 5 并且 edges = [[0, 1], [0, 2], [0, 3], [1, 4]], 返回 true.
给出n = 5 并且 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], 返回 false.

解法
用BFS去做,如果一个图是树的话,它的结点个数是n,那么它的边的个数一定是n-1,并且这n个结点一定是联通的,联通的意思就是从某个结点出发遍历一遍这个图,得到的集合里有n个结点。因为只给了边的数组,所以我们需要自己来构建图的数据结构。

class Solution:"""@param n: An integer@param edges: a list of undirected edges@return: true if it's a valid tree, or false"""def validTree(self, n, edges):# write your code hereif(n==1):return Trueif(len(edges)!=n-1):return Falsedic={}for i in range(n):dic[i]=set()  #集合里存储与该点连接的点def getGraph(dic,n,edges):   #获得图for i in range(len(edges)):u=edges[i][0]v=edges[i][1]dic[u].add(v)dic[v].add(u)return dicgraph=getGraph(dic,n,edges)  #dic{i:set}from queue import  Queueq=Queue()q.put(0)traversal=set()   #记录已经遍历过的点while(q.qsize()!=0):node=q.get()for neighbor in graph[node]:if(neighbor in traversal):continueq.put(neighbor)traversal.add(neighbor)return(len(traversal)==n)s = Solution()
print(s.validTree(5,[[0, 1], [0, 2], [0, 3], [1, 4]]))

难度较大题型--LeetCode相关推荐

  1. Leetcode 1143.最长公共子序列(求长度)

    Time: 20190906 Type: Medium 题目描述 给定两个字符串text1和text2,返回它们的最长公共子序列. 子序列是通过原字符串删除一些字符(可以不删)生成的新的字符串,不改变 ...

  2. Leetcode 300题AC的刷题总结(C与C++)

    文章目录 前引闲聊 力扣刷题心路历程 Day1 全部看题解 看思路 Day2 - Day7 基本上看题解 怀疑自己是弱智 Day8 - Day15 诶~有些题自己可以做出来了 Day16 - Day2 ...

  3. 数据分析SQL常考题型、大厂例题及面试要点

    文章目录 1. 数据分析岗位技能要求 1.1 哪一个工具是数据分析师的核心工具 1.2 对于数据分析师来说,是否需要建模能力 1.3 数据分析岗位对业务有什么要求 1.4 数据的特点 1.5 优秀数据 ...

  4. 【LeetCode 总结】Leetcode 题型分类总结、索引与常用接口函数

    文章目录 零. Java 常用接口函数 一. 动态规划 二. 链表 三. 哈希表 四. 滑动窗口 五. 字符串 六. DFS.BFS 七. 二分法 八. 二叉树 九. 偏数学.过目不忘 and 原地算 ...

  5. 面试官系列 - LeetCode链表知识点题型总结

    文章目录 前言 知识点 什么是链表 类别 单向链表 循环链表 双向链表 双向循环链表 与数组的性能对比 优缺点 常用技巧 题型总结 基本操作 删除类 翻转类题型 合并链表 环形链表 拆分链表 排序链表 ...

  6. LeetCode常见题型——树

    1. 算法思想 作为(单)链表的升级版,我们通常接触的树都是二叉树(binary tree),即每个节点最多有 两个子节点:且除非题目说明,默认树中不存在循环结构. struct TreeNode { ...

  7. leetcode适合做面试的那些题型

    文章目录 前言 二分查找 [模板题] [容易] [中等] [困难] 单链表 [模板题] [简单] [困难] 栈 [模板题] [简单] 队列 [中等] 二叉树 [简单] [中等] 双指针 [模板题] [ ...

  8. 一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题

    文章目录 1 链表 1.1 常见题型及解题策略 1.1.1 LeetCode中关于链表的题目有以下五种类型题: 1.1.2 解题策略 1.2 链表的基本内容 1.2.1 链表的基本结构: 1.2.2 ...

  9. 用这样的方法,我解决了leetcode的大部分的这种题型!

    点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了 3 个月总结的一线大厂 Java 面试总结,本 ...

最新文章

  1. 微信小程序php后台支付,微信小程序 支付功能实现PHP实例详解
  2. C语言实现pid算法(附完整源码)
  3. ELK+Kafka 企业日志收集平台(一)
  4. Elasticsearch Mapping 详解
  5. poj 1182 食物链 (并查集)
  6. spring framwork解析
  7. joomla 3.6 mysql 版本_Joomla是否支持MariaDB数据库?
  8. ajax跨域post请求的java代理实现
  9. 点击list view中一行内容可以在combox中显示_在后台添加新的产品/文章分类,如何在模板中显示产品列表/文章列表?如何调用分类?...
  10. 马上上线!谷歌与苹果联手抗疫,打造基于蓝牙设备的接触史回溯 | 凌云时刻...
  11. oracle 平均值 最大值,Oracle分析函数三——SUM,AVG,MIN,MAX,COUNT
  12. Android 修改系统时间代码
  13. java写的网络版斗地主_JAVA网络版斗地主游戏
  14. Python基础简答题
  15. 数据库学习——10-13-聚合函数+GROUP BY+HAVING学习
  16. Oracle 中的各种读
  17. 171029 windows10 桌面美化
  18. jzoj 1307. Jail
  19. 安装完Vmware-tools后找不到共享文件夹的解决办法-Ubuntu 18有效
  20. iPhone、iPad、Android UI常用设计尺寸

热门文章

  1. opencv_python答题卡自动判卷
  2. 【EduCoder答案】博客系统 - 登录注册界面
  3. 脑电图分类任务中深度神经网络模型的数据增强:综述2021
  4. PS长图快速切片_关于公众号长图排版及一些bug
  5. 【已解决】把桌面的默认存储位置存到了D盘,D盘所有的东西都出现在了桌面,无法恢复怎么办????
  6. 一人之下合鸿蒙技巧,一人之下手游武侯奇门八卦术养成搭配攻略
  7. WebGIS工程师进阶训练营
  8. 双向buck-boost变换器,采用电压外环,电流内环控制,平均电流控制
  9. 比特同步和帧同步的区别
  10. 国内php排行,国内最好的cms系统排行榜