当前位置: 首页 > wzjs >正文

域名网站建设方案南京百度seo排名

域名网站建设方案,南京百度seo排名,广州比较大的外贸公司,网站怎么申请微博登录数据结构基础(12) 文章目录数据结构基础(12)二叉树的先序遍历先序遍历中序遍历后序遍历二叉树的层序遍历由遍历序列构造二叉树前序 中序遍历序列后序 中序遍历序列层序 中序遍历序列二叉树的中序遍历(缺点&#xff…

数据结构基础(12)

文章目录

  • 数据结构基础(12)
      • 二叉树的先序遍历
      • 先序遍历
      • 中序遍历
      • 后序遍历
      • 二叉树的层序遍历
      • 由遍历序列构造二叉树
        • 前序 + 中序遍历序列
        • 后序 + 中序遍历序列
        • 层序 + 中序遍历序列
      • 二叉树的中序遍历(缺点):
      • 中序线索二叉树
      • 中序线索化
      • 先序线索化
      • 后序线索化
      • 中序线索二叉树找中序后继
      • 中序线索二叉树找中序前驱
      • 中序线索二叉树找先序后继
      • 先序线索二叉树找先序前驱
      • 后序线索二叉树找后序前驱

二叉树的先序遍历

  • 遍历:按某种次序把所有结点都访问一遍 – > 线性结构
  • 层次遍历:基于树的层次特性确定的次序规则
  • 二叉树的先/中/后序遍历:基于树的递归特性确定的次序规则

二叉树的递归特性

  1. 要么是个空二叉树
  2. 要么就是由“根节点 + 左子树 + 右子树”组成的二叉树

先序遍历:根左右(NLR)-- > 又叫先根遍历

中序遍历:左根右(LNR)- > 又叫中根遍历

后序遍历:左右根(LRN)- > 又叫后根遍历

基础图:

A
B
C

先序遍历: ABC

中序遍历:BAC

后序遍历:BCA

在加深一点:

A
B
C
D
E
F
G

先序遍历: A B D E C F G

中序遍历; D B E A F C G

后序遍历:D E B F G C A

对算数表达式的“分析树”:

先序遍历 — > 前缀表达式

中序遍历 — > 中缀表达式(需要加界限符)

后序遍历 — > 后缀表达式

先序遍历

先序遍历(PreOrder)的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    ①访问根结点;
    ②先序遍历左子树
    ③先序遍历右子树

实现代码:

typedef struct BiTNode{ElemType data;struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;//先序遍历
void PreOrder(BiTree T){if(T!=NULL){visit(T);                //访问根结点PreOrder(T->lchild);     //递归遍历左子树PreOrder(T->rchild);     //递归遍历右子树}
}

第一次被访问的点

空间复杂度:O(h)

中序遍历

中序遍历(InOrder)的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    ①先序遍历左子树;
    ②访问根结点;
    ③先序遍历右子树。

实现代码:

typedef struct BiTNode{ElemType data;struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;//中序遍历
void InOrder(BiTree T){if(T!=NULL){InOrder(T->lchild);  //递归遍历左子树visit(T);            //访问根结点InOrder(T->rchild);  //递归遍历右子树}
}

第二次被访问的点

后序遍历

后序遍历(PostOrder)的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    ①先序遍历左子树;
    ②先序遍历右子树;
    ③访问根结点。

实现代码:

typedef struct BiTNode{ElemType data;struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;//后序遍历
void PostOrder(BiTree T){if(T!=NULL){PostOrder(T->lchild);  //递归遍历左子树PostOrder(T->rchild);  //递归遍历右子树visit(T);              //访问根结点}
}

第三次被访问的点

二叉树的层序遍历

算法思想:

算法思想:

  1. 初始化一个辅助队列
  2. 根结点入队
  3. 若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)
  4. 重复步骤3直至队列为空

实现代码:

//二叉树的结点(链式存储)
typedef struct BiTNode{char data;struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;//链式队列结点
typedef struct LinkNode{BiTNode * data;struct LinkNode *next;
}LinkNode;typedef struct{LinkNode *front,*rear; //队头队尾
}LinkQueue;//层序遍历
void LevelOrder(BiTree T){LinkQueue Q;InitQueue(Q);              //初始化辅助队列BiTree p;EnQueue(Q,T);              //将根结点入队while(!IsEmpty(Q)){        //队列不空则循环DeQueue(Q, p);         //队头结点出队visit(p);              //访问出队结点if(p->lchild!=NULL)EnQueue(Q,p->lchild); //左孩子入队if(p->rchild!=NULL)EnQueue(Q,p->rchild); //右孩子入队}
}

​ 存指针而不是结点

    BiTNode * data;

由遍历序列构造二叉树

  • 一个中序遍历序列可能对应多种二叉树形态

  • 一个前序遍历序列可能对应多种二叉树形态

  • 一个后序遍历序列可能对应多种二叉树形态

  • 一个层序遍历序列可能对应多种二叉树形态

SO : 若只给出一颗二叉树的前/中/后/层 序遍历序列中的一种 ,不能唯一确定一颗二叉树

前序 + 中序遍历序列
  • 前序遍历:根结点、前序遍历左子树、前序遍历右子树

前序遍历序列:

根结点 左子树的前序遍历序列 右子树的前序遍历序列

  • 中序遍历:中序遍历左子树、根结点、中序遍历右子树

中序遍历序列:

左子树的中序遍历序列 根结点 右子树的中序遍历序列

前序遍历序列: A D B C E

中序遍历序列: B D C A E

得出的二叉树:

A
D
E
B
C
后序 + 中序遍历序列
  • 后序遍历:前序遍历左子树、前序遍历右子树、根结点

后序遍历序列:

左子树的后序遍历序列 右子树的后序遍历序列 根结点

  • 中序遍历:中序遍历左子树、根结点、中序遍历右子树

中序遍历序列

左子树的中序遍历序列 根结点 右子树的中序遍历序列

后序遍历序列: E F A H C I G B D

中序遍历序列: E A F D H C B G I

得出的二叉树:

D
A
B
E
F
C
G
H
I
层序 + 中序遍历序列
  • 层序遍历序列

    根结点 左子树的根 右子树的根

  • 中序遍历序列

    左子树的中序遍历序列 根结点 右子树的中序遍历序列

层序遍历序列:A B C D E

中序遍历序列:A C B E D

得出的二叉树:

在这里插入图片描述

KEY:找到树的根节点,并根据中序序列划分左右子树,再找到左右子树根结点

二叉树的中序遍历(缺点):

Q1:如何找到指定结点 P 在中序遍历序列中的前驱?

Q2 : 如何找到 p 的中序后继?

思路:从根节点出发,重新进行一次中序遍历,指针 q 记录当前访问的结点,指针 pre 记录上一个被访问的结点

1.当 q == p 时,pre 为前驱

2.当 pre == p 时,q 为后继

缺点:

找前驱、后继很不方便:遍历操作必须从根开始

中序线索二叉树

中序遍历序列:D G B E A F C

在这里插入图片描述

  • 前驱线索(由左孩子指针充当 —— > 红色线
  • 后继线索(由右孩子指针充当)—— > 绿色线

线索链表代码实现:

//线索二叉树结点
typedef struct ThreadNode{ElemType data;struct ThreadNode *lchild,*rchild;int ltag,rtag;      //左、右线索标志
}ThreadNode,*ThreadTree;
  • tag == 0,表示指针指向孩子
    tag ==1,表示指针是“线索”

  • 先序、后序同理

中序线索化

实现代码

// 线索二叉树结点结构定义
typedef struct ThreadNode{ElemType data;  struct ThreadNode *lchild,*rchild;  int ltag,rtag; 
}ThreadNode,*ThreadTree;// 全局变量 pre,指向当前访问结点的前驱
ThreadNode *pre=NULL;  // 中序遍历二叉树并进行线索化
void InThread(ThreadTree T){if(T!=NULL){InThread(T->lchild);visit(T); InThread(T->rchild);  }
}// 访问结点函数(实际处理线索化逻辑)
void visit(ThreadNode *q) {// 左子树为空,建立前驱线索if(q->lchild==NULL){ q->lchild=pre;q->ltag=1;}// 前驱存在且前驱的右孩子为空,建立前驱结点的后继线索if(pre!=NULL&&pre->rchild==NULL){ pre->rchild=q;  pre->rtag=1;}pre=q;  // 更新pre为当前结点
}// 封装中序线索化的入口函数,统一初始化pre并处理最终结点
void CreateInThread(ThreadTree T){pre=NULL;  // 初始化为NULLif(T!=NULL){  InThread(T); // 处理遍历的最后一个结点:若其右孩子为空,标记为线索if(pre->rchild==NULL){ pre->rtag=1;  }}
}

先序线索化

  • 先序线索化中,注意处理爱滴魔力转圈圈问题,当 ltag == 0时,才能对左子树先序线索化

实现代码;

// 线索二叉树结点结构定义
typedef struct ThreadNode{ElemType data;struct ThreadNode *lchild, *rchild;int ltag, rtag;
}ThreadNode, *ThreadTree;// 全局变量 pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;  // 先序遍历二叉树,一边遍历一边线索化
void PreThread(ThreadTree T){if(T != NULL){visit(T);  // 先处理根节点(执行线索化核心逻辑)// lchild 不是前驱线索(即 ltag == 0 时,说明是真实孩子,递归处理左子树)if (T->ltag == 0)  PreThread(T->lchild);  PreThread(T->rchild);  // 递归处理右子树}
}// 访问结点函数(实际进行线索化操作)
void visit(ThreadNode *q) {// 左子树为空,建立前驱线索if(q->lchild == NULL){  q->lchild = pre;q->ltag = 1;}// 前驱存在且前驱的右孩子为空,建立前驱结点的后继线索if(pre != NULL && pre->rchild == NULL){  pre->rchild = q;  pre->rtag = 1;}pre = q;  // 更新 pre 为当前结点,为下一个结点做准备
}// 先序线索化二叉树的入口函数,统一初始化 pre 并处理最后一个结点
void CreatePreThread(ThreadTree T){pre = NULL;  // pre 初始化为 NULLif(T != NULL){  // 非空二叉树才进行线索化PreThread(T);  // 调用先序线索化递归函数// 处理遍历的最后一个结点:若其右孩子为空,标记为线索if (pre->rchild == NULL)  pre->rtag = 1;  }
}

后序线索化

实现代码:

// 线索二叉树结点结构(需提前定义 ElemType,如 typedef int ElemType; )
typedef struct ThreadNode {ElemType data;struct ThreadNode *lchild, *rchild;int ltag, rtag;  // 0: 指向孩子,1: 线索
} ThreadNode, *ThreadTree;// 全局变量:指向当前访问结点的前驱
ThreadNode *pre = NULL;  // 后序遍历 + 线索化(递归逻辑:左 → 右 → 根)
void PostThread(ThreadTree T) {if (T != NULL) {PostThread(T->lchild);  // 后序遍历左子树PostThread(T->rchild);  // 后序遍历右子树visit(T);  // 访问根节点(执行线索化操作)}
}// 访问结点函数(处理线索化逻辑)
void visit(ThreadNode *q) {// 左子树为空 → 建立前驱线索if (q->lchild == NULL) {  q->lchild = pre;q->ltag = 1;}// 前驱存在且其右孩子为空 → 建立后继线索if (pre != NULL && pre->rchild == NULL) {  pre->rchild = q;  pre->rtag = 1;}pre = q;  // 更新前驱为当前结点
}// 后序线索化入口函数(初始化 + 处理最后结点)
void CreatePostThread(ThreadTree T) {pre = NULL;  // 初始化前驱if (T != NULL) {  // 非空树才线索化PostThread(T);  // 调用后序线索化// 处理遍历的最后一个结点(根结点)if (pre->rchild == NULL) {  pre->rtag = 1;  }}
}

中序线索二叉树找中序后继

在中序线索二叉树中找到指定结点 * p 的中序后继 next

  1. 若 p->rtag == 1,则 next = p->rchild
  2. 若 p->rtag == 0

next = p 的右子树中最左下结点

实现代码:

// 找到以 p 为根的子树中,第一个被中序遍历的结点
ThreadNode *Firstnode(ThreadNode *p){// 循环找到最左下结点(不一定是叶结点)while(p->ltag==0) p=p->lchild;  return p;
}// 在中序线索二叉树中找到结点 p 的后继结点
ThreadNode *Nextnode(ThreadNode *p){// 右子树中最左下结点if(p->rtag==0) return Firstnode(p->rchild);  else return p->rchild;  // rtag==1 直接返回后继线索
}
  • 对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void Inorder(ThreadNode *T){for(ThreadNode *p=Firstnode(T);p!=NULL; p=Nextnode(p))visit(p);
}

空间复杂度O(1)

中序线索二叉树找中序前驱

在中序线索二叉树中找到指定结点 * p 的中序前驱 pre

  1. 若 p->ltag == 1,则 pre = p->lchild
  2. 若 p->ltag == 0

pre = p 的左子树中最右下结点

实现代码:

// 找到以 p 为根的子树中,最后一个被中序遍历的结点
ThreadNode *Lastnode(ThreadNode *p){// 循环找到最右下结点(不一定是叶结点)while(p->rtag==0) p=p->rchild;  return p;
}// 在中序线索二叉树中找到结点 p 的前驱结点
ThreadNode *Prenode(ThreadNode *p){// 左子树中最右下结点if(p->ltag==0) return Lastnode(p->lchild);  else return p->lchild;  // ltag==1 直接返回前驱线索
}
  • 对中序线索二叉树进行逆向中序遍历
void RevInorder(ThreadNode *T){for(ThreadNode *p=Lastnode(T);p!=NULL; p=Prenode(p))visit(p);
}

中序线索二叉树找先序后继

在先序线索二叉树中找到指定结点 * p 的中序后继 next

  1. 若 p->rtag == 1,则 pre = p->rchild
  2. 若 p->rtag == 0

假设有左孩子

  • 若 p 有左孩子,则先序后继为左孩子

先序遍历 —— 根 左 右
根 (根 左 右) 右

假设没有左孩子

  • 若 p 没有左孩子,则先序后继为右孩子

先序遍历 —— 根 右
根 (根 左 右)

先序线索二叉树找先序前驱

在先序线索二叉树中找到指定结点 * p 的中序前驱 pre

  1. 若 p->ltag == 1,则 pre = p->lchild
  2. 若 p->ltag == 0

在先序遍历中,左右子树的结点只可能是根的后继,不可能是前驱

后序线索二叉树找后序前驱

在先序线索二叉树中找到指定结点 * p 的中序前驱 pre

  1. 若 p->ltag == 1,则 pre = p->lchild
  2. 若 p->ltag == 0

假设有右孩子

  • 若 p 有右孩子,则后序前驱为右孩子

后序遍历 —— 左 右 根
左 (左 右 根) 根

假设没有右孩子

  • 若 p 没有右孩子,则后序前驱为左孩子

后序遍历 —— 左 根
(左 右 根) 根

中序线索二叉树先序线索二叉树后序线索二叉树
找前驱
找后继
http://www.dtcms.com/wzjs/429420.html

相关文章:

  • xv10相同网站鞋子软文推广300字
  • 网站文字链接宁波seo教程推广平台
  • ecshop网站标题站长工具使用方法
  • dm网站制作软件sem优化公司
  • 电子商务网站开发策划案全媒体广告代理加盟靠谱吗
  • 万网注册域名做简单网站网络营销措施有哪些
  • 网站都到哪里做推广青岛百度seo
  • 怎么自己做彩票网站吗网络舆情的网站
  • 公司地址怎么免费上地图seo行业岗位有哪些
  • 网站公司备案推广页面
  • 个人订阅号支持微网站的建设吗公司seo营销
  • 外贸建设网站重庆seo排名公司
  • php 网站开发收费百度权重是怎么来的
  • 装饰装修公司举例说明seo
  • 福清可以做宣传的网站网站建设关键词排名
  • 医疗器械网站建设方案满十八岁可以申请abc认证吗
  • 网站建设专家 金石下拉营销策略怎么写范文
  • 台州卫浴网站建设企业建站
  • 重庆石桥铺网站建设公司百度外推排名
  • 网络营销的5种方式嘉兴seo外包
  • 做网站维护有什么要求上海网络推广专员
  • 佛山顺德做网站技成培训网
  • 网站外链建设可以提升网站权重对还是错互联网推广工作好做吗
  • 做水果生意去哪个网站百度旧版本
  • 外贸网站建设公司咨询国外网站推广
  • 吉林省建设信息网官网入吉合肥网站优化软件
  • 衡阳公司做网站真正的免费建站在这里
  • word如何做网站链接怎么在网上推广产品
  • 自己做网站买东西seo网站自动推广
  • 手机免费网站建设哪家公司好抖音推广平台