树,二叉树
树的介绍
1. 树的定义
树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。
把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
(01) 每个节点有零个或多个子节点;
(02) 没有父节点的节点称为根节点;
(03) 每一个非根节点有且只有一个父节点;
(04) 除了根节点外,每个子节点可以分为多个不相交的子树。
2. 树的基本术语
若一个结点有子树,那么该结点称为子树根的"双亲",子树的根是该结点的"孩子"。有相同双亲的结点互为"兄弟"。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。
结点的度:结点拥有的子树的数目。
叶子:度为零的结点。
分支结点:度不为零的结点。
树的度:树中结点的最大的度。
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。
树的高度:树中结点的最大层次。
无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。
有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。
二叉树的介绍
1. 二叉树的定义
二叉树是每个节点最多有两个子树的树结构。它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空。
2. 二叉树的性质
二叉树有以下几个性质:TODO(上标和下标)
性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。
性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)。
性质3:包含n个结点的二叉树的高度至少为log2 (n+1)。
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。
2.1 性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)
证明:下面用"数学归纳法"进行证明。
(01) 当i=1时,第i层的节点数目为2{i-1}=2{0}=1。因为第1层上只有一个根结点,所以命题成立。
(02) 假设当i>1,第i层的节点数目为2{i-1}。这个是根据(01)推断出来的!
下面根据这个假设,推断出"第(i+1)层的节点数目为2{i}"即可。
由于二叉树的每个结点至多有两个孩子,故"第(i+1)层上的结点数目" 最多是 "第i层的结点数目的2倍"。即,第(i+1)层上的结点数目最大值=2×2{i-1}=2{i}。
故假设成立,原命题得证!
2.2 性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)
证明:在具有相同深度的二叉树中,当每一层都含有最大结点数时,其树中结点数最多。利用"性质1"可知,深度为k的二叉树的结点数至多为:
20+21+…+2k-1=2k-1
故原命题得证!
2.3 性质3:包含n个结点的二叉树的高度至少为log2 (n+1)
证明:根据"性质2"可知,高度为h的二叉树最多有2{h}–1个结点。反之,对于包含n个节点的二叉树的高度至少为log2(n+1)。
2.4 性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)="0度结点数(n0)" + "1度结点数(n1)" + "2度结点数(n2)"。由此,得到等式一。
(等式一) n=n0+n1+n2
另一方面,0度结点没有孩子,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是:n1+2n2。此外,只有根不是任何结点的孩子。故二叉树中的结点总数又可表示为等式二。
(等式二) n=n1+2n2+1
由(等式一)和(等式二)计算得到:n0=n2+1。原命题得证!
3. 满二叉树,完全二叉树和二叉查找树
3.1 满二叉树
定义:高度为h,并且由2{h} –1个结点的二叉树,被称为满二叉树。
3.2 完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
3.3 二叉查找树
定义:二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。
在二叉查找树中:
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树。
(04) 没有键值相等的节点(no duplicate nodes)。
二叉树实现:
/**
* 二叉树的二叉链表结点类
*/
public class BinaryNode<T> {
public T data;// 数据域,存储数据元素
public BinaryNode<T> left, right;// 链域,分别指向左右孩子结点
// 构造结点,参数分别指向元素和左右孩子结点
public BinaryNode(T data, BinaryNode<T> left, BinaryNode<T> right) {
this.data = data;
this.left = left;
this.right = right;
}
// 构造指定值的叶子结点
public BinaryNode(T data) {
this(data, null, null);
}
public BinaryNode() {
this(null, null, null);
}
}
/**
* 二叉树接口,二叉树抽象数据类型
*/
public interface BinaryTTree<T> {
boolean isEmpty();// 判断二叉树是否为空
int count();// 判断二叉树的节点个数
int height();// 返回二叉树的高度
void preOrder();// 先跟次序遍历二叉树
void inOrder();// 中跟遍历二叉树
void postOrder();// 后跟次序遍历二叉树
void levelOrder();// 按层次遍历二叉树
BinaryNode<T> search(T key);// 查找并返回首次出现关键字为key元素的节点
BinaryNode<T> getParent(BinaryNode<T> node);// 返回node的父母节点
void insertRoot(T x);// 插入元素x作为根节点
BinaryNode<T> insertChild(BinaryNode<T> node, T x, boolean leftChild);// 插入孩子节点
void removeChild(BinaryNode<T> p, boolean leftChild);// 删除p节点的左或右子树
void removeAll();// 删除二叉树
}
/**
* 二叉树类,实现BinaryTree<T>接口,泛型T指定结点的元素类型
*/
public class BinaryTree<T> implements BinaryTTree<T> {
public BinaryNode<T> root;// 根节点,结点结构为二叉链表
private int i;// 作为create(T[] prelist)方法的成员变量使用
public BinaryTree() {
this.root = null;
}
// 以先跟序列和中跟序列构造二叉树
public BinaryTree(T[] prelist, T[] inlist) {
this.root = create(prelist, inlist, 0, 0, prelist.length);
}
// 以标明空子树的先跟序列构造一颗二叉树
public BinaryTree(T[] prelist) {
this.root = create(prelist);
}
// 以标明空子树的先跟序列构造一颗子二叉树,子树的跟值是prelist[i],返回所创建子树的根节点
private BinaryNode<T> create(T[] prelist) {
BinaryNode<T> p = null;
if (i < prelist.length) {
T elem = prelist[i];
i++;
if (elem != null) {// 不能elme="^",因为T不一定是String
p = new BinaryNode<T>(elem);// 创建叶子结点
p.left = create(prelist);// 创建p的左子树
p.right = create(prelist);// 创建p的右子树
}
}
return p;
}
// 创建子树的根节点(以先跟序列和中跟序列构造二叉树)
private BinaryNode<T> create(T[] prelist, T[] inlist, int preStart,
int inStart, int n) {
if (n <= 0)
return null;
BinaryNode<T> root = new BinaryNode<T>(prelist[preStart]);// 根结点值
int i = inStart;
while (i < n) {// 在中跟序列中查找跟值所在的位置
if (prelist[preStart] == inlist[i]) {
root.left = create(prelist, inlist, preStart + 1, inStart, i);// 创建左子树
root.right = create(prelist, inlist, preStart + i + 1, inStart
+ i + 1, n - i - 1);// 创建右子树
break;
}
i++;
}
return root;
}
// 判断二叉树是否为空
public boolean isEmpty() {
return this.root == null;
}
// 判断二叉树的节点个数
public int count() {
return count(root);
}
// 返回以结点p为跟的子树的节点个数
public int count(BinaryNode<T> p) {
if (p == null)
return 0;
return 1 + count(p.left) + count(p.right);
}
// 返回二叉树的高度
public int height() {
return height(root);
}
// 返回以p结点为跟的子树高度,后跟次序遍历
public int height(BinaryNode<T> p) {
if (p == null)
return 0;
int lh = height(p.left);// 返回左子树的高度
int rh = height(p.right);// 返回右子树的高度
return lh >= rh ? lh + 1 : rh + 1;// 当前子树高度为较高子树的高度加1
}
// 先跟次序遍历二叉树
public void preOrder() {
System.out.print("先跟次序遍历二叉树 ");
preOrder(root);// 调用先跟次序遍历二叉树的递归方法
System.out.println();
}
// 先跟次序遍历以p结点为跟的子二叉树,递归方法
public void preOrder(BinaryNode<T> p) {
if (p != null) {// 若二叉树不为空
System.out.print(p.data.toString() + " ");// 访问当前结点
preOrder(p.left);// 按先跟次序遍历当前结点的左子树
preOrder(p.right);// 按先跟次序遍历当前结点的右子树
}
}
// 中跟遍历二叉树
public void inOrder() {
System.out.print("中跟次序遍历二叉树 ");
inOrder(root);// 调用中跟次序遍历二叉树的递归方法
System.out.println();
}
// 中跟次序遍历以p结点为跟的子二叉树,递归调用
public void inOrder(BinaryNode<T> p) {
if (p != null) {
inOrder(p.left);// 中跟次序遍历左子树,递归调用
System.out.print(p.data.toString() + " ");
inOrder(p.right);// 中跟次序遍历右子树,递归调用
}
}
// 后跟次序遍历二叉树
public void postOrder() {
System.out.print("后跟次序遍历二叉树 ");
postOrder(root);// 调用后跟次序遍历二叉树的递归方法
System.out.println();
}
// 后跟次序遍历以p结点为跟的子二叉树,递归调用
public void postOrder(BinaryNode<T> p) {
if (p != null) {
postOrder(p.left);
postOrder(p.right);
System.out.print(p.data.toString() + " ");
}
}
// 按层次遍历二叉树
public void levelOrder() {
LinkedQueue<BinaryNode<T>> que = new LinkedQueue<BinaryNode<T>>();
BinaryNode<T> p = this.root;
System.out.println("层次遍历:");
while (p != null) {
System.out.print(p.data + "");
if (p.left != null)
que.enquenu(p.left); // p的左孩子节点入队
if (p.right != null)
que.enquenu(p.right); // p的右孩子节点入队
p = que.dequeue(); // p指向出对节点,若队列空返回null
}
System.out.println();
}
// 查找并返回首次出现关键字为key元素的节点
public BinaryNode<T> search(T key) {
return search(root, key);
}
// 在以p为跟的子树中查找并返回首次出现的关键字为key的元素结点,若未找到,则返回null
public BinaryNode<T> search(BinaryNode<T> p, T key) {
if (p == null || key == null)
return null;
if (p.data.equals(key))
return p;// 查找成功,返回找到结点
BinaryNode<T> find = search(p.left, key);// 在左子树中查找,递归调用
if (find == null)// 若在左子树中未找到
find = search(p.right, key);// 则继续在右子树中查找,递归调用
return find;// 返回查找结果
}
// 返回node的父母节点
public BinaryNode<T> getParent(BinaryNode<T> node) {
if (root == null || node == root)
return null;
return getParent(root, node);
}
// 在以p为跟的子树中查找并返回node结点的父母结点
public BinaryNode<T> getParent(BinaryNode<T> p, BinaryNode<T> node) {
if (p == null)
return null;
if (p.left == node || p.right == node)
return p;
BinaryNode<T> find = getParent(p.left, node);
if (find == null)
find = getParent(p.right, node);
return find;
}
// 返回二叉树的广义表表示字符串
public String toGenListString() {
return "二叉树的广义表表示:" + toGenListString(this.root) + "\n";
}
// 返回空子树表示
public String toGenListString(BinaryNode<T> p) {
if (p == null)
return "^";// 返回空子树表示
String str = p.data.toString();
if (p.left != null || p.right != null)// 非叶结点,有子树
str += "(" + toGenListString(p.left) + ","
+ toGenListString(p.right) + ")";// 递归调用
return str;
}
@Override
// 插入元素x作为左孩子,否则作为右孩子
public void insertRoot(T x) {
root = new BinaryNode<T>(x, root, null);
}
@Override
// 插入元素x作为p结点的孩子,若leftChild为true,插入结点作为左孩子,否则作为右孩子
public BinaryNode<T> insertChild(BinaryNode<T> p, T x, boolean leftChild) {
if (p == null || x == null)
return null;
if (leftChild) {
// 插入x作为p的左孩子,p原左孩子成为x左孩子
p.left = new BinaryNode<T>(x, p.left, null);
return p.left;// 返回插入结点
}
p.right = new BinaryNode<T>(x, null, p.right);// 插入x结点作为p的右孩子
return p.right;
}
@Override
// 删除p节点的左或右子树,若leftChild为true,则删除左子树,否则删除右子树
public void removeChild(BinaryNode<T> p, boolean leftChild) {
if (p != null)
if (leftChild)
p.left = null;
else
p.right = null;
}
@Override
// 删除二叉树
public void removeAll() {
this.root = null;
}
// 中跟次序遍历二叉树的非递归遍历
public void inOrderTraverse() {
System.out.print("中跟次序遍历(非遍历)");
LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
BinaryNode<T> p = this.root;
while (p != null || !stack.isEmpty()) {
if (p != null) {
stack.push(p);// p结点入栈
p = p.left;// 进入左子树
} else {// p为空且栈非空
p = stack.pop();// p指向出栈结点
System.out.println(p.data + " ");// 访问结点
p = p.right;// 进入右子树
}
}
}
// 先跟次序遍历二叉树的非递归遍历
public void preOrderTraverse() {
System.out.println("先跟次序遍历(非递归)");
LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
BinaryNode<T> p = this.root;
while (p != null || !stack.isEmpty())
if (p != null) {
System.out.println(p.data + " ");// 访问结点
stack.push(p);// 将p入栈
p = p.left;
} else {
p = stack.pop();
p = p.right;
}
}
}
/**
* Created by kentorvalds on 2017/7/5.
*/
public class BinarySearchTree<T extends Comparable<? super T>> {
/**结点数据结构*/
static class BinaryNode<T>
{
T data;
BinaryNode<T> left;
BinaryNode<T> right;
public BinaryNode(T data) {
this(data,null,null);
}
public BinaryNode( T data, BinaryNode<T> left, BinaryNode<T> right) {
this.data =data;
this.left = left;
this.right =right;
}
public BinaryNode()
{
data =null;
this.left = left;
this.right =right;
}
}
private BinaryNode<T> rootTree;
/**构造一颗空的二叉查找树*/
public BinarySearchTree()
{
rootTree = null;
}
/**清空二叉查找树*/
public void clear()
{
rootTree = null;
}
/**判断是否为空*/
public boolean isEmpty()
{
return rootTree == null;
}
/**查找指定的元素,默认从
* 根结点出开始查询*/
public boolean contains(T t)
{
return contains(t, rootTree);
}
/**找到二叉查找树中的最小值*/
public T findMin()
{
if(isEmpty())
{
System.out.println("二叉树为空");
return null;
}else
return findMin(rootTree).data;
}
/**找到二叉查找树中的最大值*/
public T findMax()
{
if(isEmpty())
{
System.out.println("二叉树为空");
return null;
}else
return findMax(rootTree).data;
}
/**插入元素*/
public void insert(T t)
{
rootTree = insert(t, rootTree);
}
/**删除元素*/
public void remove(T t)
{
rootTree = remove(t,rootTree);
}
/**打印二叉查找树*/
public void printTree()
{
}
/**从某个结点出开始查找元素*/
public boolean contains(T t, BinaryNode<T> node)
{
if(node==null)
return false;
int result = t.compareTo(node.data);
if(result>0)
return contains(t,node.right);
else if(result<0)
return contains(t, node.left);
else
return true;
}
/**查询出最小元素所在的结点*/
public BinaryNode<T> findMin(BinaryNode<T> node)
{
if(node==null)
return null;
else if(node.left==null)
return node;
return findMin(node.left);//递归查找
}
/**查询出最大元素所在的结点*/
public BinaryNode<T> findMax(BinaryNode<T> node)
{
if(node!=null)
{
while(node.right!=null)
node=node.right;
}
return node;
}
/**在某个位置开始判断插入元素*/
public BinaryNode<T> insert(T t,BinaryNode<T> node)
{
if(node==null)
{
//新构造一个二叉查找树
return new BinaryNode<T>(t, null, null);
}
int result = t.compareTo(node.data);
if(result<0)
node.left= insert(t,node.left);
else if(result>0)
node.right= insert(t,node.right);
else
;//doNothing
return node;
}
/**在某个位置开始判断删除某个结点*/
public BinaryNode<T> remove(T t,BinaryNode<T> node)
{
if(node == null)
return node;//没有找到,doNothing
int result = t.compareTo(node.data);
if(result>0)
node.right = remove(t,node.right);
else if(result<0)
node.left = remove(t,node.left);
else if(node.left!=null&&node.right!=null)
{
node.data = findMin(node.right).data;
node.right = remove(node.data,node.right);
}
else
node = (node.left!=null)?node.left:node.right;
return node;
}
public BinaryNode<Integer> init()
{
BinaryNode<Integer> node3 = new BinaryNode<Integer>(3);
BinaryNode<Integer> node1 = new BinaryNode<Integer>(1);
BinaryNode<Integer> node4 = new BinaryNode<Integer>(4,node3,null);
BinaryNode<Integer> node2 = new BinaryNode<Integer>(2,node1,node4);
BinaryNode<Integer> node8 = new BinaryNode<Integer>(8);
BinaryNode<Integer> root = new BinaryNode<Integer>(6,node2,node8);
return root;
}
public void preOrder(BinaryNode node) {
if (node != null) {
System.out.print(node.data);
preOrder(node.left);
preOrder(node.right);
}
}
/*简单测试*/
public static void main(String[] args) {
BinarySearchTree searchTree = new BinarySearchTree<>();
BinaryNode<Integer> node= searchTree.init();
searchTree.rootTree=node;
searchTree.preOrder(searchTree.rootTree);
searchTree.remove(4);
searchTree.preOrder(searchTree.rootTree);
}
}
/**
* Created by kentorvalds on 2017/6/21.
*/
public class BinaryTreeMake {
public static void main(String[] args) {
BinaryTree<String> bitree = make();
bitree.preOrder();//先跟便利
bitree.inOrder();//中跟遍历
bitree.postOrder();//后跟遍历
System.out.println("double++++++++++++");
BinaryTree<Double> binaryTree = new BinaryTree<>();
BinaryNode<Double> b1, b2;
b1 = new BinaryNode<>(9.0);
b2 = new BinaryNode<>(20.0, new BinaryNode(15.0), new BinaryNode<>(7.0));
binaryTree.root = new BinaryNode<>(3.0, b1, b2);
binaryTree.preOrder();//先跟便利
binaryTree.inOrder();//中跟遍历
binaryTree.postOrder();//后跟遍历
}
/*
C
B C
D E F
G
*/
public static BinaryTree<String> make() {
BinaryTree<String> bitree = new BinaryTree<String>();
BinaryNode<String> child_f, child_d, child_b, child_c;
child_d = new BinaryNode<String>("D", null, new BinaryNode<String>("G"));
child_b = new BinaryNode<String>("B", child_d, null);
child_f = new BinaryNode<String>("F", new BinaryNode<String>("H"), null);
child_c = new BinaryNode<String>("C", new BinaryNode<String>("E"),
child_f);
bitree.root = new BinaryNode<String>("C", child_b, child_c);
return bitree;
}
}
4.什么是线索二叉树
空的左孩子指针指向该结点的前驱;空的右孩子指针指向该结点的后继。这种附加的指针值称为线索,带线索的二叉树称为线索二叉树。
在不同的遍历次序下,二叉树中的每个结点一般有不同的前驱和后继。因此,线索二叉树又分为前序线索二叉树、中序线索二叉树和后序线索二叉树3种。
根据二叉树的特性,n个结点的二叉树,采用链式存储结构时,有n+1个空链域,可以利用这些空链域存放指向结点的直接前驱和直接后继结点的指针。为此做如下规定:当结点的左指针为空(即无左子树)时,令该指针指向按某种方式遍历二叉树时得到的该结点的前驱结点;当结点的右指针为空(即无右子树)时,令该指针指向按某种方式遍历二叉树时得到的该结点的后继结点;为了避免混淆,还需要增加两个标志位来区分指针指向的是其孩子还是前驱及后继。
5.平衡二叉树
平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
平衡二叉树大部分操作和二叉查找树类似,主要不同在于插入删除的时候平衡二叉树的平衡可能被改变,并且只有从那些插入点到根结点的路径上的结点的平衡性可能被改变,因为只有这些结点的子树可能变化。
平衡二叉树不平衡的情形:
把需要重新平衡的结点叫做α,由于任意两个结点最多只有两个儿子,因此高度不平衡时,α结点的两颗子树的高度相差2.容易看出,这种不平衡可能出现在下面4中情况中:
1.对α的左儿子的左子树进行一次插入
2.对α的左儿子的右子树进行一次插入
3.对α的右儿子的左子树进行一次插入
4.对α的右儿子的右子树进行一次插入
情形1和情形4是关于α的镜像对称,二情形2和情形3也是关于α的镜像对称,因此理论上看只有两种情况,但编程的角度看还是四种情形。
第一种情况是插入发生在“外边”的情形(左左或右右),该情况可以通过一次单旋转完成调整;第二种情况是插入发生在“内部”的情形(左右或右左),这种情况比较复杂,需要通过双旋转来调整。
调整措施:
一、单旋转
上图是左左的情况,k2结点不满足平衡性,它的左子树k1比右子树z深两层,k1子树中更深的是k1的左子树x,因此属于左左情况。
为了恢复平衡,我们把x上移一层,并把z下移一层,但此时实际已经超出了AVL树的性质要求。为此,重新安排结点以形成一颗等价的树。为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。
这种情况称为单旋转。
二、双旋转
对于左右和右左两种情况,单旋转不能解决问题,要经过两次旋转。
对于上图情况,为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右旋转,旋转之后就变成了左左情况,所以第二步再进行一次左左旋转,最后得到了一棵以k2为根的平衡二叉树。
AVL树的删除操作:
同插入操作一样,删除结点时也有可能破坏平衡性,这就要求我们删除的时候要进行平衡性调整。
删除分为以下几种情况:
首先在整个二叉树中搜索要删除的结点,如果没搜索到直接返回不作处理,否则执行以下操作:
1.要删除的节点是当前根节点T。
如果左右子树都非空。在高度较大的子树中实施删除操作。
分两种情况:
(1)、左子树高度大于右子树高度,将左子树中最大的那个元素赋给当前根节点,然后删除左子树中元素值最大的那个节点。
(1)、左子树高度小于右子树高度,将右子树中最小的那个元素赋给当前根节点,然后删除右子树中元素值最小的那个节点。
如果左右子树中有一个为空,那么直接用那个非空子树或者是NULL替换当前根节点即可。
2、要删除的节点元素值小于当前根节点T值,在左子树中进行删除。
递归调用,在左子树中实施删除。
这个是需要判断当前根节点是否仍然满足平衡条件,
如果满足平衡条件,只需要更新当前根节点T的高度信息。
否则,需要进行旋转调整:
如果T的左子节点的左子树的高度大于T的左子节点的右子树的高度,进行相应的单旋转。否则进行双旋转。
3、要删除的节点元素值大于当前根节点T值,在右子树中进行删除。