BST就是二叉搜索树(Binary Search Tree)的简称,因此毫无疑问BST也是二叉树,对于二叉树而言,和线性表的实现一样,我们也必须设计其数据节点,而且也必须设计其诸如插入、删除等操作。由于一般二叉树使用顺序存储会不可避免地浪费存储空间,因此我们一般都采用链式存储来表达一棵二叉树。
BST之所以称为二叉搜索树,是因为我们对其节点的存放位置做了严格的规定,从而来提高其搜索性能。BST规定:在任何子树中,根节点必须大于其左子树中任意的节点,且必须小于其右子树中任意的节点,换句话说必须满足“小 ---- 中 ----- 大”的逻辑次序。
树节点的设计,包括了三个要素:数据域、左孩子指针和右孩子指针,其代码如下:
struct node
{
	datatype data;
	struct node *lchild;
	struct node *rchild;
};
有了树的结构体,我们就可以写出初始化一棵空BST和产生一个新节点的代码了。如下:
struct node * init_tree(void)	//初始化一棵空BST
{
	return NULL;
}
struct node * new_node(datatype data)	//产生一个新节点
{
	struct node *new = malloc(sizeof(struct node));
	if(NULL != new)
	{
		new->data = data;
		new->lchild = NULL;
		new->rchild = NULL;
	}
	return new;
}
由于树本身是一种递归的结构,因此它的操作算法都可以用递归来实现。
算法实现和示例下面给出完整的代码实现,包括头文件和c文件。
首先是头文件: head4tree.h、head4queue.h、commonheader.h、drawtree.h
head4tree.h
//////////////////////////////////////////////////////////////////
//  Description: 本文件为二叉树核心头文件。
//               任何使用本二叉树结构算法的程序,在包含本头文件之前
//               都需要将如下宏定义成二叉树节点需要表达的数据类型:
//
//                     TREE_NODE_DATATYPE
//
//               否则二叉树的节点数据类型一律默认为 int
//
//////////////////////////////////////////////////////////////////
#ifndef _HEAD4TREE_H_
#define _HEAD4TREE_H_
/*
 * Any application applying this linked-tree data structure should
 * define the macro "TREE_NODE_DATATYPE" before include this head
 * file, otherwise, the macro will be defined to 'int' as follow.
 *
*/
#ifndef TREE_NODE_DATATYPE
#define TREE_NODE_DATATYPE int
#endif
#include "commonheader.h"
#define MAX(a, b) ({ \
		typeof(a) _a = a; \
		typeof(b) _b = b; \
		(void)(&_a == &_b);\
		_a > _b? _a : _b; \
		})
typedef TREE_NODE_DATATYPE tn_datatype;
#ifdef RB
#define RED   0
#define BLACK 1
#endif
typedef struct _tree_node
{
	tn_datatype data;
	struct _tree_node *lchild;
	struct _tree_node *rchild;
#ifdef AVL
	int height;
#endif
#ifdef RB
	struct _tree_node *parent;
	int color;
#endif
}treenode, *linktree;
void pre_travel(linktree, void (*handler)(linktree));
void mid_travel(linktree, void (*handler)(linktree));
void post_travel(linktree, void (*handler)(linktree));
void level_travel(linktree, void (*handler)(linktree));
linktree bst_insert(linktree root, linktree new);
linktree bst_remove(linktree root, tn_datatype data);
linktree bst_find(linktree root, tn_datatype data);
#ifdef AVL
linktree avl_insert(linktree root, linktree new);
linktree avl_remove(linktree root, tn_datatype data);
linktree avl_rotate_left (linktree root);
linktree avl_rotate_right(linktree root);
linktree avl_rotate_leftright(linktree root);
linktree avl_rotate_rightleft(linktree root);
static int height(linktree root)
{
	return root==NULL ? 0 : root->height;
}
#endif
static linktree new_node(tn_datatype data)
{
	linktree new = malloc(sizeof(treenode));
	if(new != NULL)
	{
		new->data = data;
		new->lchild = NULL;
		new->rchild = NULL;
		#ifdef AVL
		new->height = 1;
		#endif
		#ifdef RB
		new->parent = NULL;
		new->color = RED;
		#endif
	}
	return new;
}
#endif
head4queue.h
//////////////////////////////////////////////////////////////////
//  Description: 本文件为队列核心头文件。
//               任何使用本队列算法的程序,在包含本头文件之前都需要
//               将如下宏定义成队列节点需要表达的数据类型:
//
//                     QUEUE_NODE_DATATYPE
//
//               否则队列的节点数据类型一律默认为 int
//
//////////////////////////////////////////////////////////////////
#ifndef _HEAD4QUEUE_H__
#define _HEAD4QUEUE_H__
#include "commonheader.h"
#ifndef QUEUE_NODE_DATATYPE 
#define QUEUE_NODE_DATATYPE int 
#endif
typedef QUEUE_NODE_DATATYPE qn_datatype;
struct _queue_node
{
	qn_datatype data;
	struct _queue_node *next;
};
typedef struct _queuenode
{
	struct _queue_node *front;
	struct _queue_node *rear;
#ifdef QUEUE_SIZE
	int size;
#endif
}queuenode, *linkqueue;
bool is_empty_q(linkqueue);
bool out_queue(linkqueue, qn_datatype *);
bool en_queue(linkqueue, qn_datatype);
linkqueue init_queue(void);
#ifdef QUEUE_SIZE
int queue_size(linkqueue *);
#endif
#endif
commonheader.h
#ifndef _COMMONHEADER_H_
#define _COMMONHEADER_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <semaphore.h>
#include <fcntl.h>
#include <pthread.h>
#endif
drawtree.h
//////////////////////////////////////////////////////////////////
//  Description: 使用C语言写一页webpage
//////////////////////////////////////////////////////////////////
#ifndef _DRAWTREE_H_
#define _DRAWTREE_H_
#include "commonheader.h"
#include "head4tree.h"
#ifndef QUEUE_NODE_DATATYPE
#define QUEUE_NODE_DATATYPE linktree
#endif
#include "head4queue.h"
static char page_begin[] = "<html><head><title>tree map"
                           "</title></head><body>"
			   "<table border=0 cellspacing"
                           "=0 cellpadding=0>";
static char line_begin[] = "<tr>";
static char line_end  [] = "</tr>";
static char space     [] = "<td> </td>";
static char underline [] = "<td style=\"border-bottom:"
	 		   "1px solid #58CB64\"> "
                           "</td>";
#ifdef RB
static char data_begin_red[] = "<td bgcolor=\"#FF0000\";style="
			       "\"border:1px sol"
                               "id #58CB64;background-colo"
                               "r:#DDF1D8;PADDING:2px;\" t"
                               "itle=\"level: 1\">";
static char data_begin_blk[] = "<td bgcolor=\"#000000\";style="
			       "\"border:1px sol"
                               "id #58CB64;background-colo"
                               "r:#DDF1D8;PADDING:2px;\" t"
                               "itle=\"level: 1\"><font color"
				"=\"#FFFFFF\">";
static char data_end_red[] = "</td>";
static char data_end_blk[] = "</font></td>";
#else
static char data_begin[] = "<td style=\"border:1px sol"
                           "id #58CB64;background-colo"
                           "r:#DDF1D8;PADDING:2px;\" t"
                           "itle=\"level: 1\">";
static char data_end  [] = "</td>";
#endif
static char page_end  [] = "</table></body></html>";
#define MAX_NODES_NUMBER 100
#define FILENAME 32
static tn_datatype central_order[MAX_NODES_NUMBER];
void putunderline(int fd, int num);
void putspace(int fd, int num);
#ifdef RB
void putdata(int fd, linktree p);
#else
void putdata(int fd, int data);
#endif
int  get_index(tn_datatype data);
void create_index(linktree root);
void data_leftside(int fd, linktree root, int spaces);
int  data_rightside(int fd, linktree root);
void start_page(int fd);
void end_page(int fd);
void draw(linktree root);
#endif
drawtree提供的draw函数用来在webpage上面把树画出来。
然后是c文件:queue.c drawtree.c bst.c
queue.c
////////////////////////////////////////////////////////////////////
//  Description: 链队列
////////////////////////////////////////////////////////////////////
#include "head4queue.h"
linkqueue init_queue(void)
{
	linkqueue q = (linkqueue)malloc(sizeof(queuenode));
	q->front = q->rear =
		(struct _queue_node *)malloc(sizeof(struct _queue_node));
	q->rear->next = NULL;
	return q;
}
bool is_empty_q(linkqueue q)
{
	return (q->front == q->rear);
}
bool out_queue(linkqueue q, qn_datatype *pdata)
{
	if(is_empty_q(q))
		return false;
	struct _queue_node *p = q->front;
	q->front = q->front->next;
	free(p);
	*pdata = q->front->data;
	return true;
}
bool en_queue(linkqueue q, qn_datatype data)
{
	struct _queue_node *pnew;
	pnew = (struct _queue_node *)malloc(sizeof(struct _queue_node));
	if(pnew == NULL)
		return false;
	pnew->data = data;
	pnew->next = NULL;
	q->rear->next = pnew;
	q->rear = pnew;
	return true;
}
#ifdef QUEUE_SIZE
int queue_size(linkqueue *q)
{
	return q->size;
}
#endif
drawtree.c
////////////////////////////////////////////////////////////////////
//  Description: 将二叉树的逻辑层次结构,用网页展示出来
////////////////////////////////////////////////////////////////////
#include "head4tree.h"
#include "drawtree.h"
void putunderline(int fd, int num)
{
	int i;
	for(i=0; i<num; i++)
	{
		write(fd, underline, strlen(underline));
	}
}
void putspace(int fd, int num)
{
	int i;
	for(i=0; i<num; i++)
	{
		write(fd, space, strlen(space));
	}
}
#ifdef RB
void putdata(int fd, linktree p)
{
	char s[50];
	bzero(s, 50);
	snprintf(s, 50, "%d", p->data);
	switch(p->color)
	{
	case RED:
		write(fd, data_begin_red, strlen(data_begin_red));
		write(fd, s, strlen(s));
		write(fd, data_end_red, strlen(data_end_red));
		break;
	case BLACK:
		write(fd, data_begin_blk, strlen(data_begin_blk));
		write(fd, s, strlen(s));
		write(fd, data_end_blk, strlen(data_end_blk));
	}
}
#else
void putdata(int fd, int data)
{
	char s[50];
	bzero(s, 50);
	snprintf(s, 50, "%d", data);
	write(fd, data_begin, strlen(data_begin));
	write(fd, s, strlen(s));
	write(fd, data_end, strlen(data_end));
}
#endif
static int Index = 0;
void create_index(linktree root)
{
	if(Index >= MAX_NODES_NUMBER-1)
		return;
	central_order[Index++] = root->data;
}
int get_index(tn_datatype data)
{
	int i;
	for(i=0; i<100; i++)
	{
		if(central_order[i] == data)
			return i;
	}
	return -1;
}
void data_leftside(int fd, linktree root, int spaces)
{
	if(root == NULL)
		return;
	int s_line=0;
	if(root->lchild != NULL)
	{
		s_line = get_index(root->data)-
			 get_index(root->lchild->data)-1;
	}
	putspace(fd, spaces-s_line);
	putunderline(fd, s_line);
}
int data_rightside(int fd, linktree root)
{
	if(root == NULL)
		return 0;
	int s_line=0;
	if(root->rchild != NULL)
	{
		s_line = get_index(root->rchild->data)-
			 get_index(root->data)-1;
	}
	putunderline(fd, s_line);
	return s_line;
}
void start_page(int fd)
{
	write(fd, page_begin, strlen(page_begin));
}
void end_page(int fd)
{
	write(fd, page_end, strlen(page_end));
}
void draw(linktree root)
{
	if(root == NULL)
		return;
	time_t t;
	time(&t);
	static char filename[FILENAME];
	bzero(filename, FILENAME);
	snprintf(filename, FILENAME, "%u.html", (unsigned)t);
	int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0644);
	if(fd == -1)
	{
		perror("open() failed");
		exit(1);
	}
	Index = 0;
	mid_travel(root, create_index);
	linkqueue q = init_queue();
	linktree tmp = root;
	int ndata = 1;
	start_page(fd);
	while(1)
	{
		write(fd, line_begin, strlen(line_begin));
		int i, n = 0;
		int nextline = 0;
		for(i=0; i<ndata; i++)
		{
			int index = get_index(tmp->data);
			data_leftside(fd, tmp, index-n);
			#ifdef RB
			putdata(fd, tmp);
			#else
			putdata(fd, tmp->data);
			#endif
			int rightline = data_rightside(fd, tmp);
			if(tmp->lchild != NULL)
			{
				nextline++;
				en_queue(q, tmp->lchild);
			}
			if(tmp->rchild != NULL)
			{
				nextline++;
				en_queue(q, tmp->rchild);
			}
			if(!out_queue(q, &tmp))
				return;
			n = index + rightline;
			n++;
		}
		write(fd, line_end, strlen(line_end));
		ndata = nextline;
	}
	end_page(fd);
	close(fd);
}
bst.c
////////////////////////////////////////////////////////////////////
//  Description: BST算法实现代码
////////////////////////////////////////////////////////////////////
#include "head4tree.h"
#include "drawtree.h"
linktree bst_insert(linktree root, linktree new)
{
	if(new == NULL)
		return root;
	if(root == NULL)
		return new;
	if(new->data > root->data)
	{
		root->rchild = bst_insert(root->rchild, new);
	}
	else if(new->data < root->data)
	{
		root->lchild = bst_insert(root->lchild, new);
	}
	else
	{
		printf("%d is already exist.\n", new->data);
	}
	return root;
}
linktree bst_find(linktree root, tn_datatype data)
{
	if(root == NULL)
		return NULL;
	if(data < root->data)
		return bst_find(root->lchild, data);
	else if(data > root->data)
		return bst_find(root->rchild, data);
	else
		return root;
}
linktree bst_remove(linktree root, tn_datatype n)
{
	if(root == NULL)
		return NULL;
	if(n < root->data)
		root->lchild = bst_remove(root->lchild, n);
	else if(n > root->data)
		root->rchild = bst_remove(root->rchild, n);
	else
	{
		linktree tmp;
		if(root->lchild != NULL)
		{
			for(tmp=root->lchild; tmp->rchild!=NULL;
			    tmp=tmp->rchild);
			root->data = tmp->data;
			root->lchild = bst_remove(root->lchild, tmp->data);
		}
		else if(root->rchild != NULL)
		{
			for(tmp=root->rchild; tmp->lchild!=NULL;
			    tmp=tmp->lchild);
			root->data = tmp->data;
			root->rchild = bst_remove(root->rchild, tmp->data);
		}
		else
		{
			free(root);
			return NULL;
		}
	}
	return root;
}
int main(void)
{
	linktree root;
	root = NULL;
	printf("输入大于0的数插入节点\n");
	printf("输入小于0的数删除节点\n");
	printf("输入0退出程序\n");
	int n;
	while(1)
	{
		scanf("%d", &n);
		if(n > 0)
		{
			linktree new = new_node(n);
			root = bst_insert(root, new);
		}
		else if(n < 0)
		{
			root = bst_remove(root, -n);
		}
		if(n == 0)
			break;
		draw(root);
		
		system("firefox -new-tab *.html &");
	}
	system("rm *.html");
	return 0;
}
该文件里面包含了测试入口函数main,实现了以下功能:
1、输入大于0的整数就插入节点
2、输入小于0的整数就删除其绝对值对应的节点
3、输入0就退出程序
4、退出程序之前用网页把这棵树画出来
运行效果如下:


目前实现了最很简单的二叉搜索树,但是存在一个问题:这颗树可能会由于插入或删除的节点的随机性导致不平衡,从而影响BST的搜索性能。
什么是二叉树的不平衡?平衡树?红黑树?后面我有时间再一一道来。
总结本文简单介绍了BST树的概念,并提供了相关的算法实现,演示了运行效果。
后面我有时间会把平衡树和红黑树的知识点及算法实现补充进来,以便日后使用。
