二叉搜索樹(二叉排序樹/二叉查找樹)的總結(數據結構7.3.1)

//二叉搜索樹(二叉排序樹/二叉查找樹)(BST)(數據結構7.3.1)
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
using namespace std;
//鏈式結構結構體
typedef int KeyType;
typedef char InfoType;
typedef struct{
	KeyType key;//關鍵字項,用來排序的項
	InfoType name[20];//其他數據項
}ElemType;
typedef struct BSTNode{
	ElemType data;
	struct BSTNode * lchild,* rchild;//數據類型是BSTreet前面記得都加*變成指針
}BSTNode, *BSTree;

//遞歸查找
BSTree SearchBST(BSTree T, KeyType key){
	//邊界條件
	//如果樹空或根節點就是關鍵字代表的節點
	//返回空就是查找失敗
	if ((!T) || key == T->data.key)return T;
	//如果比根小,返回左子樹查找的結果(遞歸)
	else if (key < T->data.key)return SearchBST(T->lchild, key);
	//如果比根大,返回右子樹查找的結果(遞歸)
	else if (key > T->data.key)return SearchBST(T->rchild, key);
}

//遞歸插入
void InsertBST(BSTree &T, ElemType e){
	//邊界條件
	//如果該節點不存在,就在此插入該元素
	if (!T){
		//BSTree S = (BSTree)malloc(sizeof(BSTNode));
		//這次用new運算符實現動態內存規劃,用new用完該空間記得用delete釋放
		BSTree S = new BSTNode;//生成新節點*S並分配內存空間
		S->data = e;
		S->lchild = S->rchild = NULL;//新節點*S作爲葉子節點
		T = S;
	}
	//如果比根小,遞歸到左子樹上插入
	else if (e.key < T->data.key) 
		InsertBST(T->lchild, e);
	//如果比根大,遞歸到右子樹上插入
	else if (e.key > T->data.key) 
		InsertBST(T->rchild, e);
	return;
}

//創建
void CreatBST(BSTree &T){
	//將二叉樹T初始化爲空樹
	T = NULL;
	ElemType e;
	cout << "序號:";
	cin >> e.key;
	cout << "名字:";
	cin >> e.name;
	//getchar();
	while (e.key != -1){
		InsertBST(T, e);
		cout << "序號:";
		cin >> e.key;
		cout << "名字:";
		cin >> e.name;
		//getchar();
	}
	return;
}

//刪除
void DeleteBST(BSTree &T, KeyType key){
	/*查找刪除的節點
		p爲要刪除的節點(重接下面的樹),f爲p的前驅(掛接上面的樹)*/
	BSTree p = T,f=NULL;
	while (p){
		if (p->data.key == key)break;
		f = p;
		if (key < p->data.key)p = p->lchild;
		else if (key > p->data.key)p = p->rchild;
	}
	if (p == NULL){//未找到
		printf("未找到\n");
		return;
	}
	
	/*p爲要刪除的節點,s是p的前驅(比p小的臨點),q是s的父親
			三種情況:左右子樹都不空,無左子樹,無右子樹*/

	BSTree q = p;

	//1.若左右子樹都不空

	if (p->lchild&&p->rchild){
		BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
		while (s->rchild){
			q = s;
			s = s->rchild;
		}
		p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
		
		/*1.1p值變爲s,轉爲刪除s*/

		//1.2重接s(s是右子樹爲空的情況)
		BSTree temp = s;//用來釋放s的空間
		s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹

		//1.3和q掛接:s爲重接好的樹(要接替被刪節點temp的位置),q爲temp的父親(掛接到重接的樹上),temp保留的是被刪節點
		if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
		//p是temp的父親所以沒有被刪節點爲根節點的情況
		/*相同:
		if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
		
		delete temp;//釋放被刪除節點temp的空間
		return;
	}
	
	//2.無左子樹或無右子樹
	//2.1重接p
	//若無左子樹,重接右子樹
	else if (!p->lchild){
		p = p->rchild;
	}
	//若無右子樹,重接左子樹
	else if (!p->rchild){
		p = p->lchild;
	}

	//2.2和f掛接:p爲重接好的樹(要接替被刪節點q的位置),f爲q的父親(掛接到重接的樹上),q保留的是被刪節點
	if (!f)T = p;//被刪節點爲根節點
	else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
	else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
	delete q;//q保留的是重接前的待釋放的被刪除節點
	return;
}

void InorderTraversal(BSTree T){
	if (T == NULL)return;
	InorderTraversal(T->lchild);
	cout << T->data.name << " ";
	InorderTraversal(T->rchild);
	return;
}

int main(){
	BSTree BST;
	CreatBST(BST);
	KeyType key;
	cout << "刪除序號:";
	cin >> key;
	while (key != -1){
		DeleteBST(BST, key);
		InorderTraversal(BST);
		cout << endl<<"刪除序號:";
		cin >> key;
	}

	system("PAUSE");
	return 0;
}

0.結構體

//鏈式結構結構體
typedef int KeyType;
typedef char InfoType;
typedef struct{
	KeyType key;//關鍵字項,用來排序的項
	InfoType name[20];//其他數據項
}ElemType;
typedef struct BSTNode{
	ElemType data;
	struct BSTNode * lchild,* rchild;//數據類型是BSTreet前面記得都加*變成指針
}BSTNode, *BSTree;

1.遞歸查找

1.邊界條件如果樹空或根節點就是關鍵字代表的節點
2.如果比根小,返回左子樹查找的結果(遞歸)
3.如果比根大,返回右子樹查找的結果(遞歸)
4.返回空就是查找失敗

BSTree SearchBST(BSTree T, KeyType key){
	//邊界條件
	//如果樹空或根節點就是關鍵字代表的節點
	//返回空就是查找失敗
	if ((!T) || key == T->data.key)return T;
	//如果比根小,返回左子樹查找的結果(遞歸)
	else if (key < T->data.key)return SearchBST(T->lchild, key);
	//如果比根大,返回右子樹查找的結果(遞歸)
	else if (key > T->data.key)return SearchBST(T->rchild, key);
}

2.遞歸插入(不允許插入相同key,不符合二叉搜索樹定義)

1.如果該節點不存在就在此插入元素(邊界)
2.如果key大於該點的key,遞歸進入該點的右孩子
3.如果key小於該點的key,遞歸進入該點的左孩子
用new實現動態內存規劃更簡單
(BSTree)malloc(sizeof(BSTNode));等同於new BSTNode;
用new運算符實現動態內存規劃,用new用完該空間也記得用delete釋放

//遞歸插入
void InsertBST(BSTree &T, ElemType e){
	//邊界條件
	//如果該節點不存在,就在此插入該元素
	if (!T){
		//BSTree S = (BSTree)malloc(sizeof(BSTNode));
		//這次用new運算符實現動態內存規劃,用new用完該空間記得用delete釋放
		BSTree S = new BSTNode;//生成新節點*S並分配內存空間
		S->data = e;
		S->lchild = S->rchild = NULL;//新節點*S作爲葉子節點
		T = S;
	}
	//如果比根小,遞歸到左子樹上插入
	else if (e.key < T->data.key) 
		InsertBST(T->lchild, e);
	//如果比根大,遞歸到右子樹上插入
	else if (e.key > T->data.key) 
		InsertBST(T->rchild, e);
	return;
}

3.創建二叉排序樹

1.將二叉樹初始化爲空樹
2.寫入第一個要插入的數
3.進入循環循環直到遇到結束標識符(此處我寫的是key==-1結束循環)
4.在循環中不斷使用遞歸插入

void CreatBST(BSTree &T){
	//將二叉樹T初始化爲空樹
	T = NULL;
	ElemType e;
	cout << "序號:";
	cin >> e.key;
	cout << "名字:";
	cin >> e.name;
	//getchar();
	while (e.key != -1){
		InsertBST(T, e);
		cout << "序號:";
		cin >> e.key;
		cout << "名字:";
		cin >> e.name;
		//getchar();
	}
	return;
}

4.刪除

1.查找刪除節點

若未找到直接退出

p爲要刪除的節點(重接下面的樹),f爲p的父親(掛接上面的樹)

BSTree p = T,f=NULL;
	while (p){
		if (p->data.key == key)break;
		f = p;
		if (key < p->data.key)p = p->lchild;
		else if (key > p->data.key)p = p->rchild;
	}
	if (p == NULL){//未找到
		printf("未找到\n");
		return;
	}

2.以p爲根重接下面的樹

p爲要刪除的節點,s是p的前驅(比p小的臨點),q是s的父親

2.0三種情況:左右子樹都不空,無左子樹,無右子樹
在這裏插入圖片描述

	BSTree q = p;

2.1若左右子樹都不空

2.1.1p值變爲s,轉爲刪除s

2.1.2重接s(s是右子樹爲空的情況)

2.1.3和q掛接

s爲重接好的樹(要接替被刪節點temp的位置);
q爲temp的父親(掛接到重接的樹上);
temp保留的是被刪節點

2.1.4釋放被刪除節點temp的空間

在這裏插入圖片描述

	if (p->lchild&&p->rchild){
		BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
		while (s->rchild){
			q = s;
			s = s->rchild;
		}
		p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
		
		/*1.1p值變爲s,轉爲刪除s*/

		//1.2重接s(s是右子樹爲空的情況)
		BSTree temp = s;//用來釋放s的空間
		s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹

		//1.3和q掛接:s爲重接好的樹(要接替被刪節點temp的位置),q爲temp的父親(掛接到重接的樹上),temp保留的是被刪節點
		if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
		//p是temp的父親所以沒有被刪節點爲根節點的情況
		/*相同:
		if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
		
		delete temp;//釋放被刪除節點temp的空間
		return;
	}

2.2若無左子樹,重接右子樹;若無右子樹,重接左子樹

無左子樹說明取代p的一定是p->rchild;
無右子樹說明取代p的一定是p->lchild

else if (!p->lchild){
		p = p->rchild;
	}
else if (!p->rchild){
		p = p->lchild;
	}

2.3和f掛接(左右子樹都不空 不需要這一步)

p爲重接好的樹(要接替被刪節點q的位置);
f爲q的父親(掛接到重接的樹上);
temp保留的是被刪節點

if (!f)T = p;//被刪節點爲根節點
	else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
	else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
	delete q;//q保留的是重接前的待釋放的被刪除節點
	return;

刪除的函數整體一覽

void DeleteBST(BSTree &T, KeyType key){
	BSTree p = T,f=NULL;
	while (p){
		if (p->data.key == key)break;
		f = p;
		if (key < p->data.key)p = p->lchild;
		else if (key > p->data.key)p = p->rchild;
	}
	if (p == NULL){//未找到
		printf("未找到\n");
		return;
	}
	
	BSTree q = p;
	if (p->lchild&&p->rchild){
		BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
		while (s->rchild){
			q = s;
			s = s->rchild;
		}
		p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
		
		BSTree temp = s;//用來釋放s的空間
		s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹

		if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
		//p是temp的父親所以沒有被刪節點爲根節點的情況
		/*相同:
		if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
		else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
		
		delete temp;//釋放被刪除節點temp的空間
		return;
	}
	
	//若無左子樹,重接右子樹
	else if (!p->lchild){
		p = p->rchild;
	}
	//若無右子樹,重接左子樹
	else if (!p->rchild){
		p = p->lchild;
	}
	if (!f)T = p;//被刪節點爲根節點
	else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
	else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
	delete q;//q保留的是重接前的待釋放的被刪除節點
	return;
}

5.中序遍歷(遍歷二叉排序樹應是序號按從小到大輸出的)

void InorderTraversal(BSTree T){
	if (T == NULL)return;
	InorderTraversal(T->lchild);
	cout << T->data.name << " ";
	InorderTraversal(T->rchild);
	return;
}

主函數

功能比較簡單

int main(){
	BSTree BST;
	CreatBST(BST);
	KeyType key;
	cout << "刪除序號:";
	cin >> key;
	while (key != -1){
		DeleteBST(BST, key);
		InorderTraversal(BST);
		cout << endl<<"刪除序號:";
		cin >> key;
	}

	system("PAUSE");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章