最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
解法:
- 最简单的就可以想到使用列表。但是要实现检索最小值的功能,可以想到维护一个最小值,就像前缀树中维护一个word的布尔类型一样。注意随时更新min,可以加快运行速度。但是在pop的时候需要重新遍历栈来更新值,所以就不是O(1)的时间了
- 辅助栈法:使用一个主栈模拟入栈出栈的操作,使用另一个栈来实现最小栈,每当入栈的数小于等于栈顶元素时,随主栈都进行一次 pushpush 操作;当出栈的数与最小栈的栈顶元素相等时,最小栈也随主栈进行一次 poppop 操作。
解法一:
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min = None
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.stack.append(x)
if self.min == None or self.min > x:
self.min = x
def pop(self):
"""
:rtype: None
"""
tmp = self.stack.pop()
if len(self.stack) == 0:
self.min = None
if self.min == tmp:
self.min = self.stack[0]
for x in self.stack:
if self.min > x:
self.min = x
def top(self):
"""
:rtype: int
"""
return self.stack[-1]
def getMin(self):
"""
:rtype: int
"""
return self.min
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
解法二:
class MinStack:
def __init__(self):
self.stack = []
self.minstack = []
def push(self, x: int) -> None:
self.stack.append(x)
if not self.minstack or x <= self.minstack[-1]:
self.minstack.append(x)
def pop(self) -> None:
x = self.stack.pop()
if x == self.minstack[-1]:
self.minstack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.minstack[-1]
栈排序
栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。该栈支持如下操作:push、pop、peek 和 isEmpty。当栈为空时,peek 返回 -1。
示例1:
输入:
[“SortedStack”, “push”, “push”, “peek”, “pop”, “peek”]
[[], [1], [2], [], [], []]
输出:
[null,null,null,1,null,2]
示例2:
输入:
[“SortedStack”, “pop”, “pop”, “push”, “pop”, “isEmpty”]
[[], [], [], [1], [], []]
输出:
[null,null,null,null,null,true]
说明:
栈中的元素数目在[0, 5000]范围内。
解法
用一个辅助栈
class SortedStack:
def __init__(self):
self.data = []
self.helper = []
def push(self, val: int) -> None:
if not self.data: # 如果 data 为空,直接将元素添加到data中
self.data.append(val)
else:
# 如果 data 顶的元素比val小,将 data 中比 val 小的元素倒到 helper 中
#然后将 val 放入 data 顶
if self.data[-1] < val:
while self.data and self.data[-1] < val:
self.helper.append(self.data.pop(-1))
self.data.append(val)
# 如果 data 顶的元素等于 val,直接将 val 放在 data 顶
elif self.data[-1] == val:
self.data.append(val)
else:
# 此时的情况为:val < sel.data[-1],需要把 helper 中比 val 大的元素倒到data顶去
# case 1, 如果helper 为空,或者 val 大于等于 helper 顶的元素
# 直接将val 放到 data 顶
if not self.helper or self.helper[-1] <= val:
self.data.append(val)
else:
# case 2, val 小于 helper 的栈顶元素,则把小于 val 的元素倒回 data 中
# 然后把 val 放在 data 栈顶
while self.helper and val < self.helper[-1]:
self.data.append(self.helper.pop())
self.data.append(val)
def pop(self) -> None:
if not self.data:
return -1
# 由于 helper 中会放有较小的元素,
# 首先检查 helper 是否有元素,有的话将其倒入 data 中
# pop data 顶的元素(当前最小值)
while self.helper:
self.data.append(self.helper.pop())
return self.data.pop()
def peek(self) -> int:
if not self.data:
return -1
while self.helper:
self.data.append(self.helper.pop())
return self.data[-1]
def isEmpty(self) -> bool:
return self.data == []
数据流的中位数
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
解法
对于有序数组求中位数很简单了,但是题目给的是数据流,也就是我们必须做到可以时刻求出中位数的程度。
第一种方法:我们可以给每个从数据流出来的数进行排序,然后数组长度除以2就能得到中位数,但显然这样的做法是非常不好的,如果新进来的数是比当前排好序的所有元素都小的数,那么意味着数组中的所有数都必须向后移动一位,也就是O(n)的复杂度,假设每进来一个数都来一遍O(n),性能还是很低的。
第二种方法:采用优先队列,也就是大根堆跟小根堆,我们将较小的n/2个数放到大根堆中,将较大的n/2个数放到小根堆中,显然,如果n是偶数,那么大根堆的堆顶跟小根堆的堆顶就是我们要找的两个中位数,将其相加除以2作为结果返回即可。如果n是奇数,那么就看大根堆跟小根堆谁的节点个数比另一个堆多一个,节点数量多的那个堆的堆顶就是我们要找的中位数,此时我们直接返回结果即可。
需要注意的是:
1.对于取堆顶元素的操作的时间复杂度是常数级别的。
2.插入新节点时我们需要判断节点的值是否小于大根堆堆顶的值或者大于小根堆堆顶的值,
如果小于大根堆堆顶的值,那么节点应该插入大根堆,反过来应该插入小根堆。
3.每次插入新节点我们还需要判断两个堆之间的元素个数是否平衡。插入新节点后,我们判断两个堆的元素个数,如果相差为2那么我们就要对堆进行调整。比如新插入一个节点到小根堆中,而此时大根堆的个数+1小于小根堆的节点个数,这个时候只需要将小根堆的堆顶元素弹出,然后将这个弹出的元素插入大根堆即可。反过来也是一样的操作。为什么可以这样做呢?这是因为我们说了小根堆保存的是较大的n/2个数,而小根堆的堆顶是小根堆中最小的元素,同时也是大根堆中最大的元素,因此我们将这个堆顶元素弹出并插入大根堆的操作并不会破坏“小根堆保存较大的n/2个数,大根堆保存较小的n/2”这样的前提。
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.max_heap = []
self.min_heap = []
def addNum(self, num):
if not self.max_heap:
heapq.heappush(self.max_heap, -num) # python的heaqp模块是小根堆,因此要保存大根堆的话需要加上一个负号
return
if num > -self.max_heap[0]:
heapq.heappush(self.min_heap, num)
if len(self.max_heap) + 1 < len(self.min_heap):
heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap)) # 插入大根堆需要给值加负号
else:
heapq.heappush(self.max_heap, -num)
if len(self.max_heap) > len(self.min_heap) + 1:
heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap)) # 弹出大根堆堆顶的时候需要加负号变为正数
def findMedian(self):
if len(self.max_heap) > len(self.min_heap): # 大根堆元素多一个,中位数是大根堆堆顶
return -self.max_heap[0]
if len(self.max_heap) < len(self.min_heap): # 小根堆元素多一个,中位数是小根堆堆顶
return self.min_heap[0]
else:
return (-self.max_heap[0] + self.min_heap[0])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
解法
- 最简单的就是给数组排序,求k这个Index位置上的数
- 使用快速排序思想,就是找第k个pointer。注意是从左到右从大到小排序,使用partition比较简单些。
- 递归,就是麻烦一点,和解法二差不多
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
return self.quick(nums, 0, len(nums)-1, k-1)
def quick(self, nums, l, r, k):
if l == r:
return nums[l]
p = self.partition(nums, l, r)
if p == k:
return nums[p]
elif k < p:
return self.quick(nums, l, p-1, k)
else:
return self.quick(nums, p+1, r, k)
def partition(self, nums, l, r):
j = l
for i in range(l+1, r+1):
if nums[i] > nums[l]:
j+=1
nums[i], nums[j] = nums[j], nums[i]
nums[j], nums