leetcode每日一题复盘(二叉树,前中后层序遍历)

leetcode 347 前k个高频元素 

关键词:堆排序,优先队列,小根堆

这道题真没想出来怎么做,只能想到哈希统计数目,对优先队列还不是很熟悉,后来看了详解自己重写了一遍

主要思路是用哈希统计每个元素出现次数,再利用优先队列的性质创建小根堆(优先队列默认是从大到小排序,将是一个大根堆,因此需要重写比较函数cmp)

题目要求是前k个元素,因此我们维护一个大小为k的队列,每次插入元素队列自动排序将最小的排在堆顶,当数量>k时弹出堆顶元素,继续排序直到全部元素都遍历过

最后用vector存放堆中元素即可

为什么创建小根堆是a.second>b.second?

这个与优先队列的底层代码有关,其他数据结构a.second>b.second表示的是从大到小,只需要记住优先队列是相反的就可以了

leetcode 145 二叉树的后序遍历 

二叉树的后序遍历和144的前序遍历,94的中序遍历大同小异,这里就放一起当作一个题目来讲了

递归法

优点是代码简洁易写,但是效率差点,虽然时间复杂度都是O(n)

贴一个递归算法的代码,把递归顺序改一下就可以是其他遍历了

 void traversal(TreeNode*node,vector<int>&v)
    {
        if(node==nullptr)
        {
            return;
        }
        traversal(node->left,v);
        traversal(node->right,v); 
        v.emplace_back(node->val);
    }
  vector<int> postorderTraversal(TreeNode* root) {
        vector<int>res;
        traversal(root,res);
        return res;
}

迭代法

递归是隐式地调用栈,而迭代法就是显式地调用栈,将调用栈的过程以代码呈现出来

前序遍历

也就是我们的说的根左右,因为根在前,所以我们在遍历到根的时候就可以把根处理了(结果数组中插入根的值,不着急弹出根因为后面还要从根的右子树进行遍历)

先将根入栈,然后一直其根的左子树入栈,直到左子树为空就换成在右子树遍历,右子树遍历完后将根弹出,完成一个树遍历

 vector<int> preorderTraversal(TreeNode* root) {
        vector<int>res;
        stack<TreeNode*>Nodestack;
        while(root!=nullptr||!Nodestack.empty())
        {
            while(root)
            {
                Nodestack.push(root);
                res.emplace_back(root->val);
                root=root->left;
            }
            root=Nodestack.top()->right;
            Nodestack.pop();

        }
        return res;
    }

后序遍历

后序是左右根,因此根要放到最后才处理(也就是说,根最后才加入结果数组)

先将根的左子树全部入栈,直到root为空,此时将栈顶(左边最后一个根)设为root,再遍历其右子树,如果右子树为空,则可以将根加入结果数组中了

vector<int> postorderTraversal(TreeNode* root) {
        vector<int>res;
        stack<TreeNode*>Nodestack;
        TreeNode*pre=nullptr;
        while(root!=nullptr||!Nodestack.empty())
        {
            while(root)
            {
                Nodestack.push(root);
                root=root->left;
            }
            root=Nodestack.top();
            Nodestack.pop();
            if(root->right==nullptr||root->right==pre)
            {
                res.emplace_back(root->val);
                pre=root;
                root=nullptr;
            }
            else
            {
                Nodestack.push(root);
                root=root->right;
            }

        }
        return res;
    }

当然也有一种方法,可以将前序的遍历顺序换一下再reverse结果数组,就可以将前序遍历变成后序遍历

中序遍历

中序遍历的算法和前序遍历差不多,不过是根的处理时间换了一下

先遍历完左子树,再将根处理,再遍历右子树

vector<int> inorderTraversal(TreeNode* root) {
        vector<int>res;
        stack<TreeNode*>Nodestack;
        while(root!=nullptr||!Nodestack.empty())
        {
            while(root)
            {
                Nodestack.push(root);
                root=root->left;
            }
            root=Nodestack.top();
            res.emplace_back(root->val);
            Nodestack.pop();
            root=root->right;
           
        }
        return res;
    }

统一遍历法

具体可看代码随想录 (programmercarl.com)

leetcode 199 二叉树的右视图

经典的bfs广度优先算法,这类题目都可以套模板做

这题本质上还是考察层序遍历,想到层序遍历就不难做, 只要把每一层最后一个元素取下来即可

层序遍历模板代码

vector<vector<int>> levelOrder(TreeNode* root)
    {
        queue<TreeNode*>que;
        if(root)que.push(root);
        vector<vector<int>> ans;
        while(!que.empty())
        {
            int size=que.size();
            vector<int>res;
            for(int i=0;i<size;i++)
            {
                TreeNode*node=que.front();
                que.pop();
                res.push_back(node->val);
                if(node->left){que.push(node->left);}
                if(node->right){que.push(node->right);}
            }
            ans.emplace_back(res);
        }
        return ans;
    }

 leetcode 429 N叉树的层序遍历

这个是层序遍历的变式,改变的是子节点传入队列的方式,本质上还是层序遍历

Node类中包含了子节点的指针数组(代替了原来的左右子树),只要遍历数组就可以访问N个子节点,这样就完成了从二到N的转变 

leetcode 111 二叉树的最小深度 

这一题用层序遍历模板的方法做很容易能写出来,但递归的方法代码量少而且效率更高,这里讲一下

递归的方法

首先root为空的情况返回0就不用说了(确定递归的结束条件)

其次确定递归的参数是root节点的左右子树(确定参数)

最后是递归单层的处理(确定单层递归的逻辑)

单层递归:

我们将树的root节点分成三种情况:

root无子树         root只有一个子树         root有两个子树

惯性思维我们会将有子树和无子树进行分类然后写递归代码,但是这样写是错误的

两个子树的情况可以独立出来,用min()挑选左右子树中短的那一棵

有一个子树和无子树的情况是一样的,因为题目是求最小深度而不是最大深度,无子树是叶子节点,有一个子树的root并不是叶子节点,而我们写的代码很可能会用min()处理有一个子树的情况从而取到为空的子树(即算法找到最短的路径但不是叶子节点),因此我们要将一个子树的情况和无子树的情况当作同一种,用max()来挑选有叶子节点的子数 

写递归不要用从头开始的思想写,从结果"归"的思想写更容易

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值