1. 引言
主要涉及知識點
- 二叉樹的概念。
- 二叉查詢樹的概念跟常用操作。
- 二叉樹的遍歷(先序遍歷、中序遍歷、後序遍歷、層序遍歷)。
其他
- 佇列(層序遍歷用到)。
最後留了兩個思考的點
- delete 為什麼要 return t。
- 為什麼是 t->right = delete(x, t->right) 而不直接 delete(x, t->right)。
2. 概念
樹是一種非線性資料格式,計算機裡面非常有用的資料格式之一,最常用的其中就有二叉樹、二叉查詢樹,
二叉樹:每個節點都不能有多餘兩個兒子的一棵樹
二叉查詢樹:對於樹種的每一個節點 X ,都滿足它的左子樹的關鍵字小於 X 的關鍵字,右子樹的關鍵字大於 X 的關鍵字並且 X 的關鍵字是不重複的。
二叉查詢樹是基礎性資料結構,用於構建更為抽象的資料結構,如集合、multiset、關聯陣列等,二叉樹的優點是插入、查詢時間複雜度低 O(logN)。
3. 二叉查詢樹的思路
定義一顆二叉查詢樹的結構體
typedef struct tree_node
{
element_type element; //節點關鍵字
struct tree_node *left; //左子樹
struct tree_node *right; //右子樹
} *search_tree;
typedef search_tree position;
一棵標準二叉查詢樹需要實現的方法
search_tree make_empty(search_tree t);
position find(element_type x, search_tree t);
position find_min(search_tree t);
position find_max(search_tree t);
search_tree insert(element_type x, search_tree t);
search_tree delete(element_type x, search_tree t);
element_type retrieve(position p); //檢索值
麻煩一點兒的在刪除。
刪除的節點有三種情況:
- 是樹葉——直接 delete。
- 有一個兒子:父節點直接指向子節點。
- 有兩個兒子:右子樹最小的資料代替該節點,並刪除那個節點(右子樹最小節點、直接遞迴刪)。
4. 二叉樹遍歷
- 先序遍歷:root 節點->左節點->右節點。
- 中序遍歷:左節點->root 節點->右節點。
- 後序遍歷:左節點->右節點->root 節點。
- 層序遍歷:逐層遍歷。
重點說層序遍歷。
5. 具體實現
queue.h
標頭檔案,就是一個佇列,具體實現程式碼參照佇列那篇文章
#include <stdio.h>
#include <stdlib.h>
#include "bitree.h"
#define error(str) fatal_error(str)
#define fatal_error(str) fprintf(stderr, "%s\n", str),exit(1)
struct node;
typedef search_tree queue_element_type;
typedef struct node *ptr_to_node;
typedef ptr_to_node queue;
queue create_queue();
void queue_make_empty(queue q);
int is_empty(queue q);
void enqueue(queue_element_type x, queue q);
void dequeue(queue q);
queue_element_type front(queue q);
queue_element_type front_and_dequeue(queue q);
void dispose_queue(queue *q);
bitree.c
檔案
/**
* 二叉查詢樹
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "queue.h"
#include "bitree.h"
struct tree_node
{
element_type element; //節點關鍵字
struct tree_node *left; //左子樹
struct tree_node *right; //右子樹
};
search_tree make_empty(search_tree t)
{
if (NULL != t) {
make_empty(t->left);
make_empty(t->right);
free(t);
}
return NULL;
}
position find(element_type x, search_tree t)
{
if (NULL == t)
return NULL;
if (t->element > x)
return find(x, t->left);
else if(t->element < x)
return find(x, t->right);
else
return t;
}
position find_min(search_tree t)
{
/*非遞迴*/
/*if (NULL != t)
while (t->left != NULL)
t = t->left;
return t;*/
//遞迴
if (NULL == t)
return NULL;
if (NULL == t->left)
return t;
else
return find_min(t->left);
}
position find_max(search_tree t)
{
/*遞迴*/
/*if (NULL == t)
return NULL;
if (NULL == t->right)
return t;
else
return find_max(t->right);*/
/*非遞迴*/
if (NULL != t)
while (t->right != NULL)
t = t->right;
return t;
}
search_tree insert(element_type x, search_tree t)
{
if (NULL == t) {
t = (search_tree)malloc(sizeof(struct tree_node));
if (NULL == t)
fatal_error("out of space");
else {
t->element = x;
t->left = t->right = NULL;
}
} else if (x < t->element) {
t->left = insert(x, t->left);
}
else if (x > t->element) {
t->right = insert(x, t->right);
}
return t;
}
search_tree delete(element_type x, search_tree t)
{
position temp_cell;
if ( NULL == t)
error("empty tree");
else if(x < t->element)
t->left = delete(x, t->left);
// delete(x, t->left); /*思考:為什麼不直接 delete*/
else if (x > t->element)
t->right = delete(x, t->right);
// delete(x, t->right); /*思考:為什麼不直接 delete*/
else if (t->left && t->right) { // 有兩個兒子 找右子樹最小節點
temp_cell = find_min(t->right);
t->element = temp_cell->element; // 右子樹最小關鍵字替換上來
// delete right child
delete(temp_cell->element, t->right);
} else { // 一個 or 0 個節點,不左還是右都是直接跨過 t 直接指向該節點
temp_cell = t;
if (t->left == NULL)
t = t->right;
else if (t->right == NULL)
t = t->left;
free(temp_cell);
temp_cell = NULL;
}
return t; //是何用意?
}
element_type retrieve(position p)
{
return p->element;
}
void print_tree(search_tree t, int type)
{
if (NULL == t) {
fatal_error("empty tree");
}
// 先序遍歷
if (type == 1)
printf("%d ", t->element);
if (t->left)
print_tree(t->left, type);
// 中序遍歷
if (type == 2)
printf("%d ", t->element);
if (t->right)
print_tree(t->right, type);
// 後序遍歷
if (type == 3)
printf("%d ", t->element);
}
void print_layer(search_tree t)
{
queue q;
position p;
q = create_queue();
enqueue(t, q);
while (is_empty(q) != 1) {
p = front(q);
printf("%d ", p->element);
if (NULL != p->left)
enqueue(p->left, q);
if (NULL != p->right)
enqueue(p->right, q);
dequeue(q);
}
}
void test()
{
search_tree t;
int i, x;
t = make_empty(NULL);
srand( (unsigned)time(NULL) );
for( i = 0; i < 10; i++ )
t = insert(rand() % 50, t);
printf("\n先序遍歷:");
print_tree(t, 1);
printf("\n中序遍歷:");
print_tree(t, 2);
printf("\n後序遍歷:");
print_tree(t, 3);
printf("\n層次遍歷:");
print_layer(t);
printf("\n");
printf("max:%d\n", find_max(t)->element);
printf("min:%d\n", find_min(t)->element);
}
int main(int argc, char const *argv[])
{
test();
return 0;
}