linux BST树算法实现

简介

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>&nbsp;</td>";
static char underline [] = "<td style=\"border-bottom:"
	 		   "1px solid #58CB64\">&nbsp;"
                           "</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、退出程序之前用网页把这棵树画出来

运行效果如下:
1234

3

延伸

目前实现了最很简单的二叉搜索树,但是存在一个问题:这颗树可能会由于插入或删除的节点的随机性导致不平衡,从而影响BST的搜索性能。

什么是二叉树的不平衡?平衡树?红黑树?后面我有时间再一一道来。

总结

本文简单介绍了BST树的概念,并提供了相关的算法实现,演示了运行效果。
后面我有时间会把平衡树和红黑树的知识点及算法实现补充进来,以便日后使用。

posted @ 2022-05-20 03:16  李星云姬如雪  阅读(47)  评论(0编辑  收藏  举报