leetcode二叉树(BFS)

leetcode102-109(递归)

102. 二叉树的层次遍历

题目描述

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)

Example

给定二叉树: [3,9,20,null,null,15,7],
   3
   / \
  9  20
    /  \
   15   7
[
  [3],
  [9,20],
  [15,7]
]

两种通用的遍历树的策略:

  • 深度优先搜索DFS

    在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。

    深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为先序遍历中序遍历后序遍历

  • 宽度优先搜索BFS

    我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。

solution idea

递归+BFS
  • 层次遍历,用变量level记录当前层数

  • 新的一层只在左边第一个节点处添加

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    vector<vector<int>> res;
public:
    void levelBSTtravel(TreeNode* node,int level)
    {
        vector<int> tmp_res;
        if(level==res.size()) res.push_back(tmp_res);
        res[level].push_back(node->val);
        if(node->left) levelBSTtravel(node->left,level+1);
        if(node->right) levelBSTtravel(node->right,level+1); 
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(!root) return res;
        levelBSTtravel(root,0);
        return res;
    }
};

107. 二叉树的层次遍历 II

题目描述

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

idea

递归+队列存储层
  • queue<TreeNode*> nextLevel 存放下一层的结点

  • BFS对队列中的层节点遍历

  • 递归,从最后一层逐个返回,追加到res

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 /*
 ** 递归
 ** 从最后一层逐个返回,追加到`res`
 ** 队列结构存储每一层的节点,因为队列结构是先入先出
 ** std::queue http://www.cplusplus.com/reference/queue/queue/?kw=queue
 ** queue -> empty;front;push;pop;back
 */
class Solution {
    vector<vector<int>> res;
public:
    void levelNodeTravel(queue<TreeNode*> curLevel)
    {
        vector<int> cur_val;
        queue<TreeNode*> nextLevel;
        while(!curLevel.empty())
        {
            TreeNode* tmpNode=curLevel.front();//队头元素
            curLevel.pop();
            cur_val.push_back(tmpNode->val);
            if(tmpNode->left) nextLevel.push(tmpNode->left);
            if(tmpNode->right) nextLevel.push(tmpNode->right);
        }
        if(!nextLevel.empty()) levelNodeTravel(nextLevel);

        res.push_back(cur_val);
    }
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> nextLevel; //存放下一层的结点
        if(root)
        {
            nextLevel.push(root);
            levelNodeTravel(nextLevel);
        }
        return res;
    }
};

队列的基本操作

  • (constructor)

    Construct queue (public member function )

  • empty

    Test whether container is empty (public member function )

  • size

    Return size (public member function )

  • front

    Access next element (public member function )

  • back

    Access last element (public member function )

  • push

    Insert element (public member function )

  • emplace

    Construct and insert element (public member function )

  • pop

    Remove next element (public member function )

  • swap

105. 从前序与中序遍历序列构造二叉树

题目描述

根据一棵树的前序遍历与中序遍历构造二叉树。

Example

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回:
    3
   / \
  9  20
    /  \
   15   7

idea

递归+BFS
  • 先从先序序列判断根节点,再由根节点确定左右子树

  • 删除先序的根节点遍历下一位(这里通过pre_idx++实现),

  • 递归操作,直至找到叶子节点终止(此时叶子节点的左右区间是空集)

  • 这里需要注意左右区间是左闭右开时,终止条件left==right.

  • 左闭右闭情形,终止条件是left>right

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 /*
 ** 递归
 ** 先从先序序列判断根节点,再由根节点确定左右子树
 ** 删除先序的根节点遍历下一位(这里通过pre_idx++实现),
 ** 递归操作,直至找到叶子节点终止(此时叶子节点的左右区间是空集)
 
 ** 这里需要注意左右区间是左闭右开时,终止条件`left==right`.
 ** 左闭右闭情形,终止条件是`left>right`
 */
class Solution {
    vector<int> preorder;
    map<int,int> inorder_map;
    int pre_idx=0;
public:
    TreeNode* helper(int left,int right)
    {
        if(left==right) return NULL;
        TreeNode* root=new TreeNode(preorder[pre_idx]);
        int inorder_idx=inorder_map[preorder[pre_idx]]; // pre idx -> inorder idx.
        pre_idx++;
        // 连接左右子树
        root->left=helper(left,inorder_idx);
        root->right=helper(inorder_idx+1,right);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        this->preorder=preorder;
        for(int i=0;i<inorder.size();i++) inorder_map[inorder[i]]=i;
        return helper(0,inorder.size());
    }
};

106. 从中序与后序遍历序列构造二叉树

题目描述

根据一棵树的中序遍历与后序遍历构造二叉树

Idea:

递归+BFS
  • 先从后序序列逆向判断根节点,再由根节点确定左右子树

  • 注意:这里应该先构建右子树,在构建左子树,负责会出现数组越界

  • 这是因为post_idx在后序遍历数组往前移动(post_idx–)的时候,先指向右子节点的值

  • 删除先序的根节点遍历下一位(这里通过pre_idx++实现),

  • 递归操作,直至找到叶子节点终止(此时叶子节点的左右区间是空集)

  • 这里需要注意左右区间是左闭右开时,终止条件left==right.

  • 左闭右闭情形,终止条件是left>right

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

  /*
 ** 递归
 ** 先从后序序列逆向判断根节点,再由根节点确定左右子树
 ** 注意:这里应该先构建右子树,在构建左子树,负责会出现数组越界
 ** 这是因为post_idx在后序遍历数组往前移动(post_idx--)的时候,先指向右子节点的值
 
 ** 删除先序的根节点遍历下一位(这里通过pre_idx++实现),
 ** 递归操作,直至找到叶子节点终止(此时叶子节点的左右区间是空集)
 
 ** 这里需要注意左右区间是左闭右开时,终止条件`left==right`.
 ** 左闭右闭情形,终止条件是`left>right`
 */
class Solution {
    vector<int> postorder;
    map<int,int> inorder_map;
    int post_idx;
public:
    TreeNode* treeBuild(int left,int right)
    {
        if(left==right) return NULL;
        TreeNode* root=new TreeNode(postorder[post_idx]);
        int inorder_idx=inorder_map[postorder[post_idx]];
        post_idx--;
        // 构建左右子树
        root->right=treeBuild(inorder_idx+1,right);
        root->left=treeBuild(left,inorder_idx);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        this->postorder=postorder;
        post_idx=postorder.size()-1;
        for(int i=0;i<inorder.size();i++) inorder_map[inorder[i]]=i;
        return treeBuild(0,inorder.size());
    }
};

108. 将有序数组转换为二叉搜索树

题目描述

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

Example:

给定有序数组: [-10,-3,0,5,9],

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

      0
     / \
   -3   9
   /   /
 -10  5

Idea:

递归+BFS
  • BFS(广度优先遍历)

  • 区间左闭右开,left==right说明区间为空,无左右子树

  • 区间长度分为三种情形

    • length==0: NULL
    • length==1: 一个节点
    • length==2: 两个节点(一个作为根节点,另一个成为左叶子节点)
    • length>=3:采用递归,中间值作为根节点。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 /*
 ** BFS(广度优先遍历)
 ** 区间左闭右开,`left==right`说明区间为空,无左右子树
 ** 区间长度分为三种情形
 `length==0`: NULL
 `length==1`: 一个节点
 `length==2`: 两个节点(一个作为根节点,另一个成为左叶子节点)
 `length>=3`:采用递归,中间值作为根节点。
 */
class Solution {
    vector<int> nums;
public:
    TreeNode* levelNodeBST(int left,int right)
    {
        TreeNode* root=new TreeNode;
        int length=right-left;
        if(length==0) return NULL;
        else if(length==1) root->val=nums[(left+right)/2];
        else if(length==2) 
        {
            root->val=nums[(left+right)/2];
            root->left=levelNodeBST(left,(left+right)/2);
        }
        else
        {
            root->val=nums[(left+right)/2];
            root->left=levelNodeBST(left,(left+right)/2);
            root->right=levelNodeBST((left+right)/2+1,right);
        }
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        this->nums=nums;
        return levelNodeBST(0,nums.size());
        
    }
};

109. 有序链表转换二叉搜索树

题目描述

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

Idea

List to vector+递归BFS
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 /*
 ** BFS
 ** List to vector
 ** 递归
 */
class Solution {
    vector<int> nums;
public:
    TreeNode* bstHelper(int left,int right)
    {
        int length=right-left;
        TreeNode* root=new TreeNode(0);
        if(length==0) return NULL;
        else if(length==1) root->val=nums[(left+right)/2];
        else if(length==2)
        {
            root->val=nums[(left+right)/2];
            root->left=bstHelper(left,(left+right)/2);
        }
        else
        {
            root->val=nums[(left+right)/2];
            root->left=bstHelper(left,(left+right)/2);
            root->right=bstHelper((left+right)/2+1,right);
        }
        return root;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        // if(!head) return NULL;
        while(head)   // list 2 vector 
        {
            nums.push_back(head->val);
            head=head->next;
        }
        return bstHelper(0,nums.size());
    }
};
双指针法访问链表中间元素
  1. 由于我们得到的是一个有序链表而不是数组,我们不能直接使用下标来访问元素。我们需要知道链表中的中间元素。
  2. 我们可以利用两个指针来访问链表中的中间元素。假设我们有两个指针 slow_ptrfast_ptrslow_ptr 每次向后移动一个节点而 fast_ptr 每次移动两个节点。当 fast_ptr 到链表的末尾时 slow_ptr 就访问到链表的中间元素。对于一个偶数长度的数组,中间两个元素都可用来作二叉搜索树的根。
  3. 当找到链表中的中间元素后,我们将链表从中间元素的左侧断开,做法是使用一个 prev_ptr 的指针记录 slow_ptr 之前的元素,也就是满足 prev_ptr.next = slow_ptr。断开左侧部分就是让 prev_ptr.next = None
  4. 我们只需要将链表的头指针传递给转换函数,进行高度平衡二叉搜索树的转换。所以递归调用的时候,左半部分我们传递原始的头指针;右半部分传递 slow_ptr.next 作为头指针。
private ListNode findMiddleElement(ListNode head) {

    // The pointer used to disconnect the left half from the mid node.
    ListNode prevPtr = null;
    ListNode slowPtr = head;
    ListNode fastPtr = head;

    // Iterate until fastPr doesn't reach the end of the linked list.
    while (fastPtr != null && fastPtr.next != null) {
        prevPtr = slowPtr;
        slowPtr = slowPtr.next;
        fastPtr = fastPtr.next.next;
    }

    // Handling the case when slowPtr was equal to head.
    if (prevPtr != null) {
        prevPtr.next = null;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值