leetcode--二叉树和图

来源:互联网 发布:淘宝上最火的店铺 编辑:程序博客网 时间:2024/06/06 12:40

基础知识

二叉树的数据结构

struct TreeNode{    int val;   //数据域    TreeNode *left //左右指针    TreeNode *right    TreeNode(int x):val(x),left(NULL),right(NULL){}    //构造函数}

构造二叉树

构造如下的一棵二叉树:
二叉树

int main(){    TreeNode a(1);    TreeNode b(2);    TreeNode c(3);    TreeNode d(4);    TreeNode e(5);    TreeNode f(6);    a.left=&b;    a.right=&c;    b.left=&d;    b.right=&e;    c.right=&f;}

二叉树的深度遍历

前序遍历访问
traversal(node->left)
中序遍历访问
traversal(node->right)
后序遍历访问

前中后指的是访问根节点的顺序,前序:先根再左再右

二叉树的层次遍历

也就是广度优先搜索,用队列一层一层的访问节点,访问一个节点压入该节点的孩子节点,队列不空,持续该过程。
这里写图片描述

void BFS_print(TreeNode* root){    queue<TreeNode*> Q;    Q.push(root);    while(!Q.empty()){        TreeNode* node=Q.front();        Q.pop();        printf("(%d)\n",node->val);        if(node->left){            Q.push(node->left;        }        if(node->right){            Q.push(node->right;        }    }}

leetcode题目

113 Path Sum

题意:

给定一个二叉树与整数sum,找出所有从根节点到叶节点的路径,要求这些路径上节点值的和等于sum。

解题思路:

用深度优先搜索路径。
1 先序遍历时将节点值存储到path栈中,path_value累加节点值
2 当遍历到叶节点时判断path_value是不是等于sum,if等于push进result中。
3 在后序遍历时,将节点从path栈中弹出,path_value减去节点值

代码:

class Solution {public:    vector<vector<int>> pathSum(TreeNode* root, int sum) {        vector<vector<int>> result;        vector<int> path;        int path_value=0;        preorder(root,path_value,sum,path,result);        return result;    }private:    void preorder(TreeNode *node,int &path_value,int sum,vector<int> &path,vector<vector<int>> &result){        //先序遍历内容:        if(!node){            return;        }        path_value=path_value+node->val;        path.push_back(node->val);        if(!node->left&&!node->right&&path_value==sum){            result.push_back(path);        }        preorder(node->left,path_value,sum,path,result);        preorder(node->right,path_value,sum,path,result);        //后序遍历内容:        path.pop_back();        path_value=path_value-node->val;    }};

236 Lowest Common Ancestor of a Binary Tree

题意:

给出两个节点,求出他们最近的公共祖先。

解题思路:

1 用深度优先搜索找点,找到点后保存路径。
2 同时遍历两个路径,找最后一个相同点。

代码:

void preorder(TreeNode* node,              TreeNode *search,              std::vector<TreeNode*> &path,              std::vector<TreeNode*> &result,              int &finish){    if (!node || finish){        return;      //结束技巧,finish用于标记已找到节点    }    path.push_back(node);    if (node == search){        finish = 1;        result = path;    }    preorder(node->left, search, path, result, finish);    preorder(node->right, search, path, result, finish);    path.pop_back();//后序遍历时根节点要做退出操作,就是左右节点都不是要找的点,根节点弹出}class Solution {public:    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {        std::vector<TreeNode*> path;        std::vector<TreeNode*> node_p_path;        std::vector<TreeNode*> node_q_path;        int finish = 0;        preorder(root, p, path, node_p_path, finish);//找p点的路径并保存于node_p_path        path.clear();        finish = 0;        preorder(root, q, path, node_q_path, finish);//找q点的路径并保存于node_q_path        int path_len = 0;        if (node_p_path.size() < node_q_path.size()){            path_len = node_p_path.size();        }        else{            path_len = node_q_path.size();        }        TreeNode *result = 0;        //找两个路径中最后一个相同点        for (int i = 0; i < path_len; i++){            if (node_p_path[i] == node_q_path[i]){                result = node_p_path[i];//少写一点代码            }        }        return result;    }};

114. Flatten Binary Tree to Linked List

题意:

不用大的额外空间,将二叉树转换为链表,left为null,right为链表的next指针。链表的顺序是树的先序遍历。

解题思路:

如果不考虑大的额外空间,最简单的做法是用一个vector存先序遍历的结果,然后遍历这个vector。

代码:

class Solution {public:    void flatten(TreeNode *root) {        std::vector<TreeNode *> node_vec;        preorder(root, node_vec);        for (int i = 1; i < node_vec.size(); i++){            node_vec[i-1]->left = NULL;            node_vec[i-1]->right = node_vec[i];        }    }private:    void preorder(TreeNode *node,vector<TreeNode *> &node_vec){        if (!node){            return;        }        node_vec.push_back(node);        preorder(node->left, node_vec);        preorder(node->right, node_vec);    }};

解题思路2:

思考过程是:独立开树的一部分,思考前中后遍历时候应该做什么。考虑输入和输出:
* 先序遍历:第一次访问根节点时,我们要保留左右节点,让right指针指向左节点。右指针指向NULL。
* 中序遍历:第二次访问根节点,这时左子树完成了链表变形,要将左子树的最后节点指向,右子树的开始节点。所以要输出一个末尾节点,可以用&引用去维护。
* 后续遍历:维护最末的节点。

代码:

class Solution {public:    void flatten(TreeNode* root) {        TreeNode *last=NULL;        preorder(root,last);    }private:    void preorder(TreeNode* node,TreeNode* &last){        //*& TreeNode*的意思是传入TreeNode的指针变量,&是可以在函数中改变这个变量        if(!node){            return;        }        if(!node->left&&!node->right){            last=node;            return;        }        TreeNode* left=node->left; //保留左右节点        TreeNode* right=node->right;        TreeNode* left_last=NULL;//初始化要维护的末尾节点。        TreeNode* right_last=NULL;        if(left){            preorder(left,left_last);//递归,维护左子树的末尾节点            node->left=NULL; //放在中序遍历执行时因为可以省去判断是否有左子树的语句。            node->right=left;            last=left_last;        }        if(right){            preorder(right,right_last);            if(left_last){                left_last->right=right;//让左子树的末尾节点指向右子树的开始节点。放在后序遍历执行可以省去判断是否有右子树的语句。            }            last=right_last;//维护右子树的末尾节点        }    }};

199.Binary Tree Right Side View

题意:

从右边观察二叉树,输出每一层最右边的节点。

解题思路:

层次遍历二叉树,将节点和层数绑定微pair,压入队列时,将节点和层数同时压入,记录每一层的最后一个节点。

代码:

class Solution {public:    vector<int> rightSideView(TreeNode* root) {        vector<int> view; //存储每层最后的节点。        queue<pair<TreeNode*,int>> Q;        //广度优先搜索,用一个pair存储节点和它对应的层数。        if(root){            Q.push(make_pair(root,0));        }        while(!Q.empty()){            TreeNode* node=Q.front().first;              int depth=Q.front().second;            Q.pop();            if(view.size()==depth){                view.push_back(node->val); //当出现新的层时压入一个节点。            }            else{                view[depth]=node->val;            }            if(node->left){                Q.push(make_pair(node->left,depth+1));  //压入左节点,标记为下一层            }            if(node->right){                Q.push(make_pair(node->right,depth+1)); //压入右节点,标记为下一层            }        }        return view;    }};

图

图的表示(邻接矩阵):

这里写图片描述

代码:

int main(){    const int MAX_N = 5;    int Graph[MAX_N][MAX_N] = {0};    Graph[0][2] = 1;    Graph[0][4] = 1;    Graph[1][0] = 1;    Graph[1][2] = 1;    Graph[2][3] = 1;    Graph[3][4] = 1;    Graph[4][3] = 1;    printf("Graph:\n");    for (int i = 0; i < MAX_N; i++){        for (int j = 0; j < MAX_N; j++){            printf("%d ", Graph[i][j]);        }        printf("\n");    }    return 0;}

表示稠密图作用比较大,但是表示稀疏图一般用邻接表。

图的表示(邻接表):

这里写图片描述

代码:

#include <stdio.h>#include <vector>struct GraphNode{    int label;    std::vector<GraphNode *> neighbors;    GraphNode(int x) : label(x) {};};int main(){     const int MAX_N = 5;    GraphNode *Graph[MAX_N];    for (int i = 0; i < MAX_N; i++){        Graph[i] = new GraphNode(i);    }    Graph[0]->neighbors.push_back(Graph[2]);    Graph[0]->neighbors.push_back(Graph[4]);    Graph[1]->neighbors.push_back(Graph[0]);    Graph[1]->neighbors.push_back(Graph[2]);    Graph[2]->neighbors.push_back(Graph[3]);    Graph[3]->neighbors.push_back(Graph[4]);    Graph[4]->neighbors.push_back(Graph[3]);    printf("Graph:\n");    for (int i = 0; i < MAX_N; i++){        printf("Label(%d) : ", i);        for (int j = 0; j < Graph[i]->neighbors.size(); j++){            printf("%d ", Graph[i]->neighbors[j]->label);        }        printf("\n");    }    for (int i = 0; i < MAX_N; i++){        delete Graph[i];    }    return 0;}

深度优先搜索

struct GraphNode{    int label;    std::vector<GraphNode *> neighbors;    GraphNode(int x) : label(x) {};};void DFS_graph(GraphNode *node, int visit[]){    visit[node->label] = 1;    printf("%d ", node->label);    for (int i = 0; i < node->neighbors.size(); i++){        if (visit[node->neighbors[i]->label] == 0){            DFS_graph(node->neighbors[i], visit);        }    }} 

广度优先搜索

struct GraphNode{    int label;    std::vector<GraphNode *> neighbors;    GraphNode(int x) : label(x) {};};void BFS_graph(GraphNode *node, int visit[]){    std::queue<GraphNode *> Q;    Q.push(node);    visit[node->label] = 1;    while(!Q.empty()){        GraphNode *node = Q.front();        Q.pop();        printf("%d ", node->label);        for (int i = 0; i < node->neighbors.size(); i++){            if (visit[node->neighbors[i]->label] == 0){                Q.push(node->neighbors[i]);                visit[node->neighbors[i]->label] = 1;            }        }    }}

leetcode题目

207. Course Schedule

题意:给出课程之间的依赖关系,求是否可以将所有的课程全部完成。其实就是求有向图是否有环。

解题思路一:

  • 用深度优先搜索,如果递归搜索某一条路径时发现路径中有重复的节点,则有环,不能完成。
  • 这里的visit的数组要设计三种状态:-1是未被搜索,0是正在被搜索的路径上,1是已经完成搜索的节点。
  • 像递归二叉树思考方式一样,思考先序中序后序遍历要进行什么操作:
    • 先序遍历:将该节点标记状态visit为0
    • 中序遍历:如果访问过程中出现访问到visit为0的点则返回false
    • 后续遍历:访问完成将该节点标记状态visit为-1

代码:

struct GraphNode{    int label;    std::vector<GraphNode *> neighbors;    GraphNode(int x) : label(x) {};};bool DFS_graph(GraphNode *node, std::vector<int> &visit){    visit[node->label] = 0;    for (int i = 0; i < node->neighbors.size(); i++){        if (visit[node->neighbors[i]->label] == -1){            if (DFS_graph(node->neighbors[i], visit) == 0){                return false;            }        }        else if (visit[node->neighbors[i]->label] == 0){            return false;        }    }    visit[node->label] = 1;    return true;}class Solution {public:    bool canFinish(int numCourses,        std::vector<std::pair<int, int> >& prerequisites) {        std::vector<GraphNode*> graph;        std::vector<int> visit;        for (int i = 0; i < numCourses; i++){            graph.push_back(new GraphNode(i));            visit.push_back(-1);        }        for (int i = 0; i < prerequisites.size(); i++){            GraphNode *begin = graph[prerequisites[i].second];            GraphNode *end = graph[prerequisites[i].first];            begin->neighbors.push_back(end);        }        for (int i = 0; i < graph.size(); i++){            if (visit[i] == -1 && !DFS_graph(graph[i], visit)){                return false;            }        }        for (int i = 0; i < numCourses; i++){            delete graph[i];        }        return true;    }};

解题思路二:

用广度优先搜索,用入度的概念,每次只将入度为0的点压入队列,它指向的所有节点的入度都-1,-1后入度为0的点可以压入队列,如果能遍历整个图,则可以完成,不能遍历则不能完成。

代码:

struct GraphNode{    int label;    std::vector<GraphNode *> neighbors;    GraphNode(int x) : label(x) {};};class Solution {public:    bool canFinish(int numCourses,        std::vector<std::pair<int, int> >& prerequisites) {        std::vector<GraphNode*> graph;        std::vector<int> degree;        for (int i = 0; i < numCourses; i++){            degree.push_back(0);            graph.push_back(new GraphNode(i));        }        for (int i = 0; i < prerequisites.size(); i++){            GraphNode *begin = graph[prerequisites[i].second];            GraphNode *end = graph[prerequisites[i].first];            begin->neighbors.push_back(end);            degree[prerequisites[i].first]++;        }               std::queue<GraphNode *> Q;        for (int i = 0; i < numCourses; i++){            if (degree[i] == 0){                Q.push(graph[i]);            }        }        while(!Q.empty()){            GraphNode *node = Q.front();            Q.pop();            for (int i = 0; i < node->neighbors.size(); i++){                degree[node->neighbors[i]->label]--;                if (degree[node->neighbors[i]->label] == 0){                    Q.push(node->neighbors[i]);                }            }        }               for (int i = 0; i < graph.size(); i++){            delete graph[i];        }               for (int i = 0; i < degree.size(); i++){            if (degree[i]){                return false;            }        }        return true;    }};
原创粉丝点击