数据结构与算法之 树

二叉搜索树的使用

//这一个版本写的是较为简单的树,分为了三个部分组成,
主要是利用栈的思想来进行前序遍历我们的树,本程序没有采用递归去前序遍历,用递归的话效率过低,不推荐使用.

//本程序所实现的功能有
//插入元素(必须小于栈的最大容量). 前序遍历 二叉搜索树的删除(必须小于当前树内元素的个数) .再次前序遍历 .查找指定节点
teer.h

#pragma once

#define MAX_NODE 1024
typedef int ElemeType;

typedef struct _Bnode {
	ElemeType data;		//元素类型
	struct _Bnode *lchild, *rchild;	//指向左右孩子结点
}Bnode, Btree;

stack.h

#pragma once
#include<stdlib.h>
#include<stdio.h>
#include"tree.h"

#define MaxSize 128	//预分配空间, 这个数值根据实际大小预估确定

typedef struct _SqStack {
	Bnode *base;	//栈底指针
	Bnode *top;		//栈顶指针
}SqStack;

//构造一个空栈S
bool InitStack(SqStack &S) {	
	S.base = new Bnode[MaxSize];	//为栈分配最大容量
	if (!S.base) return false;	//空间分配失败

	S.top = S.base;		//top初始化为base,空栈
	return true;
}
//插入元素e为新的栈顶元素
bool PushStack(SqStack &S, Bnode e) {
	if (S.top - S.base == MaxSize)
		return false;		//栈满

	*(S.top++) = e;
	return true;

}
//删除S的栈顶元素,暂存在变量e
bool PopStack(SqStack &S, Bnode &e) {
	if (S.base == S.top)
		return false;	//栈空
	e = *(--S.top);
	return true;
}
//返回S的栈顶元素,栈顶指针不变
Bnode *GetTop(SqStack &S) {
	if (S.top != S.base) {	//栈非空
		return S.top - 1;	//返回栈顶元素的值,栈顶指针不变
	} else {	//这种情况是栈空的状况
		return NULL;
	}
}
//返回栈中元素个数
int GetSize(SqStack &S) {
	return(S.top - S.base);
}
//判断栈是否为空
bool IsEmpty(SqStack &S) {
	if (S.top == S.base) {
		return true;
	} else {
		return false;
	}
}
//销毁栈
void DestroyStack(SqStack &S) {
	if (S.base) {
		free(S.base);
		
		S.base = NULL;
		S.top = NULL;
	}
}
tree.cpp

//1.插头 2. 全插 3.前序遍历 4.二叉搜索树的删除 5.再次前序遍历 6.查找指定节点
#include<iostream>
#include<Windows.h>
#include"stack.h"

using namespace std;
static int length = 0;

//插入元素
bool InsertBnode(Btree* &root, Bnode* node) {
	Btree *tmp=NULL, *parent = NULL;;
	if (!node) return false;	//待插入的节点为空时,直接就返回
	//将待插入的节点的左右后指针清空,为了以后的插入做准备
	node->lchild = NULL;
	node->rchild = NULL;

	//如果没有根节点那么,根节点的值就等于要插入结点的值
	if (root->data == NULL) {
		root->data = node->data;
		length++;
		return true;
	}
	//再有根节点的情况下
	tmp = root;	//先让这个临时指针指向我们的根结点,代替我们的根节点移动从而去和要插入结点比较
	while (tmp) {	//遍历能比较的点,知道tmp指向的结点的左右子节点都不存在为止
		parent = tmp;	//让parent 充当现在的父节点,tmp就能继续向下实现遍历了
		if (node->data > parent->data) {	//当要插入结点大于我们的父节点时tmp继续向下遍历
			tmp = tmp->rchild;
		} else {	//当要插入结点小于我们的父节点时tmp继续向下遍历
			tmp = tmp->lchild;
		}
	}
	//现在已经到了最后的结点了,现在比较带插入结点和这个parent结点哪个大就能确定待插入结点的位置
	if (node->data > parent->data) {	//待插入结点大于父节点 放右边
		parent->rchild = node;
	} else {	//待插入结点小于父节点 放左边
		parent->lchild= node;
	}
	length++;
	return true;
}
//前序遍历
void proprint(Btree* root) {
	Btree tmp;
	SqStack stack;
	InitStack(stack);

	PushStack(stack, *root);	//根节点进栈

	while (!(IsEmpty(stack))) {	//栈为空,所有节点均已处理 
		PopStack(stack, tmp);		//要遍历的节点 	
		printf("- %d -", tmp.data);		//打印要便利的节点的值

		if (tmp.rchild != NULL) {
			PushStack(stack, *(tmp.rchild)); //右子节点先入栈,后处理 	
		}
		if (tmp.lchild != NULL) {
			PushStack(stack, *(tmp.lchild)); //左子节点后入栈,接下来先 处理 	
		}
	}
	DestroyStack(stack);	//将我们的之前创建好的栈销毁掉
}

//寻找二叉搜索树左子树上最大的结点 
 
int findMax(Btree* root) {
	//方式二 采用循环 
	while(root->rchild){ 
		root = root->rchild; 
	}
	return root->data; 
}

//删除指定元素
Btree* DeleteNode(Btree* root, int key) {
	if (root == NULL) return NULL;//没有找到删除节点 
	if (root->data > key) {
		root->lchild = DeleteNode(root->lchild, key);
		return root;
	}
	if (root->data < key) {
		root->rchild = DeleteNode(root->rchild, key);
		return root;
	}
	//删除节点不存在左右子节点,即为叶子节点,直接删除 
	if (root->lchild == NULL && root->rchild == NULL) {
		length--;
		return NULL;
	}
	//删除节点只存在右子节点,直接用右子节点取代删除节点 
	if (root->lchild == NULL && root->rchild != NULL) {
		length--;
		return root->rchild;
	}
	//删除节点只存在左子节点,直接用左子节点取代删除节点 
	if (root->lchild != NULL && root->rchild == NULL){
		length--;
	return root->lchild;
}
	//删除节点存在左右子节点,直接用左子节点最大值取代删除节点
	int val = findMax(root->lchild);
	root->data=val; 
	root->lchild = DeleteNode(root->lchild,val); 
	length--;
	return root; 
}

/** * 使用非递归方式查找结点 */
bool QueryByLoop(Bnode* root, int Element) { 
	while (root != NULL && root->data!= Element) {
		if (Element <root->data) {
			root = root->lchild; 
		} else { 
			root = root->rchild; 
		}
	}
	if (root == NULL) {
		return false;
	} else {
		return true;
	}

}



int main(void) {
	Btree *s=new Btree;
	s->lchild = s->rchild = NULL;
	s->data = NULL;
	Bnode *node;
	//将第一个数据插入到树作为根节点
	node = new Bnode;
	cout << "请输入你想要入树的元素个数(不得超过本栈的最大容量:   " << MaxSize << "): ";
	int num=-1;
	cin >> num;
	//不让用户输入的值超过栈的最大容量
	while (1) {
		if (num > MaxSize) {
			cout << "输入的个数超过了本栈的最大容量,请重新输入!!!! ";
			cout << "请输入你想要入树的元素个数(不得超过本栈的最大容量:   " << MaxSize << "): ";
			cin >> num;
		} else {
			break;
		}
	}

	//插入头结点
	cout << "请输入你想要插入的元素: ";
	cin>>node->data;
	if(InsertBnode(s, node)) {
		cout << "插入元素 " << node->data << "成功! " << endl;
	} else {
		cout << "插入元素失败! " << endl;
	}
	
	for (int i = 1; i < num; i++) {
		node = new Bnode;
		cout << "请输入你想要插入的元素: ";
		cin >> node->data;
		
		if (InsertBnode(s, node)) {
			cout << "插入元素 " << node->data << "成功! " << endl;;
		} else {
			cout << "插入元素失败! " << endl;
		}
	}

	//前序遍历树
	proprint(s);
	cout << "一共有 " << length << " 个元素" << endl;
	//二叉搜索树的删除

	cout << "请输入你要删除的元素的个数(不能超过现在的树的最大个数: " << length << "): ";
	int Element;
	cout << endl;
	cin >> num;
	while (1) {
		if (num > length) {
			cout << "输入的个数超过了本栈的最大容量,请重新输入!!!! ";
			cout << "请输入你想要入树的元素个数(不得超过本栈的最大容量:   " << length << "): ";
			cin >> num;
		} else {
			break;
		}
	}

	for (int i = 0; i < num; i++) {
		cout << "请输入你想删除的值: ";
		cin >> Element;
		DeleteNode(s, Element);

		cout << "***********************************************" << endl;
		proprint(s);
		cout << "一共有 " << length << " 个元素)" << endl;
		cout << "***********************************************" << endl;
	}

	cout << "请输入你想要查找的值:  " << endl;
	cin >> Element;
	if (QueryByLoop(s, Element)) {
		cout << "查找成功!! 查找的值是:   " << Element << endl;
	} else {
		cout << "查找失败!不存在这个值" << endl;
	}
	
	proprint(s);
	cout << "一共有 " << length << " 个元素" << endl;

	system("pause");
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值