代码随想录算法训练营day11

3 阅读3分钟

利用栈求解逆波兰表达式,注意,这个逆波兰表达式是输入合法的,并没有对不合法情况进行判定,所以代码的健壮性存在欠缺,而且演算符缺少!,想要更加详细地学习逆波兰表达式的处理,可以参考邓俊辉老师的数据结构,栈那个章节内容。

public:
    int evalRPN(vector<string>& tokens) {
     // 空表达式直接返回0(题目不会出现空输入,仅做鲁棒性处理)
        if (tokens.empty()) return 0;
        
        // 用栈存储数字,逆波兰表达式的核心数据结构
        stack<int> numStack;
        
        for (const string& token : tokens) {
            // 判断是否是运算符
            if (token == "+" || token == "-" || token == "*" || token == "/") {
                // 弹出栈顶两个操作数(注意顺序!)
                int num2 = numStack.top();  // 第一个弹出的是右操作数
                numStack.pop();
                int num1 = numStack.top();  // 第二个弹出的是左操作数
                numStack.pop();
                
                // 根据运算符计算,结果重新入栈
                if (token == "+") {
                    numStack.push(num1 + num2);
                } else if (token == "-") {
                    numStack.push(num1 - num2);
                } else if (token == "*") {
                    numStack.push(num1 * num2);
                } else if (token == "/") {
                    // 整数除法,题目要求向零取整(C++默认就是,无需额外处理)
                    numStack.push(num1 / num2);
                }
            } else {
                // 是数字,转换为int后入栈
                numStack.push(stoi(token));
            }
        }
        
        // 最终栈中只剩一个数,就是结果
        return numStack.top();
    }
};

单调队列求滑动窗口最大值。 1.滑动窗口应当设计为双端队列,如果使用单端队列的话,在保持队列单调时,要额外设置max记录处理 2.应当注意到,使用单调队列时,最大值始终在队列顶部。

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
      
   // 处理边界情况:数组为空或窗口大小为0/1
        if (nums.empty() || k <= 0) return {};
        if (k == 1) return nums;

        vector<int> result;          // 存储每个窗口的最大值
        deque<int> monoDeque;        // 单调递减队列,存储nums的下标

        for (int i = 0; i < nums.size(); ++i) {
            // 1. 移除队列中超出当前窗口范围的下标(窗口左边界:i - k + 1)
            while (!monoDeque.empty() && monoDeque.front() < i - k + 1) {
                monoDeque.pop_front();
            }

            // 2. 移除队列尾部所有比当前元素小的下标(保证队列单调递减)
            // 因为当前元素更大,之前的小元素不可能成为后续窗口的最大值
            while (!monoDeque.empty() && nums[i] > nums[monoDeque.back()]) {
                monoDeque.pop_back();
            }

            // 3. 将当前元素下标入队
            monoDeque.push_back(i);

            // 4. 从第k-1个元素开始(窗口首次形成),记录每个窗口的最大值(队首)
            if (i >= k - 1) {
                result.push_back(nums[monoDeque.front()]);
            }
        }

        return result;
    }
};

347,前k个高频元素的选取。 1.使用hash表统计出现频率 2.将hash表转变为堆

public:
//重载括号比较
   // 小顶堆
    class mycomparison {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second;
        }
    };
    vector<int> topKFrequent(vector<int>& nums, int k) {
        //使用哈希表统计出现频率,采用map
        unordered_map<int,int>map;
        for(int i=0;i<nums.size();i++){
            map[nums[i]]++;
        }
        //将map转换为优先级队列
         priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;

        // 用固定大小为k的小顶堆,扫面所有频率的数值
        for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                pri_que.pop();
            }
        }

        // 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
        vector<int> result(k);
        for (int i = k - 1; i >= 0; i--) {
            result[i] = pri_que.top().first;
            pri_que.pop();
        }
        return result;
    }
};