您的位置:首页 > 其它

BST算法原理及其实现

2016-04-28 00:35 363 查看
首先,我们先了解下递归。说到递归,数据结构中树介绍章节中有大量的实例用了,包括二叉树定义与遍历等。当然一般公司项目中很少用它,主要是效率低下,同时递归涉及到函数反复建栈,压栈,销毁栈的过程,开销较大。这里仅仅作为一种技术探讨来研究。递归,简单地说就是函数自己调用自己,网上也有不少解释了。这里我的看法是将整个递归过程看做一种函数映射关系,即 F(A) --> B,如果B与A具有相同的属性和操作,同时B的作用域小于A的作用域,算法逐步收敛的话,就可以用递归来描述了。之前看过SCIP《计算机程序构造与解释》一书,里面讲到了函数式编程语言scheme,该书中将函数作为头等公民,所有操作都用函数来体现,用了大量的递归实现,状态值也是保存在函数入参中(这里也称作尾递归)。书中将函数看做某种规则,数据作为函数的输入,可以抽象理解为函数为对数据进行的操作行为,函数参数为输入数据;如果我们用更底层地用汇编方式描述就是函数可以看做代码段(对一类算法行为的描述),数据看做数据段(是对现有数据的集合)。那么程序本质上就是一段用可计算公式描述的抽象过程,简单地说就是一组相同属性集合上面的相同映射关系。比如说整数域上的加法运算,其动作为加法,数据就是整个整数集合。每个整数对于加法这个“规则”来说都是等价的,用scheme语言很容易描述这一点:(+
a b)。将算法与数据区别对待的好处就是很容易识别出规则。如果是数值计算的话,则便于用递归来描述。总之,递归适用于有明显递归特性的数据结构(比如树),或者有着明显递归公式,可以用数学归纳法证明的问题。我们来看一位大牛给出的通用递归公式:

Result M(Problem prob)

{

if (<problem can be solved easily>) //递归入口条件,不然就是无限递归了

return <easy solution>;

// The problem cannot be solved easily. //其他条件,满足相同的处理方式就可以了。

Problem smaller1 = <reduce problem to smaller problem>

Result result1 = M(smaller1); //注意用递归方式,子问题的规模一定要缩小,即算法要保证收敛

Problem smaller2 = <reduce problem to smaller problem>

Result result2 = M(smaller2);

...

//递归函数下面可以想象是栈底,上面的部分一直在将数据压栈,下面的步骤可以看做恢复栈的过程。

Result finalResult = <combine all results of smaller problem to solve large problem>

return finalResult;

}

我们这里看几段经典递归的例子:

#include <stdio.h>

void print(const char *s)
{
if (*s == '\0')//假设已经到栈底了
{
return;
}
print (s+1);
//从栈底开始计算或者打印
printf("%c\n", *s);
}

int length(const char *s)
{
if (*s == '\0') //假设已经到了栈底
{
return 0;
}
//否则,通过观察法,查看下一次递归参数与本次之间有啥关系
return length(s+1) + 1;
}

int main()
{

print("abcdef");
printf("height= %d\n", length("abcdef"));

return 0;
}


=========================下面再看BST主要函数接口实现========================

/*
============================================================================
Name        : BST.c
Author      : @CodingGeek
Version     :
Copyright   : V1.0
Description : BST算法的程序演示
============================================================================
*/

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

typedef struct tagBST
{
struct tagBST *lchild;
struct tagBST *rchild;
int val;
int height; //高度
}BST;

//生成结点
BST *makeNode(int key)
{
BST *pBST = (BST*)malloc(sizeof(BST));
pBST->lchild = NULL;
pBST->rchild = NULL;
pBST->height = 1;
pBST->val = key;
return pBST;
}

//获取BST树的高度
int getHeight(BST *root)
{
return (root == NULL)? 0: root->height;
}

int max(int a, int b)
{
return a > b ? a : b;
}

//在树中新增一个结点
BST *insertNode(BST *root, int key)
{
if (root == NULL)
{
return makeNode(key);
}

if (root->val == key)
{
return root;
}
else if (key < root->val) //插入的值比根节点小,插入左子树
{
root->lchild = insertNode(root->lchild ,key);
}
else
{
root->rchild = insertNode(root->rchild, key);
}

//递归函数后面的求值,一般是自底向上一层层计算,这里从根节点开始计算高度

root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;

return root;

}

BST *removeNode(BST *root, int key)
{
if (root == NULL)
{
return NULL;
}

if (key == root->val)
{
if(root->rchild == NULL)
{
BST* temp = root;
root = root->lchild;
free(temp);
return root;
}
else
{
BST* temp = root->rchild;
while(temp->lchild)
{
temp = temp->lchild;
}
root->val = temp->val;
root->rchild = removeNode(root->rchild, temp->val);
}
}
else if (key < root->val)
{
root->lchild = removeNode(root->lchild, key);
}
else
{
root->rchild = removeNode(root->rchild, key);
}
root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;
return root;
}

//遍历所有元素,默认是排序好的
void traverse(BST *root)
{
if (root == NULL)//假设已经到栈底,一般设为叶子结点的孩子
{
return;
}

traverse(root->lchild);
printf("结点: %d; 深度: %d\n", root->val, root->height);
traverse(root->rchild);

}

void print_tree_indent(BST *node, int indent)
{
for (int ix = 0; ix < indent; ix++)
{
printf(" ");
}
if (node == NULL)
{
printf("空孩子结点\n");
}
else
{
printf("结点: %d; 深度: %d\n", node->val, node->height);
print_tree_indent(node->lchild, indent + 4);
print_tree_indent(node->rchild, indent + 4);
}
}

void print_tree(BST *node)
{
print_tree_indent(node, 0);
}

int main(void)
{
BST* root = NULL;
int arr[] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};

const int length = sizeof(arr) / sizeof(int);
for(int i = 0; i< length;i++)
{
root = insertNode(root, arr[i]);
}

root = removeNode(root, 7);

printf("打印BST树:\n");
print_tree(root);
printf("==================================\n");
traverse(root);
}

其实树的数据结构实现用递归是最直观的,一般的教科书中也是这么定义与实现的。如果改用非递归算法,那就要在函数里面维护一个堆栈的数据结构,用来保留每次递归时候的中间状态变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: