剑指offer(四):栈和队列及数学相关(cpp)

上一篇

1. 用两个栈实现队列

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

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int res = stack2.top();
        stack2.pop();
        return res;
    }
private:
    stack<int> stack1;
    stack<int> stack2;
};
2.包含min函数的栈

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

class Solution {
private:
    stack<int> m_data;
    stack<int> m_min;
public:
    void push(int value) {
        m_data.push(value);
        if(m_min.empty() || value<m_min.top())
            m_min.push(value);
        else
            m_min.push(m_min.top());
    }
    void pop() {
        m_data.pop();
        m_min.pop();
    }
    int top() {
        return m_data.top();
    }
    int min() {
        return m_min.top();
    }
};
3. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        bool res = false;
        if(pushV.size()!=popV.size()||popV.empty()||pushV.empty())
            return res;
        stack<int>data;
        int index1=0,index2=0;
        while(index1<pushV.size()){
            data.push(pushV[index1++]);
            while(index2<popV.size()&&data.top()==popV[index2]){
                ++index2;
                data.pop();
            }
        }
        if(index2==popV.size() && data.empty())
            res = true;
        return res;
    }
};
4.滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

class Solution {
public:
    void push(list<int>& q_max,int val){//完成插入元素,确保其中值都有成为某一个窗口最大值的潜力
        while(!q_max.empty() && q_max.back()<val)
            q_max.pop_back();
        q_max.push_back(val);
    }
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        if(nums.size()<k || k<1)    return res;
        list<int> q_max;//辅助队列
        for(int i=0;i<k;++i)
            push(q_max,nums[i]);
        for(int i=k;i<nums.size();++i){
            res.push_back(q_max.front());//第i-k个窗口的最大值
            if(q_max.front()==nums[i-k])    
                q_max.pop_front();
            push(q_max,nums[i]);//插入下一个元素,求第i-k+1窗口的最大值
        }
        res.push_back(q_max.front());//别忘了最后一个窗口的最大值
        return res;
    }
};
5. 扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if(numbers.empty())
            return false;
        sort(numbers.begin(),numbers.end());
        int numofzero = 0;
        int numofgap = 0;
        for(int i=0;i<numbers.size()&&numbers[i]==0;++i)
            ++numofzero;
        int small = numofzero;
        int big  = small+1;
        while(big<numbers.size()){
            if(numbers[small]==numbers[big])
                return false;
            numofgap += numbers[big]-numbers[small]-1;
            small = big;
            ++big;
        }
        return (numofzero>=numofgap)?true:false;
    }
};
6.圆圈中最后剩下的数

从0开始

class Solution {
public:
//模拟法,效率不高会超时
    int lastRemaining(int n, int m) {
        if(n<=0 || m<=0)    return -1;
        list<int> nums;
        for(int i=0;i<n;++i){
            nums.push_back(i);//构建模拟数组
        }
        int c= (m-1)%n;//第一个要删除的数的下标,本来应该是c=(0+m-1)%n;
        while(nums.size()>1){//当数组中的数大于时不停的迭代执行
            auto iter=nums.begin();
            for(int i=0;i<c;++i)    ++iter;//找到要删除元素的迭代器
            nums.erase(iter);//删除它
            c = (c+m-1)%nums.size();//继续求下一个待删除元素的下标
        }
        return nums.front();
    }
};
//约瑟夫环数学解法,由递推公式求解
class Solution {
public:
    int lastRemaining(int n, int m) {
        //f(n,m) = (f(n-1,m)+m)%n;  //n为当前数列长
        int flag = 0;
        for(int i=2;i<=n;++i){
            flag = (flag+m)%i;
        }
        return flag;
    }
};
7. 字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。如果当前字符流没有存在出现一次的字符,返回#字符。

class Solution
{
public:
  //Insert one char from stringstream
    void Insert(char ch)
    {
        s +=ch;
        map[ch]++;
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        for(auto c:s){
            if(map[c]==1)
                return c;
        }
        return '#';
    }
private:
    unordered_map<char,int> map;
    string s;
};
8.丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

class Solution {
public:
    int mine(int u2, int u3, int u5){
        int min = (u2<u3)?u2:u3;
        min = (min<u5)?min:u5;
        return min;
    }
    int GetUglyNumber_Solution(int index) {
        if(index<1)
            return 0;
        vector<int> res;
        res.push_back(1);
        int uglys = 1;
        int u2=0,u3=0,u5=0;
        while(uglys<index){
            int min = mine(res[u2]*2,res[u3]*3,res[u5]*5);
            res.push_back(min);
            while(res[u2]*2<=res[uglys])
                ++u2;
            while(res[u3]*3<=res[uglys])
                ++u3;
            while(res[u5]*5<=res[uglys])
                ++u5;
            ++uglys;
        }
        return res.back();
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值