二叉查找树,也称二叉搜索树或二叉排序树,其有如下的性质:
-
若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值
-
任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值
-
任意节点的左、右子树也分别为二叉查找树
-
没有键值相等的节点
基于二分的思想,二叉查找树的优势在于其查找、搜索的时间复杂度低为O(logN),中序遍历输出的结果是单调的,常用于二分搜索。查找树变成一个有序序列,构造树的过程即为对无序序列进行查找的过程。每次插入的新的结点都是二叉查找树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可,因为二叉查找树要求键值不同,故一定可以找到一个合适的空位置将其插入。
由此可知,搜索,插入,删除的复杂度等于树高,期望为O(log N),最坏情况为O(N)(此时数列有序,二叉树退化成线性表)。
二叉查找树的基本操作有插入(insert)、删除(remove)、查找(find)、遍历输出(traverse)等,其中最复杂的是删除操作:
在二叉查找树删去一个结点,分三种情况讨论:
① 若p是叶子结点: 直接删除p。
② 若p只有一棵子树(左子树或右子树):直接用p的左子树(或右子树)取代p的位置而成为f的一棵子树。即原来p是f的左子树,则p的子树成为f的左子树;原来p是f的右子树,则p的子树成为f的右子树。
③ 若p既有左子树又有右子树 :处理方法有以下两种,可以任选其中一种。
◆ 用p的直接前驱结点代替p。即从p的左子树中选择值最大的结点s放在p的位置(用结点s的内容替换结点p内容),然后删除结点s。s是p的左子树中的最右边的结点且没有右子树,对s的删除同②。(前驱结点:其左子树中最右的结点)
◆ 用p的直接后继结点代替p。即从p的右子树中选择值最小的结点s放在p的位置(用结点s的内容替换结点p内容),然后删除结点s。s是p的右子树中的最左边的结点且没有左子树,对s的删除同②。(后继结点:其右子树中最左的结点)
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
typedef struct node //搜索二叉树的声明
{
int key;
struct node* left;
struct node* right;
}node,*bst;
bst find(int x, bst root) //查找操作,这里我们没有对传入的指针进行修改,故不需要使用引用!
{
if (root == NULL)
return NULL;
if (root->key < x)
return find(x, root->right);
else if (root->key > x)
return find(x, root->left);
else
return root;
}
bst find_min(bst root) //查找最小值的递归方式,查找最大值相同
{
if (root == NULL)
return NULL;
if (root->left != NULL)
find_min(root->left);
else
return root;
}
bool insert(int x, bst &root) //递归插入操作,需要注意的是我们传入的指针会在函数中被修改操作,但实参到形参单向传递的,因此这里注意需要使用引用!
{
if (root == NULL)
{
root = new node;
root->key = x;
root->left = root->right = NULL;
return true;
}
if (root->key == x) //搜索二叉树不允许有相同的值!
return false;
else if (root->key > x)
insert(x, root->left); //递归寻找合适的位置
else
insert(x, root->right);
}
bool remove(int x, bst &root) //删除操作,同理需要使用引用!
{
if (root == NULL)
return false;
if (root->left == NULL&&root->right == NULL) //该节点是树叶
{
if (root->key == x) //找到该值,进行直接删除
{
delete root;
root = NULL;
return true;
}
else //没有找到该值,该值不存在无法删除
return false;
}
if (root->key > x)
remove(x, root->left);
else if (root->key < x)
remove(x, root->right);
else
{
if (root->right == NULL) //找到该值,且其只有左子树,令左子树代替其位置,删除原位置
{
bst tmp = root;
root = root->left;
delete tmp;
return true;
}
else if (root->left == NULL) //找到该值,且其只有右子树,令右子树代替其位置,删除原位置
{
bst tmp = root;
root = root->right;
delete tmp;
return true;
}
else //找到该值,其有左右子树。那么找它的前驱(左子树最右的节点)或后继(右子树最左的节点)代替原位置,然后删除前驱或后继!
{
bst rightfirst = root->right;
while (rightfirst->left) //找后继点
{
rightfirst = rightfirst->left;
}
swap(root->key, rightfirst->key); //交换替代然后删除后继点
remove(x, root->right);
return true;
}
}
}
void inorderTraverse(bst root) //中序遍历法。前序后序遍历只需调换位置
{
if (root)
{
inorderTraverse(root->left);
cout << root->key << ' ';
inorderTraverse(root->right);
}
}
int main() //测试数据
{
bst myroot = NULL;
int a[10] = { 2,52,10,4,56,74,21,87,11,33 };
for (int i = 0; i < 10; i++)
{
insert(a[i], myroot);
}
cout << "中序遍历为:" << endl;
inorderTraverse(myroot);
cout << endl;
remove(52, myroot);
cout << "删除52后顺序为:" << endl;
inorderTraverse(myroot);
cout << endl;
cout << "最小元素为:" << endl;
bst tmp = find_min(myroot);
cout << tmp->key << endl;
cout << "插入15后的顺序为:" << endl;
insert(15, myroot);
inorderTraverse(myroot);
cout << endl;
return 0;
}
程序运行结果:
例题之一:
Hardwood Species
Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 26197 | Accepted: 10037 |
Description
Hardwoods are the botanical group of trees that have broad leaves, produce a fruit or nut, and generally go dormant in the winter.
America's temperate climates produce forests with hundreds of hardwood species -- trees that share certain biological characteristics. Although oak, maple and cherry all are types of hardwood trees, for example, they are different species. Together, all the hardwood species represent 40 percent of the trees in the United States.
On the other hand, softwoods, or conifers, from the Latin word meaning "cone-bearing," have needles. Widely available US softwoods include cedar, fir, hemlock, pine, redwood, spruce and cypress. In a home, the softwoods are used primarily as structural lumber such as 2x4s and 2x6s, with some limited decorative applications.
Using satellite imaging technology, the Department of Natural Resources has compiled an inventory of every tree standing on a particular day. You are to compute the total fraction of the tree population represented by each species.
Input
Input to your program consists of a list of the species of every tree observed by the satellite; one tree per line. No species name exceeds 30 characters. There are no more than 10,000 species and no more than 1,000,000 trees.
Output
Print the name of each species represented in the population, in alphabetical order, followed by the percentage of the population it represents, to 4 decimal places.
Sample Input
Red Alder
Ash
Aspen
Basswood
Ash
Beech
Yellow Birch
Ash
Cherry
Cottonwood
Ash
Cypress
Red Elm
Gum
Hackberry
White Oak
Hickory
Pecan
Hard Maple
White Oak
Soft Maple
Red Oak
Red Oak
White Oak
Poplan
Sassafras
Sycamore
Black Walnut
Willow
Sample Output
Ash 13.7931
Aspen 3.4483
Basswood 3.4483
Beech 3.4483
Black Walnut 3.4483
Cherry 3.4483
Cottonwood 3.4483
Cypress 3.4483
Gum 3.4483
Hackberry 3.4483
Hard Maple 3.4483
Hickory 3.4483
Pecan 3.4483
Poplan 3.4483
Red Alder 3.4483
Red Elm 3.4483
Red Oak 6.8966
Sassafras 3.4483
Soft Maple 3.4483
Sycamore 3.4483
White Oak 10.3448
Willow 3.4483
Yellow Birch 3.4483
Hint
This problem has huge input, use scanf instead of cin to avoid time limit exceeded.
明显可以使用二叉查找树进行排序,每一个的键是植物种类名,值是该植物的个数,只需要插入和遍历操作。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int num = 0;
typedef struct node
{
char key[50];
int sum; //注意有些C++编辑器不允许int sum=0的操作!(vs2017可行)
struct node *left, *right;
}node,*bst;
void insert(char* s, bst &root)
{
if (root == NULL)
{
root = new node;
strcpy(root->key,s);
root->left = root->right = NULL;
root->sum = 0; //在建立时给sum赋值为零
}
if (strcmp(s,root->key)<0) //键的比较
insert(s, root->left);
else if (strcmp(s, root->key)>0)
insert(s, root->right);
else //键相同则是同种植物,个数加一
{
(root->sum)++;
}
}
void inorderTraverse(bst &root) //中序遍历按要求输出
{
if (root)
{
inorderTraverse(root->left);
printf("%s %.4f\n", root->key,(double)root->sum / num * 100.0);
inorderTraverse(root->right);
}
}
int main()
{
char s[50];
bst myroot = NULL;
while (cin.getline(s,50))
{
insert(s, myroot);
num++; //每读入一个总个数加一
}
inorderTraverse(myroot);
return 0;
}