基本概念

深度优先搜索:Depth First Search, DFS
广度优先搜索:Breadth First Search, BFS
路径搜索的两个基本思路,一个以深度优先(一条路走到黑),另一个以广度优先(每个分岔口都走一下看看)
主要可以参考二叉树的遍历:

  • 前序遍历、中序遍历、后序遍历属于深度优先搜索DFS
  • 层次遍历属于广度优先搜索BFS

104. 二叉树的最大深度

方法一:递归

  1. class Solution {
  2. public:
  3. int maxDepth(TreeNode* root) {
  4. if(root==NULL)
  5. {
  6. return 0;
  7. }
  8. else
  9. {
  10. int left_height = maxDepth(root->left);
  11. int right_height = maxDepth(root->right);
  12. return max(left_height,right_height)+1;
  13. }
  14. }
  15. };

方法二:迭代

我们还可以在栈的帮助下将上面的递归转换为迭代。
我们的想法是使用 DFS 策略访问每个结点,同时在每次访问时更新最大深度。
所以我们从包含根结点且相应深度为 1 的栈开始。然后我们继续迭代:将当前结点弹出栈并推入子结点。每一步都会更新深度

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  8. * };
  9. */
  10. class Solution {
  11. public:
  12. int maxDepth(TreeNode* root) {
  13. queue<TreeNode*>q;//队列, q是队列对象
  14. if(root!=nullptr){//用nullptr 不用NULL, 这样不会出现构造函数int 空,精确为空指针
  15. q.push(root); //此时q队列 size为1,只有根节点
  16. }
  17. int depth = 0; //初始depth
  18. while(q.size()!=0){ //遍历我们的队列
  19. int n = q.size(); // n等于 队列里面元素的个数
  20. for(int i=0; i<n; i++){ // 把队列里面每个元素都进行处理
  21. TreeNode* tr = q.front(); //把队列第一个元素赋值给tr
  22. q.pop(); //把队列第一个元素弹出, 先进先出原则
  23. if(tr->left!=nullptr){ //如果该元素有左子结点,则把左子结点推入队列q
  24. q.push(tr->left);
  25. }
  26. if(tr->right!=nullptr){//如果有右子结点,则把右子结点推入队列,
  27. q.push(tr->right);
  28. } //for循环会把本层树的所有结点都作为root遍历他们各自的左右子,然后把该层结点都弹出(在if之前弹出, 弹出前把该元素赋值给tr,每次tr都会更新)
  29. }
  30. depth++;//一层处理完,depth++, 深度+1 , 非常好理解
  31. }//直到都没有左右子结点, q则没有push元素进来了,则跳出,返回depth
  32. return depth;
  33. }
  34. };

144. 二叉树的前序遍历(mdium)

方法一:递归

  1. class Solution {
  2. public:
  3. vector<int> preorderTraversal(TreeNode* root) {
  4. vector<int> result;
  5. preorder(root,result);
  6. return result;
  7. }
  8. void preorder(TreeNode* root, vector<int> &result)
  9. {
  10. if(root)
  11. {
  12. result.push_back(root->val);
  13. preorder(root->left,result);
  14. preorder(root->right,result);
  15. }
  16. }
  17. };

方法二:迭代

  1. vector<int> preorderTraversal(TreeNode* root) {
  2. vector<int> res;
  3. // 空树
  4. if(root == NULL)
  5. return res;
  6. // 开始遍历
  7. stack<TreeNode*> stack;
  8. stack.push(root);
  9. while(!stack.empty()){
  10. // 取出栈顶
  11. root = stack.top();
  12. stack.pop();
  13. // 右结点先入栈(后访问)
  14. if(root->right != NULL)
  15. stack.push(root->right);
  16. // 左节点后入栈(先访问)
  17. if(root->left != NULL)
  18. stack.push(root->left);
  19. // 访问
  20. res.push_back(root->val);
  21. }
  22. return res;

94. 二叉树的中序遍历(mdium)

方法一:递归

  1. void inorder(TreeNode* root, vector<int> &result){
  2. if(root != NULL){
  3. inorder(root->left, result);
  4. result.push_back(root->val); // !!!
  5. inorder(root->right, result);
  6. }
  7. }
  8. vector<int> inorderTraversal(TreeNode* root) {
  9. vector<int> result;
  10. inorder(root, result);
  11. return result;
  12. }

方法二:迭代

  1. vector<int> inorderTraversal(TreeNode* root) {
  2. vector<int> res;
  3. // 空树
  4. if(root == NULL)
  5. return res;
  6. // 开始遍历
  7. stack<TreeNode*> stack;
  8. do{
  9. // 找到以root为根的子树的最左节点
  10. while(root != NULL){
  11. stack.push(root);
  12. root = root->left;
  13. }
  14. // 以root为根的子树的最左节点如果存在
  15. if(!stack.empty()){
  16. // 弹出栈顶
  17. root = stack.top();
  18. stack.pop();
  19. // 访问
  20. res.push_back(root->val);
  21. // 进入右结点
  22. root = root->right;
  23. }
  24. }while(root != NULL || !stack.empty());
  25. // 返回结果
  26. return res;
  27. }

145. 二叉树的后序遍历(hard)

左->右->中——这个访问顺序不易实现,需要记录已访问的节点
反序遍历(中->右->左)+翻转结果是一种作弊解法,并不是真正意义上的后序遍历
https://leetcode-cn.com/problems/binary-tree-postorder-traversal
可以与前序遍历比较一下——

  1. 每次循环取出栈顶的时候不直接出栈,只有左右节点都访问过了才能出栈
  2. 如果左/右节点未访问过,才会进入对应分支
  3. 只有左右节点都访问过了或者不存在,才能访问当前节点

    方法一:递归

    1. class Solution {
    2. public:
    3. vector<int> postorderTraversal(TreeNode* root) {
    4. vector<int> result;
    5. postorder(root,result);
    6. return result;
    7. }
    8. void postorder(TreeNode* root, vector<int>& result)
    9. {
    10. if(root!=NULL)
    11. {
    12. postorder(root->left,result);
    13. postorder(root->right,result);
    14. result.push_back(root->val);
    15. }
    16. }
    17. };

    方法二:迭代

    1. // 可以根前序遍历比较一下
    2. vector<int> postorderTraversal(TreeNode* root) {
    3. vector<int> res;
    4. unordered_set<TreeNode*> visited;
    5. // 空树
    6. if(root == NULL)
    7. return res;
    8. // 开始遍历
    9. stack<TreeNode*> stack;
    10. stack.push(root);
    11. while(!stack.empty()){
    12. bool left_visited = true, right_visited = true;
    13. // 取出栈顶(但不弹出)
    14. root = stack.top();
    15. // 如果右结点未访问,则入栈(先入栈后访问)
    16. if(root->right != NULL && visited.find(root->right) == visited.end()){
    17. right_visited = false;
    18. stack.push(root->right);
    19. }
    20. // 如果左节点未访问,则入栈(后入栈先访问)
    21. if(root->left != NULL && visited.find(root->left) == visited.end()){
    22. left_visited = false;
    23. stack.push(root->left);
    24. }
    25. // 如果左右节点都访问过了,那么可以访问本节点
    26. if(left_visited && right_visited){
    27. visited.insert(root);
    28. res.push_back(root->val);
    29. stack.pop();
    30. }
    31. }
    32. return res;
    33. }

    102. 二叉树的层序遍历

    方法一:宽度优先搜索

  • 首先根元素入队
  • 当队列不为空的时候

    • 求当前队列的长度 si_s__i
    • 依次从队列中取 si_s__i 个元素进行拓展,然后进入下一次迭代

      1. class Solution {
      2. public:
      3. vector<vector<int>> levelOrder(TreeNode* root) {
      4. vector <vector <int>> ret;
      5. if (!root) return ret;
      6. queue <TreeNode*> q;
      7. q.push(root);
      8. while (!q.empty()) {
      9. int currentLevelSize = q.size();
      10. ret.push_back(vector <int> ());
      11. for (int i = 1; i <= currentLevelSize; ++i) {
      12. auto node = q.front(); q.pop();
      13. ret.back().push_back(node->val);
      14. if (node->left) q.push(node->left);
      15. if (node->right) q.push(node->right);
      16. }
      17. }
      18. return ret;
      19. }
      20. };