今天想对物品进行一次线索二叉树的存储,但感觉还是理解不够,插入和删除涉及到前驱和后驱,并且每次都需要进行一次更新并线索化,写的云里雾里。感觉时间复杂度反而递增,建立只能对一个条件进行了区分建立,反而费力不讨好。
网上也很少有关线索二叉树的一个增删操作,因其操作繁琐,只适合对已存数据进行线索化然后进行遍历查找。根据ChatGPT提供的c代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct ThreadNode {
int data;
struct ThreadNode* lchild;
struct ThreadNode* rchild;
int ltag;
int rtag;
} ThreadNode, *ThreadTree;
ThreadNode* pre = NULL; // 全局变量,用于记录当前节点的前驱
void CreateThreadTree(ThreadTree* T) {
int data;
scanf("%d", &data); // 输入节点的值
if (data == -1) { // 输入-1表示该子树为空
*T = NULL;
} else {
*T = (ThreadNode*)malloc(sizeof(ThreadNode)); // 分配节点空间
(*T)->data = data;
(*T)->ltag = 0; // 标记左右孩子结点是否为线索
(*T)->rtag = 0;
CreateThreadTree(&((*T)->lchild)); // 递归创建左子树
CreateThreadTree(&((*T)->rchild)); // 递归创建右子树
}
}
void InThread(ThreadTree T) {
if (T != NULL) {
InThread(T->lchild); // 递归线索化左子树
if (T->lchild == NULL) { // 如果左子树为空,将ltag标记为1,并指向前驱
T->ltag = 1;
T->lchild = pre;
}
if (pre != NULL && pre->rchild == NULL) { // 如果前驱右子树为空,将rtag标记为1,并指向后继
pre->rtag = 1;
pre->rchild = T;
}
pre = T; // 更新前驱为当前节点
InThread(T->rchild); // 递归线索化右子树
}
}
void CreateInThread(ThreadTree T) {
if (T != NULL) {
InThread(T); // 线索化二叉树
if (pre->rchild == NULL) { // 处理最后一个节点的后继
pre->rtag = 1;
}
}
}
ThreadNode* FirstNode(ThreadNode* p) {
while (p->ltag == 0) { // 如果ltag为0,说明该节点有左子树
p = p->lchild;
}
return p;
}
ThreadNode* NextNode(ThreadNode* p) {
if (p->rtag == 0) { // 如果rtag为0,说明该节点有右子树
return FirstNode(p->rchild);
} else { // 否则,返回右指针的后继
return p->rchild;
}
}
void InOrder(ThreadTree T) {
for (ThreadNode* p = FirstNode(T); p != NULL; p = NextNode(p)) {
printf("%d ", p->data);
}
printf("\n");
}
void InsertNode(ThreadTree* T, int data) {
ThreadNode* p = *T, * q = NULL; // p指向待插入节点的位置,q指向p的父节点
while (p != NULL) {
if (data < p->data) {
q = p;
p = p->lchild;
} else if (data > p->data) {
q = p;
p = p->rchild;
} else { // 如果二叉树中已经有该节点,则直接返回
return;
}
}
ThreadNode* node = (ThreadNode*)malloc(sizeof(ThreadNode)); // 分配节点空间
node->data = data;
node->ltag = 1; // 新节点一定是叶节点,标记为线索
node->rtag = 1;
if (q == NULL) { // 如果二叉树为空,将新节点作为根节点
*T = node;
} else if (data < q->data) { // 插入左子树
node->lchild = q->lchild;
q->lchild = node;
q->ltag = 0; // 父节点的ltag改为0,表示有左孩子
} else { // 插入右子树
node->rchild = q->rchild;
q->rchild = node;
q->rtag = 0; // 父节点的rtag改为0,表示有右孩子
}
}
void DeleteNode(ThreadTree* T, int data)
{
ThreadNode* p = *T, * q = NULL; // p指向待删除节点,q指向p的父节点
while (p != NULL && p->data != data) { // 找到待删除节点的位置
q = p;
if (data < p->data) {
p = p->lchild;
} else {
p = p->rchild;
}
}
if (p == NULL) { // 如果待删除节点不存在,则直接返回
return;
}
if (p->ltag == 0 || p->rtag == 0) { // 如果待删除节点有子节点
ThreadNode* sub; // sub指向待删除节点的子节点
if (p->ltag == 0) { // 如果待删除节点有左子树
sub = p->lchild;
while (sub->rtag == 0) { // 找到左子树中最右下的节点
sub = sub->rchild;
}
} else { // 否则,待删除节点有右子树
sub = p->rchild;
while (sub->ltag == 0) { // 找到右子树中最左下的节点
sub = sub->lchild;
}
}
// 用子节点代替待删除节点
if (p == *T) { // 如果待删除节点是根节点
*T = sub;
} else if (p == q->lchild) { // 如果待删除节点是父节点的左孩子
q->lchild = sub;
} else { // 否则,待删除节点是父节点的右孩子
q->rchild = sub;
}
// 更新前驱和后继
ThreadNode* lthread = FirstNode(p->lchild); // 待删除节点的左子树中最右下的节点
ThreadNode* rthread = NextNode(p); // 待删除节点的右子树中最左下的节点
if (lthread != NULL) {
lthread->rchild = rthread;
}
if (rthread != NULL) {
rthread->lchild = lthread;
}
}
else
{ // 否则,待删除节点为叶节点
if (p == *T) { // 如果待删除节点是根节点
*T = NULL;
} else if (p == q->lchild) { // 如果待删除节点是父节点的左孩子
q->lchild = NULL;
q->ltag = 1; // 父节点的ltag改为1,表示没有左孩子
} else { // 否则,待删除节点是父节点的右孩子
q->rchild = NULL;
q->rtag = 1; // 父节点的rtag改为1,表示没有右孩子
}
}
free(p); // 释放待删除节点的空间
}
int main() {
ThreadTree T = NULL;
// 创建线索二叉树
printf("请输入结点,-1表示该子树为空:\n");
CreateThreadTree(&T);
CreateInThread(T);
// 中序遍历线索二叉树
printf("中序遍历线索二叉树:");
InOrder(T);
// 插入节点
InsertNode(&T, 6);
printf("插入结点后的中序遍历:");
InOrder(T);
// 删除节点
DeleteNode(&T, 5);
printf("删除结点后的中序遍历:");
InOrder(T);
return 0;
}
这是一个对线索二叉树进行构建以及增删的C语言代码,对它理了好久人都麻了,应用于系统中维护起来效率反而更低效。感觉超市系统只能利用双向链表进行一个建立;今天对其编写都是基与树进行函数的创建,之后进行一个修改:
// 按编号查询结点
ThreadNode* searchByID(ThreadTree T, char id[]);
void* searchshelf(ThreadTree p,char *s); //货物架查寻
ThreadNode* searchByPurchaseTime(ThreadTree T, char purchaseTime[]);
ThreadNode* searchByName(ThreadTree T, char name[]);
void shelfManagement(ThreadNode *t);
int main()
{
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_INTENSITY);
//
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),15 | 8 | 128 | 64);
//
// HANDLE JB = GetStdHandle(STD_OUTPUT_HANDLE);
//
// SetConsoleTextAttribute(JB, 2 | 8);
ThreadTree T = NULL;
goods c;
int choice = 0;
while (1) {
printf("超市物品信息管理系统\n");
printf("***========================***\n");
printf("1. 按编号查询\n");
printf("2. 按名称查询\n");
printf("3. 按进货时间查询\n");
printf("4. 货架管理\n");
printf("5. 进货\n");
printf("6. 出货\n");
printf("7. 退出系统\n");
printf("***========================***\n");
printf("请选择功能:");
scanf("%d", &choice);
switch (choice)
{
case 1:// 按编号查询
printf("请输入物品编号:");
scanf("%s", c.id);
ThreadNode *p = searchByID(T, c.id);
if (p == NULL)
{
printf("没有找到该物品!\n");
}
else
{
printf("****物品编号*******物品名称*******物品单价****库存数量****架货编号****进货日期");
printf(" %5d %10s %4d %8s %8s",p->data.id,p->data.id,p->data.price,p->data.amount,p->data.shelf,p->data.purchaseTime);
}
break;
case 2:// 按名称查询
printf("请输入物品名称:\n");
scanf("\n%s", c.name);
ThreadNode *p1=searchByName(T,c.name);
if (p1 == NULL)
{
printf("没有找到该物品!\n");
}
else
{
printf("****物品编号*******物品名称*******物品单价****库存数量****架货编号****进货日期");
printf(" %5d %10s %4d %8s %8s",p1->data.id,p1->data.id,p1->data.price,p1->data.amount,p1->data.shelf,p1->data.purchaseTime);
}
break;
case 3:// 按进货时间查询
printf("请输入进货时间(yyyy-mm-dd):");
scanf("%s", c.purchaseTime);
ThreadNode *p2=searchByPurchaseTime(T,c.purchaseTime);
if (p2 == NULL)
{
printf("没有找到该物品!\n");
}
else
{
printf("****物品编号*******物品名称*******物品单价****库存数量****架货编号****进货日期");
printf(" %5d %10s %4d %8s %8s",p2->data.id,p2->data.id,p2->data.price,p2->data.amount,p2->data.shelf,p2->data.purchaseTime);
}
break;
case 4:// 货架管理
ThreadNode *p3=shelfManagement(T);
break;
case 5:
// 进货
purchase();
break;
case 6:
// 出货
sale();
break;
case 7:
// 退出系统
printf("感谢使用本系统,再见!\n");
exit(0);
default:
printf("请正确输入选项!\n");
break;
}
}
return 0;
}
ThreadNode* searchByID(ThreadTree T, char id[]) {
ThreadNode *p = T->left;
while (p != T) {
if (strcmp(id, p->data.id) == 0) {
return p;
} else if (strcmp(id, p->data.id) < 0) {
if (p->ltag == 1) {
break;
}
p = p->left;
} else {
if (p->rtag == 1) {
break;
}
p = p->right;
}
}
return NULL;
}
// 按名称查询结点
ThreadNode* searchByName(ThreadTree T, char name[])
{
ThreadNode *p =T;
if(p==NULL)
return NULL;
if (strcmp(name, p->data.name) == 0)
{
return p;
}
return searchByName(T->left,name);
return searchByName(T->right,name);
return NULL;
}
// 按进货时间查询结点
ThreadNode* searchByPurchaseTime(ThreadTree T, char purchaseTime[])
{
ThreadNode *p=T;
if(p==NULL)
return NULL;
if (strcmp(purchaseTime, p->data.purchaseTime) == 0)
{
return p;
}
return searchByName(T->left,purchaseTime);
return searchByName(T->right,purchaseTime);
return NULL;
}
// 货架管理功能
void shelfManagement(ThreadNode *t)
{
char s[10];
printf("请输入你要查找的货架:\n");
scanf("%s",s);
printf("\n****物品编号*******物品名称*******物品单价****库存数量****架货编号****进货日期\n");
searchshelf(t,s);
}
void* searchshelf(ThreadTree p,char *s)
{
if (strcmp(s,p->data.shelf) == 0)
{
printf(" %5d %10s %4d %8s %8s\n",p->data.id,p->data.id,p->data.price,p->data.amount,p->data.shelf,p->data.purchaseTime);
}
return searchByName(p->left,s);
return searchByName(p->right,s);
return NULL;
}
格式上还要对其进行修改,将输出全部放到子函数中,并将树存储改为双向链表存储,麻了,想换个项目搞了。,。,。