剑指offer笔试题21~25

21.包含min函数的栈

题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路一: 要使栈有min函数,那就需要有一个结构能够存储这个最小值,同时还需要保存次最小值,再次最小值。很容易想到在一开始入栈的时候就保存一个最小值,接下来每次push都要进行value与min的比较。所有的最小值也许要有序排列,保证可以做到连续的pop最小值。因此需要构建一个辅助栈stack_min。
(1) 两个栈为空,二者直接push
(2) value <= stack_min.top(): 对辅助栈进行push(value)
(3) value > stack_min.top(): 对辅助栈进行push(stack_min.top())
(4) pop时,辅助栈只需要同时也pop即可

class Solution {
public:
    void push(int value) 
    {
        stack_data.push(value);
        if(stack_min.empty() || value < stack_min.top())
            stack_min.push(value);
        else
            stack_min.push(stack_min.top());
    }
    void pop() 
    {
        if(stack_min.empty() && stack_data.empty())
            throw "empty stack";
        stack_data.pop();
        stack_min.pop();
    }
    int top() 
    {
        if(stack_min.empty() && stack_data.empty())
            throw "empty stack";
        return stack_data.top();
    }
    int min() 
    {
        if(stack_min.empty() && stack_data.empty())
            throw "empty stack";
        return stack_min.top();
    }
private:
    stack<int> stack_data;
    stack<int> stack_min;
};

思路二: 和上面是同样的思路,只是在发现更小的value时才会对辅助栈进行插入,以减少对辅助栈所占用的空间。在本地ide上能够运行,但是在牛客上运行失败。

class Solution {
public:
    void push(int value) 
    {
        stack_data.push(value);
        if(stack_min.empty() || value <= stack_min.top())
            stack_min.push(value);
    }
    void pop() 
    {
        if(stack_data.empty())
            throw "empty stack";
        stack_data.pop();
        if(stack_data.top() == stack_min.top())
            stack_min.pop();
    }
    int top() 
    {
        if(stack_data.empty())
            throw "empty stack";
        return stack_data.top();
    }
    int min() 
    {
        if(stack_min.empty())
            throw "empty stack";
        return stack_min.top();
    }
private:
    stack<int> stack_data;
    stack<int> stack_min;
};

22.栈的压入和弹出

题目描述: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路一: 因为出栈是栈顶的元素先出去,既然知道了栈顶元素值a,那我们就可以将pushV中包含a在内的前面部分以此入栈,此时达到了 出栈条件:栈顶元素值等于popV首元素 。此时栈非空,我们需要判断是否还满足出栈条件,所以应该将出栈条件设置为while循环。直到跳出循环时再继续将pushV中剩下的元素逐个入栈。 当pushV中所有元素均已入栈,且跳出whike循环,此时栈非空的话说明两个序列并不匹配;栈为空则说明匹配。
(1) 两个栈为空,return false

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) 
    {
        if(pushV.empty() || popV.empty())
            return false;
        stack<int> stackData;
        int len = popV.size();
        int indexOfPop = 0;
        for(int i=0; i<len; ++i)
        {
            stackData.push(pushV[i]);
            while(stackData.top() == popV[indexOfPop] && indexOfPop<len)
            {
                stackData.pop();
                indexOfPop++;
            }
        }
        return stackData.empty();
    }
};

思路一: 一样的思路,只不过是使用vector来作为辅助空间。

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) 
    {
        if(pushV.empty() || popV.empty())
            return false;
        vector<int> tmp;
        for(int i=0,j=0; i<pushV.size(); )
        {
            tmp.push_back(pushV[i++]);
            while(tmp.back() == popV[j] && j < popV.size())
            {
                tmp.pop_back();
                j++;
            }
        }
        return tmp.empty();
    }
};

23.从上往下打印二叉树

题目描述: 从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路: 在打印根结点时,需要现将它的左右子结点保存在一个容器中,然后再按照容器中结点的顺序,逐个打印并将其左右子结点也按照顺序放在该容器中,再将其从出队。很明显这是一个队列。在队列非空的情况就一直打印队首元素。
(1) 根节点为空,返回空

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) 
    {
        vector<int> result;
        if(root == NULL)
            return result;
        TreeNode* pNode = root;
        queue<TreeNode*> tmpData;
        tmpData.push(pNode);
        while(!tmpData.empty())
        {
            pNode = tmpData.front();
            result.push_back(pNode->val);
            if(pNode->left != NULL)
                tmpData.push(pNode->left);
            if(pNode->right != NULL)
                tmpData.push(pNode->right);
            tmpData.pop();
        }
        return result;
    }
};

24.二叉搜索树的后序遍历序列

题目描述: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
递归版本的思路一: 后序遍历的特点:最后一个元素必定是根节点。 然后再从前往后遍历,小于根节点元素的(连续)部分构成左子树,接着后面大于根节点元素的(连续)部分构成右子树。
(1) 输入为空,返回false
(2) 如果不满足上述的条件必定不是BST的后序遍历。
(3) 再递归地判断左右子树两部分是否也是BST

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) 
    {
        if(sequence.empty())
            return false;
        return helper(sequence,0,sequence.size()-1);
    }
    bool helper(vector<int> & seq,int begin,int end)
    {
        if(begin == end)
            return true;
        //找到左子树截至的位置
        int lessEnd = begin;
        while(lessEnd < end && seq[lessEnd] < seq[end])
            lessEnd++;
        //确认右子树截至的位置
        int bigerEnd = lessEnd;
        while(bigerEnd < end && seq[bigerEnd] > seq[end])
            bigerEnd++;
        //判断是否比较了所有元素,不是的话,返回false
        if(bigerEnd < end)
            return false;
        //其中一个子树为空,那么直接判断整体即可
        if(lessEnd==begin || bigerEnd==end)
            return helper(seq,begin,end-1);
        else
            return helper(seq,begin,lessEnd-1) && helper(seq,lessEnd,bigerEnd-1);
    }
};

非递归的思路二: 整体是利用 左子树所有元素 < 根节点元素 < 右子树所有元素,。 因此从最后开始判断。

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) 
    {
        int len = sequence.size();
        if(len == 0)
            return false;
        
        int index = 0;
        while(--len)
        {
            //找到左子树的截至位置
            while(sequence[index] < sequence[len])
                index++;
            //找到右子树的截至位置
            while(sequence[index] > sequence[len])
                index++;
            //判断是否遍历所有元素
            if(index < len)
                return false;
            index = 0;
        }
        return true;
    }
};

25.二叉树中和为某一值的路径

题目描述: 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
思路: 该函数需要返回一个从根节点到叶节点路径,因此采用前序遍历。每遍历一个结点就将其插入路径path中,并从用期望的数值expectNumber减去该结点的值val。此时会有三种情况:
(1) 此时expectNumber==0,且该结点就是叶节点,那么将path插入result;
(2) 此时expectNumber>0,先后遍历其左右子结点;
(3) 此时expectNumber<0,已经没有继续向下查找的必要,直接将该结点从path中去除。

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) 
    {
        if(root == NULL)
            return result;
        //遍历根节点,就将其插入到路径中
        expectNumber -= root->val;
        path.push_back(root->val);
        //该结点为叶节点就插入result
        if(expectNumber==0 && !root->left && !root->right)
            result.push_back(path);
        //不是叶节点就继续遍历子结点
        if(expectNumber>0)
        {
            FindPath(root->left,expectNumber);
            FindPath(root->right,expectNumber);
        }
        path.pop_back();
        return result;
    }

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值