2018年力扣高频算法面试题3堆栈与队列

基本计算器1【需二刷】

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
分析:用一个栈来存放 ‘(’ 外计算得到的数值,一个栈用于存放 ‘(’ 外的运算符。如果遇到 ‘)’,就更新一下括号内外表达式的运算结果。

class Solution {
public:
    int calculate(string s) {
        stack <int> nums, ops;
        long num = 0;
        int res = 0;
        int op = 1;
        for (char c : s) {
            if (isdigit(c)) {
                num = num * 10 + c - '0';
            }
            else {
                res += op * num;
                num = 0;
                if (c == '+') op = 1;
                else if (c == '-') op = -1;
                else if (c == '(') {
                    nums.push(res);
                    ops.push(op);
                    res = 0;
                    op = 1;
                }
                else if (c == ')' && ops.size()) {
                    res = ops.top() * res + nums.top();
                    ops.pop();
                    nums.pop();
                }
            }
        }
        res += op * num;
        return res;
    }
};

基本计算器2【需二刷】

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。

class Solution {
public:
    int calculate(string s) {
        
        long res = 0, num = 0, n = s.size();
        char op = '+';
        stack<int> st;
        
        for (int i = 0; i < n; ++i) 
        {
            if (isdigit(s[i])) 
            {
                num = num * 10 + s[i] - '0';
            }
            if ((s[i] < '0' && s[i] != ' ') || i == n - 1) 
            {
                if (op == '+') st.push(num);
                if (op == '-') st.push(-num);
                if (op == '*' || op == '/') 
                {
                    int tmp = (op == '*') ? st.top() * num : st.top() / num;
                    st.pop();
                    st.push(tmp);
                }
                op = s[i];
                num = 0;
            } 
        }
        while (!st.empty()) {
            res += st.top();
            st.pop();
        }
        return res;
    }
};

基本计算器3【需二刷】

加减乘除以及小括号

class Solution {
public:
    int calculate(string s) {
        long res = 0, curRes = 0, num = 0, n = s.size();
        char op = '+';
        for (int i = 0; i < n; ++i) {
            char c = s[i];
            if (c >= '0' && c <= '9') {
                num = num * 10 + c - '0';
            }
            if (c == '+' || c == '-' || c == '*' || c == '/' || i == n - 1) {
                switch (op) {
                    case '+': curRes += num; break;
                    case '-': curRes -= num; break;
                    case '*': curRes *= num; break;
                    case '/': curRes /= num; break;
                }
                if (c == '+' || c == '-' || i == n - 1) {
                    res += curRes;
                    curRes = 0;
                }
                op = c;
                num = 0;
            } 
        }
        return res;
    }
};

至少有K个重复字符的最长子串【需二刷】

找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。

遍历字符串,找到出现次数少于k次的字符,位置记为i,分为(0,i-1) 和(i+1, s.size() - 1)两个区间,再递归,最后取两区间的最大值。

int longestSubstring(string s, int k) 
{
	if (!s.size()) return 0;
	int *hashMap = new int[26];
	for (int i = 0; i < 26; i++)
		hashMap[i] = 0;
	for (int i = 0; i < s.size(); i++)  //hashmap[i]表示字符i出现的次数
		hashMap[s[i] - 'a']++;
	int sum = 0;
	bool flag = false;  //记录是否有小于k次的字符
	for (int i = 0; i < s.size(); i++) 
	{
		if (hashMap[s[i] - 'a'] < k) 
		{
			flag = true;
			sum = max(longestSubstring(s.substr(0, i), k), longestSubstring(s.substr(i + 1, s.size()), k));
			//这里直接break 如果不break 不影响结果 但影响效率 会重复计算相同的情况
			break;
		}
	}
	if (!flag)
		return s.size();
	return sum;
}

数据流的中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

class MedianFinder {
    priority_queue<int>max;  //从大到小排序
    priority_queue<int,vector<int>,greater<int> >min; //从小到大排序
public:
    MedianFinder() {
    }
    
    void addNum(int num) {
        max.push(num);
        min.push(max.top());
        max.pop();
        if(max.size()<min.size())
        {
            max.push(min.top());
            min.pop();
        }
    }
    
    double findMedian() {
        return (max.size()+min.size())%2 ? max.top() : ((double)max.top()+min.top())/2;
    }
};

滑动窗口最大值

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
分析:题目要求我们代码的时间复杂度为 O(n),提示我们要用双向队列deque来解题,并提示我们窗口中只留下有用的值,没用的全移除掉。大概思路是用双向队列保存数字的下标,遍历整个数组,如果此时队列的首元素是 i-k 的话,表示此时窗口向右移了一步,则移除队首元素。然后比较队尾元素和将要进来的值,如果小的话就都移除,然后此时我们把队首元素加入结果中即可,参见代码如下:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) 
    {
        vector<int> res;
        deque<int> q;
        for (int i = 0; i < nums.size(); ++i) 
        {
            if (!q.empty() && q.front() == i - k) q.pop_front();
            while (!q.empty() && nums[q.back()] < nums[i]) q.pop_back();
            q.push_back(i);
            if (i >= k - 1) res.push_back(nums[q.front()]);
        }
        return res;
    }
};

数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
分析:直接用sort()排序肯定是最简单的方法,但是确不是本题真正想考察的东西,可以说有一定的偷懒嫌疑。
用小跟堆做复杂度是O(n),先把数组的前k个元素push到优先队列里面,再循环剩下的元素,如果元素比队头元素(优先队列里面最小的元素)还要小,continue,否则,删除队头元素,把该元素push到队列里面,这样,循环结束后队头元素就是这k个元素里面最小的元素,即第k个最大元素。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        
        priority_queue<int,vector<int>,greater<int> >q(nums.begin(),nums.begin()+k);
        
        int i=k;
        for(;i<nums.size();i++)
        {
            if(nums[i]<q.top())continue;
            if(nums[i]>q.top())
            {
                q.pop();
                q.push(nums[i]);
            }
        }
        return q.top();
    }
};

有序矩阵中第K小的元素

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
分析:我们使用一个最大堆,然后遍历数组每一个元素,将其加入堆,根据最大堆的性质,大的元素会排到最前面,然后我们看当前堆中的元素个数是否大于k,大于的话就将首元素去掉,循环结束后我们返回堆中的首元素即为所求。

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        priority_queue<int>q;
        for(int i=0;i<matrix.size();i++)
        {
            for(int j=0;j<matrix[0].size();j++)
            {
                q.push(matrix[i][j]);
                if(q.size()>k)q.pop();
            }
        }
        return q.top();
    }
};

前 K 个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        map<int,int>m;
        priority_queue<pair<int,int> >q;
        vector<int>ans;
        for(auto a:nums)
            m[a]++;
        for(auto it:m)
            q.push({it.second,it.first});
        for(int i=0;i<k;i++)
        {
            ans.push_back(q.top().second);
            q.pop();
        }
        return ans;
    }
};

补充题目:

Subarray Sum Equals K

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.

方法一:一目了然,可以用暴力求解,但是时间复杂度肯定是O(n2)

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int count = 0;
        int n=nums.size();
        for (int start = 0; start < n; start++) {
            int sum=0;
            for (int end = start; end < n; end++) {
                sum+=nums[end];
                if (sum == k)
                    count++;
            }
        }
        return count;
    }
};

方法二:可以利用hashmap记录和的累加值来避免重复计算
时间复杂度和空间复杂度都是O(n)

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        map<int,int>m;
        m[0]=1;
        int res=0;
        int sum=0;
        for(int i=0,len=nums.size();i<len;i++)
        {
            sum+=nums[i];
            res += m[sum - k];
		   m[sum]++;
        }
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章