看到上篇留言,要C语言实现,本篇是二叉查找树操作,上两篇也会更新C语言实现;
【问题描述】编写一个操纵二叉查找树的程序。它将从标准输入接收命令,并将这些命令的响应打印到标准输出。二叉查找树是一棵二叉树,它在内部节点存储整数值。特定节点的值大于存储在其左侧子树中的每个值,小于存储在其右侧子树中的每个值。该树不包含重复值。请注意,在创建新节点时,需要使用malloc为它们分配空间;一旦不再需要任何已分配的空间,就应该使用free将其释放。
【基本要求】操纵二叉查找树的命令有4个:插入n: 向树中添加一个值,如果还没有的话,新节点将始终作为现有节点的子节点或根节点添加,现有节点不会因为插入数据而改变或移动。如果n不存在,因此将被插入,程序打印插入;否则,它将打印不插入。 指令格式是一个i后跟一个十进制整数n 。搜索n: 在树中搜索值n。如果n存在,程序将打印存在;否则会打印缺席。 指令格式是一个s后跟一个空格和一个整数n 。打印: 空树(即空)被打印为空字符串。节点被打印为一个(,后跟左边的子树、该节点的项、右边的子树和),没有空格。 指令格式是一个p。 例如,对应于图3-1的输出是((1)2((3(4))5(6)))删除n: 从树中删除一个值。删除二叉树排序中的节点有几种策略。如果一个节点没有子节点,可以简单地删除它;也就是说,指向它的指针可以更改为空指针。如果一个节点有一个子节点,它可以被那个子节点替换。如果一个节点有两个子节点,其值将被更改为其左子树中的最大元素,之前包含该值的节点将被删除。请注意,正在删除的值可能在根节点上。 指令格式是一个d后跟一个空格和一个整数n 。
//C语言实现:
#include
#include
typedef struct TreeNode{
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
//创建一个新的树节点
TreeNode* createNode(int val){
TreeNode* newNode = (TreeNode*) malloc(sizeof(TreeNode));
newNode->val = val;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
//将值插入二叉查找树中
void insert(TreeNode** root,int val){
if(*root == NULL){
*root = createNode(val);
printf("inserted\n");
return;
}
if(val == (*root)->val){
printf("not inserted\n");
return;
}
if(val < (*root)->val){
insert(&((*root)->left),val);
}else{
insert(&((*root)->right),val);
}
}
//在二叉查找树中查找值
void search(TreeNode* root,int val){
TreeNode* curr = root;
while(curr != NULL){
if(val == curr->val){
printf("present\n");
return;
} else if(val < curr->val){
curr = curr->left;
} else{
curr = curr->right;
}
}
printf("absent\n");
}
void deleted(TreeNode** root, int val){
if(*root == NULL){
return;
}
if(val == (*root)->val){
if((*root)->left == NULL){
*root = (*root)->right;
} else if((*root)->right == NULL){
*root = (*root)->left;
}else{
TreeNode* curr = (*root)->left;
while (curr->right != NULL){
curr = curr->right;
}
(*root)->val = curr->val;
deleted(&((*root)->left),curr->val);
}
printf("deleted\n");
return;
}
if(val < (*root)->val){
deleted(&((*root)->left),val);
} else{
deleted(&((*root)->right),val);
}
}
//打印二叉查找树
void print(TreeNode* root){
// if(root == NULL){
// printf("none");
// return;
// }
if(root->left != NULL){
print(root->left);
}
printf("(%d",root->val);
if (root->right != NULL) {
print(root->right);
}
printf(")");
}
//释放树节点动态分配的内存
void freeTree(TreeNode* root){
if(root == NULL){
return;
}
freeTree(root->left);
freeTree(root->right);
free(root);
}
int main(){
TreeNode* root = NULL;
char command;
int val;
while(1){
scanf("%c %d",&command,&val);
switch (command) {
case 'i':
insert(&root,val);
break;
case 's':
search(root,val);
break;
case 'd':
deleted(&root,val);
case 'p':
print(root);
break;
default:
printf("illegal\n");
}
//清空缓存区
while(getchar() != '\n');
}
freeTree(root);
return 0;
}
在这个代码中该程序通过标准输入接收四种命令:
- i 插入n:调用insert函数向二叉查找树中添加值n;
- s 搜索n:调用search函数在二叉查找树中查找值n的存在;
- d 删除n:调用delete函数从二叉查找树中删除值n;
- p 打印:调用print函数打印整个二叉查找树。
每次读入命令后,用switch语句根据不同的命令调用相应的函数。该程序在操作二叉查找树时,使用了动态内存分配机制,需要在不需要时及时释放相应的内存。
需要注意的是,我在这个代码中并未进行输入区分,所以打印时格式也得是`p+空格+数字`,有需要的可以自己编写此部分代码进行区分,用以优化代码。
//Java实现:
import java.util.Scanner;
@SuppressWarnings("all")
class TreeNode {
public int val; //节点值
public TreeNode left; //左子节点
public TreeNode right; //右子节点
public TreeNode(int val){
this.val = val;
this.left = null;
this.right = null;
}
public TreeNode(TreeNode left,int val,TreeNode right){
this.left = left;
this.val = val;
this.right = right;
}
}
public class demo3 {
public static void main(String[] args) {
SearchTree st = new SearchTree(); //创建二叉搜索树
Scanner sc = new Scanner(System.in);
//分别拿到指令和数据
while (true) { // 当仍有输入数据时继续循环
//分别拿到指令和数据
String s = sc.nextLine();
if (s.length() <= 1) {
st.print(SearchTree.root);
}else {
String[] s1 = s.split(" ");
char c = s1[0].charAt(0);
int x = Integer.parseInt(s1[1]);
//通过指令进行对应操作
switch (c) {
case 'I':
case 'i': {
st.insert(x); //执行插入操作
break;
}
case 'S':
case 's': {
st.search(x);
break;
}
case 'D':
case 'd': {
st.delete(x);
break;
}
default:
System.out.println("您输入的格式有误,请重新输入!");
}
}
}
}
}
class SearchTree{
public static TreeNode root = null; //二叉树的根节点
//插入二叉树
public void insert(int val){
root = insertNode(root,val);
}
private TreeNode insertNode(TreeNode root,int val){
if (root == null){
System.out.println("inserted");
return new TreeNode(val);
}
if (val < root.val){
root.left = insertNode(root.left,val);
}else if(val > root.val){
root.right = insertNode(root.right,val);
}else {
System.out.println("not inserted");
}
return root;
}
//查找二叉树
public void search(int val){
if (searchNode(root,val)){
System.out.println("present");
}else {
System.out.println("absent");
}
}
private boolean searchNode(TreeNode root,int val){
if (root == null){
return false;
}
if (val == root.val) {
return true;
} else if (val < root.val) {
return searchNode(root.left,val);
}else{
return searchNode(root.right,val);
}
}
//删除某一元素
public void delete(int val){
root = deleteNode(root,val);
}
private TreeNode deleteNode(TreeNode root,int val){
if (root == null){
return null;
}
if (val < root.val){
root.left = deleteNode(root.left,val);
}else if (val > root.val){
root.right = deleteNode(root.right,val);
}else {
if (root.left == null && root.right == null){
//没有子节点
System.out.println("deleted");
root = null;
} else if (root.left == null) {
//有一个右节点
System.out.println("deleted");
root = root.right;
} else if (root.right == null) {
//有一个左节点
System.out.println("deleted");
root = root.left;
}else {
//有两个子节点
TreeNode maxNode = findMax(root.left);
root.val = maxNode.val;
root.left = deleteNode(root.left, maxNode.val);
System.out.println("deleted");
}
}
return root;
}
private TreeNode findMax(TreeNode node){
while(node.right != null){
node = node.right;
}
return node;
}
//打印二叉树
public void print(TreeNode root) {
// System.out.print("print: ");
printNode(root);
// System.out.print();
}
void printNode(TreeNode root){
if (root != null) {
System.out.print("(");
printNode(root.left);
System.out.print(root.val);
printNode(root.right);
System.out.print(")");
}
}
}
每次读入指令后,用switch语句根据不同的指令调用相应的方法。搜索树的操作中,通过向左或者向右查找叶子节点,该代码的封装操作较多,root实体搜索树一直在searchTree中,直至生命周期结束。
需要注意的是,由于输入指令时i、d和s需要读入空格,而打印操作只需要输入一个p字母,为了加以区分,我使用了一个if结构在读取完后先进行一次判断。