LeetCode 之 Min Stack — C++ 实现

本文介绍如何设计一个支持push、pop、top和获取最小元素的栈,确保所有操作在常量时间内完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Min Stack

 

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • getMin() -- Retrieve the minimum element in the stack.
设计一个栈,支持 push,pop,top 和取得最小值的功能,取得最小值要常量时间。

分析

    可以使用 vector 或者 stack 来实现功能数据存储。

关键是要在常量时间取得最小值,可以在插入数据时就判断最小值然后保存在成员变量中,在 pop 时需要重新计算最小值(如果出栈的值等于最小值)。

还要注意 top 和 pop 时空栈的判断。

class MinStack {
public:
    void push(int x) {
        if(data.size() >= data.max_size())
        {
            return;
        }
        
        if(data.empty())
            minData = x;
        else if(x < minData)
        {
            minData = x;
        }
        data.push_back(x);
    }

    void pop() {
        if(data.empty())
        {
            return;
        }
        
        int size = data.size();
        int re = data[size-1];
        if((1 == size)||(re > minData))
        {
            data.pop_back();
            return;
        }
        else
        {
           minData = data[0];
           for(int i = 1; i<size-1; ++i)
           {
               if(data[i] < minData)
               {
                   minData = data[i];
               }
           }
           data.pop_back();
        }
        
    }

    int top() {
        if(data.empty())
        {
            return -1;
        }
        return data[data.size()-1];
    }

    int getMin() {
        if(data.empty())
        {
            return 0;
        }
        return minData;
    }
    
private:
   vector<int> data;
   int minData;
};

<think>我们正在处理一个C++编译错误:error: field has incomplete type 'MinStack' 这个错误通常发生在类定义中,当一个类的成员变量被声明为该类自身的类型,但此时类的定义尚未完成(即不完整类型)。在C++中,类在定义结束之前(即遇到右花括号之前)被视为不完整类型。因此,在类内部不能有该类的非静态成员变量(因为此时类型不完整),但可以有指向该类的指针或引用(因为指针和引用的大小是已知的,不依赖于类型的完整性)。 例如: class MinStack { MinStack next; // 错误:不完整类型 }; 而应该使用指针或引用: class MinStack { MinStack* next; // 正确:指针的大小是固定的 }; 但是,根据错误信息,用户可能是在定义MinStack类时,有一个成员变量是MinStack类型(而不是指针或引用)。这种设计在链表或树结构中常见,但必须使用指针或引用。 因此,解决方法是将成员变量改为指针(或引用,但引用成员在类中不常用,因为引用一旦初始化就不能改变,且必须在构造函数的初始化列表中初始化)。 另外,还有一种可能是用户试图在类中嵌套定义另一个类的对象,而那个类还没有完全定义。例如: class A { class B b; // 如果B在A内部定义但尚未定义完全,或者B是A的前向声明但未定义,则会出现此错误。 }; 这种情况下,需要确保B是一个完全类型。如果B在A后面定义,那么需要在A之前声明B,并在A中声明B的成员时使用指针,然后在A的定义之后定义B,再在A的成员函数中(如构造函数)初始化指针。 根据错误信息,我们假设是第一种情况:在MinStack类中有一个MinStack类型的成员(非指针)。因此,我们建议改为指针。 然而,用户的问题是关于一个名为“MinStack”的栈,通常用于实现一个支持常数时间获取最小值的栈。这种结构通常有两种实现方式: 1. 每个节点保存当前值和当前最小值,然后使用链表连接。这样,每个节点需要有一个指向下一个节点的指针。 2. 使用两个标准栈,一个存数据,一个存最小值。 如果是第一种实现,那么节点结构应该包含一个指向下一个节点的指针,而不是直接包含下一个节点。 因此,我们给出以下解决方案: 假设MinStack类的设计如下(链表节点方式): ```cpp class MinStackNode { public: int val; int minVal; // 当前节点及其以下的最小值 MinStackNode* next; // 指向下一个节点的指针 MinStackNode(int x, int m, MinStackNode* nxt) : val(x), minVal(m), next(nxt) {} }; class MinStack { private: MinStackNode* topNode; // 指向栈顶节点的指针 public: MinStack() : topNode(nullptr) {} void push(int val) { if (topNode == nullptr) { topNode = new MinStackNode(val, val, nullptr); } else { int currentMin = (val < topNode->minVal) ? val : topNode->minVal; topNode = new MinStackNode(val, currentMin, topNode); } } void pop() { if (topNode) { MinStackNode* temp = topNode; topNode = topNode->next; delete temp; } } int top() { if (topNode) { return topNode->val; } // 错误处理,这里简单返回0,实际应处理 return 0; } int getMin() { if (topNode) { return topNode->minVal; } // 错误处理 return 0; } }; ``` 但是,用户可能并没有使用节点结构,而是尝试在MinStack类中直接包含一个MinStack类型的成员(比如用于实现链式结构),这就会导致编译错误。 所以,正确的做法是使用指针。 如果用户的设计是类似这样的: ```cpp class MinStack { int val; int minVal; MinStack next; // 错误:不完整类型 }; ``` 应该改为: ```cpp class MinStack { int val; int minVal; MinStack* next; // 改为指针 }; ``` 但是,注意这种设计下,MinStack的析构函数需要小心处理,因为可能有多个节点需要删除(避免内存泄漏)。 另外,如果用户的设计是第二种(使用两个标准栈),则不会出现这个问题。例如: ```cpp #include <stack> class MinStack { private: std::stack<int> dataStack; std::stack<int> minStack; public: void push(int val) { dataStack.push(val); if (minStack.empty() || val <= minStack.top()) { minStack.push(val); } } void pop() { if (dataStack.top() == minStack.top()) { minStack.pop(); } dataStack.pop(); } int top() { return dataStack.top(); } int getMin() { return minStack.top(); } }; ``` 因此,我们需要根据用户的具体代码来给出建议。但根据错误信息,很可能是第一种情况(链表实现)中成员变量没有使用指针。 总结解决方法: 1. 检查MinStack类中是否有成员变量是MinStack类型(而不是指针或引用)。 2. 如果有,将其改为指针(或引用,但指针更常用)。 3. 如果使用链表结构,记得在析构函数中释放动态分配的内存。 如果用户的问题不是链表实现,而是其他嵌套类型,同样需要确保成员变量是完整类型。如果成员变量是另一个类类型,则需要包含相应的头文件,并且该类型已经定义完全。 因此,我们给出一般性建议: - 在类定义中,不能将类自身的类型作为非静态成员(因为此时类不完整),但可以是指针或引用。 - 如果成员是另一个类的对象,确保该类的定义在包含之前已经可见(即包含头文件,且该类的定义已经完成)。 如果用户提供更多代码,我们可以更具体地分析。 由于用户没有提供完整代码,我们只能给出常见原因和解决方法。</think>### 错误分析:`field has incomplete type 'MinStack'` 这个编译错误通常发生在以下场景中: ```cpp class MinStack { MinStack next; // 错误:使用不完整类型声明成员变量 }; ``` #### 根本原因: 1. **自包含类型**:C++不允许类直接包含自身类型的**非指针成员**(大小无法确定) 2. **定义顺序问题**:当类成员需要完整类型时,类型定义尚未完成 3. **循环依赖**:两个类相互引用时可能同时成为不完整类型 --- ### 解决方法 #### ✅ 方案1:改用指针/智能指针(推荐) ```cpp class MinStack { std::unique_ptr<MinStack> next; // 改为智能指针 // 或 MinStack* next; // 原始指针(需手动管理内存) }; ``` **原理**:指针大小固定(4/8字节),不依赖类型完整性 #### ✅ 方案2:前置声明 + 延迟定义 ```cpp // 头文件 minstack.h class MinStackImpl; // 前置声明 class MinStack { std::unique_ptr<MinStackImpl> impl; // PIMPL惯用法 public: void push(int val); int pop(); // ... 接口声明 }; // 源文件 minstack.cpp class MinStackImpl { // 完整定义 MinStackImpl* next; int val; }; // 成员函数实现... ``` #### ✅ 方案3:分离数据结构 ```cpp struct MinStackNode { // 独立节点类型 int val; int min_val; std::unique_ptr<MinStackNode> next; }; class MinStack { std::unique_ptr<MinStackNode> top_node; public: void push(int val) { /* 实现 */ } // ... }; ``` --- ### 关键注意事项 1. **内存管理**: ```cpp ~MinStack() { while (top_node) { auto temp = std::move(top_node); top_node = std::move(temp->next); // 自动释放内存 } } ``` 2. **禁止方案**: ```cpp class MinStack { MinStack& next; // 错误:引用仍需完整类型 static MinStack member; // 错误:静态成员仍需完整类型 }; ``` 3. **设计建议**: - 优先使用`std::unique_ptr`避免内存泄漏 - 链表节点建议独立为`struct Node` - 接口与实现分离(PIMPL模式) > 示例完整实现:[最小栈的指针实现参考](https://leetcode.com/problems/min-stack/solutions/49022/beats-100-java-solution-with-comment)[^1] --- ### 相关问题 1. 如何实现支持O(1)时间复杂度获取最小值的数据结构? 2. C++中`std::unique_ptr`和原始指针在链表实现中有何性能差异? 3. PIMPL模式(Pointer to Implementation)有哪些优点和缺点? 4. 遇到`invalid use of incomplete type`错误时如何排查? [^1]: LeetCode 最小栈参考解法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值