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

关于服装的网站规划与设计免费发布产品的网站

关于服装的网站规划与设计,免费发布产品的网站,广东品牌网站建设,wordpress图片无限放大数据结构基础(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/216204.html

相关文章:

  • 平湖网站制作济南seo优化公司助力网站腾飞
  • 重庆本地网站有哪些百度搜索官网
  • 做效果图的兼职网站广告联盟平台入口
  • 做网站背景步骤综合搜索引擎
  • ico在线制作网站齐三seo顾问
  • 测试网站免费空间网站seo排名优化价格
  • 锦州网站做优化优化排名案例
  • 建网站能赚钱吗网络推广员怎么做
  • 百度网站地图制作百度网络营销的概念
  • 北京海淀区网站建设抖音seo优化怎么做
  • 以网站做跳板入侵营销咨询公司经营范围
  • 不用网站做淘宝客百度推广费用多少钱
  • 广告公司网站策划苏州seo服务热线
  • 温州网站开发平台百度站长工具添加不了站点
  • h5效果展示网站如何提高自己在百度的排名
  • 中山网站建设公司哪个好aso推广平台
  • 佛山网站建设怎样做浏览器网址
  • 网站用户引导seo自媒体培训
  • 如何做介绍一门课程的网站客户关系管理
  • 交互式网站开发技术上海推广服务
  • 怎样做企业营销网站电商网站对比表格
  • 网站开发项目有哪些html网页制作网站
  • 国家建设部网站华为云速建站
  • 深圳南山网站建设站长工具whois查询
  • 中美军事最新消息汕头网站快速优化排名
  • PHP+MySQL网站开发全程实例搜索引擎营销的步骤
  • 网站建设营销方案宁波网站seo诊断工具
  • 提升网站排名百度网盘登录
  • 重庆网络安全公司关键字优化
  • 网站充值链接怎么做广东近期新闻