1. 程式人生 > >抽象性設計——用C語言實現B樹的基本操作

抽象性設計——用C語言實現B樹的基本操作

這次做的是資料結構的一個抽象性實驗,我選擇的是B樹的基本操作。

編譯環境是:VS 2015

BTree.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>


#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define OK 1
#define ERROR 0
#define M 20//定義階數最大值




typedef int KeyType;
typedef int Status;
typedef struct {  //記錄的結構定義
KeyType key;
char data;
}Record;


typedef struct BTNode{        //B樹結點型別定義
int keynum;      //結點中關鍵字個數,即結點的大小
KeyType key[M + 1]; //關鍵字,key[0]未用
struct BTNode *parent; //雙親結點指標
struct BTNode *ptr[M + 1];//孩子結點指標陣列
Record *recptr[M + 1];    //記錄指標向量,0號單元未用
}BTNode,*BTree;  //B樹結點和B樹型別


typedef struct {
BTree pt;  //指向找到的結點
int i;  //1<=i<=m,在結點中的關鍵字位序
int tag;  //1:查詢成功,0:查詢失敗
}result,*resultPtr;  //B樹的查詢結果型別


//介面定義


void CreatBTree(BTree&T, int n, int m);
/*
初始條件:初始化關鍵字個數n大於等於0,B樹的階數m大於3小於等於20
操作結果:構建一顆階數為m,含有n個關鍵字的B樹
*/


void SearchBTree(BTree T, int k, result &r);
/*
初始條件:樹T存在
操作結果:在m階B數T上查詢關鍵字k,返回p{pt,i,tag}
*/


void InsertBTree(BTree &T, int k, BTree q, int i,int m);
/*
初始條件:樹T存在
操作結果:在B樹T上結點p->pt的key[i]和key[i+1]之間插入關鍵字k
*/


void DeleteBTree(BTree p, int i, int m, BTree &T);
/*
初始條件:B樹上p結點存在
操作結果:刪除B樹T上結點p->pt的關鍵字k
*/


void PrintBTree(BTree T);
/*
初始條件:樹T存在
操作結果:中序遍歷B樹
*/


void DestroyBTree(BTree T);
/*
初始條件:樹T存在
操作結果:銷燬B樹
*/


int menu();
/*
輸出選擇選單
*/


OperationDefine.cpp

#include "BTree.h"


void CreatBTree(BTree &T, int n, int m) {//構建一顆階數為m,含有n個關鍵字的B樹(3<=m<=M,0<=n<=10000)
//建立B樹
int i, j;
resultPtr p = NULL;
p = (result*)malloc(sizeof(result));
srand((unsigned)time(NULL));
if (n == 0)
printf("已成功初始化一棵空樹。\n");
else {
for (j = 0; j < n; j++) {
i = rand() % 1000;//生成隨機數i
SearchBTree(T, i, *p);//查詢i插入位置
InsertBTree(T, i, p->pt, p->i, m);  //進行插入
}
printf("建立B樹成功!\n");
}
}


void PrintBTree(BTree T) {
//中序遍歷B樹
int i = 1;
if (NULL != T) {
for (; i <= T->keynum; i++) {
PrintBTree(T->ptr[i - 1]);
printf("%d  ", T->key[i]);
}
PrintBTree(T->ptr[i - 1]);
}
}


int Search(BTree p, int k) {
int i = 1;
while (i <= p->keynum&&k > p->key[i])
i++;
return i;
}


void SearchBTree(BTree T, int k, result &r) {
//在m階B樹T上查詢關鍵字k,返回(pt,i,tag)
//若查詢成功,則特徵值tag=1,指標pt所致結點中第i個關鍵字等於k;否則
//特徵值tag=0,等於k的關鍵字記錄應插入在指標pt所指結點中第i-1個和第i個關鍵字間
int i = 0, found = 0;
BTree p = T, q = NULL;
while (p != NULL && 0 == found) {
i = Search(p, k);//在p->key[1..keynum]中查詢p->key[i-1]<k<=p->p->key[i]
if (i > 0 && p->key[i] == k)
found = 1;//找到待查關鍵字
else{
q = p;
p = p->ptr[i - 1];
}
}
if (1 == found) {//查詢成功
r.pt = p;
r.i = i;
r.tag = 1;
}
else {//查詢不成功,返回key的插入位置i
r.pt = q;
r.i = i;
r.tag = 0;
} 
}


void split(BTree &q, int s, BTree &ap) {
//將q結點分裂成兩個結點,前一半保留,後一半移入新結點ap
int i, j, n = q->keynum;
ap = (BTNode*)malloc(sizeof(BTNode));//生成新結點ap
ap->ptr[0] = q->ptr[s];
for (i = s + 1, j = 1; i <= n; i++, j++) {//後一半移入ap結點
ap->key[j] = q->key[i];
ap->ptr[j] = q->ptr[i];
}
ap->keynum = n - s;
ap->parent = q->parent;
for (i = 0; i <= n - s; i++) {
if (ap->ptr[i])
ap->ptr[i]->parent = ap;//將ap所有孩子結點指向ap
}
q->keynum = s - 1;//q結點的前一半保留,修改keynum
}


void newroot(BTree &T, BTree p, int x, BTree ap) {//生成新的根結點
T = (BTNode*)malloc(sizeof(BTNode));
T->keynum = 1;
T->ptr[0] = p;
T->ptr[1] = ap;
T->key[1] = x;
if (p != NULL) p->parent = T;
if (ap != NULL) ap->parent = T;
T->parent = NULL;//新根的雙親是空指標
}


void Insert(BTree &q, int i, int x, BTree ap) {//x和ap分別插到q->key[i]和q->ptr[i]
int j, n = q->keynum;
for (j = n; j >= i; j--) {
q->key[j + 1] = q->key[j];//關鍵字指標向後移一位
q->ptr[j + 1] = q->ptr[j];//孩子結點指標向後移一位
}
q->key[i] = x;//賦值
q->ptr[i] = ap;
if (ap != NULL) ap->parent = q;
q->keynum++;//關鍵字數+1
}


void InsertBTree(BTree &T, int k, BTree q, int i, int m) {
//在B樹T上q結點的key[i-1]和key[i]之間插入關鍵字k
//若引起結點過大,則沿雙親指標進行必要的結點分裂調整,使T仍是m階的B樹
int x, s, finished = 0, neednewroot = 0;
BTree ap;
if (NULL == q)//q為空,則新建根結點
newroot(T, NULL, k, NULL);
else {
x = k;
ap = NULL;
while (0 == neednewroot && 0 == finished) {
Insert(q, i, x, ap);//key和ap分別插到q->key[i]和q->ptr[i]
if (q->keynum < m) finished = 1;//插入完成
else {//分裂q結點
s = (m + 1) / 2;
split(q, s, ap);
x = q->key[s];
if (q->parent != NULL) {
q = q->parent;
i = Search(q, x);//在雙親結點中查詢x的插入位置
}
else neednewroot = 1;
}
}//while
if (1 == neednewroot)//T是空樹或者根結點已分裂為q和ap結點
newroot(T, q, x, ap);//生成含資訊(q,x,ap)的新的根結點T
}
}


void Successor(BTree &p, int i) {//由後繼最下層非終端結點的最小關鍵字代替結點中關鍵字key[i]。
BTNode *temp;
temp = p->ptr[i];
for (; NULL != temp->ptr[0]; temp = temp->ptr[0]) ;//找出關鍵字的後繼
p->key[i] = temp->key[1];
p = temp;
}


void Remove(BTree &p, int i) {   //從結點p中刪除key[i]
int j;
int n = p->keynum;
for (j = i; j < n; j++) {  //關鍵字左移
p->key[j] = p->key[j + 1];
p->ptr[j] = p->ptr[j + 1];
}
p->keynum--;
}


void Restore(BTree &p, int i, int m, BTree &T) {//調整B樹
int j;
BTree ap = p->parent;
BTree lc, rc, pr;
int finished = 0, r = 0;
while (0 == finished) {
r = 0;
while (ap->ptr[r] != p)//確定p在ap子樹的位置
r++;
if (r == 0) {
r++;
lc = NULL;
rc = ap->ptr[r];
}
else if (r == ap->keynum) {
rc = NULL;
lc = ap->ptr[r - 1];
}
else {
lc = ap->ptr[r - 1];
rc = ap->ptr[r + 1];
}
if (r > 0 && lc != NULL && (lc->keynum > (m - 1) / 2)) {//向左兄弟借關鍵字
p->keynum++;
for (j = p->keynum; j > 1; j--) {//結點關鍵字右移
p->key[j] = p->key[j - 1];
p->ptr[j] = p->ptr[j - 1];
}
p->key[1] = ap->key[r];//父親插入到結點
p->ptr[1] = p->ptr[0];
p->ptr[0] = lc->ptr[lc->keynum];
if (NULL != p->ptr[0])//修改p中的子女的父結點為p
p->ptr[0]->parent = p;
ap->key[r] = lc->key[lc->keynum];//左兄弟上移到父親位置
lc->keynum--;
finished = 1;
break;
}
else if (ap->keynum > r&&rc != NULL && (rc->keynum > (m - 1) / 2)) {
p->keynum++;
p->key[p->keynum] = ap->key[r];//父親插入到結點
p->ptr[p->keynum] = rc->ptr[0];
if (NULL != p->ptr[p->keynum]) {//修改p中的子女的父結點為p
p->ptr[p->keynum]->parent = p;
}
ap->key[r] = rc->key[1];//右兄弟上移到父親位置
rc->ptr[0] = rc->ptr[1];
for (j = 1; j < rc->keynum; j++) {//右兄弟結點關鍵字左移
rc->key[j] = rc->key[j + 1];
rc->ptr[j] = rc->ptr[j + 1];
}
rc->keynum--;
finished = 1;
break;
}
r = 0;
while (ap->ptr[r] != p) r++;//重新確定p在ap子樹的位置
if (r > 0 && (ap->ptr[r - 1]->keynum <= (m - 1) / 2)) {//與左兄弟合併
lc = ap->ptr[r - 1];
p->keynum++;
for (j = p->keynum; j > 1; j--) {//將p結點關鍵字和指標右移1位
p->key[j] = p->key[j - 1];
p->ptr[j] = p->ptr[j - 1];
}
p->key[1] = ap->key[r];//父結點的關鍵字與p合併
p->ptr[1] = p->ptr[0];//從左兄弟右移一個指標
ap->ptr[r + 1] = lc;
for (j = 1; j <= lc->keynum + p->keynum; j++) {//將結點p中關鍵字移到p左兄弟中
lc->key[lc->keynum + j] = p->key[j];
lc->ptr[lc->keynum + j] = p->ptr[j];
}
if (p->ptr[0]) {//修改p中的子女的父結點為lc
for (j = 1; j <= p->keynum; j++) {
p->ptr[p->keynum + j]->parent = lc;
}
}
lc->keynum = lc->keynum + p->keynum;//合併後的關鍵字個數
ap->keynum--;
pr = p;
free(pr);//釋放p結點空間
pr = NULL;
p = lc;
}
else {//與右兄弟合併
rc = ap->ptr[r + 1];
if (r == 0) r++;
p->keynum++;
p->key[p->keynum] = ap->key[r];//父結點的關鍵字與p合併
p->ptr[p->keynum] = rc->ptr[0];//從右兄弟左移一個指標
rc->keynum = p->keynum + rc->keynum;//合併後關鍵字的個數
ap->ptr[r - 1] = rc;
for (j = 1; j <= (rc->keynum - p->keynum); j++) {//將p右兄弟的關鍵字和指標右移
rc->key[p->keynum + j] = rc->key[j];
rc->ptr[p->keynum + j] = rc->ptr[j];
}
for (j = 1; j <= p->keynum; j++) {//將結點p中關鍵字和指標移到p右兄弟中
rc->key[j] = p->key[j];
rc->ptr[j] = p->ptr[j];
}
rc->ptr[0] = p->ptr[0];
if (p->ptr[0]) {//修改p中的子女的父結點為rc
for (j = 1; j <= p->keynum; j++) {
p->ptr[p->keynum + j]->parent = rc;
}
}
for (j = r; j < ap->keynum; j++) {//將父結點中關鍵字和指標左移
ap->key[j] = ap->key[j + 1];
ap->ptr[j] = ap->ptr[j + 1];
}
ap->keynum--;//父結點的關鍵字個數減1
pr = p;
free(pr);//釋放p結點空間
pr = NULL;
p = rc;
}
ap = ap->parent;
if (p->parent->keynum >= (m - 1) / 2 || (NULL == ap&&p->parent->keynum > 0)) {
finished = 1;
}
else if (NULL == ap) {//若調整後出現空的根結點,則刪除該根結點,樹高減1
pr = T;
T = p;//根結點下移
free(pr);
pr = NULL;
finished = 1;
}
p = p->parent;
}
}


void DeleteBTree(BTree p, int i, int m, BTree &T) {
//刪除B樹上p結點第i個關鍵字
if (p->ptr[i - 1] != NULL) {
Successor(p, i);         //若不是最下層非終端結點
DeleteBTree(p, 1, m, T);      //由後繼最下層非終端結點的最小關鍵字代替它
}
else {//若是最下層非終端結點
Remove(p, i);  //從結點p中刪除key[i]
if (p->keynum < (m - 1) / 2)  //刪除後關鍵字個數小於(m-1)/2
Restore(p, i, m, T); //調整B樹
}
}


void DestroyBTree(BTree T) {
int i = 1;
if (NULL != T) {
for (; i <= T->keynum; i++) {
DestroyBTree(T->ptr[i - 1]);
free(T->ptr[i - 1]);
}
DestroyBTree(T->ptr[i - 1]);
}
}


int menu() {//選單
int choice;
printf("\n\n\t\t\t|**********************************************|\n");
printf("\t\t\t|**********************************************|\n");
printf("\t\t\t _____________請先建立B樹再進行操作!__________\n");
printf("\t\t\t|                  B樹測試介面                 |\n");
printf("\t\t\t|                                              |\n");
printf("\t\t\t|   1.建立B樹            2.B樹結點的查詢       |\n");
printf("\t\t\t|                                              |\n");
printf("\t\t\t|   3.B樹結點的插入      4.B樹結點的刪除       |\n");
printf("\t\t\t|                                              |\n");
printf("\t\t\t|   5.B樹的遍歷          6.B樹的銷燬           |\n");
printf("\t\t\t|                                              |\n");
printf("\t\t\t|   0.退出                                     |\n");
printf("\t\t\t|______________________________________________|\n");
printf("\t\t\t|**********************************************|\n");
printf("\t\t\t|**********************************************|\n");
printf("\t\t\t|              15軟體工程(4)班                 |\n");
printf("\t\t\t|                 3115005372                   |\n");
printf("\t\t\t|                   楊宇傑                     |\n");
printf("\t\t\t|********************S**************************|\n");
do {
printf("\t\t\t請選擇功能(輸入1-6任意一個數字):");
scanf_s("%d", &choice);
} while (choice<0||choice>6);//避免非法輸入
return choice;
}


BTree_Test.cpp

#include<stdio.h>
#include"BTree.h"


int main() {
BTree T=NULL;
result r;
int choice, k, i, m, n;

do{
choice = menu();
if (choice >= 0 && choice <= 7) {
system("cls");//把選單清除
switch (choice) {
case 1:
printf("請輸入B樹的階數m:(3<=m<=20)\n");
scanf_s("%d", &m);
printf("請輸入B樹的初始化關鍵字個數:(0<=n<=10000)\n");
scanf_s("%d", &n);
CreatBTree(T, n, m);
break;
case 2:
printf("請輸入要查詢的關鍵字:\n");
scanf_s("%d", &k);
SearchBTree(T, k, r);
if (r.tag) {
printf("該關鍵字的位置為該結點中第%d個關鍵字\n",r.i);
}
else {
printf("該關鍵字不存在!\n");
}
break;
case 3:
printf("請輸入要插入的關鍵字k:\n");
scanf_s("%d", &k);
SearchBTree(T, k, r);
InsertBTree(T, k, (&r)->pt, (&r)->i, m);
printf("插入成功!\n");
break;
case 4:
printf("請輸入要刪除B樹T上的關鍵字:\n");
scanf_s("%d", &i);
SearchBTree(T, i, r);
DeleteBTree(r.pt, r.i, m, T);
printf("刪除成功!\n");
break;
case 5:
printf("此時的B樹序列為:\n");
PrintBTree(T);
printf("\n");
break;
case 6:
DestroyBTree(T);
printf("銷燬成功!\n");
break;
default:;
}
}
}while (choice > 0 && choice < 7);
return 0;
}


相關推薦

抽象性設計——C語言實現B基本操作

這次做的是資料結構的一個抽象性實驗,我選擇的是B樹的基本操作。 編譯環境是:VS 2015 BTree.h #include<stdio.h> #include<stdlib.h> #include<time.h> #defi

C語言實現線性表基本操作

style eal struct fine fin delete class logs destroy #include <stdio.h> #include <tchar.h> #include <stdlib.h> #define

C語言-二叉基本操作以及二叉搜尋基本操作

功能 二叉樹操作: 建立二叉樹 遍歷二叉樹(前序,中序,後續) 計算高度 計算結點數目 清空二叉樹 空樹判斷 二叉搜尋樹操作: 插入 最值(最大值,最小值) 刪除 程式碼 #include &l

超全C語言二叉基本操作及講解

今天刷LeetCode上的題的時候,做到了關於二叉樹的題,於是決定把這一塊的知識整理一下。1、二叉樹的定義二叉樹通常以結構體的形式定義,如下,結構體內容包括三部分:本節點所儲存的值、左孩子節點的指標、右孩子節點的指標。這裡需要注意,子節點必須使用指標,就像我們定義結構體連結串

嚴蔚敏資料結構C語言實現棧的基本操作

int main(){   SqStack S;   SElemType *e;   int n,i;   InitStack(&S);   printf("請輸入需要入棧的資料個數\n");   scanf("%d",&n);   for(i=0;i<n;i++)   {    sca

c語言實現圖的基本操作--鄰接矩陣儲存

利用鄰接矩陣容易判定任意兩個頂點之間是否有邊(或弧)相連,並容易求得各個頂點的度。 c語言程式碼實現如下: #include<stdio.h> #include<stdlib.h> #define MAX_VER_NUM 50 typedef cha

C語言實現:將數組A中的內容和數組B中的內容進行交換(數組一樣大)。

image pri 之前 es2017 sys 變量 ret 只需要 題目 之前我們已經完成了對兩個變量內容進行交換的程序,這兩道題目大同小異,不過是將兩數變成了兩數組。 可能我們會想:我們是不是需要第三個數組作為中間變量進行交換操作? 答案是no,我們只需要通過一個循環體

【資料結構】二叉介面的實現(c語言實現

二叉樹 概念 一棵二叉樹是結點的一個有限集合,該集合或者為空,或者是由一個根節點加上兩棵別稱為左子樹和右子樹的二又樹組成。 二叉樹的特點: 1.每個結點最多有兩棵子樹,即二叉樹不存在度大於2的結點。2.二又樹的子樹有左右之分,其子樹的次序不能顛倒   特殊的二

[原始碼和文件分享]基於C語言B-實現

1 軟體結構設計 1.1 軟體功能結構 用下圖所示的方式描述軟體的功能結構。 1.1.1 B-樹的查詢 B-樹的查詢過程:根據給定值查詢結點和在結點的關鍵字中進行查詢交叉進行。首先從根結點開始重複如下過程: 若比結點的第一個關鍵字小,則查詢在該結點第一個指標指向的結點進行;

C語言實現二叉的結構和常用操作

#include<stdio.h> #include <stdlib.h> typedef float ElemType; typedef struct S_BiTNode//定義結點型別結構體 { ElemType data;//資料域 str

C語言實現websocket服務器

sockaddr extend ++i set strlen ner ace == perl Websocket Echo Server Demo 背景 嵌入式設備的應用開發大都依靠C語言來完成,我去研究如何用c語言實現websocket服務器也是為了在嵌入式設備中實現一個

C語言實現:將三個數按從大到小輸出。

temp clas 實現 ima 編程 程序 c語言實現 從大到小 code 這個題目用編程來實現非常簡單,由於我在上一篇博客中已經介紹過使用“冒泡排序”的方法。 所以我在這裏直接給出使用“冒泡排序”寫出的代碼: #include<stdio.h> int m

C語言實現窗口抖動

clu 變量 win 句柄 span nbsp idt 兩個 spa #include "stdafx.h" #include <stdio.h> #include<Windows.h> int main() { int s

Linux終端程序c語言實現改變輸出的字的顏色

光標位置 高亮 AI 藍色 屬性 用c語言實現 TE c語言 說明 顏色代碼: 格式: echo "\033[字背景顏色;字體顏色m字符串\033[0m" 例如: echo "\033[41;36m something here \033[0m" 其中41的位置代表

c語言實現日誌功能

windows.h 用c語言實現 pause truct argc ifdef open info oid // 參考鏈接 // C語言實現寫入日誌文件 https://blog.csdn.net/sunlion81/article/details/8647028 #

c++語言實現出四則運算的題

using div style cin 隨機函數 color 地方 switch ase 設計思路 1.利用隨機函數 隨機兩個兩位數 2.用其中一個數除以4取余函數 0 1,2,3 用switch語句分表代表加、減、乘、除。 3.輸出式子 4.利用for循環實現題的量增 5

c語言實現單鏈表的逆序輸出

<span style="font-family: Arial, Helvetica, sans-serif;">可以用遞迴,如果沒到連結串列尾,則遞迴查詢,否則輸出當前值。下面只是演算法表示,不能直接放到程式裡編譯執行。</span><span style="fo

atoi函式的用法及C語言實現ato

庫函式原型: #inclue <stdlib.h> int atoi(const char *nptr); 用法:將字串裡的數字字元轉化為整形數。返回整形值。 注意:轉化時跳過前面的空格字元,直到遇上數字或正負符號才開始做轉換,而再遇到非數字或字串結束時('/0')才結束

不依賴任何系統API,c語言實現gbk/utf8/unicode編碼轉換

轉載地址:https://blog.csdn.net/bladeandmaster88/article/details/54837338 漢字'我' Unicode編碼是0x6211       01100010 00010001 UTF8編碼是&

C語言實現最小二乘法演算法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!