java满二叉树增加,腾讯大牛教你如何使用Java实现二叉树的添加,删除,获取以及遍历...

一段来自百度百科的对二叉树的解释:

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子节点,至多有2k-1个节点。

二叉树的结构:

1bc726e582de

二叉树节点的声明:

static final class Entry>{

//保存的数据

private T item;

//左子树

private Entry left;

//右子树

private Entry right;

//父节点

private Entry parent;

Entry(T item,Entry parent){

this.item = item;

this.parent = parent;

}

}

类属性:

//根节点

private Entry root;

//数据量

private int size = 0;

二叉树添加数据:

/**

* 添加元素

* @param item 待添加元素

* @return 已添加元素

*/

public T put(T item){

//每次添加数据的时候都是从根节点向下遍历

Entry t = root;

if (t == null){

//当前的叉树树的为空,将新节点设置为根节点,父节点为null

root = new Entry<>(item,null);

size++;

return root.item;

}

//自然排序结果,如果传入的数据小于当前节点返回-1,大于当前节点返回1,否则返回0

int ret = 0;

//记录父节点

Entry p = t;

while (t != null){

//与当前节点比较

ret = item.compareTo(t.item);

p = t;

//插入节点比当前节点小,把当前节点设置为左子节点,然后与左子比较,以此类推找到合适的位置

if (ret < 0)

t = t.left;

//大于当前节点

else if (ret > 0)

t = t.right;

else {

//相等就把旧值覆盖掉

t.item = item;

return t.item;

}

}

//创建新节点

Entry e = new Entry<>(item,p);

//根据比较结果将新节点放入合适的位置

if (ret < 0)

p.left = e;

else

p.right = e;

size++;

return e.item;

}

在put的过程中,使用Comparable中的compareTo来比较两个元素的大小的,所以在二叉树中存储的元素必须要继承Comparable 类,覆写compareTo方法。

二叉树删除数据

/**

* 删除元素

* 删除元素如果细分的话,可以分为4中:没有子节点,只有左节点,只有右节点,有两个子节点

* 1)没有子节点这种情况比较简单,直接删除就可以了

* 2)只有左节点或右节点,这两种情况处理方式是一致的,只是方向相反,所以在一起讲了,

* 将删除节点的父节点的左节点(右节点)指向删除节点的子节点,将左节点(右节点)指向删除节点的父节点

* 3)有两个子节点,这种情况相对来说比较复杂一点:

* 找到删除节点的前驱节点或后继节点,然后将前驱或后继节点的值赋给删除节点,然后将前驱或后继节点删除。本质是删除前驱或后继节点

* 前驱节点的特点:

* 1)删除的左子节点没有右子节点,那么左子节点即为前驱节点

* 2)删除节点的左子节点有右子节点,那么最右边的最后一个节点即为前驱节点

* 后继节点的特点:

* 与前驱节点刚好相反,总是右子节点,或则右子节点的最左子节点;例如:删除节点为c ,那么前驱节点为 m,后继节点为n

* a

* / \

* b c

* / \ / \

* d e f g

* / \ / \ / \ / \

* @param item 删除元素 h i j k l m n o

* @return 删除结果

*/

public boolean remove(T item){

//获取删除节点

Entry delEntry = getEntry(item);

if (delEntry == null) return false;

//删除节点的父节点

Entry p = delEntry.parent;

size--;

//情况1:没有子节点

if (delEntry.left == null && delEntry.right == null){

//删除节点为根节点

if (delEntry == root){

root = null;

}else {//非根节点

//删除的是父节点的左节点

if (delEntry == p.left){

p.left = null;

}else {//删除右节点

p.right = null;

}

}

//情况2:删除的节点只有左节点

}else if (delEntry.right == null){

Entry lc = delEntry.left;

//删除的节点为根节点,将删除节点的左节点设置成根节点

if (p == null) {

lc.parent = null;

root = lc;

} else {//非根节点

if (delEntry == p.left){//删除左节点

p.left = lc;

}else {//删除右节点

p.right = lc;

}

lc.parent = p;

}

//情况3:删除节点只有右节点

}else if (delEntry.left == null){

Entry rc = delEntry.right;

//删除根节点

if (p == null) {

rc.parent = null;

root = rc;

}else {//删除非根节点

if (delEntry == p.left)

p.left = rc;

else

p.right = rc;

rc.parent = p;

}

//情况4:删除节点有两个子节点

}else {//有两个节点,找到后继节点,将值赋给删除节点,然后将后继节点删除掉即可

Entry successor = successor(delEntry);//获取到后继节点

delEntry.item = successor.item;

//后继节点为右子节点

if (delEntry.right == successor){

//右子节点有右子节点

if (successor.right != null) {

delEntry.right = successor.right;

successor.right.parent = delEntry;

}else {//右子节点没有子节点

delEntry.right = null;

}

}else {//后继节点必定是左节点

successor.parent.left = null;

}

return true;

}

//让gc回收

delEntry.parent = null;

delEntry.left = null;

delEntry.right = null;

return true;

}

/**

* 获取节点节点

* @param item 获取节点

* @return 获取到的节点,可能为null

*/

private Entry getEntry(T item){

Entry t = root;

int ret;

//从根节点开始遍历

for (;t != null;){

ret = item.compareTo(t.item);

if (ret < 0)

t = t.left;

else if (ret > 0)

t = t.right;

else

return t;

}

return null;

}

/**

* 查找后继节点

* @param delEntry 删除节点

* @return 后继节点

*/

private Entry successor(Entry delEntry) {

Entry r = delEntry.right;//r节点必定不为空

while (r.left != null){

r = r.left;

}

return r;

}

二叉树获取节点

/**

* 判断是否存在该元素

* @param item 查找元素

* @return 结果

*/

public boolean contains(T item){

return getEntry(item) != null;

}

/**

* 获取节点节点

* @param item 获取节点

* @return 获取到的节点,可能为null

*/

private Entry getEntry(T item){

Entry t = root;

int ret;

//从根节点开始遍历

for (;t != null;){

ret = item.compareTo(t.item);

if (ret < 0)

t = t.left;

else if (ret > 0)

t = t.right;

else

return t;

}

return null;

}

因为我这个例子是存储一个元素,获取到的元素和传进去的元素是一致的,所以我用contains方法来判断返回true即表示获取成功了,不返回获取到的结果了。当然,如果将entry存储的元素改为kv形式的话,就可以使用get方法了。

二叉树的遍历

二叉树的遍历可以分为三种:前序遍历、中序遍历和后续遍历,其中中序遍历是最常用的遍历方式,因为它遍历出来的结果的升序的。

前序遍历:

/**

* 前序遍历

* @param e 开始遍历元素

*/

public void prevIterator(Entry e){

if (e != null) {

System.out.print(e.item + " ");

prevIterator(e.left);

prevIterator(e.right);

}

}

中序遍历:

/**

* 中序遍历

* @param e

*/

public void midIterator(Entry e){

if (e != null){

midIterator(e.left);

System.out.print(e.item + " ");

midIterator(e.right);

}

}

后序遍历:

/**

* 后续遍历

* @param e 开始遍历元素

*/

public void subIterator(Entry e){

if (e != null) {

subIterator(e.left);

subIterator(e.right);

System.out.print(e.item + " ");

}

}

package com.rainple.collections;

/**

* 二叉树

* @param

*/

public class BinaryTree> {

//根节点

private Entry root;

//数据量

private int size = 0;

public BinaryTree(){}

/**

* 添加元素

* @param item 待添加元素

* @return 已添加元素

*/

public T put(T item){

//每次添加数据的时候都是从根节点向下遍历

Entry t = root;

size++;

if (t == null){

//当前的叉树树的为空,将新节点设置为根节点,父节点为null

root = new Entry<>(item,null);

return root.item;

}

//自然排序结果,如果传入的数据小于当前节点返回-1,大于当前节点返回1,否则返回0

int ret = 0;

//记录父节点

Entry p = t;

while (t != null){

//与当前节点比较

ret = item.compareTo(t.item);

p = t;

//插入节点比当前节点小,把当前节点设置为左子节点,然后与左子比较,以此类推找到合适的位置

if (ret < 0)

t = t.left;

//大于当前节点

else if (ret > 0)

t = t.right;

else {

//相等就把旧值覆盖掉

t.item = item;

return t.item;

}

}

//创建新节点

Entry e = new Entry<>(item,p);

//根据比较结果将新节点放入合适的位置

if (ret < 0)

p.left = e;

else

p.right = e;

return e.item;

}

public void print(){

midIterator(root);

}

/**

* 中序遍历

* @param e

*/

public void midIterator(Entry e){

if (e != null){

midIterator(e.left);

System.out.print(e.item + " ");

midIterator(e.right);

}

}

/**

* 获取根节点

* @return 根节点

*/

public Entry getRoot(){return root;}

/**

* 前序遍历

* @param e 开始遍历元素

*/

public void prevIterator(Entry e){

if (e != null) {

System.out.print(e.item + " ");

prevIterator(e.left);

prevIterator(e.right);

}

}

/**

* 后续遍历

* @param e 开始遍历元素

*/

public void subIterator(Entry e){

if (e != null) {

subIterator(e.left);

subIterator(e.right);

System.out.print(e.item + " ");

}

}

/**

* 获取节点节点

* @param item 获取节点

* @return 获取到的节点,可能为null

*/

private Entry getEntry(T item){

Entry t = root;

int ret;

//从根节点开始遍历

for (;t != null;){

ret = item.compareTo(t.item);

if (ret < 0)

t = t.left;

else if (ret > 0)

t = t.right;

else

return t;

}

return null;

}

/**

* 判断是否存在该元素

* @param item 查找元素

* @return 结果

*/

public boolean contains(T item){

return getEntry(item) != null;

}

/**

* 删除元素

* 删除元素如果细分的话,可以分为4中:没有子节点,只有左节点,只有右节点,有两个子节点

* 1)没有子节点这种情况比较简单,直接删除就可以了

* 2)只有左节点或右节点,这两种情况处理方式是一致的,只是方向相反,所以在一起讲了,

* 将删除节点的父节点的左节点(右节点)指向删除节点的子节点,将左节点(右节点)指向删除节点的父节点

* 3)有两个子节点,这种情况相对来说比较复杂一点:

* 找到删除节点的前驱节点或后继节点,然后将前驱或后继节点的值赋给删除节点,然后将前驱或后继节点删除。本质是删除前驱或后继节点

* 前驱节点的特点:

* 1)删除的左子节点没有右子节点,那么左子节点即为前驱节点

* 2)删除节点的左子节点有右子节点,那么最右边的最后一个节点即为前驱节点

* 后继节点的特点:

* 与前驱节点刚好相反,总是右子节点,或则右子节点的最左子节点;例如:删除节点为c ,那么前驱节点为 m,后继节点为n

* a

* / \

* b c

* / \ / \

* d e f g

* / \ / \ / \ / \

* @param item 删除元素 h i j k l m n o

* @return 删除结果

*/

public boolean remove(T item){

//获取删除节点

Entry delEntry = getEntry(item);

if (delEntry == null) return false;

//删除节点的父节点

Entry p = delEntry.parent;

size--;

//情况1:没有子节点

if (delEntry.left == null && delEntry.right == null){

//删除节点为根节点

if (delEntry == root){

root = null;

}else {//非根节点

//删除的是父节点的左节点

if (delEntry == p.left){

p.left = null;

}else {//删除右节点

p.right = null;

}

}

//情况2:删除的节点只有左节点

}else if (delEntry.right == null){

Entry lc = delEntry.left;

//删除的节点为根节点,将删除节点的左节点设置成根节点

if (p == null) {

lc.parent = null;

root = lc;

} else {//非根节点

if (delEntry == p.left){//删除左节点

p.left = lc;

}else {//删除右节点

p.right = lc;

}

lc.parent = p;

}

//情况3:删除节点只有右节点

}else if (delEntry.left == null){

Entry rc = delEntry.right;

//删除根节点

if (p == null) {

rc.parent = null;

root = rc;

}else {//删除非根节点

if (delEntry == p.left)

p.left = rc;

else

p.right = rc;

rc.parent = p;

}

//情况4:删除节点有两个子节点

}else {//有两个节点,找到后继节点,将值赋给删除节点,然后将后继节点删除掉即可

Entry successor = successor(delEntry);//获取到后继节点

delEntry.item = successor.item;

//后继节点为右子节点

if (delEntry.right == successor){

//右子节点有右子节点

if (successor.right != null) {

delEntry.right = successor.right;

successor.right.parent = delEntry;

}else {//右子节点没有子节点

delEntry.right = null;

}

}else {//后继节点必定是左节点

successor.parent.left = null;

}

return true;

}

//让gc回收

delEntry.parent = null;

delEntry.left = null;

delEntry.right = null;

return true;

}

/**

* 查找后继节点

* @param delEntry 删除节点

* @return 后继节点

*/

private Entry successor(Entry delEntry) {

Entry r = delEntry.right;//r节点必定不为空

while (r.left != null){

r = r.left;

}

return r;

}

public int size(){return size;}

public boolean isEmpty(){return size == 0;}

public void clear(){

clear(getRoot());

root = null;

}

private void clear(Entry e){

if (e != null){

clear(e.left);

e.left = null;

clear(e.right);

e.right = null;

}

}

static final class Entry>{

//保存的数据

private T item;

//左子树

private Entry left;

//右子树

private Entry right;

//父节点

private Entry parent;

Entry(T item,Entry parent){

this.item = item;

this.parent = parent;

}

}

}

测试代码示例:

public static void main(String[] args) {

BinaryTree binaryTree = new BinaryTree<>();

//放数据

binaryTree.put(73);

binaryTree.put(22);

binaryTree.put(532);

binaryTree.put(62);

binaryTree.put(72);

binaryTree.put(243);

binaryTree.put(42);

binaryTree.put(3);

binaryTree.put(12);

binaryTree.put(52);

System.out.println("size: " + binaryTree.size());

binaryTree.put(52);

System.out.println("添加相同元素后的size: " + binaryTree.size());

//判断数据是否存在

System.out.println("数据是否存在:" + binaryTree.contains(12));

//中序遍历

System.out.print("中序遍历结果: ");

binaryTree.midIterator(binaryTree.getRoot());

System.out.println();

//前序遍历

System.out.print("前遍历结果: ");

binaryTree.prevIterator(binaryTree.getRoot());

System.out.println();

//后序遍历

System.out.print("后续遍历结果: ");

binaryTree.subIterator(binaryTree.getRoot());

//删除数据

System.out.println();

binaryTree.remove(62);

System.out.println("删除数据后判断是否存在:" + binaryTree.contains(62));

//清空二叉树

binaryTree.clear();

System.out.print("清空数据后中序遍历: ");

binaryTree.midIterator(binaryTree.getRoot());

}

测试结果:

size: 10

添加相同元素后的size: 10

数据是否存在:true

中序遍历结果: 3 12 22 42 52 62 72 73 243 532

前遍历结果: 73 22 3 12 62 42 52 72 532 243

后续遍历结果: 12 3 52 42 72 62 22 243 532 73

删除数据后判断是否存在:false

清空数据后中序遍历:

纯手写的demo,有什么错误的地方欢迎指正,谢谢大家的阅读!!!