数据结构第三章

6 篇文章 0 订阅

第三章

3.1.1引子

什么是树

客观世界中许多事务存在层次关系

分层次组织在管理上具有更高的效率
数据管理的基本操作之一:查找
如何实现有效率的查找?

查找:根据某个给定的关键字K,从集合R中找出关键字与K相同的记录
静态查找:集合中记录是固定的
》没有插入和删除操作,只有查找
动态查找:集合中记录是动态变化的
》除查找,还可能发生插入和删除

静态查找
方法1:顺序查找

int SequentialSearch(List Tb1, ElementType K){
//在Element[1]~Element[n]中查找关键字为K的数据元素
int i;
Tb1->Element[0] = K;//建立哨兵
for(i = Tb1->Length; Tb1->Element[i] != K; i--);
return i;//查找成功返回所在单元下标,不成功返回0
}
typedef struct LNode* List;
struct LNode{
ElementType Element[MAXSIZE];
int Length;
};

//顺序查找的一种实现(无“哨兵”)
int SequentialSearch(List Tb1, ElementType K){
//在Element[1]~Element[n]中查找关键字为K的数据元素
int i;

for(i=Tb1->Length; i > 0 && Tb1->Element[i] != K; i--);
return i;//查找成功返回所在单元下标,不成功返回0
}

顺序查找算法的时间复杂度为 O ( n ) O(n) O(n)

方法2:二分查找(Binary Search)

》假设n个数据元素的关键字满足有序(比如:小到大)
k 1 < k 2 < . . . < k n k_1 < k_2 < ... < k_n k1<k2<...<kn
并且是连续存放(数组),那么可以进行二分查找

例:假设有13个数据元素,按关键字由小到大顺序存放,二分查找关键字为444的数据元素过程如下:
在这里插入图片描述
1.left = 1, right = 13; mid = (1 + 13) / 2 = 7; 100 < 444;
2.left = mid + 1 = 8, right = 13; mid = (8 + 13) / 2 = 10; 321 < 444;
3.left = mid + 1 = 11, right = 13; mid = (11 + 13) / 2 = 12; 查找结束

找不到的情况
例:任然以上面13个数据元素构成的有序线性表为例
二分查找关键字为43的数据元素如下:
在这里插入图片描述
1.left = 1, right = 13; mid = (1 + 13) / 2 = 7; 100 > 43;
2.left = 1, right = mid - 1 = 6; mid = (1 + 6) / 2 = 3; 39 < 43;
3.left = mid + 1 = 4, right = 6; mid = (4 + 6) / 2 = 5; 51 > 43;
4.left = 4; right = mid - 1 = 4; mid = (4 + 4) / 2 = 4; 45 > 43;
5.left = 4; right = mid - 1 = 3; left > right ? 查找失败,结束;

二分查找算法

int BinarySearch(List Tbl, ElementType K){
//在表Tbl中查找关键字为K的数据元素
int left, right, mid, NoFound = -1;

left = 1;//初始左边界
right = Tbl->Length;//初始右边界
while(left <= right){
mid = (left + right)/2;//计算中间元素坐标
if(K < Tbl->Element[mid]){
right = mid - 1;//调整有右边界
}
else if(K > Tbl->Element[mid]){
left = mid + 1;//调整左边界
}
else
return mid;//查找成功,返回数据元素下标
}
return NotFound;//查找不成功,返回-1
}

》11个元素的二分查找判定树
》》判定树上每个结点需要查找次数刚好为该结点所在的层数
》》查找成功是查找次数不会超过判定树的深度
n个结点的判定树的深度为 $[log_2n]+1
ASL = (44 + 43 + 2*2 + 1)/11 = 3
在这里插入图片描述

3.1.2树的定义和术语

树(Tree): n(n >= 0)个结点构成的有限集合
当 n = 0时,称为空树;
对于任意一棵非空树(n > 0),它具备以下性质:
》树中有一个称为“根(Root)”的特殊结点,用r表示;
》其余结点可分为m(m > 0)个互不相交的有限集 T 1 , T 2 , . . . , T m T_1, T_2, ..., T_m T1,T2,...,Tm,其中每个集合本身又是一棵树,称为原来的“子树(SubTree)”
在这里插入图片描述
树与非树?
在这里插入图片描述
》子树是不相交的
》除了根结点外,每个结点有且仅有一个父节点
》一棵N个结点的树有N-1条边

树的一些基本术语
在这里插入图片描述
在这里插入图片描述

3.1.3树的表示

数组实现?在数组中无法判断结点的父结点和字结点

链表实现?
在这里插入图片描述
每个结点的结构不一样,有的结点有3个指针,有的有2个指针,有的没有指针

想法:将每个结点的指针域统一
会带来空间上的浪费

》儿子——兄弟表示法
在这里插入图片描述
在这里插入图片描述

3.2.1二叉树的定义及性质

二叉树T:一个有穷的结点的集合。
这个集合可以为空
若不为空,则它是由根结点和称为其左子树 T L T_L TL和右子树 T R T_R TR的两个不相交的二叉树组成。
》二叉树具体五中基本形态
在这里插入图片描述
》二叉树的子树有左右顺序之分
在这里插入图片描述
##特殊二叉树
》斜二叉树(Skewed Binary Tree)
在这里插入图片描述
》完美二叉树(Perfect Binary Tree)
满二叉树(Full Binary Tree)

在这里插入图片描述
》完全二叉树(Complete Binary Tree)
有n个结点的二叉树,对树中结点按从上至下、从左到右顺序进行编号,编号为i(1 <= i <= n)结点与满二叉树中编号为i结点在二叉树中位置相同

在这里插入图片描述

二叉树几个重要性质

》一个二叉树第i层的最大结点树为: 2 i − 1 , i > = 1 2^{i - 1}, i >= 1 2i1,i>=1
》深度为K的二叉树的最大结点总数为: 2 k − 1 , k > = 1 2^{k-1}, k >= 1 2k1,k>=1
》对任何非空二叉树T,若 n 0 n_0 n0表示叶结点的个数、 n 2 n_2 n2是度为2的非叶结点个数,那么两者之间满足关系 n 0 = n 1 + 1 n_0 = n_1 + 1 n0=n1+1

二叉树抽象数据类型定义

类型名称:二叉树
数据对象集:一个有穷的结点集合
若不为空,则由根结点和其左、右二叉子树组成。

操作集: B T ∈ B i n T r e e , I t e m ∈ E l e m e n t T y p e BT \in BinTree, Item \in ElementType BTBinTree,ItemElementType,重要操作有:
1.Boolean IsEmpty(BinTree BT);//判读BT是否为空;
2.void Traversal(BinTree BT);//遍历,按某顺序访问每个结点
3.BinTree CreateBinTree();//创建一个二叉树

常用的遍历方法有:
void PreOrderTraversal(BinTree BT);//先序——根、左子树、右子树;
void InOrderTraversal(BinTree BT);//中序——左子树、根、右子树;
void PostOrderTraversal(BinTree BT);//后序——左子树、右子树、根
void LevelOrderTraversal(BinTree BT);//层次遍历,从上到下,从左到右

3.2.2二叉树的存储结构

1.顺序存储结构
完全二叉树:按从上至下、从左到右顺序存储
n个结点的完全二叉树的结点父子关系;
》非根结点(序号 i > 1)的父结点的序号是[i / 2];
》结点(序号为 i)的左孩子结点的序号是 2i
(若 2i <=n, 否则没有左孩子);
》结点(序号为 i)的右孩子结点的序号是 2i + 1
(若 2i + 1 <= n, 否则没有右孩子)

一般二叉树也可以采用这种结构,但会造成空间浪费

2.链表存储
在这里插入图片描述

typedef struct TreeNode* BinTree;
typedef BinTree Position;
struct TreeNode{
ElementType Data;
BinTree Left;
BinTree Right;
};

3.3.1二叉树的遍历

(1)先序遍历

//先访问根结点
//先序遍历左子树
//先序遍历右子树
void PreOrderTraversal(BinTree BT){
if(BT){
printf("%d", BT->data);
PreOrderTraversal(BT->left);
PreOrderTraversal(BT->right);
}
}

(2)中序遍历

//中序遍历根结点左子树
//访问根结点
//中序变量里根结点右子树
void InOrderTraversal(BinTree BT){
if(BT){
InOrderTraversal(BT->left);
printf("%d", BT->data);
InOrderTraversal(BT->right);
}
}

(2)后序遍历

//后序遍历根结点左子树
//后序遍历根结点右子树
//访问根结点

void PostOrderTraversal(BinTree BT){
if(BT){
PostOrderTraversal(BT->left);
PostOrderTraversal(BT->right);
printf("%d", BT->data);
}
}

先序、中序和后序遍历过程:遍历过程中经过结点的路线一样,只是访问各结点的时机不同
在这里插入图片描述

3.3.2二叉树的非递归遍历

》中序遍历非递归遍历算法
非递归算法实现的基本思路:使用堆栈

//遇到一个结点,就把它压栈,并去遍历它的左子树
//当左子树遍历结束后,从栈顶弹出这个结点并访问它
//然后按其右指针去中序遍历该结点的右子树
void InOrderTraversal(BinTree BT){
BinTree T = BT;
Stack S = CreatStack(MaxSize);//创建并初始化堆栈S
while(T || !IsEmpty(S)){
whlie(T){//如果根结点存在
push(S, T);//将根结点压栈
T = T->left//一直向左
}
if(!IsEmpty(S)){
T = Pop(S);//结点弹出堆栈
printf("%5d", T->Data);//打印结点
T = T->right;//转向右子树
}
}
}

》先序遍历的非递归遍历算法

void InOrderTraversal(BinTree BT){
BinTree T = BT;
Stack S = CreatStack(MaxSize);//创建并初始化堆栈S
while(T || !IsEmpty(S)){
while(T){
Push(S, T);//第一次碰到结点
printf("%5d", T->Data);
T = T->Left;
}
if(!IsEmpty(S)){
T = Pop(S);//结点弹出堆栈  //第二次碰到结点
T = T->Right;//转向右子树
}
}
}

》后序遍历的非递归算法

//后序遍历
//先遍历左子树
//再遍历右子树
//最后输出根结点
//如果根结点存在,将根结点压栈
//一直向左
//如果左结点没有转向右
void PostOrderTraversal(BinTree BT){
BinTree T = BT;
BinTree P = null;//用于记录上一次访问的根结点
Stack S = CreateStack(MaxSize);//创建并初始化堆栈S
while(T || !IsEmpty(S)){
while(T){
Push(S, T);//第一次碰到结点,压栈
P = T;
T = T->Left;
}
if(!IsEmpty(S)){
T = Pop(S);//第二次碰到结点,出栈
if(P == T->Left || P == T){//如果访问的前一结点是该结点的左结点,或左结点为空,不打印,转向右
T = T->Right;
}
else{//如果访问的前一结点不是该结点的左结点,或左结点为空,打印,转向右
printf("%5d", T->Data);
T = T->Right;
}
}
}
}

3.3.3层序遍历

二叉树遍历的核心问题:二维结构的线性化
》从结点访问其左、右儿子结点
》访问左儿子结点后,右儿子结点怎么办?
需要一个存储结构保存暂时不访问的结点
存储结构:堆栈、队列

队列实现

遍历从根结点开始,首先将根结点入队,然后开始执行循环:
结点出队、访问该结点、其左右儿子入队

//层序基本过程:先根结点入队,然后
//1.从队列中取出一个元素;
//2.访问该元素所指结点;
//若该元素所知结点的左、有孩子结点非空
//则将其左右孩子的指针顺序入队
void LevelOrderTraversal(BinTree BT){
Queue Q;
BinTree T;
if(!BT){
return;//若树是空直接返回
}
Q = CreateQueue(MaxSize);//创建并初始化队列Q
AddQ(Q, BT);
while(!IsEmptyQ(Q)){
T = DeleteQ(Q);
printf("%d\n", T->Data);//访问取出队列的结点
if(T->Left){
AddQ(Q, T->Left);
}
if(T->Right)
{
AddQ(Q, T->Right);
}
}
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p style="color:#666666;"> <span style="font-size:14px;">本门课程重实战,将基础知识拆解到项目里,让你在项目情境里学知识。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">这样的学习方式能让你保持兴趣、充满动力,时刻知道学的东西能用在哪、能怎么用。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">平时不明白的知识点,放在项目里去理解就恍然大悟了。</span> </p> <p style="color:#666666;"> <span></span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>一、融汇贯通</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">本视频采用了前后端分离的开发模式,前端使用Vue.js+Element UI实现了Web页面的呈现,后端使用Python 的Django框架实现了数据访问的接口,前端通过Axios访问后端接口获得数据。在学习完本章节后,真正理解前后端的各自承担的工作。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>二、贴近实战</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">本系列课程为练手项目实战:学生管理系统v4.0的开发,项目包含了如下几个内容:项目的总体介绍、基本功能的演示、Vuejs的初始化、Element UI的使用、在Django中实现针对数据的增删改查的接口、在Vuejs中实现前端增删改查的调用、实现文件的上传、实现表格的分页、实现导出数据到Excel、实现通过Excel导入数据、实现针对表格的批量化操作等等,所有的功能都通过演示完成、贴近了实战</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>三、课程亮点</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">在本案例中,最大的亮点在于前后端做了分离,真正理解前后端的各自承担的工作。前端如何和后端交互</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>适合人群:</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">1、有Python语言基础、web前端基础,想要深入学习Python Web框架的朋友;</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">2、有Django基础,但是想学习企业级项目实战的朋友;</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">3、有MySQL数据库基础的朋友</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="font-size:14px;"><img alt="" src="https://img-bss.csdnimg.cn/202009070752197496.png" /><br /> </span> </p> <p style="color:#666666;"> <span style="font-size:14px;"><br /> </span> </p>
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值