文章目录
- leetcode102-109(递归)
- [102. 二叉树的层次遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/)
- [107. 二叉树的层次遍历 II](https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/)
- [105. 从前序与中序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- [106. 从中序与后序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)
- [108. 将有序数组转换为二叉搜索树](https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/)
- [109. 有序链表转换二叉搜索树](https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/)
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;
}
};
-
Construct queue (public member function )
-
Test whether container is empty (public member function )
-
Return size (public member function )
-
Access next element (public member function )
-
Access last element (public member function )
-
Insert element (public member function )
-
Construct and insert element (public member function )
-
Remove next element (public member function )
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
: NULLlength==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());
}
};
双指针法访问链表中间元素
- 由于我们得到的是一个有序链表而不是数组,我们不能直接使用下标来访问元素。我们需要知道链表中的中间元素。
- 我们可以利用两个指针来访问链表中的中间元素。假设我们有两个指针
slow_ptr
和fast_ptr
。slow_ptr
每次向后移动一个节点而fast_ptr
每次移动两个节点。当fast_ptr
到链表的末尾时slow_ptr
就访问到链表的中间元素。对于一个偶数长度的数组,中间两个元素都可用来作二叉搜索树的根。 - 当找到链表中的中间元素后,我们将链表从中间元素的左侧断开,做法是使用一个
prev_ptr
的指针记录slow_ptr
之前的元素,也就是满足prev_ptr.next
=slow_ptr
。断开左侧部分就是让prev_ptr.next = None
。 - 我们只需要将链表的头指针传递给转换函数,进行高度平衡二叉搜索树的转换。所以递归调用的时候,左半部分我们传递原始的头指针;右半部分传递
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;
}