剑指offer刷题记(C++版本)

也算是临时抱佛脚了吧,3月之前刷了lintcode100多道题吧,后来发文章什么的就放下了,最近秋招在即在牛客网上想着把剑指offer这本书刷完,尽量早刷完吧,最近也很忙。

1. 二维数组中查找数字。

  • 题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
  • 思路:从右上角来入手,如果右上角的数字大于目标,那么最右边一列都不满足,则去掉这一列,如果这个数小于目标,则最上面一行都不满足,删除这一行,如果刚好是目标就可以输出了。(这里的删除也不是真的去删掉,只需要挪动记录右上角的坐标数值就可以了)。
bool Find(int target, vector<vector<int> > array) {
        if(array.empty())    return false;
        int rows=array.size();
        int cols=array[0].size();
        int row=0;
        int col=cols-1;
        while(row<rows&&col>=0)
        {
            if(array[row][col]==target)  return true;
            else if(array[row][col]>target)
            {
                col--;
            }
            else  row++;
        }
        return false;
    }

2.替换空格。

  • 题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
  • 思路:从后向前替换,先遍历一遍统计空格的数目,然后扩张字符串使得可以放下替换后的字符,然后从后向前依次复制,非空格字符直接复制,空格字符用题目要求的替换。
 //C风格的字符串最后一个字符是\n,而且是记在字符串长度里的。
    void replaceSpace(char *str,int length) {
        int i=0;
        int cnt=0;
        
        if(length==0||str==nullptr) return ;     //空字符串
        
        for(i=0;i<length;i++)      //统计空格数
        {
            if(str[i]==' ')
                cnt++;
        }
        if(cnt==0)  return;     //如果没有空格自然不用替换
        
        int newlength=length+2*cnt;     //新字符串的长度 
        
        int index_new=newlength-1;
        int index_old=length-1;
        
        while(index_old>=0&&index_new>index_old)   //没有到头或者两个指针追上了
        {
            if(str[index_old]==' ')
            {
                //依次替换三个字符,并且把index_old--
                str[index_new--]='0';
                str[index_new--]='2';
                str[index_new--]='%';    
                index_old--;
            }
            else 
            {
                //如果不是空格直接复制就可以了
                str[index_new--]=str[index_old--];
            }
        }

    }

3. 从尾到头打印链表。

  • 题目:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
  • 思路:使用栈。
vector<int> printListFromTailToHead(ListNode* head) {
        if(head==nullptr)
            return vector<int>();
        stack<int> tmp_stack;
        vector<int> res;
        ListNode *phead=head;   //如果要求不改变原链表,这里换一下
        while(phead!=nullptr)
        {
            tmp_stack.push(phead->val);
            phead=phead->next;
        }
        while(!tmp_stack.empty())
        {
            res.push_back(tmp_stack.top());
            tmp_stack.pop();   //出栈
        }
        return res;
    }

4.重建二叉树

  • 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
  • 思路: 必须注意到前序遍历的第一个节点是根节点,中序遍历的和根节点相同的那个节点(不含重复节点)是根节点且把树分成左右两个子树,左边子树是左子树,右边是右子树,左右两个子树也都是同样的规律,所以可以用递归来做,我递归写的不好,借鉴了别人的思路,自己改进了下。
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
/*
必须注意到前序遍历的第一个节点是根节点,中序遍历的和根节点相同的那个节点(不含重复节点)是根节点且
把树分成左右两个子树,左边子树是左子树,右边是右子树,左右两个子树也都是同样的规律,所以可以用递归
来做,我递归写的不好,借鉴了别人的思路,自己改进了下。
*/


class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        //如果size为0,则返回为空
        if(pre.size()==0||vin.size()==0)
            return NULL;
        
        vector<int> pre_left,pre_right;    //前序遍历的左子树和右子树
        vector<int> vin_left,vin_right;   //中序遍历的左子树和右子树
        //根据前序遍历的首节点为根节点这一个规律,找到中心节点,然后把原来的分成两个子二叉树,
        //准备使用递归来做。
        TreeNode *head=new TreeNode(pre[0]);   //调用构造函数先把第一个放进去。
        
        //找首节点在中序遍历中的位置。
        auto head_position=find(vin.begin(),vin.end(),head->val);
        int position=head_position-vin.begin();   //这里找到是第几个,下面取构建。
        pre_left=vector<int>(pre.begin()+1,pre.begin()+position+1);
        pre_right=vector<int>(pre.begin()+position+1,pre.end());
        //复制到前序遍历的左子树和右子树
        vin_left=vector<int>(vin.begin(),vin.begin()+position);
        vin_right=vector<int>(vin.begin()+position+1,vin.end());
        //复制到中序遍历的左子树和右子树 
        
        //调用递归
        head->left=reConstructBinaryTree(pre_left,vin_left);
        head->right=reConstructBinaryTree(pre_right,vin_right);
        
        //返回树
        return head;

    }
};

5. 用两个栈实现一个队列。

  • 题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
  • 思路:用一个栈来入栈,元素入栈的时候都从这里来入栈,如果要删除的话,则利用另外一个空栈来另一个的所有元素取出并压入空栈,然后出栈,每次出栈检查那个栈是否是空的,如果不是空的,直接出栈,空的话则从入栈的栈里出来。
    语言描述起来有点麻烦,看图:
class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        //题目要求返回删除的那个元素,C++satck的pop是不返回值得,而只是删除,所以就要自己来做了!
        int tmp;
        if(stack2.empty())
        {
            while(!stack1.empty())
            {
                stack2.push(stack1.top());
                stack1.pop();
            }
            tmp=stack2.top();
            stack2.pop();
        }
        else 
        {
            tmp=stack2.top();
            stack2.pop();
        }
        return tmp;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

另外,可用两个队列来做一个栈。
思路: 队列的话因为队列都是先进先出,所以如果把一个队列的数字全部复制到另外一个队列的话顺序是没有变的,所以有必要借助两个队列么? 自然是有必要的,虽然顺序没有变,但是我们可以在转移元素的时候把最后一个删除掉,也就是说入栈的时候挑非空的队列入栈,出栈的时候把非空的队列复制到空的中,复制过程中把最后一个元素删掉。

6.旋转数组的最小数字。

  • 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
  • 思路: 首先找到两段的分界点,分界点的找法:找到第一个递减的数字,要考虑边界条件。这样循环 肯定是可以做的,但是既然是局部有序的,实际上是可以用二分法来做这个事情的。
class Solution {
public:

    int minSearch(vector<int> rotateArray)
    {
        if(rotateArray.size()==0) return 0;
        if(rotateArray.size()==1) return rotateArray[0];
        for(auto beg=rotateArray.begin();beg!=rotateArray.end()-1;beg++)
        {
            if(*(beg+1)<*beg)   return *(beg+1);
        }
        return rotateArray[0];  //如果上面没有返回则说明是全等的
    }
    
    int minNumberInRotateArray(vector<int> rotateArray)
    {
        if(rotateArray.size()==0)  
           return 0;
        int index1=0;
        int index2=rotateArray.size()-1;
        int indexMid=0;
        while(rotateArray[index1]>=rotateArray[index2])
        {
            if(index2-index1==1)      
            {
                indexMid=index2;
                break;
            }
            indexMid=(index1+index2)/2;
            if(rotateArray[index1]==rotateArray[indexMid]&&
              rotateArray[index2]==rotateArray[indexMid])
            {
                return minSearch(rotateArray);
            }
            if(rotateArray[index1]<=rotateArray[indexMid])
            {
                index1=indexMid;
            }
            else if(rotateArray[indexMid]<=rotateArray[index2])
            {
                index2=indexMid;
            }
        }
        return rotateArray[indexMid];
    }
};

7. 斐波拉切数列。

  • 题目: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
    n<=39 。
  • 思路: 有递归和循环两种写法,但是递归消耗的资源是很大的,所以还是推荐使用循环的方式来写这个。
class Solution {
public:
    int Fibonacci(int n) {
        if(n<0)
            return 0;
        int first=0;
        int second=1;
        int tmp=0;
        if(n==0) return 0;
        if(n==1) return 1;
        //只要有前两个树就能生成后面的所有的,只记录倒数两个数就可以
        for(int i=1;i<n;i++)
        {
            tmp=second;         //把后面的一个存起来
            second=second+first;   //后面的等于前两个相加
            first=tmp;    //然后把后面的那个赋值给前一个
        }
        return second;
        
    }
};

8.跳台阶问题。

  • 问题: 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
  • 思路: 见代码注释。
class Solution {
public:
    int jumpFloor(int number) {
        //虽然是个跳台阶的问题,但是实际上和刚才做的那个斐波那契数列是一样的。
        /*分析:
        向上跳台阶:我们假设跳上N阶台阶的方法有F(N)中,那么很容易可以得到:
        F(N)=F(N-1)+F(N-2) 
        为什么是这样的呢?我们分析其跳最后一次的情况:
        如果跳的是两阶,则说明前面有F(N-2)种方式。
        加入跳的是一阶,则说明前面有F(N-1)种方式。
        所以得到的递推关系就如上。而且我们也很容易可以知道。
        F(1)=1,F(2)=2.(1,1或者2)
        代码和斐波那契数列那个没什么不同,只要把first改成1基本就可以了。
        */
        if(number<=0)  return 0;
        if(number==1)  return 1;
        if(number==2)  return 2;
        int first=1;
        int second=2;
        int tmp=0;
        for(int i=2;i<number;i++)
        {
            tmp=second;
            second+=first;
            first=tmp;
        }
        return second;
    }
};

9.变态跳台阶问题。

  • 题目: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
  • 思路: 见代码注释。

class Solution {
public:
    int jumpFloorII(int number) {
        //题目虽然变难了,但是实际上程序是更简单了,结合上一个题的分析
        /*我们可以得到:
        F(N)=F(N-1)+F(N-2)+F(N-3)+...+F(1)
        把N-1带入则可以得到:
        F(N-1)=F(N-2)+F(n-3)+...+F(1)
        两式相减呢?:
        F(N)=2*F(N-1)
        则这是一个等比数列啊,且F(1)=1,所以最后的结果就是一个幂次。

          */
        int first=1;
        for(int i=1;i<number;i++)
        {
            first*=2;
        }
        return first;
    }
};

10. 矩形覆盖。

  • 题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
  • 思路: 见代码注释。
class Solution {
public:
    int rectCover(int number) {
        //这个问题啊,仔细一想和跳台阶完全是一样的,而且时简单的跳台阶问题
        /*我们分析啊,我们假设2*N个格子的覆盖方式公有F(N)中,而且我们注意到每一个田字格是只有两种填充方式的。
        如果要递推得分析,我们必须假设前面是铺满的。
        如果只剩两个格子:则肯定是'日'字型的,那么只有一种方法来填充了。就是竖着。
        如果只剩一个田字格: 则还有两种方式可以填充,但有一种和上面N-1那种是重复的。
        所以我们还是可以得到:
        F(N)=F(N-1)+F(N-2)
        
        这样说起来可能有点绕,但是换一种说法:
        假设我们从前往后填:我们只能横着或者竖着,横着放的话一下子占横向的两个格子,而且这个要横着放的话,它下面的空间没得选。
        竖着放的话一下子占横向的一个格子。
        直到我们铺满地砖为止:这不就相当于和跳台阶是完全一样的么?
        */
        if(number<=0)  return 0;
        if(number==1)  return 1;
        if(number==2)  return 2;
        
        int first=1;
        int second=2;
        int tmp=0;
        
        for(int i=2;i<number;i++)
        {
            tmp=second;
            second+=first;
            first=tmp;
        }
        return second;

    }
};

11.二进制中1的个数。

  • 题目: 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
  • 思路: 见代码注释。
class Solution {
public:
     int  NumberOf1(int n) {
         //这个题就是要注意到n&n-1的话会把二进制中少个1,当然自己一开始是注意不到这个技巧的,只是这个题我做过而已。
         
         int res=0;
         while(n!=0)
         {
             n=n&(n-1);
             res++;
         }
         return res;
         
     }
};
/*关于二进制这样类似的操作还有很多用处:
1. 判断一个数是不是2的整数次方。
我们知道n&(n-1)会减少二进制中1的个数,而如果是2的整数次方,就只有一个1,所以我们只要判断n&(n-1)是否是零就可以了。
2. 判断m和n的二进制有多少不同的数字.
先做异或(^),则不同的话就标记为1,然后再统计1的个数。
*/

12.数值的整数次方。

  • 题目: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
  • 思路: 主要是考虑指数的各种情况就可以。
class Solution {
public:
    //题倒不难,主要是要考虑到各种情况。
    //可以优化的地方还有连乘那里,可以考虑用
    double Power(double base, int exponent)
    {
        if(-0.00000001<base&&base<0.00000001)  return 0.0;
        if(exponent==0)   return 1.0;
        else if(exponent<0)
            return 1.0/myPow(base,-exponent);
        else
            return myPow(base,exponent);
    }
    
    double myPow(double base,int exponent)
    {
        double res=1.0;
        for(int i=0;i<exponent;i++)
        {
            res*=base;
        }
        return res;
    }
};

13.调整数组顺序使奇数位于偶数前面。

  • 题目: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
  • 思路:第一种方法,利用冒泡排序,从后往前交换书序。第二种利用STL中的stable_partition和lambda表达式。
class Solution {
public:
    
    //类似于冒泡排序,但是是从后往前
    /*
    void reOrderArray(vector<int> &array) {
        if(array.size()<=1)  return;
        for(int i=0;i<array.size();i++)
        {
            for(int j=array.size()-1;j>i;j--)
            {
                if(array[j]%2!=0&&array[j-1]%2==0)   //如果前面是偶数,后面是奇数,就交换顺序
                    swap(array[j],array[j-1]);
            }
        }
    }
    */
    
    //使用C++自带的stable_partition和lambda表达式
    void reOrderArray(vector<int> &array) 
    {
       stable_partition(array.begin(),array.end(),[](int x){return (x%2==1);});
    }
};

14.链表倒数第k个节点。

  • 题目:输入一个链表,输出该链表中倒数第k个结点。
  • 思路: 双指针,end先后移k个,然后first和end同时后移,end到null之后,first指向的就是倒数第k个。细微的+1或者-1自己画一下就知道了。
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead==NULL) return NULL;
        ListNode *First=new ListNode(0);
        First->next=pListHead;
        
        for(int i=0;i<k;i++)
        {
            if(pListHead==NULL)
                return NULL;
            pListHead=pListHead->next;
        }
        //if(pListHead==NULL) return First->next;
        
        while(pListHead!=NULL)
        {
            First=First->next;
            pListHead=pListHead->next;
        }
        return First->next;
    }
};

15.翻转链表。

  • 题目:输入一个链表,反转链表后,输出新链表的表头。
  • 思路:一个一个翻转,只需要用一个ListNode的动态空间。我比较喜欢用假表头,所以代码可能看起来不是很简介,但是比较好理解。
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    //具体的思路就是一个一个翻转,只是用一个ListNode的动态空间,
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==NULL||pHead->next==NULL)
            return pHead;
        ListNode *First=new ListNode(0);    //用来记录最前面的位置,因为要往前插入。
        First->next=pHead;      //先放在最前面
        while(pHead->next!=NULL)    //停止条件
        {
            ListNode *tmp=new ListNode(0);
            tmp->next=pHead->next;             //要翻转的链表拿出来,tmp指向它
            pHead->next=pHead->next->next;     //把pHead指向下一个
            tmp->next->next=First->next;       //把tmp指向的插入First后面
            First->next=tmp->next;              //把First移到最前面
            delete tmp;
        }
        return First->next;

    }
};

16.合并两个排序链表。

  • 题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
  • 思路:双指针最经典的题了,在归并排序的过程中也是比用的。
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==NULL)  return pHead2;
        if(pHead2==NULL)  return pHead1;
        //处理特殊情况
        
        ListNode *First=new ListNode(0);
        ListNode *Last=new ListNode(0);
        Last->next=First;
        
        //双指针来做,两个都不为空的时候循环
        while(pHead1!=NULL&&pHead2!=NULL)
        {
            if(pHead1->val<=pHead2->val)
            {
                Last->next->next=pHead1;
                Last->next=pHead1;
                pHead1=pHead1->next;
            }
            else{
                Last->next->next=pHead2;
                Last->next=pHead2;
                pHead2=pHead2->next;
            }
        }
        if(pHead1==NULL)
            Last->next->next=pHead2;
        if(pHead2==NULL)
            Last->next->next=pHead1;
        
        return First->next; 
    }
};

17.树的子结构。

  • 题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)。
  • 思路:辅助函数递归的判断两个树是否相等,主函数递归得判断是够有子树。
/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        bool result=false;
        if(pRoot1!=NULL&&pRoot2!=NULL)
        {
            if(pRoot1->val==pRoot2->val)  //如果两个相等,则进行匹配
                result=DoseTree1HaveTree2(pRoot1,pRoot2);
            if(!result)
                result=HasSubtree(pRoot1->left,pRoot2);
            if(!result)
                result=HasSubtree(pRoot1->right,pRoot2);
            //如果不等的话则分别递归查找其左边和右边
        }
        return result;   //如果没找到这里还是false
    }
    
    bool DoseTree1HaveTree2(TreeNode* pRoot1,TreeNode* pRoot2)
    {
        if(pRoot2==NULL)    //如果已经比遍历到pRoot2位空,那么说明是子树
            return true;
        if(pRoot1==NULL)     //如果pRoot1为空pRoot2不为空(如果为空已经返回),则说明不是子树
            return false;
        if(pRoot1->val!=pRoot2->val)  //如果存在两个值不相等,那么肯定也不是子树
            return false;
        return DoseTree1HaveTree2(pRoot1->left,pRoot2->left)&&
            DoseTree1HaveTree2(pRoot1->right,pRoot2->right);
        //剩下的就是递归得判断左右是否相等了。
    }
};

18.二叉树的镜像。

  • 题目: 操作给定的二叉树,将其变换为源二叉树的镜像。
  • 思路:递归的交换左右子树。交换节点需要使用动态空间。
/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        //终止条件就是当前的节点为空
        if(pRoot==NULL)   return;
        
        //交换节点
        TreeNode *tmp=new TreeNode(0);
        tmp->left=pRoot->left;
        tmp->right=pRoot->right;
        pRoot->left=tmp->right;
        pRoot->right=tmp->left;
        delete tmp;
        
        //判断左右分别是为空,不为空的话递归的来求镜像
        if(pRoot->left)
            Mirror(pRoot->left);
        if(pRoot->right)
            Mirror(pRoot->right);
           
    }
};

19.顺时针打印矩阵。

  • 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
  • 思路:见代码注释。
class Solution {
public:
    /*
    书上给的方法反而麻烦了一些,想到另外一种方法。
    用四个数来记录四个边界,startX,endX,startY,endY。四个数。
    分别记录列的始终,列的始终(均是开区间)
    最外层循环的条件是:`while (startX <= endX && startY <= endY)`
    在内层做四件事:
    第一个:最上面一行,从左到右遍历,遍历结束startY++,相当于把第一行去掉。
    第二个:最右边一列,从上到下遍历,遍历结束endX--,相当于把最后一列去掉。
    第三个:最下面一行,从右到左遍历,遍历结束后endY--,相当于把最后一列去掉。
    第四个:最左边一列,从下到上遍历,遍历结束后StartX++,相当于吧第一列去掉。
    这边完成了一个循环,停止的条件肯定是遍历完一行或者一列之后:
    出现endX<startX&&endY<startY的情况,则程序终止!
    */
    
    vector<int> printMatrix(vector<vector<int> > matrix) {
    
    int startX = 0;
    int startY = 0;
    int endY = matrix.size()-1;
    int endX;
    if (endY>=0)
        endX = matrix[0].size()-1;
    else
        endX = 0;
    vector<int> res;
    if (endY<0)
    {
        return res;
    }

    while (startX <= endX && startY <= endY)
    {
        //从左至右,遍历一行
        if (startX <= endX && startY <= endY)
        {
            for (int i = startX; i <= endX; i++)
            {
                res.push_back(matrix[startY][i]);
            }
        }
        startY++;    //遍历完一行之后,这一行就相当于去掉了,行++
        //
        if (startX <= endX && startY <= endY)
        {
            for (int i = startY; i <=endY; i++)
            {
                res.push_back(matrix[i][endX]);
            }
        }
        endX--;     //遍历完右边一列,这一列去掉,endX--
        if (startX <= endX && startY <= endY)
        {
            for (int i = endX; i >= startX; i--)
                res.push_back(matrix[endY][i]);
            
        }
        endY--;     //遍历完这一次,把最后一行去掉

        if (startX <= endX && startY <= endY)
        {
            for (int i = endY; i >= startY; i--)
                res.push_back(matrix[i][startX]);
        }
        startX++;
    }
    return res;

    }
};

20.包含min函数的栈。

  • 题目:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
  • 思路: 一个辅助站存储当前最小元素。
class Solution {
public:
    void push(int value) {
        if(stack_main.size()==0)
        {
            stack_main.push(value);
            stack_min.push(value);
        }
        else
        {
            stack_main.push(value);
            if(value<stack_min.top())
                stack_min.push(value);
            else 
                stack_min.push(stack_min.top());
        }
    }
    void pop() {
        stack_main.pop();
        stack_min.pop();
    }
    int top() {
        return stack_main.top();
    }
    int min() {
        return stack_min.top();
    }
    
private:
    stack<int> stack_main;
    stack<int> stack_min;
};

21.栈的压入,弹出序列。

  • 题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
  • 思路见代码注释。
class Solution {
public:
    /*
借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,
然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然
1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则
将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完
成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

举例:
入栈1,2,3,4,5
出栈4,5,3,2,1
首先1入辅助栈,此时栈顶1≠4,继续入栈2
此时栈顶2≠4,继续入栈3
此时栈顶3≠4,继续入栈4
此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
此时栈顶3≠5,继续入栈5
此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
….
依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
    
    */
   
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size()!=popV.size())  return false;
        int index_pop=0;
        stack<int> st;
        
        for(int i=0;i<pushV.size();i++)
        {
            st.push(pushV[i]);         //先入栈
            while(st.top()==popV[index_pop]&&!st.empty())
            {
                st.pop();      
                index_pop++;
                //如果是要用vector的话,这里一定要加上这一次判断,因为离开while循环有两个条件,
                //无论弹出序列合法不合法,最后都会导致index_pop为popV.size(),用这个值去访问
                //原vector就会越界,所以这里如果遍历到最后一个的话就跳出循环吧!
                if(index_pop==popV.size())  
                    break;
            }
        }
        return st.empty();
    }
};

未完待续!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容