二叉树遍历方式
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
- 层序遍历:从上往下、从左到右
具体实现
- 递归遍历:使用递归方式
- 迭代遍历:使用迭代方式实现递归
- morris遍历
线索二叉树
介绍
在N
个节点的二叉树中,每个节点有2
个指针,所以一共有2N
个指针,除根节点以外,每个节点都有一个指针从它的父节点指向它,所以一种使用了N-1
个指针,所以剩下2N-(N-1)
也就是N+1
个空指针;
如果能利用这些空指针域来存放指向该节点的直接前驱或直接后继的指针,则可由此信息直接找到在该遍历次序的前去节点或后继节点,从而比递归遍历提高了速度,节省了建立系统递归栈所使用的存储空间;
这些被重新利用起来的空指针被称为线索(Thread)
,加上了线索的二叉树就是线索二叉树
实现思路
按某种次序遍历二叉树,在遍历过程中用线索取代空指针即可。以中序遍历为例,首先找到中序遍历的开始节点,然后利用线索依次查找后继节点即可。
由于充分的利用了空指针域的空间, 又保证了创建时的一次遍历就可以终生受用前驱、后继的信息,所以在实际问题中,如果所使用的二叉树需要经常遍历或查找节点时需要某种遍历的前驱和后继,线索二叉树是个不错的选择。
二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
解法一:深度优先搜索
思路
从根结点往下查找,先找左子树,直至左子树为空,再找右子树。找出最小的深度并返回+1
class Solution {
public int minDepth(TreeNode root) {
// 深度优先遍历
if(root == null){
return 0;
}
// 寻找叶子结点
if(root.left == null && root.right == null){
return 1;
}
int min = Integer.MAX_VALUE;
if(root.left!=null){
min = Math.min(min,minDepth(root.left));
}
if(root.right!=null){
min = Math.min(min,minDepth(root.right));
}
return min+1;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(log(N))
解法二:广度优先搜索
class Solution {
class QueueNode{
TreeNode node;
int depth;
public QueueNode(TreeNode node,int depth){
this.node = node;
this.depth = depth;
}
}
public int minDepth(TreeNode root) {
// 广度优先搜索
if(root == null){
return 0;
}
Queue<QueueNode> queue = new LinkedList<QueueNode>();
queue.offer(new QueueNode(root,1));
while(!queue.isEmpty()){
QueueNode nodeDepth = queue.poll();
TreeNode node = nodeDepth.node;
int depth = nodeDepth.depth;
if(node.left == null && node.right == null){
return depth;
}
if(node.left!=null){
queue.offer(new QueueNode(node.left,depth+1));
}
if(node.right!=null){
queue.offer(new QueueNode(node.right,depth+1));
}
}
return 0;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
解法一:递归
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
// 递归
return preorderTraversal(res,root);
}
public List<Integer> preorderTraversal(List<Integer> res,TreeNode root){
res.add(root.val);
if(root.left!=null){
preorderTraversal(res,root.left);
}
if(root.right!=null){
preorderTraversal(res,root.right);
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
解法二:迭代
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
// 迭代
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
Stack<TreeNode> s = new Stack<>();
// 根结点入栈
s.push(root);
while(!s.isEmpty()){
TreeNode node = s.pop();
res.add(node.val);
if(node.right!=null){
s.push(node.right);
}
if(node.left!=null){
s.push(node.left);
}
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
解法三:morris遍历
思路
构建前序二叉树的过程中,如果发现前去结点的右指针指向自身,则将线索指针删除。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
// 初始化结果
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
// 定义最右结点
TreeNode mostRight = null;
while(root != null){
mostRight = root.left;
if(mostRight!=null){
// 左子树中寻找最右结点
while(mostRight.right!=null && mostRight.right!=root){
mostRight = mostRight.right;
}
// 判断最右结点
if(mostRight.right == null){
res.add(root.val);
mostRight.right = root;
root = root.left;
continue;
}else{
mostRight.right = null;
}
}else{
res.add(root.val);
}
root = root.right;
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(1)
二叉树的中序遍历
解法一:递归
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
inorderTraversal(res,root);
return res;
}
public void inorderTraversal(List<Integer> res,TreeNode root){
if(root == null){
return;
}
if(root.left!=null){
inorderTraversal(res,root.left);
}
res.add(root.val);
if(root.right!=null){
inorderTraversal(res,root.right);
}
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
解法二:迭代
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<TreeNode>();
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
解法三:morris遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
TreeNode mostRight = null;
while(root != null){
if(root.left !=null){
mostRight = root.left;
while(mostRight.right!=null && mostRight.right != root){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = root; // 建立线索指针
root = root.left; // 根结点左移
}else{
res.add(root.val);
mostRight.right = null; // 删除线索指针
root = root.right;
}
}else{
res.add(root.val);
root = root.right;
}
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(1)
二叉树的后序遍历
解法一:递归
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
postorderTraversal(res,root);
return res;
}
// 递归
public void postorderTraversal(List<Integer> res,TreeNode root){
if(root == null){
return ;
}
if(root.left!=null){
postorderTraversal(res,root.left);
}
if(root.right!=null){
postorderTraversal(res,root.right);
}
res.add(root.val);
}
}
解法二:迭代
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
TreeNode prev = null;
Stack<TreeNode> s = new Stack<>();
while(!s.isEmpty() || root!=null){
// 找到最左结点
while(root!=null){
s.push(root);
root = root.left;
}
root = s.pop();
// 没有右节点则直接弹出
if(root.right == null || root.right==prev){
res.add(root.val);
prev =root;
root =null;
}else{
// 根节点与右结点入栈
s.push(root);
root = root.right;
}
}
return res;
}
}
解法三:morris遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
TreeNode node = root;
TreeNode mostRight = null;
while(root!=null){
mostRight = root.left;
if(mostRight!=null){
while(mostRight.right!=null && mostRight.right!=root){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = root;
root = root.left;
continue;
}else{
mostRight.right = null;
addPath(res,root.left);
}
}
root = root.right;
}
addPath(res,node);
return res;
}
public void addPath(List<Integer> res,TreeNode node){
int count = 0;
while(node!=null){
++count;
res.add(node.val);
node = node.right;
}
int left = res.size()-count,right = res.size()-1;
while(left<right){
int temp = res.get(left);
res.set(left,res.get(right));
res.set(right,temp);
left++;
right--;
}
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(1)
二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
解法一:广度遍历搜索(bfs)
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
// bfs
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> queue = new ArrayDeque<>();
if(root == null){
return res;
}
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
// 队列中右多少元素,循环多少次
int size=queue.size();
for(int i=0;i<size;i++){
root=queue.poll();
list.add(root.val);
if(root.left!=null){
queue.offer(root.left);
}
if(root.right!=null){
queue.offer(root.right);
}
}
res.add(list);
}
return res;
}
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)