樹
這次我們接著來說樹,上次說了樹的基本性質和遍歷一顆樹的4種方式,這次將會說到幾種很“有用”的二叉樹
二叉搜索樹
對一顆二叉樹,該如何實作它的動態查找(會有新元素的添加,和對當前樹包含元素的洗掉),前面我們已經學過了我們二分查找,很自然的如果我們再構建一棵樹時,如果當前節點的左子樹都比他小,右子樹都比他大,對這樣的樹,我們叫做二叉搜索樹,根據這樣的性質,我們可以很自然的得出,二叉搜索樹最小的節點它的最左端的節點,二叉搜索樹最大的節點它的最右端的節點,
二叉搜索樹的查找
我們知道對于一棵二叉搜索樹,他任何節點的左子樹都比他小,右子樹都比他大,自然的一種二分查找,從根結點開始遍歷,如果當前節點比需要查找的值大從他的右子樹,比需要查找的值小從他的左子樹,相等則回傳,如果直到指向為空都無法找到,說明該節點并不在樹上,下面是代碼實作,
//find max value
Position FindMax(BinTree BST) {
if (!BST) {
return NULL;
}
while (BST->Right) {
BST = BST->Right;
}
return BST;
}
//find min value
Position FindMin(BinTree BST) {
if (!BST) {
return NULL;
}
while (BST->Left) {
BST = BST->Left;
}
return BST;
}
//find in value
Position Find(BinTree BST, ElementType X) {
//the tree is empty ,return NULL;
while (BST) {
//the X is big than now position's value, may be in the right or doesn't has.
if (X > BST->Data) {
BST = BST->Right;
}
else if (X < BST->Data) {
BST = BST->Left;
}
else {
return BST;
}
}
return NULL;
}
二叉搜索樹的插入
同查找一個值一樣,對于二叉搜索樹的插入,先從根結點開始遍歷,如果小于它就插入它的左子樹,大于它就插入它的右子樹,直到我們找到了位置,再申請一個節點將它接上去,
//insert
BinTree Insert(BinTree BST, ElementType X) {
//if the tree is empty,creat a node and return
if (!BST) {
BST = malloc(sizeof(struct TNode));
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/X;
BST->Left = NULL;
BST->Right = NULL;
}
else {
//the X is big than now position, insert X in its right tree
if (X > BST->Data) {
BST->Right = Insert(BST->Right, X);
}
//the X is small than now position, insert X in its left tree
else if (X < BST->Data) {
BST->Left = Insert(BST->Left, X);
}
//when the X already in the tree, do nothing
else {
}
}
return BST;
}
二叉搜索樹的洗掉
二叉樹最多有兩個節點,在洗掉時我們可能遇到幾種情況,分別是該節點沒有子節點(葉子節點),有一個子節點,有兩個子節點,
如果沒有子節點,我們直接釋放掉該節點,回傳一個NULL接上去,如果只有一個子節點,我們只需要釋放該節點,并把他的那個子節點接上去即可,有兩個子節點時,問題變得麻煩起來,有一個策略將是,將問題轉化為洗掉一個葉節點,或洗掉一個只有一個兒子的節點,
通過二叉搜索數的性質我們知道,一顆樹的最小值,位于他的最左端,最大值位于它的最右端,我們可以找到該節點右子樹的最小值,賦值給該節點,同時洗掉掉它(因為二叉搜索樹的性質,它只可能是葉節點,或者只有一個兒子,而且那樣做不會破壞二叉搜索樹的一個節點左子樹都比他小,右子樹都比他大的性質),找該節點左子樹的最大值同理,
//delete
BinTree Delete(BinTree BST, ElementType X) {
if (!BST) {
printf("NOT Found\n");
}
else {
//the X is big than now position, delete X in its right tree
if (X > BST->Data) {
BST->Right = Delete(BST->Right, X);
}
//the X is small than now position, delete X in its left tree
else if (X < BST->Data) {
BST->Left = Delete(BST->Left, X);
}
//find the X positon
else {
//has two sub tree
if (BST->Left && BST->Right) {
BinTree temp = FindMax(BST->Left);
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/temp->Data;
temp->Data = X;
Delete(BST->Left, X);
}
//has one or no sub tree
else {
BinTree temp = BST;
//don't has left sub tree
if (!BST->Left) {
BST = BST->Right;
}
//don't has right sub tree
else if (!BST->Right) {
BST = BST->Left;
}
free(temp);
}
}
}
return BST;
}
平衡二叉樹
平衡二叉樹的性質
前面我面討論了二叉搜索樹,現在,想象一下,如果我們按照升序序列將節點(1-10)插入樹中,不難發現,這個樹成了顆單邊樹這樣的樹有著和鏈表一樣的查找效率,肯定是不希望發生這樣的事情的,這里引入一個叫平衡二叉樹的樹(AVL),那么這種樹有什么特點呢?
平衡二叉樹由二叉搜索樹而來,同樣的,也是必須滿足BST的性質,而且,這顆樹必須滿足,所有節點的左右子樹高度差BF(T)=\(h_l\)-\(h_r\)不大于1.
考慮一下,一個n層高的平衡二叉樹最小需要幾個節點?
答案是\(a_n\)=\(1+a_(n-1)+a_(n-2)\)
對于一層高的平衡二叉樹,需要一個節點,兩層高的需要兩個節點,三層高的則需要一個節點加上它的左子樹(兩層的平衡二叉樹)和他的右子樹一層的平衡二叉樹,整個是一個遞回的程序
平衡二叉樹的調整
為了保證平衡二叉樹的性質,我們再插入節點或者洗掉節點時,使該樹不平衡時,又應該如何調整它使它平衡呢?根據上面平衡二叉樹是一個遞回的生成程序,我們可以知道,對于插入或者洗掉,只需要修正被破壞平衡的節點為根節點構成的樹,即修正整棵樹的平衡,
第一種情況,破壞了平衡的節點,位于被破壞平衡節點的右子樹的右子樹,此時將被破壞平衡節點的右兒子提起來,自己做右兒子的左兒子,將右兒子的左兒子做自己的右兒子,(RR旋轉)
第二種情況,破壞了平衡的節點,位于被破壞平衡節點的左子樹的左子樹,根據對稱性我們很容易想到,此時將被破壞平衡節點的左兒子提起來,自己做左兒子的右兒子,將左兒子的右兒子做自己的左兒子,(LL旋轉)
第三種情況,破壞了平衡的節點,位于被破壞平衡節點的左子樹的右子樹,此時將破壞平衡節點的所在樹的根節點提出來做新的根,并令該根的左兒子為原樹根的左兒子,右兒子為原樹根節點,并且把破壞平衡節點的所在樹的根節點的左右子樹,分別接在當前根節點左兒子的右邊和右兒子的左邊,(LR旋轉)
第四種情況,類似于第3種情況的對稱,破壞了平衡的節點,位于被破壞平衡節點的右子樹的左子樹,只需對稱著像第三種情況那樣做,(RL旋轉)




(圖片來源https://www.icourse163.org/learn/ZJU-93001?tid=1461682474#/learn/content?type=detail&id=1238255569&cid=1258682934)
課后練習題(4個小題)
04-樹4 是否同一棵二叉搜索樹 (25point(s))
給定一個插入序列就可以唯一確定一棵二叉搜索樹,然而,一棵給定的二叉搜索樹卻可以由多種不同的插入序列得到,例如分別按照序列{2, 1, 3}和{2, 3, 1}插入初始為空的二叉搜索樹,都得到一樣的結果,于是對于輸入的各種插入序列,你需要判斷它們是否能生成一樣的二叉搜索樹,
輸入格式:
輸入包含若干組測驗資料,每組資料的第1行給出兩個正整數N (≤10)和L,分別是每個序列插入元素的個數和需要檢查的序列個數,第2行給出N個以空格分隔的正整數,作為初始插入序列,最后L行,每行給出N個插入的元素,屬于L個需要檢查的序列,
簡單起見,我們保證每個插入序列都是1到N的一個排列,當讀到N為0時,標志輸入結束,這組資料不要處理,
輸出格式:
對每一組需要檢查的序列,如果其生成的二叉搜索樹跟對應的初始序列生成的一樣,輸出“Yes”,否則輸出“No”,
輸入樣例:
4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0
輸出樣例:
Yes
No
No
解法:模擬法,將默認樹讀入保存,每次讀入生成一個新樹,再遞回去判斷每個節點位置是否相同,不同回傳false,相同回傳判斷左右兩個子樹是否相同的合取運算,如果傳入的兩個樹都空,回傳true,其中一個不慷訓傳false,都不空再去判斷
代碼實作:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct TreeNode* BinTree;
#define ElementType int
struct TreeNode
{
ElementType Data;
BinTree Left;
BinTree Right;
};
//insert
BinTree insert(ElementType X, BinTree BST) {
//if the tree is empty,creat a node and return
if (!BST) {
BST = (BinTree)malloc(sizeof(struct TreeNode));
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/X;
BST->Left = NULL;
BST->Right = NULL;
}
else {
//the X is big than now position, insert X in its right tree
if (X > BST->Data) {
BST->Right = insert(X, BST->Right);
}
//the X is small than now position, insert X in its left tree
else if (X < BST->Data) {
BST->Left = insert(X, BST->Left);
}
//when the X already in the tree, do nothing
else {
}
}
return BST;
}
bool check(BinTree T1, BinTree T2) {
if (T1==NULL && T2==NULL) {
return true;
}
if(T1->Data==T2->Data){
return check(T1->Left, T2->Left)&&check(T1->Right, T2->Right);
}
return false;
}
int main()
{
int n, l;
while (scanf("%d", &n)) {
if (n == 0) {
break;
}
scanf("%d", &l);
BinTree OT = NULL;
for (int i = 0; i < n; i++) {
int temp;
scanf("%d", &temp);
OT = insert(temp, OT);
}
for (int j = 0; j < l; j++) {
BinTree TestTree = NULL;
for (int i = 0; i < n; i++) {
int temp;
scanf("%d", &temp);
TestTree = insert(temp, TestTree);
}
if (!check(OT, TestTree)) {
printf("No\n");
}
else {
printf("Yes\n");
}
}
}
}
04-樹5 Root of AVL Tree (25point(s))
An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.
Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤20) which is the total number of keys to be inserted. Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print the root of the resulting AVL tree in one line.
Sample Input 1:
5
88 70 61 96 120
Sample Input 1:
70
決議:題目的意思是,讓你構建一顆2叉平衡樹,給定你插入序列,讓你輸出他的根節點,emm....那就模擬做一顆AVL(思路課程已經說了,就是把代碼轉化成程式的程序)
代碼:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef struct TreeNode* BinTree;
#define ElementType int
struct TreeNode
{
ElementType Data;
BinTree Left;
BinTree Right;
int Height;
};
int getHeight(BinTree T) {
if (T->Left == NULL && T->Right == NULL) {
return 1;
}
else if (T->Left != NULL && T->Right == NULL) {
return T->Left->Height+1;
}
else if (T->Left == NULL && T->Right != NULL) {
return T->Right->Height + 1;
}
else {
return max(T->Left->Height, T->Right->Height)+1;
}
}
BinTree RR(BinTree T) {
BinTree right = T->Right;
T->Right = right->Left;
right->Left = T;
right->Height = getHeight(right);
T->Height = getHeight(T);
return right;
}
BinTree LL(BinTree T) {
BinTree left = T->Left;
T->Left = left->Right;
left->Right = T;
left->Height = getHeight(left);
T->Height = getHeight(T);
return left;
}
BinTree LR(BinTree T) {
T->Left = RR(T->Left);
return LL(T);
}
BinTree RL(BinTree T) {
T->Right = LL(T->Right);
return RR(T);
}
//insert
BinTree Insert(BinTree BST, ElementType X) {
//if the tree is empty,creat a node and return
if (!BST) {
BST = (BinTree)malloc(sizeof(struct TreeNode));
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/X;
BST->Left = NULL;
BST->Right = NULL;
BST->Height = 0;
}
else {
//the X is big than now position, insert X in its right tree
if (X > BST->Data) {
BST->Right = Insert(BST->Right, X);
int h1, h2;
if (BST->Left == NULL) {
h1 = 0;
}
else {
h1 = BST->Left->Height;
}
if (BST->Right == NULL) {
h2 = 0;
}
else {
h2 = BST->Right->Height;
}
//the tree is not avl
if (abs(h1-h2)==2) {
//LL
if (X < BST->Right->Data) {
BST = RL(BST);
}
//LR
else {
BST = RR(BST);
}
}
}
//the X is small than now position, insert X in its left tree
else if (X < BST->Data) {
BST->Left = Insert(BST->Left, X);
int h1, h2;
if (BST->Left == NULL) {
h1 = 0;
}
else {
h1 = BST->Left->Height;
}
if (BST->Right == NULL) {
h2 = 0;
}
else {
h2 = BST->Right->Height;
}
if (abs(h1-h2) == 2) {
//RR
if (X > BST->Left->Data) {
BST = LR(BST);
}
//RL
else {
BST = LL(BST);
}
}
}
//when the X already in the tree, do nothing
else {
}
}
BST->Height = getHeight(BST);
return BST;
}
int main(void) {
int n;
scanf("%d", &n);
BinTree BST = NULL;
for (int i = 0; i < n; i++)
{
int temp;
scanf("%d", &temp);
BST = Insert(BST, temp);
}
printf("%d\n", BST->Data);
return 0;
}
04-樹7 二叉搜索樹的操作集 (30point(s))
本題要求實作給定二叉搜索樹的5種常用操作,
函式介面定義:
BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );
其中BinTree結構定義如下:
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
- 函式Insert將X插入二叉搜索樹BST并回傳結果樹的根結點指標;
- 函式Delete將X從二叉搜索樹BST中洗掉,并回傳結果樹的根結點指標;如果X不在樹中,則列印一行Not Found并回傳原樹的根結點指標;
- 函式Find在二叉搜索樹BST中找到X,回傳該結點的指標;如果找不到則回傳空指標;
- 函式FindMin回傳二叉搜索樹BST中最小元結點的指標;
- 函式FindMax回傳二叉搜索樹BST中最大元結點的指標,
裁判測驗程式樣例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
void PreorderTraversal( BinTree BT ); /* 先序遍歷,由裁判實作,細節不表 */
void InorderTraversal( BinTree BT ); /* 中序遍歷,由裁判實作,細節不表 */
BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );
int main()
{
BinTree BST, MinP, MaxP, Tmp;
ElementType X;
int N, i;
BST = NULL;
scanf("%d", &N);
for ( i=0; i<N; i++ ) {
scanf("%d", &X);
BST = Insert(BST, X);
}
printf("Preorder:"); PreorderTraversal(BST); printf("\n");
MinP = FindMin(BST);
MaxP = FindMax(BST);
scanf("%d", &N);
for( i=0; i<N; i++ ) {
scanf("%d", &X);
Tmp = Find(BST, X);
if (Tmp == NULL) printf("%d is not found\n", X);
else {
printf("%d is found\n", Tmp->Data);
if (Tmp==MinP) printf("%d is the smallest key\n", Tmp->Data);
if (Tmp==MaxP) printf("%d is the largest key\n", Tmp->Data);
}
}
scanf("%d", &N);
for( i=0; i<N; i++ ) {
scanf("%d", &X);
BST = Delete(BST, X);
}
printf("Inorder:"); InorderTraversal(BST); printf("\n");
return 0;
}
/* 你的代碼將被嵌在這里 */
輸入樣例:
10
5 8 6 2 4 1 0 10 9 7
5
6 3 10 0 5
5
5 7 0 10 3
輸出樣例
Preorder: 5 2 1 0 4 8 6 7 10 9
6 is found
3 is not found
10 is found
10 is the largest key
0 is found
0 is the smallest key
5 is found
Not Found
Inorder: 1 2 4 6 8 9
決議:實作的函式即為本章第1節部分的內容,跟著思路寫就好
代碼:
//find max value
Position FindMax(BinTree BST) {
if (!BST) {
return NULL;
}
while (BST->Right) {
BST = BST->Right;
}
return BST;
}
//find min value
Position FindMin(BinTree BST) {
if (!BST) {
return NULL;
}
while (BST->Left) {
BST = BST->Left;
}
return BST;
}
//find in value
Position Find(BinTree BST, ElementType X) {
//the tree is empty ,return NULL;
while (BST) {
//the X is big than now position's value, may be in the right or doesn't has.
if (X > BST->Data) {
BST = BST->Right;
}
else if (X < BST->Data) {
BST = BST->Left;
}
else {
return BST;
}
}
return NULL;
}
//insert
BinTree Insert(BinTree BST, ElementType X) {
//if the tree is empty,creat a node and return
if (!BST) {
BST = malloc(sizeof(struct TNode));
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/X;
BST->Left = NULL;
BST->Right = NULL;
}
else {
//the X is big than now position, insert X in its right tree
if (X > BST->Data) {
BST->Right = Insert(BST->Right, X);
}
//the X is small than now position, insert X in its left tree
else if (X < BST->Data) {
BST->Left = Insert(BST->Left, X);
}
//when the X already in the tree, do nothing
else {
}
}
return BST;
}
//delete
BinTree Delete(BinTree BST, ElementType X) {
if (!BST) {
printf("Not Found\n");
}
else {
//the X is big than now position, delete X in its right tree
if (X > BST->Data) {
BST->Right = Delete(BST->Right, X);
}
//the X is small than now position, delete X in its left tree
else if (X < BST->Data) {
BST->Left = Delete(BST->Left, X);
}
//find the X positon
else {
//has two sub tree
if (BST->Left && BST->Right) {
BinTree temp = FindMax(BST->Left);
BST->Data = https://www.cnblogs.com/cs-Miscellaneous/p/temp->Data;
temp->Data = X;
Delete(BST->Left, X);
}
//has one or no sub tree
else {
BinTree temp = BST;
//don't has left sub tree
if (!BST->Left) {
BST = BST->Right;
}
//don't has right sub tree
else if (!BST->Right) {
BST = BST->Left;
}
free(temp);
}
}
}
return BST;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/203289.html
標籤:C++
