【每日一题Day42】最大频率栈 | 哈希表+大顶堆 哈希表+栈

简介: 思路:在本题中,每次需要优先弹出频率最大的元素,如果频率最大元素有多个,则优先弹出靠近栈顶的元素。因此,我们可以考虑将栈序列分解为多个频率不同的栈序列,每个栈维护同一频率的元素。当元素入栈时频率增加,将元素加入到更高频率的栈中,低频率栈中的元素不需要出栈。当元素出栈时,将频率最高的栈的栈顶元素出栈即可。

最大频率栈【LC895】


Design a stack-like data structure to push elements to the stack and pop the most frequent element from the stack.


Implement the FreqStack class:


  • FreqStack() constructs an empty frequency stack.
  • void push(int val) pushes an integer val onto the top of the stack.
  • int pop()removes and returns the most frequent element in the stack.


。If there is a tie for the most frequent element, the element closest to the stack’s top is removed and returned.


解封啦 但是真的好冷 不愿出门


哈希表+大顶堆


  • 思路:


按照题意,每次弹出使用频率最高的元素,如果使用频率相等,那么弹出最近放入栈中的元素。题目中的使用频率指放入最大频率栈的次数,不包括弹出的次数。


因此,我们可以使用HashMap记录每个元素的使用频率;并构造节点Node,将新节点的元素值、使用频率和到达时间记录其中;使用大顶堆存储节点,每次弹出顶部节点即可,弹出后需要更新使用频率。


  • 实现:


。使用valueToCount存储每个元素及其对应的使用频率即次数

。构造节点node存储val值,以及其对应的使用频率和到达时间

。使用优先队列存放节点,优先队列以节点的使用频率从大到小、到达时间从大到小的顺序排序【大顶堆】:


  • 每次添加一个元素都构造一个新节点,存放该数值的使用频率以及到达时间
  • 每次删除一个元素都将优先队列使用频率最高到达时间最近的节点删除,并将map集合中的次数-1


  • 代码


class FreqStack {
    Map<Integer,Integer> valueToCount;
    PriorityQueue<Node> queue;
    int time;
    public FreqStack() {
        valueToCount = new HashMap<>();
        queue = new PriorityQueue<>(new Comparator<Node>() {
            @Override
            public int compare(Node node1, Node node2) {
                if (node2.count == node1.count){
                    return node2.time - node1.time;
                }
                return node2.count - node1.count;
            }
        });
    }
    public void push(int val) {
        int count = valueToCount.getOrDefault(val,0) + 1;
        valueToCount.put(val, count);
        Node node = new Node(val,count,++time);
        queue.add(node);               
    }
    public int pop() {
        Node delNode = queue.poll();
        int val = delNode.val;
        valueToCount.put(val,valueToCount.get(val) - 1);
        return val;
    }
}
class Node{
    int val;
    int count;
    int time;
    public Node(int val, int count, int time){
        this.val = val;
        this.count = count;
        this.time = time;
    }
    // public int compareTo(Object o) {
    //     ListNode o1 = (ListNode)o;
    //     return o1.count - this.count;
    // }
}
/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack obj = new FreqStack();
 * obj.push(val);
 * int param_2 = obj.pop();
 */


。复杂度


  • 时间复杂度:O ( l o g n ),构建优先队列需要O ( l o g n ) 的时间复杂度,push和pop方法均需要O ( l o g n ) 的时间复杂度


  • 空间复杂度:O ( n )


哈希表+多个栈


  • 思路:在本题中,每次需要优先弹出频率最大的元素,如果频率最大元素有多个,则优先弹出靠近栈顶的元素。因此,我们可以考虑将栈序列分解为多个频率不同的栈序列,每个栈维护同一频率的元素。当元素入栈时频率增加,将元素加入到更高频率的栈中,低频率栈中的元素不需要出栈。当元素出栈时,将频率最高的栈的栈顶元素出栈即可。


  • 实现


。使用HashMap<Integer,Integer>存储每个元素及其对应的使用频率

。使用maxFreq记录当前的最大使用频率

。使用HashMap<Integer,Deque<Integer>>存储每个频率对应的元素值


  • push:将该元素压入对应的频率栈中
  • pop:将最高频率的栈的栈顶元素出栈


  • 代码


class FreqStack {
    private Map<Integer, Integer> freq;
    private Map<Integer, Deque<Integer>> group;
    private int maxFreq;
    public FreqStack() {
        freq = new HashMap<Integer, Integer>();
        group = new HashMap<Integer, Deque<Integer>>();
        maxFreq = 0;
    }
    public void push(int val) {
        freq.put(val, freq.getOrDefault(val, 0) + 1);
        group.putIfAbsent(freq.get(val), new ArrayDeque<Integer>());
        group.get(freq.get(val)).push(val);
        maxFreq = Math.max(maxFreq, freq.get(val));
    }
    public int pop() {
        int val = group.get(maxFreq).peek();
        freq.put(val, freq.get(val) - 1);
        group.get(maxFreq).pop();
        if (group.get(maxFreq).isEmpty()) {
            maxFreq--;
        }
        return val;
    }
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/maximum-frequency-stack/solutions/1997251/zui-da-pin-lu-zhan-by-leetcode-solution-moay/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


.复杂度


  • 时间复杂度:O ( 1 ),构建优先队列需要O(logn)的时间复杂度,push和pop方法均需要O(1)的时间复杂度


  • 空间复杂度:O ( n )
目录
相关文章
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1099 77
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
264 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
存储 NoSQL Java
【数据结构进阶】哈希表
哈希表是一种高效的数据结构,通过哈希函数实现数据映射,支持平均O(1)时间复杂度的查找、插入和删除操作。本文详细介绍了哈希表的基本概念、哈希函数的设计(如直接定址法和除留余数法)以及哈希冲突的解决方法(如开放定址法和链地址法)。同时,文章通过代码实例展示了线性探测和链地址法两种哈希表的实现过程,并分析了各自的优缺点。最后总结指出,合理选择哈希函数和冲突解决策略是优化哈希表性能的关键。
1385 2
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
383 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1309 10
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
421 59
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
583 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
370 9

热门文章

最新文章