05.图与树的面试题目精讲

这一生的挚爱 提交于 2020-03-03 08:15:42

目录

图论简介

面试题总体分析

一些例题:

    例1.给定二叉树的前序遍历和中序遍历,构造二叉树   LC-105

    例2.二叉树的高度、最小深度、二叉搜索树(BST)判断、对称判断、平衡判断

【用同一个递归框架解决】

    例3.二叉树与链表转换

    例4.无向图复制

    例5.直角路线遍历棋盘

总结


图论简介

面试题总体分析

图:

  连通性(割点、边)、 最小生成树 、最短路 、 搜索(BFS、DFS) 、 欧拉回路 、 哈密尔顿回路 、 拓扑排序

树:

  树的定义与判断 、、 平衡,二叉搜索树、最大(小)深度 、最近公共祖先(LCA)

  

一些例题:

    例1.给定二叉树的前序遍历和中序遍历,构造二叉树   LC-105

分析:前序遍历的第一个节点是根节点,在中序遍历中找到根节点,并把中序遍历分为左子树+右子树 

注意 下标范围

class Solution{
public:
    TreeNode * dfs(vector<int> &preorder, vector<int> &inorder,int pre, int in, int len){
        if(len==0) return null;
        TreeNode * root=new TreeNode(preorder[pre]); //树根节点为前序遍历的第一个节点
        int i;
        for(i=in;inorder[i]!=preorder[pre];i++); //在中序遍历中找到根节点
        root->left=dfs(preorder,inorder,pre+1,in,i-in); // pre开始 in开始
        root->right=dfs((preorder,inorder,pre+1+i-in,i+1,len-1-i+in)
        return root;
    }

    TreeNode * buildTree(vector<int> &preorder, vector<int> &inorder ){
        return dfs(preorder,inorder,0,0,inorder.size());
    }

}

    例2.二叉树的高度、最小深度、二叉搜索树(BST)判断、对称判断、平衡判断

【用同一个递归框架解决】

LC-124:二叉树每个节点有一个权重,计算 和最大的路径

递归计算左子树 递归计算右子树,master计算max(左)+max(右)+root->value  注意返回值与最大值的关系 

class Solution{
public:
    int dfs(TreeNode * root,int &m){
        if(root==null) return 0;

        int left=dfs(root->left,m);
        int right=dfs(root->right,m);

        int ret=max(max(left,right),0)+root->val; //不含跨界的 求root向下延伸的最长路径
        m=max( max(m,ret),left+right+root->val); //含跨界的
        return ret;
    }

    int maxPathSum(TreeNode * root){
        if(root==null) return 0;
        return dfs(root, root->val);
    }
};

leetcode-111 二叉树的最小深度 注意空子树

class Solution{
public:
    int minDepth(TreeNode * root){
        if(root==null) return 0; //00
        if(root->left){
            if(root->right){ //11
                return min( minDepth(root->left),minDepth(root->right) )+1;
            }else{   //10
                return minDepth(root->left)+1;
            }
        }else if(root->right){  //01 
            return minDepth(root->right)+1;
        }else{  //only root
            return 1;
        }
    }
};

 Leetcode-110 判断平衡

class Solution{
public:
    bool dfs(TreeNode * root,int &height){  //判断是否平衡 
        if(root==null) {height=0; return true;}

        int h1,h2;
        if(!dfs(root->left,h1))
            return false;
        if(!dfs(root->right,h2))
            return false;
        height=max(h1,h2)+1;  //记录最大树高
        return (h1-h2)>=-1 && (h1-h2)<=1;    //|h1-h2|<=1

    }

    bool isBalanced(TreeNode * root){
        int height; //树高
        return dfs(root,height);
    }
};

Leetcode-104 最大深度

class Solution{
public:
    int maxDepth(TreeNode * root){
        return root?max( maxDepth(root->left),maxDepth(root->right))+1 : 0;
    }
};

Leetcode-100 判断相同

class Solution{
public:
    bool isSameTree(TreeNode *p,TreeNode *q){
        if(p==null) return q==null;
        if(q==null) return false;
        return (p->val)==(q->val) && isSameTree(p->left,q->left) && isSameTree(p->right,q->right); 
    }
};

 Leetcode-98 判断二叉搜索树 √ 【BST 中序遍历 存到vector中 判断是否是递增的 / 在递归的时候判断】

class Solution{
public:
    bool dfs(TreeNode * root, bool &first, int &last){ //判断BST  中序遍历
        if(root==0) return true; //空树是bst
        if(!dfs(root->left,first,last)){
            return false;
        }
        if(first){
            first=false;
            last=root->val;
        }else if(last>=root->val){ //左中右 中>=左 右>=中 last代表上一个遍历的节点
               return false;
        }
        last=root->val;
        bool flg=dfs(root->right,first,last);
        return flg;
    }


    bool isValidBST(TreeNode * root){\
        bool mark=true; int val=0
        return dfs(root,mark,val);  //mark表示一上来还没有遍历过任何节点 
    }

};

    例3.二叉树与链表转换

Leetcode-114 二叉树转链表  [还是用中序遍历的框架,中间操作如下:]

class Solution{
public:
    void dfs(TreeNode *&root,TreeNode *&last){
	last=root;
	if(root==null) return ;
	TreeNode *mylast,*tmp=root->right;
	
	dfs(root->left,mylast); //左 
	if(mylast){
		last=mylast;     //中 
		last->right=tmp;
		root->right=root->left;
		root->left=null;
	}
	dfs(tmp,mylast);  //右 
	if(mylast)
		last=mylast;
    } 
    void flatten(TreeNode * root){
	    TreeNode *last;
	    dfs(root,last);
    } 

};

 

链表转(平衡)二叉树   

LC-109  有序链表转换为二叉搜索树

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

方法1. O(nlogn) 因为链表不能随机访问     如果是数组转为二叉树 O(logn)

class Solution{  //LC-109 有序链表转换为二叉搜索树
public:
    TreeNode *dfs(ListNode *head, int len){
        if(len==0) return null;
        listNode *now=head;
        for(int (i=len-1)>>1; i; i--){  //O(n)
            now=now->next;    
        } //链表1/2的位置是 root

        TreeNode * root=new TreeNode(now->val);
        root->left=dfs(head,(len-1)>>1); 
        root->right=dfs(now->next,len>>1)  
            return root;  
    }

    TreeNode *sortedListToBST(ListNode * head){
        ListNode * tmp=head;
        int len;
        for(len=0; tmp; tmp=tmp->next,++len);
        return dfs(head,len);
    }
};

方法2.优化,同时移动指针O(n) 【中序遍历框架,下图为核心操作】  T(n)=2T(n/2)+O(1)   

class Solution{  //LC-109 有序链表转换为平衡二叉搜索树
public:
    TreeNode *dfs(ListNode *head, int len){
        if(len==0) return null;
        int num=(len-1)>>1;
        TreeNode * left=dfs(head,num);

        TreeNode * root=new TreeNode(head->val);  //中序遍历 核心操作 如图
        root->left=left; 
        head=head->next;

        root->right=dfs(head,len-num-1);
            return root;
    }

    TreeNode *sortedListToBST(ListNode * head){
        ListNode * tmp=head;
        int len;
        for(len=0; tmp; tmp=tmp->next,++len);
        return dfs(head,len);
    }
};

练习:LC-108 有序数组转(平衡)二叉树

    例4.无向图复制

LC-133   

复制一个有向图,(邻接表存储)分析:DFS,图中可能有圈 

class Solution{  
public:
   UnDirectedGraphNode * dfs(const  UnDirectedGraphNode *node, map<int, UnDirectedGraphNode *> &have){
    map<int, UnDirectedGraphNode *>::iter t=have.find(node->label);
        if(t==have.end()){ //未找到
            UnDirectedGraphNode  *newnode=new UnDirectedGraphNode(node->label);
            have.insert(make_pair(node->label,newnode));
            for(int i=0;i<node.neighbors.size();i++){
                newnode->neighbors.push_back(dfs(node->neighbors[i],have));
            }
                return newnode;
        }else{
            return t->second; //UnDirectedGraphNode * 
         }
    }

    UnDirectedGraphNode *cloneGraph(UnDirectedGraphNode * node){
    map<int,UnDirectedGraphNode *> have;
    if(node==null) return null;
    return dfs(node,have);    
    }
};

 

    例5.直角路线遍历棋盘

 

构造二部图:图中每个点(x,y)的横坐标放到集合X中,纵坐标放到Y中;坐标(x,y)有边,寻找欧拉路

判断欧拉路:除了起点终点,其余点的出度=入度 ;

总结

  理解递归

  熟悉树的遍历 (递归、非递归)

  其他问题:

    LCA最近公共祖先(二叉树、非二叉树、二叉搜索树、离线算法——在线算法)

 (隐式)图搜索bfs+dfs、——强联通分量

    拓扑排序(较少)

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!