1. 程式人生 > >樹,二叉樹

樹,二叉樹

.com 插入 val 深度 就是 什麽 ++ art lds

樹的介紹

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值,在右子樹中進行刪除。

樹,二叉樹