每日做题之剑指offer(四)

1.包含min函数的栈

时间限制:1秒 空间限制:32768K 热度指数:177188

本题知识点: 栈

题目描述

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

解题思路:

  • 如果我们只有一个栈,在栈里添加一个成员变量存放最小元素是不够的,为什么呢?
  • 因为如果当前最小的元素被弹出栈顶了,那么如何获得下一个最小的元素呢?
  • 所以我们需要在压入最小的元素之前将次小的元素保存起来,可以添加一个辅助栈
  • {3,4,2,1,0}, 往空的数据栈里压入3,显然现在3是最小值,将这个最小值也压入辅助栈里;然后压入4,辅助栈压入3;压入2,辅助栈压入2,依次下去
  • 当最小元素压入辅助栈里,就可以保证辅助栈的栈顶一直都是最小的元素
  • 当栈顶的元素被弹出,同时弹出辅助栈的最小元素,辅助栈的新栈顶就是下一个最小值
class Solution {
public:
    void push(int value) {
        StackData.push(value);
        if(StackMin.empty())
            StackMin.push(value);
        else if(StackMin.top() < value)
            StackMin.push(StackMin.top());
        else
            StackMin.push(value);
    }
    void pop() {
        if(!StackData.empty())
        {
            StackData.pop();
            StackMin.pop();
        }
    }
    int top() {
        return StackData.top();
    }
    int min() {
        return StackMin.top();
    }
private:
    stack<int> StackData;
    stack<int> StackMin;
};

2.栈的压入、弹出序列

时间限制:1秒 空间限制:32768K 热度指数:212288

本题知识点: 栈

题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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) {
        if(popV.empty())
            return false;
        
        int len = pushV.size();
        int i=0,j=0;
        stack<int> s;
        s.push(pushV[0]);
        
        while(i <len && j< len){
            if(s.top()!=popV[j]){
                s.push(pushV[++i]);
            }else{
                s.pop();
                j++;
            }
        }
        if(s.empty())
            return true;
        return false;
    
    }
};

3.从上往下打印二叉树

时间限制:1秒 空间限制:32768K 热度指数:222428

题目描述从上往下打印出二叉树的每个节点,同层节点从左至右打印。

解题思路:

  • 本题实质上是考树的遍历算法,是层序遍历
  • 每次打印一个节点的时候,如果该节点有子结点,就把该节点的子节点入到一个队列的末尾
  • 然后队列的头部是最早进入队列的结点,将它取出来
  • 重复打印操作即可,直到队列中的所有节点都被打印出来

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> result; //存放最后的数组
        result.clear(); //将数组先清空
        queue<TreeNode*> tree_queue;
        
        if(NULL != root){
             tree_queue.push(root);
        }
       
        while(!tree_queue.empty())
        {
            if(tree_queue.front()->left != NULL)
            {
                tree_queue.push(tree_queue.front()->left);
            }
            if(tree_queue.front()->right != NULL)
            {
                tree_queue.push(tree_queue.front()->right);
            }
            result.push_back(tree_queue.front()->val);
            tree_queue.pop();
        }
        return result;
    }
};

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

时间限制:1秒 空间限制:32768K 热度指数:250141

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路:

  • 以{5,7,6,9,11,10,8}为例,我们知道后序遍历中的根节点位于数组的最后一个,所以在数组中的根节点8前面的是它的左右子树序列
  • 二叉搜索树的左子树的结点都小于根节点的值,右子树的结点的值都大于根节点的值
  • 所以数组中的5,7,6是根节点8的左子树;9,11,10是根节点8的右子树
  • 左子树中的根结点和8是一样的规律,左子树中的根节点6,左子树5小于6,右子树7大于6,符合规律
  • 右子树中的根节点10,左子树9小于10,右子树11大于10,符合规律
  • 可以采用递归实现
class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int len=sequence.size();
        if(len<=0) 
            return false;
         
        //找到根节点,为最后一个元素
        int root=sequence[len-1];
         
        //找左子树,小于根节点
        int i=0;
        vector<int> sequenceleft;
        for(; i<len-1; ++i) {
            if(sequence[i] > root)
                break;
            sequenceleft.push_back(sequence[i]);
        }
        //找右子树,大于根节点,有小于根节点的那么false
        int j=i;
        vector<int> sequenceright;
        for(;j<len-1; ++j) {
            if(sequence[j]<root) return false;
            sequenceright.push_back(sequence[j]);
        }
        //判断左右子树是不是二叉搜索树
        bool left=true;
        if(i>0)
            left=VerifySquenceOfBST(sequenceleft);
        bool right=true;
        if(i<len-1)
            right=VerifySquenceOfBST(sequenceright);
        if(left && right){
            cout<<"Yes"<<endl;
            return true;
        }
        else {
            cout<<"No"<<endl;
            return false;
        }
    }
};

5.二叉树中和为某一值得路径

解题思路

  • 由于路径总是从根节点开始遍历,而前中后三种遍历方式中只有前序遍历是从根结点开始的,所以按照前序遍历的方式
  • 由于没有指向父节点的指针,所以我们还需要将当前节点的上一个节点保存起来,每访问一个节点,都把当前节点添加到路径中去
  • 当达到节点5的时候,路径中包含结点10和结点5,然后遍历到节点4,将其也添加到路径中去,这时已经到达叶子结点,但是值不等于22,所以不是我们要找的路径
  • 这时候我们回到节点5,要去遍历5的右结点7,此时要删除结点4,将7添加到路径中去,值为22,使我们要找的路径
  • 再次回到节点5,删除节点7; 再接着回到节点10, 删除节点5; 将12添加到路径中去,符合要求
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
        if root and not root.left and not root.right and root.val == expectNumber:
            return [[root.val]]
        result = []
        left = self.FindPath(root.left, expectNumber-root.val)
        right = self.FindPath(root.right, expectNumber-root.val)
        for i in left + right:
            result.append([root.val] + i)
            
        return result
       

6.复杂链表的复制

时间限制:1秒 空间限制:32768K 热度指数:221374

本题知识点: 链表

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    //1. 复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
    void CloneNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        while(pNode!=NULL)
        {
            RandomListNode* pCloned=new RandomListNode(0);
            pCloned->label=pNode->label;
            pCloned->next=pNode->next;
            pCloned->random=NULL;
               
            pNode->next=pCloned;
               
            pNode=pCloned->next;
        }
    }
    //2. 如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'
    void ConnectRandomNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        while(pNode!=NULL)
        {
            RandomListNode* pCloned=pNode->next;
            if(pNode->random!=NULL)
                pCloned->random=pNode->random->next;
            pNode=pCloned->next;
        }
    }
    //3. 把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表
    RandomListNode* ReConnectNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        RandomListNode* pClonedHead=NULL;
        RandomListNode* pClonedNode=NULL;
           
        //初始化
        if(pNode!=NULL)
        {
            pClonedHead=pClonedNode=pNode->next;
            pNode->next=pClonedNode->next;
            pNode=pNode->next;
               
        }
        //循环
        while(pNode!=NULL)
        {
            pClonedNode->next=pNode->next;
            pClonedNode=pClonedNode->next;
            pNode->next=pClonedNode->next;
            pNode=pNode->next;
        }
           
        return pClonedHead;
           
    }
    //4. 三步合一
    RandomListNode* Clone(RandomListNode* pHead)
    {
        CloneNodes(pHead);
        ConnectRandomNodes(pHead);
        return ReConnectNodes(pHead);
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值