树的概念
定义树的方式是使用递归的方式。
一棵树就是一些点的集合。这个集合可以是空集;也可以是非空集,若不是空集,则树由称作根的节点r以及0个或多个非空子树T1,T2…组成这些子树中的每一棵的根都被来自根r的一条有向边所连结。
每一棵子树的根叫做根r的儿子,而r是每一棵子树的根的父亲。
没有儿子的节点被称为叶子。
对于节点n,n的深度即为从根到n的唯一的路径长度。
树的实现
实现树的一种方式可以是在每一个节点除数据外还要有一些链,使得该结点的每一个儿子都有一个链指向他。
二叉树的节点:
/**
* @program: 手搓代码
* @description: 树的节点
* @author: 郑畅道
* @create: 2020-04-14 17:03
**/
public class TreeNode<E> {
/**
* 当前节点存储的元素
*/
private E element;
/**
* 父节点
*/
TreeNode<E> previousNode;
/**
* 左孩子
*/
TreeNode<E> nextLeftNode;
/**
* 右孩子
*/
TreeNode<E> nextRightNode;
public TreeNode() {
}
public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
this.element = element;
this.previousNode = previousNode;
this.nextLeftNode = nextLeftNode;
this.nextRightNode = nextRightNode;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
}
public TreeNode<E> getPreviousNode() {
return previousNode;
}
public void setPreviousNode(TreeNode<E> previousNode) {
this.previousNode = previousNode;
}
public TreeNode<E> getNextLeftNode() {
return nextLeftNode;
}
public void setNextLeftNode(TreeNode<E> nextLeftNode) {
this.nextLeftNode = nextLeftNode;
}
public TreeNode<E> getNextRightNode() {
return nextRightNode;
}
public void setNextRightNode(TreeNode<E> nextRightNode) {
this.nextRightNode = nextRightNode;
}
@Override
public String toString() {
return "TreeNode{" +
"element=" + element +
", previousNode=" + previousNode +
", nextLeftNode=" + nextLeftNode +
", nextRightNode=" + nextRightNode +
'}';
}
}
树的遍历及应用
以此树为例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7RCFDo6O-1587370759729)(E:\宝库\markdown笔记\面试题V2.0\数据结构\1586855795265.png)]
树的遍历方法有三种:
先中后是相对根节点的。
-
先序遍历
先序遍历遵循根→左→右的顺序遍历;
则遍历顺序为A→B→C;
-
中序遍历
中序遍历遵循左→根→右的顺序遍历;
则遍历顺序为B→A→C;
-
后序遍历
中序遍历遵循左→右→根的顺序遍历;
则遍历顺序为B→C→A;
二叉树
二叉树是一棵每个节点的儿子都不多于2个的树。
表达式树
在表达式树中叶子节点为操作数,而其它节点为操作符。因为此处的所有操作都是二元的,所以此树恰为二叉树。
对于表达式树常常使用中序遍历的方式。
节点示例代码:
/**
* @program: 手搓代码
* @description: 树的节点
* @author: 郑畅道
* @create: 2020-04-14 17:03
**/
public class TreeNode<E> {
/**
* 当前节点名字
*/
private String name;
/**
* 当前节点存储的元素
*/
private E element;
/**
* 父节点
*/
private TreeNode<E> previousNode;
/**
* 左孩子
*/
private TreeNode<E> nextLeftNode;
/**
* 右孩子
*/
private TreeNode<E> nextRightNode;
public TreeNode() {
}
public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
this.element = element;
this.previousNode = previousNode;
this.nextLeftNode = nextLeftNode;
this.nextRightNode = nextRightNode;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
}
public TreeNode<E> getPreviousNode() {
return previousNode;
}
public void setPreviousNode(TreeNode<E> previousNode) {
this.previousNode = previousNode;
}
public TreeNode<E> getNextLeftNode() {
return nextLeftNode;
}
public void setNextLeftNode(TreeNode<E> nextLeftNode) {
this.nextLeftNode = nextLeftNode;
}
public TreeNode<E> getNextRightNode() {
return nextRightNode;
}
public void setNextRightNode(TreeNode<E> nextRightNode) {
this.nextRightNode = nextRightNode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 如果直接打印节点的String,则会导致爆栈
* 假设当前节点是A的子节点是B,
* toString时打印子节点,子节点的父节点又是A,此时又会打印A的信息,
* 如此循环...爆栈
*/
@Override
public String toString() {
return "TreeNode{" +
"name='" + name + '\'' +
", element=" + element +
", previousNode=" + (previousNode != null ? previousNode.getName() : "null") +
", nextLeftNode=" + (nextLeftNode != null ? nextLeftNode.getName() : "null") +
", nextRightNode=" + (nextRightNode != null ? nextRightNode.getName() : "null") +
'}';
}
}
树相关操作示例代码:
/**
* @program: 手搓代码
* @description: 二叉树
* @author: 郑畅道
* @create: 2020-04-14 17:26
**/
public class BinTree<E> {
private int size;
private TreeNode rootNode;
private List<TreeNode> nodes = new ArrayList<>();
private List<TreeNode> seq = new ArrayList<>();
public BinTree() {
}
public BinTree(TreeNode rootNode) {
this.rootNode = rootNode;
nodes.add(rootNode);
}
/**
* 添加左孩子
* @param fatherNode 父节点
* @param childNode 孩子节点
* @return 是否添加成功
*/
public boolean addLeft(TreeNode<E> fatherNode, TreeNode<E> childNode){
if(fatherNode == null){
if (rootNode == null){
this.rootNode = childNode;
nodes.add(childNode);
return true;
}
throw new NullPointerException("父节点不可为空");
}
fatherNode.setNextLeftNode(childNode);
childNode.setPreviousNode(fatherNode);
nodes.add(childNode);
return true;
}
/**
* 添加右孩子
* @param fatherNode 父节点
* @param childNode 孩子节点
* @return 是否添加成功
*/
public boolean addRight(TreeNode<E> fatherNode, TreeNode<E> childNode){
if(fatherNode == null){
if (rootNode == null){
this.rootNode = childNode;
nodes.add(childNode);
return true;
}
throw new NullPointerException("父节点不可为空");
}
fatherNode.setNextRightNode(childNode);
childNode.setPreviousNode(fatherNode);
nodes.add(childNode);
return true;
}
/**
* 先序遍历
*/
private void preorderTraversal(TreeNode rootNode){
if (!nodes.contains(rootNode)){
return;
}
seq.add(rootNode);
if (rootNode.getNextLeftNode() != null){
this.preorderTraversal(rootNode.getNextLeftNode());
}
if (rootNode.getNextRightNode() != null){
this.preorderTraversal(rootNode.getNextRightNode());
}
}
/**
* 先序遍历
* @return 遍历结果
*/
public List<TreeNode> getPreorderTraversal(TreeNode treeNode){
this.checkAndClearList();
this.preorderTraversal(treeNode);
return seq;
}
/**
* 中序遍历
*/
private void sequentialTraversal(TreeNode rootNode){
if (!nodes.contains(rootNode)){
return;
}
if (rootNode.getNextLeftNode() != null){
this.sequentialTraversal(rootNode.getNextLeftNode());
}
seq.add(rootNode);
if (rootNode.getNextRightNode() != null){
this.sequentialTraversal(rootNode.getNextRightNode());
}
}
/**
* 中序遍历
* @return 遍历结果
*/
public List<TreeNode> getSequentialTraversal(TreeNode node){
this.checkAndClearList();
this.sequentialTraversal(node);
return seq;
}
/**
* 后序遍历
*/
private void postOrderTraversal(TreeNode rootNode){
if (!nodes.contains(rootNode)){
return;
}
if (rootNode.getNextLeftNode() != null){
this.postOrderTraversal(rootNode.getNextLeftNode());
}
if (rootNode.getNextRightNode() != null){
this.postOrderTraversal(rootNode.getNextRightNode());
}
seq.add(rootNode);
}
/**
* 后序遍历
* @return 遍历结果
*/
public List<TreeNode> getPostOrderTraversal(TreeNode node){
this.checkAndClearList();
this.postOrderTraversal(node);
return seq;
}
public int getSize() {
return size;
}
public List<TreeNode> getNodes() {
return nodes;
}
private void checkAndClearList(){
if (seq.size() != 0){
seq.clear();
}
}
}
测试代码:
/**
* @program: 手搓代码
* @description: 测试
* @author: 郑畅道
* @create: 2020-04-18 21:39
**/
public class BinTreeTest {
public static void main(String[] args) {
TreeNode<String> root = new TreeNode<>();
root.setName("root");
root.setElement("A");
TreeNode<String> c1 = new TreeNode<>();
c1.setName("B");
c1.setElement("B");
TreeNode<String> c2 = new TreeNode<>();
c2.setName("D");
c2.setElement("D");
TreeNode<String> c3 = new TreeNode<>();
c3.setName("E");
c3.setElement("E");
TreeNode<String> c4 = new TreeNode<>();
c4.setName("C");
c4.setElement("C");
TreeNode<String> c5 = new TreeNode<>();
c5.setName("F");
c5.setElement("F");
BinTree<String> binTree = new BinTree<>(root);
binTree.addLeft(root, c1);
binTree.addRight(root, c4);
binTree.addLeft(c1, c2);
binTree.addRight(c1, c3);
binTree.addLeft(c4, c5);
System.out.println("所有节点:");
for (TreeNode cursor :
binTree.getNodes()) {
System.out.println("节点:" + cursor);
}
System.out.println("*********************************************************");
System.out.println("先序遍历:");
for (TreeNode cursor :
binTree.getPreorderTraversal(root)) {
System.out.print(cursor.getElement() + " ");
}
System.out.println();
System.out.println("*********************************************************");
System.out.println("中序遍历:");
for (TreeNode cursor :
binTree.getSequentialTraversal(root)) {
System.out.print(cursor.getElement() + " ");
}
System.out.println();
System.out.println("*********************************************************");
System.out.println("后序遍历:");
for (TreeNode cursor :
binTree.getPostOrderTraversal(root)) {
System.out.print(cursor.getElement() + " ");
}
System.out.println();
System.out.println("*********************************************************");
}
}
输出:
所有节点:
节点:TreeNode{name='root', element=A, previousNode=null, nextLeftNode=B, nextRightNode=C}
节点:TreeNode{name='B', element=B, previousNode=root, nextLeftNode=D, nextRightNode=E}
节点:TreeNode{name='C', element=C, previousNode=root, nextLeftNode=F, nextRightNode=null}
节点:TreeNode{name='D', element=D, previousNode=B, nextLeftNode=null, nextRightNode=null}
节点:TreeNode{name='E', element=E, previousNode=B, nextLeftNode=null, nextRightNode=null}
节点:TreeNode{name='F', element=F, previousNode=C, nextLeftNode=null, nextRightNode=null}
*********************************************************
先序遍历:
A B D E C F
*********************************************************
中序遍历:
D B E A F C
*********************************************************
后序遍历:
D E B F C A
*********************************************************
二叉查找树
使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的值,而它的右子树中所有项的值大于X中的值。这意味着该树所有的元素可以用某种一致的方式排序。
节点示例代码:
/**
* @program: 手搓代码
* @description: 树的节点
* @author: 郑畅道
* @create: 2020-04-14 17:03
**/
public class TreeNode<E>{
/**
* 当前节点名字
*/
private String name;
/**
* 当前节点存储的元素
*/
private E element;
/**
* 父节点
*/
private TreeNode<E> previousNode;
/**
* 左孩子
*/
private TreeNode<E> nextLeftNode;
/**
* 右孩子
*/
private TreeNode<E> nextRightNode;
public TreeNode() {
}
public TreeNode(E element) {
this.element = element;
this.name = element.toString();
}
public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
this.element = element;
this.previousNode = previousNode;
this.nextLeftNode = nextLeftNode;
this.nextRightNode = nextRightNode;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
if (this.name == null){
this.name = element.toString();
}
}
public TreeNode<E> getPreviousNode() {
return previousNode;
}
public void setPreviousNode(TreeNode<E> previousNode) {
this.previousNode = previousNode;
}
public TreeNode<E> getNextLeftNode() {
return nextLeftNode;
}
public void setNextLeftNode(TreeNode<E> nextLeftNode) {
this.nextLeftNode = nextLeftNode;
}
public TreeNode<E> getNextRightNode() {
return nextRightNode;
}
public void setNextRightNode(TreeNode<E> nextRightNode) {
this.nextRightNode = nextRightNode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 如果直接打印节点的String,则会导致爆栈
* 假设当前节点是A的子节点是B,
* toString时打印子节点,子节点的父节点又是A,此时又会打印A的信息,
* 如此循环...爆栈
*/
@Override
public String toString() {
return "TreeNode{" +
"name='" + name + '\'' +
", element=" + element +
", previousNode=" + (previousNode != null ? previousNode.getName() : "null") +
", nextLeftNode=" + (nextLeftNode != null ? nextLeftNode.getName() : "null") +
", nextRightNode=" + (nextRightNode != null ? nextRightNode.getName() : "null") +
'}';
}
}
二叉查找树示例代码:
/**
* @program: 手搓代码
* @description: 二叉查找树
* @author: 郑畅道
* @create: 2020-04-18 22:54
**/
public class BinSearchTree<T extends Comparable<? super T>> {
/**
* 根节点
*/
private TreeNode<T> root;
public BinSearchTree() {
root = null;
}
/**
* 清空树
*/
public void mackEmpty(){
root = null;
}
/**
* 是否为空
* @return 结果
*/
public boolean isEmpty(){
return root == null;
}
/**
* 查找值是否存在
* @param nodeElement 值
* @return 结果
*/
public boolean contains(T nodeElement){
return this.contains(nodeElement, root);
}
/**
* 寻找最小值
* @return 最小值
* @throws Exception 树为空
*/
public T findMin() throws Exception {
if (isEmpty()){
throw new Exception("当前树为空");
}
return findMin(root).getElement();
}
/**
* 寻找最大值
* @return 最大值
* @throws Exception 树为空
*/
public T findMax() throws Exception{
if (isEmpty()){
throw new Exception("当前树为空");
}
return findMax(root).getElement();
}
/**
* 将数值插入到树中
* @param nodeElement 值
*/
public void insert(T nodeElement){
root = insert(nodeElement, root);
}
/**
* 将数值从树中移除
* @param nodeElement 值
*/
public void remove(T nodeElement){
root = remove(nodeElement, root);
}
/**
* 打印整棵树
*/
public void printTree(){
printTreeNode(root);
}
/**
* 比较是否为目标值节点
* @param nodeElement 目标值
* @param node 节点
* @return 结果
*/
private boolean contains(T nodeElement, TreeNode<T> node){
//空节点检测
if(node == null){
return false;
}
//获取当前节点的值与目标值的关系
int res = nodeElement.compareTo(node.getElement());
//根据关系判断递归左子树、右子树还是当前节点
if (res < 0){
return contains(nodeElement, node.getNextLeftNode());
}else if (res > 0){
return contains(nodeElement, node.getNextRightNode());
}else {
return true;
}
}
/**
* 返回最小值节点
* @param node 根节点
* @return 节点
*/
private TreeNode<T> findMin(TreeNode<T> node){
if (node != null){
//左子树一定是最小值
while (node.getNextLeftNode() != null){
node = node.getNextLeftNode();
}
return node;
}else {
return null;
}
}
/**
* 返回最大值节点
* @param node 根节点
* @return 节点
*/
private TreeNode<T> findMax(TreeNode<T> node){
if (node != null){
//右子树一定是最大值
while (node.getNextRightNode() != null){
node = node.getNextRightNode();
}
return node;
}else {
return null;
}
}
/**
* 插入新节点
* @param nodeElement 节点值
* @param node 根节点
* @return 插入的新节点
*/
private TreeNode<T> insert(T nodeElement, TreeNode<T> node){
//检查节点是否为空
if (node == null){
return new TreeNode<>(nodeElement);
}
//检查节点值与当前节点的大小
int res = nodeElement.compareTo(node.getElement());
//递归和替换
if (res > 0){
node.setNextRightNode(
insert(nodeElement, node.getNextRightNode())
);
}else if (res < 0){
node.setNextLeftNode(
insert(nodeElement, node.getNextLeftNode())
);
}
return node;
}
/**
* 移除节点
* @param nodeElement 节点值
* @param node 根节点
* @return 移除的节点
*/
private TreeNode<T> remove(T nodeElement, TreeNode<T> node){
//检查节点是否为空
if (node == null){
return null;
}
//检查节点值与当前节点的大小
int res = nodeElement.compareTo(node.getElement());
//递归和替换
if (res < 0){
node.setNextLeftNode(remove(nodeElement, node.getNextLeftNode()));
}else if (res > 0){
node.setNextRightNode(remove(nodeElement, node.getNextRightNode()));
}else if (node.getNextRightNode() != null && node.getNextLeftNode() != null){
//如果目标节点有孩子,则找出最小的右子树中最小的孩子替换该节点并删除孩子节点
node.setElement(findMax(node.getNextRightNode()).getElement());
node.setNextRightNode(remove(node.getElement(), node.getNextRightNode()));
}else {
//绕过该节点
node = (node.getNextLeftNode() != null ? node.getNextLeftNode() : node.getNextRightNode());
}
return node;
}
private void printTreeNode(TreeNode<T> node){
if (node == null){
return;
}
System.out.println(node);
printTreeNode(node.getNextLeftNode());
printTreeNode(node.getNextRightNode());
}
}
测试代码:
/**
* @program: 手搓代码
* @description: 二叉查找树测试
* @author: 郑畅道
* @create: 2020-04-19 19:38
**/
public class BSTreeTest {
public static void main(String[] args) throws Exception {
List<Integer> integers = new ArrayList<>();
BinSearchTree<Integer> binSearchTree = new BinSearchTree<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
Integer value = random.nextInt(100);
binSearchTree.insert(value);
integers.add(value);
}
System.out.println("插入的值;");
for (int i = 0; i < integers.size(); i++) {
System.out.print(integers.get(i) + " ");
}
System.out.println();
binSearchTree.printTree();
System.out.println("*****************************************");
System.out.println("最大值:" + binSearchTree.findMax());
System.out.println("*****************************************");
System.out.println("最小值:" + binSearchTree.findMin());
System.out.println("*****************************************");
int removeVal = integers.remove(integers.size()/2);
System.out.println("移除" + removeVal + " 后:");
binSearchTree.remove(removeVal);
binSearchTree.printTree();
System.out.println("*****************************************");
}
}
输出:
插入的值;
17 81 87 26 37 20 24 84 32 92
TreeNode{name='17', element=17, previousNode=null, nextLeftNode=null, nextRightNode=81}
TreeNode{name='81', element=81, previousNode=null, nextLeftNode=26, nextRightNode=87}
TreeNode{name='26', element=26, previousNode=null, nextLeftNode=20, nextRightNode=37}
TreeNode{name='20', element=20, previousNode=null, nextLeftNode=null, nextRightNode=24}
TreeNode{name='24', element=24, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='37', element=37, previousNode=null, nextLeftNode=32, nextRightNode=null}
TreeNode{name='32', element=32, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='87', element=87, previousNode=null, nextLeftNode=84, nextRightNode=92}
TreeNode{name='84', element=84, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='92', element=92, previousNode=null, nextLeftNode=null, nextRightNode=null}
*****************************************
最大值:92
*****************************************
最小值:17
*****************************************
移除20 后:
TreeNode{name='17', element=17, previousNode=null, nextLeftNode=null, nextRightNode=81}
TreeNode{name='81', element=81, previousNode=null, nextLeftNode=26, nextRightNode=87}
TreeNode{name='26', element=26, previousNode=null, nextLeftNode=24, nextRightNode=37}
TreeNode{name='24', element=24, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='37', element=37, previousNode=null, nextLeftNode=32, nextRightNode=null}
TreeNode{name='32', element=32, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='87', element=87, previousNode=null, nextLeftNode=84, nextRightNode=92}
TreeNode{name='84', element=84, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='92', element=92, previousNode=null, nextLeftNode=null, nextRightNode=null}
*****************************************