树
树是一个神奇的数据结构……树的每个节点,都存在着多个指针,指向他的下一层,最后一层层下去通向叶子节点。
树是一个由有限个节点组成的一个具有层次关系的集合。他看起来像一棵倒着的树。
树里面的每个节点,会有一个父节点(上一层)和多个孩子节点(下一层)。
当然还会有数据域。。。数据域可以是多个。。
二叉树
其实重点就是二叉树,二叉树指的是,每个节点最多有2个分支,称为左孩子和右孩子。
所以,我们来看一下树的节点类。。。
我写的节点类里,包括了两个数据线(long和string,可以记录‘张三’,24岁之类的信息)
然后为了之后的一些乱七八糟的方法做准备。。。还有一个是标记有没有被访问过
public class Node {
//long数据项
public long data;
//String数据项
public String sData;
//左孩子节点
public Node leftChild;
//右子节点
public Node rightChild;
//是否被访问
public boolean wasVisited;
//构造方法
public Node(long data, String sData) {
this.data = data;
this.sData = sData;
}
}
之后再看一些常用的方法吧。。。
由于树的方法比较多,我分开讲。。。最后合起来的所有代码再合起来一遍放在最后(包括test)
首先是树的建立。。主要是,首先得有一个根节点,然后是插入节点的方法。插入按照排序插入,比父节点小的放在父节点的左边,比父节点大的放在右边,这样做的好处是后面讲的中序遍历可以直接排序。
public class Tree {
//根节点
public Node root;
//插入一个节点
public void insert(long data , String sData) {
//封装节点
Node newNode = new Node(data , sData);
//两个引用
//当前节点
Node current = root;
//父节点
Node parent;
//第一次插入直接赋值
if(current == null) {
root = newNode;
return;
}
//循环左走右走
while (true) {
//父节点指向当前节点
parent = current;
//比较大小左右走
if (current.data > data) {
current = current.leftChild;
if(current == null) {
parent.leftChild = newNode;
return;
}
}
else {
current = current.rightChild;
if(current == null) {
parent.rightChild = newNode;
return;
}
}
}
}
}
下面的方法我就不写calss了,大家主要看一下思路,整体代码在最后面。
这里是根据数据查找节点的算法
//查找节点
public Node find(long data) {
//引用当前节点
Node current = root;
while(current.data != data) {
//进行比较和当前节点大小
if (current.data > data) {
current = current.leftChild;
} else {
current = current.rightChild;
}
//查不到
if(current == null) {
return null;
}
}
return current;
}
这里是一个重点,树的遍历,大家知道树的遍历有4种,前序,中序,后序,层序。
前序:先跟,再左孩子,再右孩子。如果左(右)孩子是一棵子树,就吧左(右)子树当成现在的一颗树,进行跟,左孩子,右孩子的顺序遍历,以此类推。如果不懂的话,我会再写一篇关于三种遍历的文章,大家可以到我文章里看看。
中序:先左孩子,再跟,再右孩子,和上面一样,如果有子树的情况,就吧子树当成当前状态继续中序遍历。
后序:左孩子,右孩子,跟。
层序:从上往下一层层遍历。。。。
这里提供了递归,和非递归的两种实现方法
/*前序遍历
* 1.访问根节点
* 2.递归前序遍历左子树
* 3.递归前序遍历右子树
*/
public void frontOrder(Node localNode) {
if(localNode != null) {
System.out.print(localNode.data + "," + localNode.sData + " ; ");
frontOrder(localNode.leftChild);
frontOrder(localNode.rightChild);
}
}
/*
* 中序遍历
* 1.递归中序遍历左子树
* 2.访问根节点
* 3.递归中序遍历右子树
*/
public void inOrder(Node localNode) {
if (localNode != null) {
inOrder(localNode.leftChild);
System.out.print(localNode.data + "," + localNode.sData + " ; ");
inOrder(localNode.rightChild);
}
}
/*
* 后序遍历
* 1.递归后序遍历左子树
* 2.递归后序遍历右子树
* 3.访问根节点
*/
public void afterOrder(Node localNode) {
if (localNode != null) {
afterOrder(localNode.leftChild);
afterOrder(localNode.rightChild);
System.out.print(localNode.data + "," + localNode.sData + " ; ");
}
}
//层序遍历
public void LevelOrder(Node root) {
Queue<Node> queue = new LinkedList<Node>();
Node now = root;
queue.add(root);
while (!queue.isEmpty()) {
now = queue.poll();
if (now != null) {
System.out.print(now.data + "," + now.sData + " ; ");
}
if (now.leftChild != null) {
queue.add(now.leftChild);
}
if (now.rightChild != null) {
queue.add(now.rightChild);
}
}
}
//利用栈
//非递归遍历树_前序(根左右)
public void FeiFrontOrder(Node root) {
if (root == null) {
return;
}
//定义栈,通过栈存放遍历的节点
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node now = root.leftChild;
while ( !stack.isEmpty()) {
now = stack.pop();
if (now != null) {
System.out.print(now.data + "," + now.sData + " ; ");
stack.push(now.rightChild);
stack.push(now.leftChild);
}
}
}
//非递归遍历树_中序(左根右)
public void FeiInOrder(Node root) {
if (root == null) {
return;
}
//定义栈,通过栈存放遍历的节点
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node now = root.leftChild;
while (now != null || !stack.isEmpty()) {
if (now != null) {
stack.push(now);
now = now.leftChild;
}
else {
now = stack.pop();
System.out.print(now.data + "," + now.sData + " ; ");
now = now.rightChild;
}
}
}
//非递归遍历树_后序(左右根)
public void FeiAfterOrder(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node visited = null;
Node now = root.leftChild;
while (now != null || !stack.isEmpty()) {
while (null != now) {
stack.push(now);
now = now.leftChild;
}
now = stack.pop();
if (now.rightChild == null || now.rightChild.wasVisited) {
System.out.print(now.data + "," + now.sData + " ; ");
now.wasVisited = true;
now = null;
}
else {
stack.push(now);
now = now.rightChild;
}
}
}
理解了遍历的话。。。再看删除操作。。。
删除操作是比较复杂的,之后我会再开一个文章详细说删除操作的内容,这里先告诉大家,删除树的节点的时候,我们得找到一个中序后继节点来替换要删除的节点,以此来保持二叉树插入时候的平衡。代码如下
//寻找中序后继节点
public Node getHouJiNode(Node delNode) {
//定义中序后继节点
Node houji = delNode;
//定义中序后继的父节点
Node houjiparent = delNode;
//当前节点
Node current = delNode.rightChild;
while(current != null) {
houjiparent = houji;
houji = current;
current = current.leftChild;//往左边找
}
//把删除节点替换成终须后继节点
//1.如果删除节点的右节点不是后继节点(正常右左左……找到的)
if(houji != delNode.rightChild) {
//中序后继的父节点的左子节点指向中序后继节点的右子节点(中序后继节点没有左子节点)
houjiparent.leftChild = houji.rightChild;
//后继的右边接上删除的右边,左边接上删除的左边
houji.rightChild = delNode.rightChild;
houji.leftChild = delNode.leftChild;
}
//2.删除节点的右节点就是中序后继节点,右边不用动,吧左边接上就可以
else {
houji.leftChild = delNode.leftChild;
}
return houji;
}
//删除节点delete
public boolean delete(long data) {
//引用当前节点
Node current = root;
//引用当前节点的父节点
Node parent = root;
//是左子节点还是右节点
boolean isLeftChild = true;
//循环找到要删的节点
while (current.data != data) {
parent = current;
if (current.data > data) {
current = current.leftChild;
isLeftChild = true;
} else {
current = current.rightChild;
isLeftChild = false;
}
//没有
if(current == null) {
return false;
}
}
//循环结束,current指向要删除节点,parent指向父节点
//进行删除current节点
//1.如果是叶子节点(没有子节点)
if (current.leftChild == null &¤t.rightChild ==null) {
if (current == root) {
root = null;
}
//如果是左边的节点
else if (isLeftChild) {
parent.leftChild = null;
}
//如果是右边的节点
else {
parent.rightChild = null;
}
}
//2.删除带有一个子节点的节点
else if (current.rightChild == null) {
//子节点是左子节点
if (current == root) {
root = current.leftChild;
}
else if (isLeftChild) {//删除的节点是左节点
//把父节点的左子节点指向删除节点的左子节点(接上去)
parent.leftChild = current.leftChild;
}
else {//删除的节点是右节点
parent.rightChild = current.leftChild;
}
}
else if (current.leftChild == null) {
//子节点是右子节点
if (current == root) {
root = current.rightChild;
}
else if (isLeftChild) {//删除的节点是左节点
parent.leftChild = current.rightChild;
}
else {//删除的节点是右节点
parent.rightChild = current.rightChild;
}
}
//3.删除有2个子节点的节点,需要寻找中续后继节点来替换删除节点
else {
Node houji = getHouJiNode(current);
if (current == root) {
root = houji;
}
else if (isLeftChild) {
parent.leftChild = houji;
}
else {
parent.rightChild = houji;
}
}
return true;
}
最后是一些乱七八糟的操作。。。
先是求k层有多少个,求叶子节点,求树的高度,没什么难度就不说了,主要靠递归遍历然后统计。。。
//求第k层节点的个数
public int GetKLevelNode(Node root, int k) {
if(1 > k) {
return 0;
}
if (1 == k) {
return 1;
}
else {
return GetKLevelNode(root.leftChild, k - 1) + GetKLevelNode(root.rightChild, k - 1);
}
}
//求叶子节点个数
public int GetLeafCount(Node root) {
if (root == null) {
return 0;
}
if (root.leftChild == null && root.rightChild == null) {
return 1;
}else {
return GetLeafCount(root.leftChild) + GetLeafCount(root.rightChild);
}
}
//求树的高度
public int BinTreeHeight(Node root) {
//定义左右子树高度
int leftHight = 0;
int rightHight = 0;
if (null == root.leftChild &&null == root.rightChild) {
return 1;
}
//递归求左右子树高度
leftHight = BinTreeHeight(root.leftChild);
rightHight = BinTreeHeight(root.rightChild);
//取最大值
if (leftHight > rightHight) {
return leftHight + 1;
}
else {
return rightHight + 1;
}
}
下面是检测是否为完全二叉树,这里运用了层序遍历,设置一个boolean值after,完全二叉树层序遍历的话,从某个节点开始就会全是叶子节点,所以运用到这个思路,吧after改为true,这样之后遍历到的节点如果有孩子,就返回false。正常遍历下,如果有右孩子而没有左孩子的也不是完全二叉树,也返回false。具体看代码
//检测一颗树是不是完全二叉树
public boolean IsCompleteBinTree(Node root) {
boolean after = false;//表示层遍历从哪个节点之后需要全是叶子节点
Queue<Node> queue = new LinkedList<Node>();
Node now = root;
queue.add(root);
while (!queue.isEmpty()) {
now = queue.poll();
//如果必须要是叶子节点的时候,则如果有孩子节点就返回false
if (after) {
if (now.leftChild != null || now.rightChild != null) {
return false;
}
}
//如果某个节点有右孩子没有左孩子,false
if (now.leftChild == null && now.rightChild != null) {
return false;
}
if (now.leftChild != null) {
queue.add(now.leftChild);
}
if (now.rightChild != null) {
queue.add(now.rightChild);
}else {
//如果从某个节点开始没有右孩子了,需要之后都是叶子节点
after = true;
}
}
return true;
}
这些是树的基本操作了,其他的更加复杂的操作以后再更。。。下面吧代码都贴上
记得每个类建一个.java文件,方便查看,我这里也是按照每个类有一个.java文件去写的,如果直接拷贝需要吧public去掉
node类:在最上面,已经发了
test:(这里也可以自己去测试,屏蔽的是做了每个内容的测试,所以大家自己看看)
public class TestTree {
public static void main(String[] args) {
Tree aTree = new Tree();
Tree bTree = new Tree();
aTree.insert(25, "e");
aTree.insert(20, "d");
// aTree.insert(28, "c");
// aTree.insert(15, "c");
// aTree.insert(30, "f");
// aTree.insert(6, "c");
// aTree.insert(45, "i");
// aTree.insert(38, "c");
// aTree.insert(32, "c");
// aTree.insert(22, "c");
// aTree.insert(10, "b");
// aTree.insert(77, "c");
// aTree.insert(40, "h");
// aTree.insert(50, "j");
// aTree.insert(18, "c");
// aTree.insert(5, "a");
System.out.println(aTree.GetLeafCount(aTree.root));
System.out.println(aTree.IsCompleteBinTree(aTree.root));
// bTree.root = bTree.CopyBinTree(aTree.root);
// aTree.frontOrder(aTree.root);
// System.out.println();
// bTree.frontOrder(bTree.root);
// aTree.LevelOrder(aTree.root);
// aTree.frontOrder(aTree.root);
// System.out.println();
// aTree.FeiFrontOrder(aTree.root);
// System.out.println();
// aTree.inOrder(aTree.root);
// System.out.println();
// aTree.FeiInOrder(aTree.root);
// System.out.println();
// aTree.afterOrder(aTree.root);
// System.out.println();
// aTree.FeiAfterOrder(aTree.root);
// aTree.insert(35, "g");
// aTree.insert(10, "g");
// aTree.insert(20, "g");
// aTree.insert(15, "g");
// aTree.insert(3, "g");
// aTree.insert(4, "g");
// aTree.insert(90, "g");
// Node node = aTree.find(20);
// System.out.println(node.data + " " + node.sData);
// System.out.println(aTree.root.data);//10
// System.out.println(aTree.root.leftChild.data);//3
// System.out.println(aTree.root.rightChild.data);//20
// System.out.println(aTree.root.rightChild.leftChild.data);//15
// aTree.delete(10);
// aTree.delete(20);
// aTree.frontOrder(aTree.root);
// System.out.println();
// aTree.afterOrder(aTree.root);
// System.out.println();
// aTree.inOrder(aTree.root);
// System.out.println();
// TreeSet<Integer> tree = new TreeSet<Integer>();
//
// tree.add(Integer.valueOf(5));
// tree.add(Integer.valueOf(4));
// tree.add(Integer.valueOf(7));
// tree.add(Integer.valueOf(2));
// tree.add(Integer.valueOf(8));
// tree.add(Integer.valueOf(6));
// tree.add(Integer.valueOf(3));
// tree.add(Integer.valueOf(1));
//
//
// int a = tree.pollLast();
// int b = tree.pollLast();
// System.out.print(a +" " + b);
}
}
tree类(这里400多行建议还是在上面自取所需)
import java.util.Stack;
import java.util.LinkedList;
import java.util.Queue;
//二叉树
public class Tree {
//根节点
public Node root;
//插入一个节点
public void insert(long data , String sData) {
//封装节点
Node newNode = new Node(data , sData);
//两个引用
//当前节点
Node current = root;
//父节点
Node parent;
//第一次插入直接赋值
if(current == null) {
root = newNode;
return;
}
//循环左走右走
while (true) {
//父节点指向当前节点
parent = current;
//比较大小左右走
if (current.data > data) {
current = current.leftChild;
if(current == null) {
parent.leftChild = newNode;
return;
}
}
else {
current = current.rightChild;
if(current == null) {
parent.rightChild = newNode;
return;
}
}
}
}
//二叉树拷贝,返回一个根
public Node CopyBinTree(Node root) {
Node newroot = new Node(root.data, root.sData);
// Node now = newroot;
if (root.leftChild != null) {
// Node lnode = new Node(root.leftChild.data, root.leftChild.sData);
// newroot.leftChild = lnode;
Node lNode = CopyBinTree(root.leftChild);
newroot.leftChild = lNode;
}
if (root.rightChild != null) {
// Node rnode = new Node(root.rightChild.data, root.rightChild.sData);
// newroot.rightChild = rnode;
Node rNode = CopyBinTree(root.rightChild);
newroot.rightChild = rNode;
}
return newroot;
}
//查找节点
public Node find(long data) {
//引用当前节点
Node current = root;
while(current.data != data) {
//进行比较和当前节点大小
if (current.data > data) {
current = current.leftChild;
} else {
current = current.rightChild;
}
//查不到
if(current == null) {
return null;
}
}
return current;
}
/*前序遍历
* 1.访问根节点
* 2.递归前序遍历左子树
* 3.递归前序遍历右子树
*/
public void frontOrder(Node localNode) {
if(localNode != null) {
System.out.print(localNode.data + "," + localNode.sData + " ; ");
frontOrder(localNode.leftChild);
frontOrder(localNode.rightChild);
}
}
/*
* 中序遍历
* 1.递归中序遍历左子树
* 2.访问根节点
* 3.递归中序遍历右子树
*/
public void inOrder(Node localNode) {
if (localNode != null) {
inOrder(localNode.leftChild);
System.out.print(localNode.data + "," + localNode.sData + " ; ");
inOrder(localNode.rightChild);
}
}
/*
* 后序遍历
* 1.递归后序遍历左子树
* 2.递归后序遍历右子树
* 3.访问根节点
*/
public void afterOrder(Node localNode) {
if (localNode != null) {
afterOrder(localNode.leftChild);
afterOrder(localNode.rightChild);
System.out.print(localNode.data + "," + localNode.sData + " ; ");
}
}
//层序遍历
public void LevelOrder(Node root) {
Queue<Node> queue = new LinkedList<Node>();
Node now = root;
queue.add(root);
while (!queue.isEmpty()) {
now = queue.poll();
if (now != null) {
System.out.print(now.data + "," + now.sData + " ; ");
}
if (now.leftChild != null) {
queue.add(now.leftChild);
}
if (now.rightChild != null) {
queue.add(now.rightChild);
}
}
}
//利用栈
//非递归遍历树_前序(根左右)
public void FeiFrontOrder(Node root) {
if (root == null) {
return;
}
//定义栈,通过栈存放遍历的节点
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node now = root.leftChild;
while ( !stack.isEmpty()) {
now = stack.pop();
if (now != null) {
System.out.print(now.data + "," + now.sData + " ; ");
stack.push(now.rightChild);
stack.push(now.leftChild);
}
}
}
//非递归遍历树_中序(左根右)
public void FeiInOrder(Node root) {
if (root == null) {
return;
}
//定义栈,通过栈存放遍历的节点
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node now = root.leftChild;
while (now != null || !stack.isEmpty()) {
if (now != null) {
stack.push(now);
now = now.leftChild;
}
else {
now = stack.pop();
System.out.print(now.data + "," + now.sData + " ; ");
now = now.rightChild;
}
}
}
//非递归遍历树_后序(左右根)
public void FeiAfterOrder(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<Node>();
stack.push(root);
Node visited = null;
Node now = root.leftChild;
while (now != null || !stack.isEmpty()) {
while (null != now) {
stack.push(now);
now = now.leftChild;
}
now = stack.pop();
if (now.rightChild == null || now.rightChild.wasVisited) {
System.out.print(now.data + "," + now.sData + " ; ");
now.wasVisited = true;
now = null;
}
else {
stack.push(now);
now = now.rightChild;
}
}
}
//寻找中序后继节点
public Node getHouJiNode(Node delNode) {
//定义中序后继节点
Node houji = delNode;
//定义中序后继的父节点
Node houjiparent = delNode;
//当前节点
Node current = delNode.rightChild;
while(current != null) {
houjiparent = houji;
houji = current;
current = current.leftChild;//往左边找
}
//把删除节点替换成终须后继节点
//1.如果删除节点的右节点不是后继节点(正常右左左……找到的)
if(houji != delNode.rightChild) {
//中序后继的父节点的左子节点指向中序后继节点的右子节点(中序后继节点没有左子节点)
houjiparent.leftChild = houji.rightChild;
//后继的右边接上删除的右边,左边接上删除的左边
houji.rightChild = delNode.rightChild;
houji.leftChild = delNode.leftChild;
}
//2.删除节点的右节点就是中序后继节点,右边不用动,吧左边接上就可以
else {
houji.leftChild = delNode.leftChild;
}
return houji;
}
//删除节点delete
public boolean delete(long data) {
//引用当前节点
Node current = root;
//引用当前节点的父节点
Node parent = root;
//是左子节点还是右节点
boolean isLeftChild = true;
//循环找到要删的节点
while (current.data != data) {
parent = current;
if (current.data > data) {
current = current.leftChild;
isLeftChild = true;
} else {
current = current.rightChild;
isLeftChild = false;
}
//没有
if(current == null) {
return false;
}
}
//循环结束,current指向要删除节点,parent指向父节点
//进行删除current节点
//1.如果是叶子节点(没有子节点)
if (current.leftChild == null &¤t.rightChild ==null) {
if (current == root) {
root = null;
}
//如果是左边的节点
else if (isLeftChild) {
parent.leftChild = null;
}
//如果是右边的节点
else {
parent.rightChild = null;
}
}
//2.删除带有一个子节点的节点
else if (current.rightChild == null) {
//子节点是左子节点
if (current == root) {
root = current.leftChild;
}
else if (isLeftChild) {//删除的节点是左节点
//把父节点的左子节点指向删除节点的左子节点(接上去)
parent.leftChild = current.leftChild;
}
else {//删除的节点是右节点
parent.rightChild = current.leftChild;
}
}
else if (current.leftChild == null) {
//子节点是右子节点
if (current == root) {
root = current.rightChild;
}
else if (isLeftChild) {//删除的节点是左节点
parent.leftChild = current.rightChild;
}
else {//删除的节点是右节点
parent.rightChild = current.rightChild;
}
}
//3.删除有2个子节点的节点,需要寻找中续后继节点来替换删除节点
else {
Node houji = getHouJiNode(current);
if (current == root) {
root = houji;
}
else if (isLeftChild) {
parent.leftChild = houji;
}
else {
parent.rightChild = houji;
}
}
return true;
}
//求第k层节点的个数
public int GetKLevelNode(Node root, int k) {
if(1 > k) {
return 0;
}
if (1 == k) {
return 1;
}
else {
return GetKLevelNode(root.leftChild, k - 1) + GetKLevelNode(root.rightChild, k - 1);
}
}
//求叶子节点个数
public int GetLeafCount(Node root) {
if (root == null) {
return 0;
}
if (root.leftChild == null && root.rightChild == null) {
return 1;
}else {
return GetLeafCount(root.leftChild) + GetLeafCount(root.rightChild);
}
}
//求树的高度
public int BinTreeHeight(Node root) {
//定义左右子树高度
int leftHight = 0;
int rightHight = 0;
if (null == root.leftChild &&null == root.rightChild) {
return 1;
}
//递归求左右子树高度
leftHight = BinTreeHeight(root.leftChild);
rightHight = BinTreeHeight(root.rightChild);
//取最大值
if (leftHight > rightHight) {
return leftHight + 1;
}
else {
return rightHight + 1;
}
}
//检测一个节点是否在二叉树中
public boolean IsInTree(Node root, Node pNode) {
boolean is = false;
Node aNode = find(pNode.data);
if (aNode.sData.equals(pNode.sData)) {
is = true;
}
return is;
}
//检测一颗树是不是完全二叉树
public boolean IsCompleteBinTree(Node root) {
boolean after = false;//表示层遍历从哪个节点之后需要全是叶子节点
Queue<Node> queue = new LinkedList<Node>();
Node now = root;
queue.add(root);
while (!queue.isEmpty()) {
now = queue.poll();
//如果必须要是叶子节点,则如果有节点就返回false
if (after) {
if (now.leftChild != null || now.rightChild != null) {
return false;
}
}
//如果某个节点有右孩子没有左孩子,false
if (now.leftChild == null && now.rightChild != null) {
return false;
}
if (now.leftChild != null) {
queue.add(now.leftChild);
}
if (now.rightChild != null) {
queue.add(now.rightChild);
}else {
//如果从某个节点开始没有右孩子了,需要之后都是叶子节点
after = true;
}
}
return true;
}
}