LeetCode力扣(剑指offer 6-10)6.从尾到头打印链表,7.重建二叉树,9.用两个栈实现队列,10-1.斐波那契数列,10.2青蛙跳台阶问题

剑指 Offer 06. 从尾到头打印链表

题解:

方法一:递归

首先,遍历链表,直至链表的尾部。

递归返回,并将val值添加到数组res中,res中的数据就是链表从尾到头的数据

代码: 

class Solution {
public:
    void recursion(ListNode* head,vector<int> &res){
        if(head==nullptr) return;
        recursion(head->next,res);
        res.push_back(head->val);
    }
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        recursion(head,res);
        return res;
    }
};

结果:

方法二:数组反转

首先,遍历链表,并把数组记录到数组中。

然后,将数组反转即可。

代码:

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        while(head!=nullptr)
        {
            res.push_back(head->val);
            head=head->next;     
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

结果: 

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字

 

题解:

这题比较复杂,需要有二叉树的基本知识,如果对这方面不了解,可以看看数据结构相关书籍,不然很难下手。

前序序列是优先遍历根节点,中序是优先遍历左子树。在前序数组中找到根节点,中序数组中找到这个数,这个数的左右两侧就表示了它的左右子树。依次下去,找到左子树的根节点,在中序数组该数的左侧是它的左子树,右侧到根节点之间的数是右子树。

首先,判断二叉树不为空。

二、递归,将传进来前序数组第一个数作为根节点。判断前序数组是否还有数据剩余

三、在中序数组中找到这个数rootInorder,beginInorder到rootInorder之间就是它的左子树。递归构建左子树

四、递归构建右子树,条件是左子树的个数小于传入进来的前序数组区间的个数

代码: 

class Solution {
public:
    using  intIterator = vector<int>::iterator;

   TreeNode* recursion(intIterator beginPreorder,intIterator endPreorder,intIterator beginInorder,intIterator endInorder){
        TreeNode *root=new TreeNode(*beginPreorder);  

        if(beginPreorder==endPreorder){   
            if(beginInorder==endInorder&&*beginPreorder==*endPreorder)
                return root;
        }

        auto rootInorder=beginInorder;
        while(rootInorder<=endInorder && *rootInorder!=root->val)//在中序数组找到根节点的位置
            ++rootInorder;

        int leftLength=rootInorder-beginInorder;
        auto leftEnd=beginPreorder+leftLength;//中序数组中,根节点左侧是左子树

        if(leftLength>0){ //构建左子树
            root->left=recursion(beginPreorder+1,leftEnd,beginInorder,rootInorder-1);
        }
        if(leftLength<endPreorder-beginPreorder){ //构建右子树
            root->right=recursion(leftEnd+1,endPreorder,rootInorder+1,endInorder);
        }
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size()==0)
            return NULL;
        return recursion(preorder.begin(),preorder.end()-1,inorder.begin(),inorder.end()-1);
    }
};

结果:

剑指 Offer 09. 用两个栈实现队列

题解:

首先,建立两个栈instack,outstack,一个作进栈,一个作为出栈

进栈,只用作添加元素

出栈,我们需要把instack的元素全部压入到outstack中,那么outstack的栈顶元素是队列的头部元素,pop一下就删除了。值得注意的是,instack的元素全部压入到outstack后,有些小伙伴可能想在pop之后将outstack的元素再弹出压回去,其实不用。我们只需要等outstack全部删除完之后,再考虑从instack中拿过来新的数据。

代码: 

class CQueue {
public:
    //两个栈,一个出栈,一个入栈
    stack<int> instack;
    stack<int> outstack;

    CQueue() {}
    
   void appendTail(int value) {
        instack.push(value);
    }
    
   int deleteHead() {
        if(outstack.empty())
            if(instack.empty())  //说明全部数据都删除了
                return -1; 
            else 
                while(!instack.empty()){    //需要把instack的数据压入到outstack
                    int temp=instack.top();
                    instack.pop();
                    outstack.push(temp);
                }
        int res=outstack.top();
        outstack.pop();
        return res;       
    }
};

结果:

剑指 Offer 10- I. 斐波那契数列

题解:

首先想到的应该是递归,而且算法很容易想到

 上面的算法用了会超时,代码虽然简洁易懂,看起来很适合解决这道题。但是,这样解有很严重的效率问题。比如求解f(10),我们需要递归f(8)和f(9)

 可以发现,求解过程存在着大量的重复步骤,而且n越大,计算量会急剧变大。显然,单纯的递归是不可取的。

方法一:改进的递归

首先,递归到底,也就是n=1的时候,返回,从n=2开始计算,f(2)=temp2+temp1=f(1)+f(0)=1。

计算完之后temp1为f(1),temp2为f(2),这样方便返回到n=3的时候计算

值得注意的是,递归到最后时,temp2的值变成了我们需要的值,请细细考虑。

代码: 

class Solution {
public:
    int temp1=0,temp2=1;

    void recursion(int n){
        if(n==1) return;    //递归到n=1时,开始返回,从n=2时候计算
        recursion(n-1);
        int res=(temp1+temp2)%1000000007;  //题目要求,数值太大
        temp1=temp2;
        temp2=res;     
    }
    
    int fib(int n) {
        if(n<2) return n;
        recursion(n);
        return temp2;    //最后返回是temp2
    }
};

结果:

 方法二:动态规划

不得不说,动态规划是最适合这道题的。思路和方法一差不多

从n=2开始计算,f(2)=temp2+temp1=f(1)+f(0)=1。计算完之后temp1为f(1),temp2为f(2)。一直循环即可

代码:

class Solution {
public:
    int fib(int n) {
        if(n<2) return n;
        int temp1=0,temp2=1;
        int res=0;
        for(int i=2;i<=n;++i){
            res=(temp1+temp2)%1000000007;
            temp1=temp2;
            temp2=res;
        }
        return res;
    }
};

结果:

剑指 Offer 10- II. 青蛙跳台阶问题

题解:

这题和斐波那契数列思想是一模一样的,采用动态规划的思路。用递归也行,其实也是将for循环变成递归的形式,换汤不换药;

首先,0~2都可以先判断一下,如果是0返回1,如果是1返回1,如果是2返回2;

从n=3开始计算,f(3)=temp2+temp1=f(2)+f(1)=1。计算完之后temp1为f(2),temp2为f(3)。一直循环即可

代码: 

class Solution {
public:
    int numWays(int n) {
        if(n==0) return 1;
        if(n<=2) return n;
        int temp1=1,temp2=2;
        int res=0;
        for(int i=3;i<=n;++i){
            res=(temp1+temp2)%1000000007;
            temp1=temp2;
            temp2=res;
        }   
        return temp2;
    }
};

结果:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木白CPP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值