剑指offer刷题总结

目录

一、二叉树的部分

二、链表部分

三、dfs(二叉树部分除外)

四、其它部分


一、二叉树的部分

1、重新构建二叉树:用一个map容器将中序遍历中每一个点和它的位置建立联系(需要遍历一遍中序遍历的数组),递归中的内容 :通过前序遍历找到树的根节点,在中序遍历中将整个树用这个根节点分为左子树和右子树两部分,递归处理左子树和右子树,递归函数中的参数可以用右子树部分来确定,不容易出错,用左子树的部分不是太完整,(递归函数的返回值是TreeNode*,在递归函数中得到的是左右子树);

#include<iostream>
#include<vector>
using namespace std;

 struct TreeNode {
      int val;
   TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

class Solution {
public:
    vector<int> preorder;
    vector<int> inorder;
    map<int,int> mp;
    TreeNode* buildTree(vector<int>& _preorder, vector<int>& _inorder) {
        preorder=_preorder;
        inorder=_inorder;
        for(int i=0;i<inorder.size();i++) mp[inorder[i]]=i;
        return dfs(0,preorder.size()-1,0,inorder.size()-1);
    }
    TreeNode* dfs(int pl,int pr,int il,int ir)
    {
        if(pl>pr ) return nullptr;
        //创建一个新的树得到根节点的值
        auto root=new TreeNode(preorder[pl]);
        //
        //得到在中序遍历中根节点的位置
        int pos=mp[preorder[pl]];
        int num=pos-il;
        //得到树的左子树
        auto left=dfs(pl+1,pl+num,il,pos-1);
        //得到树的右子树
        auto right=dfs(pl+num+1,pr,pos+1,ir);
        //给根节点的左子树赋值
        root->left=left;
        //给根节点的右子树赋值
        root->right=right;
        //将根节点返回
        return root;
    }
};

2、不分行从上到下打印二叉树:用队列queue记录每次遍历到的树的节点,只要队列不为空,就将队列的头(ptemp)放入传出的数组中,并且将它从队列中弹出,如果ptemp的左右子树都有东西,将它的左右子树都放入队列中

进阶版:分行从上到下打印二叉树:先将树根放入队列中,然后将一个空指针放入队列中,剩下的和分行打印二叉树是一样的操作,只不过在判断队列中的元素为nullptr时,再次在队列中放入一个空指针即可

进阶版2:之字形打印二叉树:和进阶版一样,只不过多加入一个判断是否需要旋转的bool类型的变量,如果是真的,旋转即可

3、对称的二叉树:另外写一个递归函数,传入的参数为二叉树的左右子树,在递归函数中判断二叉树的左右子树对应的元素是否相等,在这个递归函数中处理特殊情况

二叉树的镜像:(只用判断根节点是否存在而不用判断左右节点是否存在)将根节点的左右子树进行位置的互换,然后分别处理左右子树即可

4、二叉树的下一个节点:判断当前的这个节点是否有右子树,如果有右子树,找到右子树的最左端的那个点,就是下一次需要打印出来的点  如果没有右子树且已经是右子树了,则执行 while(p->father && p==p->father->right)循环中一直找p的父亲节点即可,循环结束后返回当前节点的父节点,如果不是右子树,直接返回当前节点的父节点即可,所以后两种情况可以合并成一个

5、树的子结构:进行了两次递归的操作,一次是在主函数里,另外一次是在自己定义的函数中,判断传入的两个树是否合法,如果合法的话,开始进行判断(在自定义的函数中),如果成立,则表明入口节点是根节点,返回true,如果第一步判断出错,表示入口的节点不是第一个树的根节点,分别判断它的左右节点是否可以满足,如果可以满足,返回true,如果不可以返回false;

在自定义的函数中,从传入的节点开始遍历整个二叉树,判断是否满足条件,但是要保证第二个树比第一个树短,如果满足条件,返回true,否则反之

6、二叉搜索树的后序遍历序列:为了后续方便,这里把用一个全局变量存放传入的数组,故而使用了vector容器另外写一个函数,在这个函数中通过后序遍历的最后一个节点判断出树的左右子树的区间范围,(二叉搜索树的左子树节点小于根节点,右子树节点大于根节点),出现了比根节点大的数就表示它不在左子树的范围内了,如果在右子树的范围内出现了比根节点小的值,则说明二叉树不合法,返回false,否则继续处理左右子树的范围,判断是否合法,主函数里判断新写的这个函数是否满足条件即可。

7、二叉树中和为某一值的路径:定义两个全局的vector容器,一个vector容器中存储走过的节点vector<int>path,另外一个vector容器中存储答案vector<vector<int>> ans,深度优先遍历整个二叉树,将走过的每一个节点都放到path中,如果走到了叶子节点,就判断这个值和目标值是不是相等,如果相等,就将这个路径放入到ans中,如果没有走到叶子节点,就去处理这个树的左右节点,如果走到了叶子节点后的值和目标值还是不相等,就将这个节点从path中移除,深度优先遍历整个二叉树后,将ans返回

8、二叉搜索树与双向链表:深度优先遍历返回的结果应该是pair<TreeNode*,TreeNode*> ,判断根节点是否为空,如果不为空,深度优先遍历整个二叉树,分为以下几种情况(在leedcode下需要换方法)

a、节点是根节点,直接返回该节点即可

b、节点的左右子树都存在,递归处理左右子树

c、如果左子树或者右子树不存在,只处理左子树或者右子树即可,根节点分别为右子树和左子树

9、树中两个节点的最低公共祖先:深度优先遍历如果这个树是空的,直接返回空即可,如果不是空的,并且左右子树都存在,且左右子树都可以和所要找的节点相匹配,就返回根节点,定义两个树节点,TreeNode* left=fun(root->left,p,q) TreeNode* right=fun(root->right,p,q) 如果只有左节点或者只有右节点,直接返回左右节点即可

二、链表部分

三、dfs(二叉树部分除外)

1、机器人的运动范围:queue<pair<int,int>> p 容器记录每次走过的节点,用

vector<vector<bool>> flag(rows,vector<bool>(cols)) 容器记录遍历的节点是不是已经走过了,只要队列p中有元素,先用一个东西记录队列的头,然后将队列的头弹出,判断头是否满足条件,如果满足条件,输出结果值加1,并将它的四周的节点放入队列,继续处理,如果不满足,跳过本次循环,处理下一次循环,返回得到的结果值即可

2、最长不含重复字符的子字符串(重点):unordered_map<char,int> count 记录在区间内的每个字符出现的次数   可以和面试官讲一下怎么证明第二个指针向后走第一个指针就一定向后走的     用反证法证明,即假设第二个指针向后走,第一个指针向前走,推出矛盾   示例:arabcacfr

用两根指针,第一个指针i在前面,第二个指针j在后面,第一个指针每走一步,就判断count中的这个字符出现的次数是否大于1,如果有大于1的,更新区间,让第一个指针向后移动,移动的条件是while(count[s[i]]==1),并且让hash表中的数值也更新,当走到大于1的那个元素后,让那个元素的值减一,第一个指针继续向后移动一个单位

四、其它部分

1、数据流中的中位数:用容器priority_queue<>分别建立大堆和小堆建立默认的情况下建立的堆是大堆,小堆只需要将第三个参数进行修改改为greater<int>即可,priority_queue<int> max_heap (存放比较小的那一半数据,如果是奇数的话,堆顶就是中位数),priority_queue<int,vector<int>,greater<int>> min_heap;每来一个元素,就向大根堆里放,维护大根堆,判断大根堆的元素是不是比小根堆的多一个,如果多的话,将大根堆的堆顶放入小根堆中,大根堆的堆顶是不是比小根堆的堆顶还要大,如果大的话,大小堆的堆顶互换,根据数组的长度求出中位数

2、剪绳子:最后的最优解中,一定只有若干个3或者2,而且2的数目不会超过2个,如果绳子的长度N%3等于1,可以得到2个2,即一个4,如果N%3等于2,则可以拆出一个2,如果N%3等于0,则可以拆出若干个3

难点:需要证明为什么不会超过5,为什么没有4,为什么最多只有2个2,为什么没有1或者0,只有2或者3

注意:当绳子的长度小于等于3的时候,因为要至少拆成两个数,以3为例,只能拆成1和2,故需要特别处理

3、把字符串转换为整数  首先遍历整个字符串,除去字符串中的空格,然后判断这个字符串的非空格的第一位是不是‘-’,如果是符号,将标识符变为true,然后将字符串的每一位都转换为数字,存放在需要返回的结果中,这一步得到的数字是正数,判断是不是负数,如果是负数,将得到的这个数字变为它的相反数,判断这个数字是不是超过了范围

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值