資料結構之---二叉樹C實現
阿新 • • 發佈:2019-01-06
學過資料結構的都知道樹,那麼什麼是樹?
1、每個節點最多有兩個子節點的樹形結構
2、其中起始節點叫做根節點,除了根節點之外,每個節點有且只有一個父節點
3、
4、除了根節點和葉子節點之外,剩下的節點叫枝節點,枝節點有父節點也有子節點
5、二叉樹中每層節點均達到最大值,並且除了葉子節點之外每個節點都有兩個子節點,叫做滿二叉樹
6、二叉樹中除了最後一層之外,每層節點數均達到最大值,並且最後一層的節點連續集中在左邊,叫完全二叉樹
對於二叉樹的處理採用遞迴的方法:處理(二叉樹)
{
if(二叉樹為空) 直接處理;
else
{
處理根節點;
}
}
二叉樹的儲存結構
(1)順序儲存結構從上到下,從左到右,依次儲存每個節點
(2)鏈式儲存結構每個節點中除了儲存資料元素本身之外,還需要兩指標如:
typedef struct Node
{
int data;//資料內容
struct Node* left;//指向左子樹
struct Node* right;//指向右子樹
}Node;
遍歷方式(1)先序遍歷 => 根左子樹右子樹
(2)中序遍歷 => 左子樹根右子樹
(3)後序遍歷 => 左子樹右子樹根有序二叉樹左子樹節點 <= 根節點 <= 右子樹節點主要搜尋和查詢資料的功能中 接下來我們來看看二叉樹的各類操作的實現:
//實現有序二叉樹的各種操作
#include <stdio.h>
#include <stdlib.h>
//定義節點的資料型別
typedef struct Node
{
int data;//儲存資料內容
struct Node* left;//左子樹的地址
struct Node* right;//右子樹的地址
}Node;
//定義有序二叉樹的資料型別
typedef struct
{
Node* root;//記錄根節點的地址
int cnt;//記錄節點的個數
}Tree;
//實現向有序二叉樹中插入新節點的操作
void insert_data(Tree* pt,int data);
//插入新節點的遞迴函式
void insert(Node** pRoot,Node* pn);
//採用中序遍歷方法進行遍歷
void travel_data(Tree* pt);
//遍歷的遞迴函式
void travel(Node* pRoot);
//實現建立新節點
Node* create_node(int data);
//實現清空樹中的所有節點
void clear_data(Tree* pt);
//實現清空的遞迴函式
void clear(Node** pRoot);
//實現查詢一個指定的節點
Node** find_data(Tree* pt,int data);
//查詢的遞迴函式
Node** find(Node** pRoot,int data);
//實現刪除指定的節點
void del_data(Tree* pt,int data);
//修改指定元素的操作
void modify(Tree* pt,int data,int new_data);
//判斷二叉樹是否為空
int empty(Tree* pt);
//判斷二叉樹是否為滿
int full(Tree* pt);
//計算二叉樹中節點的個數
int size(Tree* pt);
//獲取根節點的元素值
int get_root(Tree* pt);
int main(void)
{
//建立有序二叉樹,並且進行初始化
Tree tree;
tree.root = NULL;
tree.cnt = 0;
//插入新節點,進行遍歷
insert_data(&tree,50);
travel_data(&tree);//50
insert_data(&tree,70);
travel_data(&tree);//50 70
insert_data(&tree,20);
travel_data(&tree);//20 50 70
insert_data(&tree,60);
travel_data(&tree);//20 50 60 70
printf("------------------\n");
//clear_data(&tree);
travel_data(&tree);//20 50 60 70
del_data(&tree,50);
travel_data(&tree);//20 60 70
del_data(&tree,30);//刪除失敗
travel_data(&tree);//20 60 70
del_data(&tree,20);
travel_data(&tree);//60 70
printf("--------------------\n");
modify(&tree,10,20);//插入20
travel_data(&tree);//20 60 70
printf("二叉樹中根節點的元素是:%d\n",get_root(&tree));//70
printf("二叉樹中節點的個數是:%d\n",size(&tree));//3
printf("%s\n",empty(&tree)?"二叉樹為空":"二叉樹不為空");
printf("%s\n",full(&tree)?"二叉樹已滿":"二叉樹沒有滿");
return 0;
}
//修改指定元素的操作
//舊元素不存在時,直接插入新元素即可
void modify(Tree* pt,int data,int new_data)
{
//1.刪除舊元素
del_data(pt,data);
//2.插入新元素
insert_data(pt,new_data);
}
//判斷二叉樹是否為空
int empty(Tree* pt)
{
return NULL == pt->root;
}
//判斷二叉樹是否為滿
int full(Tree* pt)
{
return 0;
}
//計算二叉樹中節點的個數
int size(Tree* pt)
{
return pt->cnt;
}
//獲取根節點的元素值
int get_root(Tree* pt)
{
if(empty(pt))
{
return -1;//表示失敗(以後講到)
}
return pt->root->data;
}
//實現刪除指定的節點
void del_data(Tree* pt,int data)
{
//1.查詢目標元素所在節點的地址
Node** pp = find_data(pt,data);
//2.判斷查詢失敗情況,不需要刪除
if(NULL == *pp)
{
printf("目標元素不存在,刪除失敗\n");
return;
}
//3.合併左右子樹,左子樹插入到右子樹中
if((*pp)->left != NULL)
{
//左子樹不為空時,需要插入到右子樹中
insert(&(*pp)->right,(*pp)->left);
}
//4.尋找指標記錄要刪除的節點地址
Node* q = *pp;
//5.將原來指向要刪除節點的指標 重新指向 合併之後的右子樹
*pp = (*pp)->right;
//6.刪除目標元素所在的節點
free(q);
q = NULL;
//7.節點個數減1
pt->cnt--;
}
//查詢的遞迴函式
Node** find(Node** pRoot,int data)
{
//1.判斷二叉樹是否為空,為空直接返回
if(NULL == *pRoot)
{
return pRoot;//&pt->root;
}
//2.比較根節點元素和目標元素的大小,如果相等,直接返回
if(data == (*pRoot)->data)
{
return pRoot;//&pt->root;
}
//3.若目標元素小於根節點元素值,左子樹查詢
else if(data < (*pRoot)->data)
{
return find(&(*pRoot)->left,data);
}
//4.若目標元素大於根節點元素,去右子樹查詢
else
{
return find(&(*pRoot)->right,data);
}
}
//實現查詢一個指定的節點
//返回 指向目標元素所在節點的指標 的地址
Node** find_data(Tree* pt,int data)
{
//呼叫遞迴函式實現查詢
return find(&pt->root,data);
}
//實現清空的遞迴函式
void clear(Node** pRoot)
{
//判斷二叉樹是否為空
if(*pRoot != NULL)
{
//1.清空左子樹
clear(&(*pRoot)->left);
//2.清空右子樹
clear(&(*pRoot)->right);
//3.清空根節點
free(*pRoot);
*pRoot = NULL;
}
}
//實現清空樹中的所有節點
void clear_data(Tree* pt)
{
//呼叫遞迴函式實現清空
clear(&pt->root);
//二叉樹的節點個數清零
pt->cnt = 0;
}
//實現建立新節點
Node* create_node(int data)
{
Node* pn = (Node*)malloc(sizeof(Node));
pn->data = data;
pn->left = NULL;
pn->right = NULL;
return pn;
}
//遍歷的遞迴函式
void travel(Node* pRoot)
{
//判斷二叉樹不為空時才需要遍歷
if(pRoot != NULL)
{
//1.遍歷左子樹
travel(pRoot->left);
//2.遍歷根節點
printf("%d ",pRoot->data);
//3.遍歷右子樹
travel(pRoot->right);
}
}
//採用中序遍歷方法進行遍歷
void travel_data(Tree* pt)
{
//呼叫遞迴函式進行遍歷
travel(pt->root);
//列印換行
printf("\n");
}
//插入新節點的遞迴函式
void insert(Node** pRoot,Node* pn)
{
//1.判斷二叉樹是否為空,如果為空則讓根節點指標直接指向新節點
if(NULL == *pRoot)
{
*pRoot = pn;
return;
}
//2.如果二叉樹非空,比較根節點和新節點大小
//2.1 如果根節點大於新節點,插入左子樹
if((*pRoot)->data > pn->data)
{
insert(&(*pRoot)->left,pn);
}
//2.2 如果根節點小於等於新節點,插入右子樹
else
{
insert(&(*pRoot)->right,pn);
}
}
//實現向有序二叉樹中插入新節點的操作
void insert_data(Tree* pt,int data)
{
//1.建立新節點,進行初始化 create_node
//Node* pn = (Node*)malloc(sizeof(Node));
//pn->data = data;
//pn->left = NULL;
//pn->right = NULL;
//2.插入新節點到二叉樹中,呼叫遞迴函式
insert(&pt->root,create_node(data));
//3.二叉樹中節點個數加1
pt->cnt++;
}
執行結果: