二叉搜索树(Binary Search Tree)是一种有如下特征的二叉树:
a,若它的左子树不为空,则左子树上所有结点的值均小于对应根结点的值;
b,若它的右子树不为空,则右子树上所有结点的值均大于对应根结点的值;
c,左子树和右子树分别构成一个二叉搜索树。
二叉树搜索树的查找、插入、删除等操作的复杂度为O (logN)。
结点结构定义为:
typedef struct node
{
int data;
struct node *left;
struct node *right;
}Node;
创建结点函数定义为:
Node *new_node(int data)
{
Node *node = (Node *)malloc(sizeof(Node));
if(node == NULL)
{
errno = ERR_MAL_MEMORY;
print_error("new_node()");
return NULL;
}
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
1 查找
根据根结点与左右结点的相对位置,二叉树的查找分前序、中序和后序三种情况。一般的,中序查找能够得到一组从小到大的排序数据。
若结点值等于搜索值,返回该结点;若结点值大于搜索值,根据BST的构造原则,搜索该结点的左子树,反之,搜索右子树。
中序遍历如下:
a,递归方式
Node* search_node(Node *root, int data)
{
Node *node = root;
if(node == NULL)
return NULL;
if(node->data == data)
{
printf("Find out!\nValue = %d, Mem = %p\n", data, &(node->data));
return node;
}
else
{
if(node->data > data)
return search_node(node->left, data);
else
return search_node(node->right, data);
}
}
b,非递归方式
Node *search_node_nonrecursive(Node *root, int data)
{
Node *node = root;
while(node)
{
if(node->data == data)
{
printf("Find out!\nValue = %d, Mem = %p\n", data, &(node->data));
return node;
}
else
{
if(node->data > data)
node = node->left;
else
node = node->right;
}
}
printf("\nCannot find %d.\n", data);
return NULL;
}
将结点插入BST里时,只需将该结点插入到符合规则的合适位置,即修改相对应的根结点指针,再将两个子树赋值为NULL。其他的结点不用更改。
根据BST的构造原理,先搜索结点的左子树找到合适的位置,若不满足,再搜索右子树找到合适的位置,最后创建新结点。
a,递归方式
Node *insert_node(Node *root, int data)
{
Node *node = root;
if(node == NULL)
return new_node(data);
else
{
if(node->data > data)
node->left = insert_node(node->left, data);
else
{
node->right = insert_node(node->right, data);
}
return node;
}
}
b,非递归方式
Node *insert_node_nonrecursive(Node *root, int data)
{
Node *node = root;
Node *pnode = NULL;
if(node == NULL)
return new_node(data);
while(node)
{
pnode = node;
if(node->data > data)
{
node = node->left;
if(node == NULL)
pnode->left = new_node(data);
}
else
{
node = node->right;
if(node == NULL)
pnode->right = new_node(data);
}
}
return root;
}
3 ,删除
删除结点操作根据待删结点的子树分为三种情况,即无子树,含有单子树和含有双子树。在进行相关的删除操作之前,通过查找定位待删除的结点。
若无法定位待删除的结点,返回原树根结点。记待删除结点为child,其父结点为parent。
a,无子树
将父结点对应的子树置NULL;若该结点为跟结点,说明该树只有一个结点,即根结点,将根结点置为NULL;
b,单子树
将该结点的单一子树连接至结点父结点对应的子树,释放结点。
c,双子树
由于待删除的结点有双子树,因此必须在该结点的所有子树中选取一个结点来代替该结点的位置。选取替代结点有两种选择,即选取该结点左子树的最右端
的结点或者选取该结点右子树的最左端。一般取前者方式。在定位该结点后,操作和单子树相似。
删除代码如下:
Node *delete_node(Node *root, int data)
{
Node *tree = root;
Node *parent, *child;
parent = NULL;
child = NULL;
while(tree)
{
if(tree->data == data)
{
child = tree;
break;
}
else if(tree->data > data)
{
parent = tree;
tree = tree->left;
}
else
{
parent = tree;
tree = tree->right;
}
}
if(!child)
{
printf("cannot find target node.\n");
return root;
}
// child != NULL
if((child->left == NULL) && (child->right == NULL)) // leaf node
{
if(parent == NULL) // root node
{
root == NULL;
// return root;
}
else if(child == parent->left)
{
parent->left = NULL;
// return root;
}
else
{
parent->right = NULL;
// return root;
}
}
else if(!(child->left && child->right)) // has one child node
{
if(child->left)
{
if(parent == NULL)
{
root = child->left;
// return root;
}
else
{
parent->left = child->left;
// return root;
}
}
if(child->right)
{
if(parent == NULL)
{
root = child->right;
// return root;
}
else
{
parent->right = child->right;
// return root;
}
}
}
else // has two children nodes
// find the max node of the deleted note, according to the rightmost of deleted node's left tree
{
Node *pMax = child->left;
Node *pMaxParent = NULL;
while(pMax->right)
{
pMaxParent = pMax;
pMax = pMax->right; // stored the right child value
}
if(pMax == child->left) // deleted note has one generation, as pMaxParent=NULL
{
pMax->right = child->right;
}
else // deleted note has more than one generations, as pMaxParent=NULL
{
pMaxParent->right = pMax->left;
pMax->left = child->left;
pMax->right = child->right;
}
if(parent == NULL)
{
root = pMax;
}
else if(parent->left == child)
parent->left = pMax;
else
parent->right = pMax;
}
free(child);
return root;
}