【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树

本文主要是介绍【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 三叉链表存储二叉树
    • 三叉链表的前序遍历(不使用栈)法一
    • 三叉链表的前序遍历(不使用栈)法二
  • 一维数组存储二叉树
    • 一维数组存储二叉树的先序遍历
  • 线索二叉树的建立
    • 中序线索二叉树的遍历
  • 真题演练

三叉链表存储二叉树

三叉链表结构体表示如下图所示:

构造三叉链表方式:

typedef struct node{char data;struct node*parent,*lchild,*rchild;
}BTNode,*BiTree;
BTNode * creattree(BiTree &t){ // 易错点:树的引用char ch;cin>>ch;if(ch=='#'){t=NULL;}else{t=(BTNode*)malloc(sizeof(BTNode));// 易错点:忘记重新创建新结点t->data=ch;t->parent=NULL;t->lchild=NULL;t->rchild=NULL;if(t->lchild) t->lchild->parent=t;if(t->rchild) t->rchild->parent=t;creattree(t->lchild);creattree(t->rchild);}return t;
}

另外设计一个填充函数,函数功能是将所有结点的parent结点填充正确。

void FillParent(BiTree &root){if(root==NULL) return;if(root->lchild) {root->lchild->parent=root;FillParent(root->lchild);}if(root->rchild){root->rchild->parent=root;FillParent(root->rchild);}
}

三叉链表的前序遍历(不使用栈)法一

void PreOrder(BiTree t){   //访问顺序:根左右BTNode *p;while(t){visit(t);//访问根if(t->lchild) t=t->lchild;//找到左下结点,下一次就循环访问左else if(t->rchild) t=t->rchild;//下一次循环访问右else{//如果当前结点既没有左孩子也没有有孩子while(1){//一直往上回溯到有非空的父亲结点、同时找到二叉树的那个“叉”p=t;//p指向根tt=t->parent;//t指向父亲结点if(!t) break;//如果t为空,则说明该节点是空结点,排除这种情况if(t->lchild==p&&t->rchild) break;//如果t的左孩子是p且t的右孩子不为空,跳出while之后到右结点}if(t)t=t->rchild;//往右边访问}}
}

三叉链表的前序遍历(不使用栈)法二

// 【题目】二叉树采用三叉链表的存储结构,试编写
// 不借助栈的非递归中序遍历算法。
// 三叉链表类型定义:
typedef struct TriTNode
{char data;struct TriTNode *parent, *lchild, *rchild;
} TriTNode, *TriTree;void InOrder(TriTree PT, void (*visit)(char))
/* 不使用栈,非递归中序遍历二叉树PT,  */
/* 对每个结点的元素域data调用函数visit */
{TriTree p = PT, pr;while (p){if (p->lchild)p = p->lchild; // 寻找最左下结点else{visit(p->data); // 找到最左下结点并访问if (p->rchild){p = p->rchild; // 若有右子树,转到该子树,继续寻找最左下结点}else{pr = p; // 否则返回其父亲p = p->parent;while (p && (p->lchild != pr || !p->rchild)){                        // 若其不是从左子树回溯来的,或左结点的父亲并没有右孩子if (p->lchild == pr) // 若最左结点的父亲并没有右孩子visit(p->data);  // 直接访问父亲(不用转到右孩子)pr = p;              // 父亲已被访问,故返回上一级p = p->parent;       // 该while循环沿双亲链一直查找,若无右孩子则访问,直至找到第一个有右孩子的结点为止(但不访问该结点,留给下步if语句访问)}if (p){ // 访问父亲,并转到右孩子(经上步while处理,可以确定此时p有右孩子)visit(p->data);p = p->rchild;}}}}
}

一维数组存储二叉树

// 动态输入
void CreateTreeArray(int a[], int n)
{char ch;for (int i = 0; i < n; i++){cin >> ch;a[i] = ch;}
}

一维数组存储二叉树的先序遍历

// 前序遍历
#define Maxsize 50
typedef struct BTNodeArray
{int data[Maxsize];int length;
} BTNodeArray;
void PreOrderArray(BTNodeArray t, int i)
{if (i >= t.length)return;printf("%d", t.data[i]);PreOrderArray(t, i * 2);     // 遍历左子树PreOrderArray(t, i * 2 + 1); // 遍历右子树
}

线索二叉树的建立

线索二叉的的基本结构:

使用中序遍历的顺序进行线索化。代码中有一个难以理解的点,为什么不用p直接找后继,而是使用了前驱结点找后继。实际上,不是不用p找后继,而是从p找不到后继,所以只能间接地找前驱的后继,这样的方式找后继,明白了这点,代码就不难懂了。

//中序遍历线索化
void InOrder(ThreadTree &p,ThreadTree &pre){if(p!=NULL){InOrder(p->lchild,pre);if(p->lchild==NULL){    //只能通过当前结点找前驱p->lchild=pre;p->ltag=1;}if(pre!=NULL&&pre->rchild==NULL){  //只能通过前驱结点找后继pre->rchild=p;pre->rtag=1;}pre=p;InOrder(p->rchild,pre);}return;
}
void InOrderThread(ThreadTree t){ThreadNode *pre=NULL;if(t!=NULL){InOrder(t,pre);pre->rchild=NULL;pre->rtag=1;}
}

中序线索二叉树的遍历

//中序线索二叉树的遍历
//--最左下的结点
ThreadTree FirstNode(ThreadTree p){while(p->ltag==0) p=p->lchild;return p;
}
//--结点的后继
ThreadTree NextNode(ThreadTree p){if(p->rtag==0) p=p->rchild;else FirstNode(p->rchild);
}
//--开始遍历
void InOrderThreadCount(ThreadTree t){for(ThreadNode *p=FirstNode(t);p!=NULL;p=NextNode(p)) cout<<p->data<<endl;
}

真题演练

在这里插入图片描述

//建立三叉链表,
//删除每一个元素为x的结点,以及以他为根的子树且释放相应存储空间
#include<iostream>
using namespace std;void BuildTree(BiTree &t){char ch;cin>>ch;if(ch=='#'){t=NULL;}else{t=(BTNode *)malloc(sizeof(BTNode));t->data=ch;t->parent=NULL;t->lchild=NULL;t->rchild=NULL;if(t->lchild) t->lchild->parent=t;if(t->rchild) t->rchild->parent=t;BuildTree(t->lchild);BuildTree(t->rchild);}
}
void Destory(BiTree t){if(t==NULL) return;if(t->lchild) Destory(t->lchild);if(t->rchild) Destory(t->rchild);free(t);    //释放根节点t=NULL;     //空指针赋值0
}
void DeleteX(BiTree &t,char x){if(t==NULL) return;if(t->data==x) Destory(t);DeleteX(t->lchild,x);DeleteX(t->rchild,x);
}

这篇关于【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/478278

相关文章

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

MySQL中存储过程(procedure)的使用及说明

《MySQL中存储过程(procedure)的使用及说明》存储过程是预先定义的SQL语句集合,可在数据库中重复调用,它们提供事务性、高效性和安全性,MySQL和Java中均可创建和调用存储过程,示例展... 目录概念示例1示例2总结概念存储过程:在数据库中预先定义好一组SQL语句,可以被程序反复调用。

MySQL存储过程实践(in、out、inout)

《MySQL存储过程实践(in、out、inout)》文章介绍了数据库中的存储过程,包括其定义、优缺点、性能调校与撰写,以及创建和调用方法,还详细说明了存储过程的参数类型,包括IN、OUT和INOUT... 目录简述存储过程存储过程的优缺点优点缺点存储过程的创建和调用mysql 存储过程中的关键语法案例存储

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

k8s搭建nfs共享存储实践

《k8s搭建nfs共享存储实践》本文介绍NFS服务端搭建与客户端配置,涵盖安装工具、目录设置及服务启动,随后讲解K8S中NFS动态存储部署,包括创建命名空间、ServiceAccount、RBAC权限... 目录1. NFS搭建1.1 部署NFS服务端1.1.1 下载nfs-utils和rpcbind1.1

Redis高性能Key-Value存储与缓存利器常见解决方案

《Redis高性能Key-Value存储与缓存利器常见解决方案》Redis是高性能内存Key-Value存储系统,支持丰富数据类型与持久化方案(RDB/AOF),本文给大家介绍Redis高性能Key-... 目录Redis:高性能Key-Value存储与缓存利器什么是Redis?为什么选择Redis?Red

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2