您的位置:首页 > 编程语言 > Java开发

Java实现二叉树的遍历、添加、删除

2018-01-23 15:08 513 查看

定义

二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。如果不考虑连通性,允许图中有多个连通分量,这样的结构叫做森林。

为什么需要使用二叉树

二叉树,本质上,是对链表和数组的一个折中。

比如,我有一个任务,需要输入 10万个数据(32位整数),然后有两个操作:

1.添加(删除)一个整数。

2.询问第x大的数据。

比如,我给你 1, 8, 13, 10(等等一堆数据)…….

然后我询问第3大的数据,然后我插入 18

然后我询问第4大的数据我再插入 9

我再询问第2大的数据不停的重复1,2重复10万次。。应该如何实现。

你会发现,用有序链表,不行,查找(包括1需要找到对应位置,以及2查找)成本大O(N),但具体这个插入操作成本小O(1)。用有序数组,查找(2的查找)成本小O(1)。但1的插入操作成本很大O(N)。

所以,我们折中使用排序二叉树(二叉树仅仅作为排序二叉树的基础),查找(包括1需要找到对应位置,以及2查找)成本挺小O(logN)。具体这个插入操作成本也挺小O(logN)。具体的应用就是由排序二叉树(由于普通排序二叉树可能会有不平衡的情况)引申出来的红黑树(linux中ext3文件系统管理),avl树“windows对进程地址空间的管理”。

引用于知乎:二叉树可以解决什么问题

二叉查找树

定义

二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。

性质

若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

任意节点的左、右子树也分别为二叉查找树。

没有键值相等的节点(no duplicate nodes)。

Java实现

Node节点类

可以添加多个data值或对象,只要有能进行比较区分的值即可。比如说我用二叉查找树存储person,那么节点中就有一个person的索引,和一个person对象,这里为了方便添加就直接使用了int。

public class Node {
public int data;
public Node rightChild;
public Node leftChild;

public Node(int value) {
data = value;
rightChild = null;
leftChild = null;
}
}


二叉查找树的插入

在插入的时候会比较节点的大小,小的节点放在左边,大的节点放在右边。其中root变量属于二叉树类的属性变量。

public void insertNode(int value) {
Node newNode = new Node(value);
Node curr = root;
if (root == null) {
root = newNode;
} else {
while (true) {
if (curr.data > value) {
if (curr.leftChild == null) {
curr.leftChild = newNode;
return;
} else
curr = curr.leftChild;
} else {
if (curr.rightChild == null) {
curr.rightChild = newNode;
return;
} else
curr = curr.rightChild;
}
}
}
}


查找某个节点

public Node getNode(int value) {
if (root == null) {
return null;
} else {
Node current = root;
while (current.data != value) {
if (current.data > value) {
current = current.leftChild;
} else {
current = current.rightChild;
}
if (current == null)
return null;
}
return current;
}
}


二叉树的遍历(递归版本)

二叉树的遍历百度百科讲的很清楚了,如果有不清楚的可以去看看:二叉树的遍历

下面我们来看Java程序的实现。

/**
* 前序遍历 递归
* @param node
*/
public void frontOrder_Recursion(Node node) {
if (node != null) {
// 输出中间节点
System.out.print(node.data + " ");
// 前序遍历左子树
frontOrder_Recursion(node.leftChild);
// 前
4000
序遍历右子树
frontOrder_Recursion(node.rightChild);
}
}

/**
* 中序遍历 递归
*
* @param node
*/
public void midOrder_Recursion(Node node) {
if (node != null) {
// 中序遍历左子树
midOrder_Recursion(node.leftChild);
// 输出中间节点
System.out.print(node.data + " ");
// 中序遍历右子树
midOrder_Recursion(node.rightChild);
}
}

/**
* 后序遍历 递归
*
* @param node
*/
public void rareOrder_Recursion(Node node) {
if (node != null) {
// 后序遍历左子树
rareOrder_Recursion(node.leftChild);
// 后序遍历右子树
rareOrder_Recursion(node.rightChild);
// 输出中间节点
System.out.print(node.data + " ");
}
}


二叉树的遍历(非递归)

/**
* 前序遍历 非递归
*
* @param node
*/
public void frontOrder_NonRecursion(Node node) {
Stack<Node> nodeStark = new Stack<Node>();
Node curr = node;
if (curr != null) {
nodeStark.push(curr);
while (!nodeStark.isEmpty()) {
curr = nodeStark.pop();
System.out.print(curr.data + " ");
if (curr.rightChild != null)
nodeStark.push(curr.rightChild);
if (curr.leftChild != null) {
nodeStark.push(curr.leftChild);
}
}
}
}

/**
* 中序遍历 非递归
*
* @param node
*/
public void midOrder_NonRecursion(Node node) {
Stack<Node> nodeStark = new Stack<Node>();
Node curr = node;
while (curr != null) {
while (curr != null) {
if (curr.rightChild != null)
nodeStark.push(curr.rightChild);
nodeStark.push(curr);
curr = curr.leftChild;
}
curr = nodeStark.pop();
while (!nodeStark.isEmpty() && curr.rightChild == null) {
System.out.print(curr.data + " ");
curr = nodeStark.pop();
}
System.out.print(curr.data + " ");
if (curr.rightChild != null) {
curr = nodeStark.pop();
} else {
return;
}
}
}

/**
* 中序遍历2 非递归
*
* @param node
*/
public static void midOrder_NonRecursion2(Node t) {
Stack<Node> s = new Stack<Node>();
while (t != null || !s.empty()) {
while (t != null) {
s.push(t);
t = t.leftChild;
}
if (!s.empty()) {
t = s.pop();
System.out.print(t.data + " ");
t = t.rightChild;
}
}
}

/**
* 后续遍历 非递归 单栈
*
* @param p
*/
public void rareOrder_NonRecursion_SingleStack(Node p) {
Stack<Node> stack = new Stack<Node>();
Node node = p;
while (p != null) {
// 左子树入栈
for (; p.leftChild != null; p = p.leftChild) {
stack.push(p);
}
// 当前结点无右子树或右子树已经输出
while (p != null && (p.rightChild == null || p.rightChild == node)) {
System.out.print(p.data + " ");
// 纪录上一个已输出结点
node = p;
if (stack.empty())
return;
p = stack.pop();
}
// 处理右子树
stack.push(p);
p = p.rightChild;
}
}

/**
* 后续遍历 非递归 双栈
*
* @return
*/
public void rareOrder_NonRecursion_DoubleStack(Node node) {
Stack<Integer> flags = new Stack<Integer>();
Stack<Node> nodeStark = new Stack<Node>();
Integer i = new Integer(1);
while (node != null || !nodeStark.isEmpty()) {
while (node != null) {
nodeStark.push(node);
flags.push(0);
node = node.leftChild;
}

while (!nodeStark.isEmpty() && flags.peek().equals(i)) {
System.out.print(nodeStark.pop().data + " ");
flags.pop();
}

if (!nodeStark.isEmpty()) {
node = nodeStark.peek();
flags.pop();
flags.push(1);
node = node.rightChild;
}
}
}


层序遍历(非递归)

/**
* 层序遍历
* 非递归
* @param node
*/
public void levelorder_NonRecursion(Node node) {
LinkedList<Node> linkedList = new LinkedList<Node>();
Node curr = root;
if(root == null) return;
else linkedList.offer(node);
while(!linkedList.isEmpty()){
curr = linkedList.remove();
System.out.print(curr.data + " ");
if(curr.leftChild != null){
linkedList.offer(curr.leftChild);
}
if(curr.rightChild != null){
linkedList.offer(curr.rightChild);
}
}

}


删除节点

/**
* 删除节点
* @param value
* @return
*/
public boolean deleteNode(int value){
//引用当前节点,从根节点开始
Node curr = root;
//应用当前节点的父节点
Node parentNode = root;
//是否为左节点
boolean isLeftChild = true;
//进行比较,查找是否存在要删除的节点
while(curr.data != value){
parentNode = curr;
if(curr.data > value){
curr = curr.leftChild;
isLeftChild = true;
}else {
curr = curr.rightChild;
isLeftChild = false;
}
//如果查找不到
if(curr == null) {
return false;
}
}
//刪除叶子节点
if(curr.leftChild == null && curr.rightChild == null){
if(curr == root) {
root = null;
} else if(isLeftChild) {
parentNode.leftChild = null;
} else {
parentNode.rightChild = null;
}
}else if(curr.leftChild == null){//删除节点的左节点为空
if(curr == root){
root = root.rightChild;
}else if(isLeftChild) {
parentNode.leftChild = curr.rightChild;
}else {
parentNode.rightChild = curr.rightChild;
}
}else if (curr.rightChild == null) {//删除节点的右节点为空
if(curr == root){
root = root.leftChild;
}else if(isLeftChild) {
parentNode.leftChild = curr.leftChild;
}else {
parentNode.rightChild = curr.leftChild;
}
}else {//如果要删除的节点有左右两个子节点
Node successor = getSuccessor(curr);
if(curr == root){
root = successor;
}else if(isLeftChild){
parentNode.leftChild = successor;
}else {
parentNode.rightChild = successor;
}
successor.leftChild = curr.leftChild;
}
return true;
}

/**
* 获取要删除节点的中序后继节点
* @param delNode
* @return
*/
public Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = delNode;
Node curr = delNode.rightChild;
while(curr != null){
successorParent = successor;
successor = curr;
curr = curr.leftChild;
}
if(successor != delNode.rightChild){
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
}
return successor;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息