实现二叉树前、中、后序以及按层遍历
1、递归版:
package codingTest5;
public class PreInPosTraversal {
public static class Node{
int val;
Node left;
Node right;
public Node() {}
public Node(int data) {
this.val = data;
}
}
public static void PreTraversal(Node head) {
if(head == null) {
return;
}
System.out.print(head.val + " ");
PreTraversal(head.left);
PreTraversal(head.right);
}
public static void InTraversal(Node head) {
if(head == null) {
return;
}
InTraversal(head.left);
System.out.print(head.val + " ");
InTraversal(head.right);
}
public static void PosTraversal(Node head) {
if(head == null) {
return;
}
PosTraversal(head.left);
PosTraversal(head.right);
System.out.print(head.val + " ");
}
public static void main(String[] args) {
Node head = new Node(3);
head.left = new Node(4);
head.left.left = new Node(5);
head.right = new Node(6);
head.right.right = new Node(7);
PreTraversal(head);
System.out.println("----");
Node head2 = new Node(3);
head2.left = new Node(4);
head2.left.left = new Node(5);
head2.right = new Node(6);
head2.right.right = new Node(7);
InTraversal(head2);
System.out.println("----");
Node head3 = new Node(3);
head3.left = new Node(4);
head3.left.left = new Node(5);
head3.right = new Node(6);
head3.right.right = new Node(7);
PosTraversal(head3);
System.out.println("----");
}
}
2、非递归版
使用栈结构的原因:
当前节点走向左孩子,走向右孩子,只有从上到下,从左到右,而没有逆序的方式。而栈具有天然的“先进后出”的特性,因此选择它。
1)先序遍历:
先序遍历是:根--左--右
如果一开始头结点不为空:
首先新建一个栈,把根节点入栈;
当栈不为空时,
每次都使栈顶元素弹出,打印该元素的值。
并访问该元素,如果该元素的右子树存在,就先将右元素入栈;如果该元素的左子树存在,就将该元素入栈。这样弹出的
时候,就是先弹左再弹右。
2)中序遍历:
中序遍历是:左--根--右
如果一开始头结点不为空:
新建一个栈,
如果栈不为空,且当前节点不为空,就继续循环:
(1)当前节点不为空,则当前节点入栈;当前节点往它的左子节点移动;
(2)当前节点为空,则弹出栈顶元素作为当前的头结点,打印该结点的值,并将当前节点往其右子节点移动。
3)后序遍历:
后序遍历是:左--右--根
前序(根--左--右)遍历入栈的顺序是先右再左。此时我们想到的是,可以反过来,先左再右。这样就可以做到中--左--右。
这样把这个存到栈里,再一一弹出,就可以得到我们后序遍历所需的“左--右--根”。每次先不打印,而是先把要打印的数,存到
一个栈里,然后依次弹出并打印栈中元素。
如果一开始头结点不为空:
准备两个栈:
栈1,头结点先存入栈1。
只要栈1不为空时,弹出栈中元素,并将该元素存入栈2。左不为空先压左,右不为空再压右,直到结束循环。
当栈2不为空时,逐个弹出栈内元素。
package codingTest5;
import java.util.Stack;
import codingTest5.PreInPosTraversal.Node;
public class PreInPosTraversal2 {
private static void PreTraversal(Node head) {
System.out.println("PosTraversal:");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
stack.push(head);
while (!stack.isEmpty()) {
head = stack.pop();
System.out.print(head.val);
if (head.right != null) {
stack.push(head.right);
}
if (head.left != null) {
stack.push(head.left);
}
}
}
System.out.println();
}
private static void InTraversal(Node head) {
System.out.println("InTraversal:");
if(head != null) {
Stack<Node> stack = new Stack<Node>();
while(!stack.isEmpty() || head != null) {
if(head != null) {
stack.push(head);
head = head.left;
}else {
head= stack.pop();
System.out.print(head.val);
head = head.right;
}
}
System.out.println();
}
}
private static void PosTraversal(Node head) {
System.out.println("PosTraversal:");
if(head != null) {
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.push(head);
while(!stack1.isEmpty()) {
head = stack1.pop();
stack2.push(head);
if(head.left != null) {
stack1.push(head.left);
}
if(head.right != null) {
stack1.push(head.right);
}
}
while(!stack2.isEmpty()) {
System.out.print(stack2.pop().val);
}
}
System.out.println();
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right = new Node(3);
head.right.left = new Node(6);
head.right.right = new Node(7);
PreTraversal(head);
System.out.println("----");
Node head2 = new Node(1);
head2.left = new Node(2);
head2.left.left = new Node(4);
head2.left.right = new Node(5);
head2.right = new Node(3);
head2.right.left = new Node(6);
head2.right.right = new Node(7);
InTraversal(head2);
System.out.println("----");
Node head3 = new Node(1);
head3.left = new Node(2);
head3.left.left = new Node(4);
head3.left.right = new Node(5);
head3.right = new Node(3);
head3.right.left = new Node(6);
head3.right.right = new Node(7);
PosTraversal(head3);
System.out.println("----");
}
}
层次遍历
BFS
package codingTest5;
import java.util.LinkedList;
import java.util.Queue;
import codingTest5.PreInPosTraversal.Node;
public class levelTraversal {
public static class Node{
int val;
Node left;
Node right;
public Node() {}
public Node(int data) {
this.val = data;
}
}
public static void levelTraversal(Node node) {
Queue<Node> queue = new LinkedList<Node>();
if(node != null) {
queue.add(node);
while(!queue.isEmpty()) {
Node now = queue.poll();
System.out.print(now.val + " ");
if(now.left != null) {
queue.add(now.left);
}
if(now.right != null) {
queue.add(now.right);
}
}
}
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right = new Node(3);
head.right.left = new Node(6);
head.right.right = new Node(7);
levelTraversal(head);
}
}
验证一个数是否为搜索二叉树:
1、搜索二叉树:
二叉搜索树上任意一个节点为头的子树,左子树都比它小,右子树都比它大。即有:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
在中序遍历下,二叉树的节点数值是依次升序的,那么这种树就是搜索二叉树。(通常搜索二叉树中不包含重复节点)
所以可以考虑改一个非递归版的中序遍历,在遍历的过程中,保证其升序即可。将打印的时机,替换为比较的时机即可。用有一个变量记录上一个变量是啥。就可以判断,中序遍历的过程中,是否是升序的。
package codingTest5;
import java.util.Stack;
import codingTest5.precursorNode.Node;
public class isValidBST {
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
public static boolean isValidBST(TreeNode node) {
if(node == null) {
return true;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode cur = node;
TreeNode pre = null;
while(!stack.isEmpty() || cur != null) {
if(cur != null) {
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
if(pre != null && cur.val <= pre.val) {
return false;
}
pre = cur;
cur = cur.right;
}
}
return true;
}
在二叉树中寻找某个节点的后继、前驱节点:
1、定义:
后继节点:在二叉树中序遍历的序列中,一个节点的下一个节点叫做它的后继节点;
前驱节点:在二叉树中序遍历的序列中,一个节点的前一个节点叫做它的前驱节点。
2、节点的结构
Node类中包含一个Node类型的parent指针。每一个节点可以通过Node指针找到它的父节点。(头结点的父节点总会指向null)
3、寻找后继节点:
如果一个节点有右子树,那么这个节点的后继节点,一定是右子树上最左边的节点;
如果一个节点没有右子树,那么这个节点的后继节点,则:
假设当前节点是其父节点的左孩子,那么其父节点就是当前节点的后继节点;
假设当前节点是其父节点的右孩子,那么就逐个向上找,直到找到当前节点是其父节点的左孩子时,这个父节点就是起始节点的后继。
package codingTest5;
public class successorNode {
public static class Node{
int val;
Node left;
Node right;
Node parent;
public Node(int data) {
this.val = data;
}
}
public static Node getSuccessorNode(Node node) {
if(node == null) {
return node;
}
if(node.right != null) {
return getLeftMost(node.right);
}else {
Node parent = node.parent;
while(parent != null && parent.left != node ) {
node = parent;
parent = node.parent;
}
return parent;
}
}
private static Node getLeftMost(Node node) {
if(node == null) {
return node;
}
while(node.left != null) {
node = node.left;
}
return node;
}
public static void main(String[] args) {
Node head = new Node(6);
head.parent = null;
head.left = new Node(3);
head.left.parent = head;
head.left.left = new Node(4);
head.left.left.parent = head.left;
head.left.right = new Node(5);
head.left.right.parent = head.left;
head.right = new Node(7);
head.right.parent = head;
Node test = head.left;
System.out.println(test.val + " next: " + getSuccessorNode(test).val);
Node test2 = head.right;
System.out.println(test2.val + " next: " + getSuccessorNode(test2));
}
}
4、寻找前驱节点:
如果一个节点有左子树,那么这个节点的前驱节点,一定是左子树上最右边的节点;
如果一个节点没有左子树,那么这个节点的前驱节点,则:
假设当前节点是其父节点的右孩子,则该父节点就是当前节点的前驱节点;
假设当前节点是其父节点的左孩子,则逐个往上寻找,知道找到当前节点是其父孩子的右孩子时,这个父节点就是起始节点的前驱。
package codingTest5;
import codingTest5.successorNode.Node;
public class precursorNode {
public static class Node{
int val;
Node parent;
Node left;
Node right;
public Node(int data) {
this.val = data;
}
}
public static Node precursorNode(Node node){
if(node == null) {
return node;
}
if(node.left != null) {
return getMostRight(node.left);
}else {
Node parent = node.parent;
while(parent != null && parent.right != node) {
node = parent;
parent = node.parent;
}
return parent;
}
}
public static Node getMostRight(Node node) {
if(node == null) {
return null;
}
while(node.right != null) {
node = node.right;
}
return node;
}
public static void main(String[] args) {
Node head = new Node(6);
head.parent = null;
head.left = new Node(3);
head.left.parent = head;
head.left.left = new Node(4);
head.left.left.parent = head.left;
head.left.right = new Node(5);
head.left.right.parent = head.left;
head.right = new Node(7);
head.right.parent = head;
Node test = head.left.left;
System.out.println(test.val + " before: " + precursorNode(test));
Node test1 = head.left;
System.out.println(test1.val + " before: " + precursorNode(test1).val);
Node test2 = head.left.right;
System.out.println(test2.val + " before: " + precursorNode(test2).val);
Node test3 = head;
System.out.println(test3.val + " before: " + precursorNode(test3).val);
Node test4 = head.right;
System.out.println(test4.val + " before: " + precursorNode(test4).val);
}
}
leetcode
1、Maximum Depth of Binary Tree(二叉树的最大深度)
package codingTest5;
import java.util.LinkedList;
import java.util.Queue;
/*
*
* 给定一个二叉树,找出其最大深度。
* 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
*
* */
public class maxDepthofBinaryTree {
public static class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int data) {
this.val = data;
}
}
//非递归版
//利用层次遍历,记录总层数,即为二叉树的总深度
public static int maxDepthofBinaryTree2(TreeNode root) {
if(root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int res = 0;
queue.offer(root);
while(!queue.isEmpty()) {
++res;
for(int i = queue.size(); i > 0; --i) {
TreeNode t = queue.poll();
if(t.left != null) {
queue.offer(t.left);
}
if(t.right != null) {
queue.offer(t.right);
}
}
}
return res;
}
//递归版
public static int maxDepthofBinaryTree1(TreeNode root) {
//为什么之前这里写成:
//int left, right=0;
//while(root != null){
//left = maxDepthofBinaryTree(root.left);
//right = maxDepthofBinaryTree(root.right);
//}
//就不对呢?
if(root == null) {
return 0;
}
int left = maxDepthofBinaryTree1(root.left);
int right = maxDepthofBinaryTree1(root.right);
int maxDepth = left >= right? left + 1: right + 1;
return maxDepth;
}
public static void main(String[] args) {
TreeNode head = new TreeNode(5);
head.left = new TreeNode(6);
head.left.left = new TreeNode(6);
head.right = new TreeNode(1);
System.out.println(1);
//System.out.print(maxDepthofBinaryTree1(head));
System.out.print(maxDepthofBinaryTree2(head));
}
}
2、Invert Binary Tree(翻转二叉树)
package codingTest5;
import java.util.LinkedList;
import java.util.Queue;
public class InvertBinaryTree {
public class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int data) {
this.val = data;
}
}
//递归版本:
//先序遍历这棵树的每个节点,如果遍历到的节点有子节点,就交换它的两个子节点;
//当交换完所有非叶节点的左右子节点后,就可以的得到树的镜像。
//之前一直有bug,是因为我还写了这样一句判断:
//if(root.left == null || root.right == null){
// return null;
//}
//可是注意:当根节点只有一个数,且没有左右节点的时候,它就直接返回null了,这是不对的!应该返回该节点自身!
// public static TreeNode invertTree(TreeNode root) {
// if(root == null) {
// return null;
// }
//
// TreeNode temp = root.left;
// root.left = root.right;
// root.right = temp;
//
// if(root.left != null) {
// invertTree(root.left);
// }
//
// if(root.right != null) {
// invertTree(root.right);
// }
//
// return root;
// }
//非递归版
//使用一个队列来辅助遍历树
//按层遍历树的节点,一边遍历一边交换当前节点的左右节点
//层次遍历,根节点不为 null 将根节点入队,
//判断队不为空时,节点出队,
//交换该节点的左右孩子,如果左右孩子不为空,将左右孩子入队
public static TreeNode invertTree(TreeNode root) {
if(root == null) {return null;}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()) {
TreeNode current = queue.poll();//移除并返问队列头部的元素
TreeNode temp = current.left;
current.left = current.right;
current.right = temp;
if(current.left != null) {
queue.add(current.left);
}
if(current.right != null) {
queue.add(current.right);
}
}
return root;
}
}
3、Validate Binary Search Tree(验证二叉查找树) 与上面复习的部分一样,不再赘述
4、Path Sum(路径总和)
package codingTest5;
import codingTest5.maxDepthofBinaryTree.TreeNode;
/**
*
*给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
*说明: 叶子节点是指没有子节点的节点。
*示例:
*给定如下二叉树,以及目标和 sum = 22,
5.
/ \
4. 8
/ / \
11. 13 4
/ \ \
7 2. 1
*返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
*
* */
public class PathSum {
public static class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int data) {
this.val = data;
}
}
public static boolean hasPathSum(TreeNode root, int sum) {
if(root == null) {
return false;
}
if(root.val == sum && root.left == null && root.right == null) {
return true;
}else {
return(hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val));
}
}
public static void main(String[] args) {
TreeNode head = new TreeNode(5);
head.left = new TreeNode(6);
head.left.left = new TreeNode(6);
head.right = new TreeNode(1);
}
}