算法&数据结构(五):栈与队列

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_18310041/article/details/95195760

leetcode:347. Top K Frequent Elements

问题描述:给定一个非空数组,返回前K个出现频率最高的元素。

解法:字典存放元素和频率,创建优先队列保存前K个(频率,元素)自动最小堆排序。

# 排序算法
# 时间复杂度:O(),其中 n 表示数组的长度。
# 空间复杂度:O(n),最极端的情况下(每个元素都不同),用于存储元素及其频率的 Map 需要存储 n 个键值对

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 创建字典哈希表,无序   key,value 元素,频率
        dict1 = {}
        for i in nums:
            if i not in dict1:
                dict1[i] = 1
            else:
                dict1[i] += 1
        # 按照频率进行排序
        freq_dict = sorted(dict1.items(), key=lambda x: x[1], reverse=True)
        res = []
        for i in range(k):
            res.append(freq_dict[i][0])
        return res

# 最小堆
# 时间复杂度:O(nlogk),其中 n 表示数组的长度。首先,统计元素的频率,时间复杂度是 O(n) 的;接着,遍历用于存储元素频率的 map,如果元素的频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,时间复杂度是 O(nlogk) 的;最后,弹出堆中的元素所需的时间复杂度是 O(klogk) 的。因此,总的时间复杂度是 O(nlogk) 
# 空间复杂度:O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k 个元素

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 创建字典哈希表,无序   key,value 元素,频率
        dict1 = {}
        for i in nums:
            # 字典(Dictionary) get() 函数返回指定键的值,如果值不在字典中返回默认值
            dict1[i] = dict1.get(i, 0) + 1
        # 创建优先队列  维护一个大小为k的最小堆,使得堆中的元素即为前k个高频元素
        # heapq.heappush(heap,item)  # heap为定义堆,item 增加的元素
        # heapq.heappop(heap)        # 删除最小的值
        # heapq.heapreplace(heap, item)     #删除最小元素值,添加新的元素值
        pq = []
        for key, value in dict1.items():
            if len(pq) < k:
                heapq.heappush(pq, (value, key))
            elif value > pq[0][0]:
                heapq.heapreplace(pq, (value, key))
        res = []      
        while pq:
            res.append(heapq.heappop(pq)[1])
        return res

# 桶排序
# 时间复杂度:O(n),其中 n表示数组的长度。
# 空间复杂度:O(n)
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 创建字典哈希表,无序   key,value 元素,频率
        #生成字典映射
        dict1 = {}
        for i in nums:
            dict1[i] = dict1.get(i,0) + 1
        max_value = max(dict1.values())
        # 用数组表示每个桶,从0到最大出现次数,一共是(max_value+1)个桶
        tong = [[] for i in range(max_value+1)]
        for key, value in dict1.items():
            # 将元素key放入桶中
            tong[value].append(key)
        res = []
        # 按桶索引排序
        for i in range(max_value,-1,-1):
            # extend() 函数用于在列表末尾一次性追加另一个序列中的多个值
            if tong[i]:
                res.extend(tong[i])
            if len(res) == k:
                break
        return res

剑指offer:用两个栈来实现队列

问题描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

解法:一个栈处理节点的放入,一个栈处理输出节点,初始化为空。入队时,直接压入;出队时,判断pop_stack是否为空,如不为空,直接弹出栈顶元素,如为空,将push_stack的元素逐个压入pop_stack,然后弹出栈顶元素。

时间复杂度:O(1)

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.push_stack = []
        self.pop_stack = []
        
    def push(self, node):
        self.push_stack.append(node)
    
    def pop(self):
        if len(self.pop_stack) == 0:
            # while 而不是if 需要循环遍历!!!
            while len(self.push_stack) > 0:
                self.pop_stack.append(self.push_stack.pop())
        head = self.pop_stack.pop()
        return head

面试问题:用两个队列来实现栈

解法:一个队列放入,一个队列输出。因为栈是后入先出,所以把q1的元素依次删除并插入q2,再删除最后一个元素。然后q1赋值为q2,q2初始化为空,这样才能不断删除。

# 用两个队列实现一个栈
    class Stack:
        def __init__(self):
            self.q1 = []
            self.q2 = []

        def append(self, nums):
            self.q1.append(nums)
            return self.q1

        def pop(self):
            while len(self.q1) != 1:
                self.q2.append(self.q1.pop(0))
            stack_top = self.q1.pop(0)
            self.q1 = self.q2
            self.q2 = []
            return stack_top


    a = Stack()
    a.append(1)
    a.append(2)
    a.append(3)
    a.append(4)
    print(a.append(5))
    print(a.pop())
    print(a.pop())
    print(a.append(6))

剑指offer:包含min函数的栈

问题描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))

解法:数据栈data_stack、辅助栈min_stack,元素入栈时,辅助栈进入最小值(与栈顶元素比较)

时间复杂度:O(1)

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.data_stack = []
        self.min_stack = []
        
        
    def push(self, node):
        self.data_stack.append(node)
        if len(self.min_stack) == 0 or self.min_stack[-1] > node:
            self.min_stack.append(node)
        else:
            self.min_stack.append(self.min_stack[-1])
            
    def pop(self):
        self.data_stack.pop()
        self.min_stack.pop()
        
    def top(self):
        return self.data_stack[-1]
    
    def min(self):
        return self.min_stack[-1]

剑指offer:栈的压入、弹出序列

问题描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。压入序列1,2,3,4,5,弹出序列4,5,3,2,1,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解法:建立一个辅助栈,如果栈顶数字是弹出数字,则弹出;如果栈顶数字不是弹出数字,则压入栈。直到所有数字入栈且找不到下一个弹出的数字,则不是弹出序列。

时间复杂度:O(1)

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        stack = []
        j = 0
        # 把压入序列的元素放入栈
        for i in range(len(pushV)):
            # 如果压入元素==弹出元素,则不需要压入
            if pushV[i] == popV[j]:
                j += 1
            else:
                stack.append(pushV[i])
        # 栈里的元素依次弹出,与弹出元素比较
        for i in range(len(stack)):
            if stack[-1] == popV[j]:
                j += 1
                stack.pop()
            else:
                return False
        return True

leetcode:94. 二叉树的中序遍历

问题描述:给定一个二叉树,返回它的中序 遍历。

解法:不断压入根节点的所有左节点直到当前节点为空,然后弹出节点,并且压入节点的右节点。如果有节点有左子树,则继续压入。

时间复杂度:O(n)

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        # cur当做指针
        cur = root
        res = []
        stack = []
        while cur or stack:
            # 把左子树压入栈中
            while cur:
                stack.append(cur)
                cur = cur.left
            # 输出栈顶元素
            cur = stack.pop()
            res.append(cur.val)
            # 检查右子树
            cur = cur.right
        return res

leetcode:144. 二叉树的前序遍历

问题描述:给定一个二叉树,返回它的 前序 遍历。

解法:从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。

(与前序遍历的唯一区别是先右后左)

时间复杂度:O(n)

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if root is None:
            return []
        
        res = []
        stack = [root, ]
        
        while stack:
            cur = stack.pop()
            res.append(cur.val)
            if cur.right:
                stack.append(cur.right)
            if cur.left:
                stack.append(cur.left)
            
        return res

leetcode:145. 二叉树的后序遍历

问题描述:给定一个二叉树,返回它的 后序 遍历。

解法:从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中,最后结果返回回文序列。后序遍历:左右根,压入根-弹出根-压入左-压入右-弹出右-弹出左,得到根-右-左,回文则是左-右-根。(与前序遍历的唯一区别是先左后右,再回文)

时间复杂度:O(n)

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if root is None:
            return []
        res = []
        stack = [root,]
        while stack:
            cur = stack.pop()
            res.append(cur.val)
            if cur.left:
                stack.append(cur.left)
            if cur.right:
                stack.append(cur.right)
        return res[::-1]

剑指offer:最小的K个数

问题描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

解法:放入最大堆中k个元素,如果当前元素小于最大值,则替换

时间复杂度:O(n) or O(n * logk)

# -*- coding:utf-8 -*-
# 方法一:直接通过堆返回前k个最小数
import heapq
# 堆:集合中找出最大或最小的N个元素
# print(heapq.nlargest(3,nums))   heapq.nsmallest(3,nums)
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        if k > len(tinput):
            return []
        res = heapq.nsmallest(k, tinput)
        res.sort()
        return res


# -*- coding:utf-8 -*-
# 方法二:创建长度为k的最大堆,因为python实现的是最小堆,这里用列表排序替代
import heapq

class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        if k <= 0 or k > len(tinput):
            return []
        res = []
        for i in range(len(tinput)):
            if len(res) < k:
                res.append(tinput[i])
            else:
                res.sort()
                if tinput[i] < res[-1]:
                    res[-1] = tinput[i]
        res.sort()
        return res

剑指offer:

问题描述:

解法:

时间复杂度:O(n) or O(n * logk)

猜你喜欢

转载自blog.csdn.net/qq_18310041/article/details/95195760