数据结构笔记2

数据结构

第5章 树

5.1 树的基本概念

树的度: 是节点的度的最大值

树深度: 节点层次的最大值

节点的度:子树的个数

  • 树的性质:

      1. n 个结点,每个节点

        \[度为\ d_i,则 \ n=\sum_{i=1}^{n}d_i +1 \]

      1. 度为 k 的树第 i 层的结点个数最多为

        \[k^{i-1} \]

      1. 深度为h的k叉树,最多结点为

        \[\frac{k^{h}-1}{k-1} \]

      2. 具有n个结点的k叉树深度最小为

        \[\lceil log_k(n(k-1))+1 \rceil \]

  • 树的基本操作

    • bool CreateTree(position &t);
    • bool DelTree(position &t);
    • void DelSubTree(position &t,position p);
    • TElelmType GetData(position p);
  • //基本操作的代码实现
    InitTree(&T)
    DestroyTree(&T)
    CreateTree(&T,definition)
    TreeEmpty(T)
    TreeDepth(T)
    Root(T)
    Parent(T, x)
    FirstChild(T,x)
    Nextsibling(T,x)
    InsertChild(&T,x,i,p)
    DeleteChild(&T,x,i)
    Traverse(T,visit()) 
    
    
  • 设计算法

    • 递归计算树的深度

      若树空则返回 0 或者递归到树的叶子节点,则返回1

      否则递归遍历其他子树,取其最大值加上1 则是树的高度

    • 算法描述:

//递归计算树的深度
int height(Tree T){
    if(IsEmpty(T)) return 0;
    else if(Isleaf(T)) return 1;
    else  {
        int h = 0,k;
        for (Tree p=firstchild(T);P != NULL;p = Nextsibling(T,p))
            if((k = height(P)) > h) h = k;
        return h+1;
    }
}

5.2 二叉树的基本概念

结点的度: 非空子树的个数

叶子结点 : 左右子树均空,度为0

完全二叉树: 深度为h,h-1为满二叉树,h层的结点都集中在左侧

5.2.1 二叉树的存储结构
    1. 顺序存储结构

      完全二叉树适用

      一般二叉树:造成存储空间大量浪费

      typedef struct {
      	Telemtype Nodes[Max_size];//存放二叉树结点的数组
       int n;//二叉树结点个数
      }SqBiTree;
      
    1. 链式存储结构

      image-20220422232702700

      typedef struct BiTree{
      TElemType data;
      Struct BiTNode *lchild,*rchild;
      
      }BiTNode,*BiTree;
      
5.2.2 二叉树的遍历及应用
1. 先序、中序和后序遍历二叉树
  • 先序遍历

    先序遍历定义

    若树为空,则空操作;

    否则:

    1. 访问根节点;

    2. 先序遍历左子树;

    3. 先序遍历右子树。

    void PreOrder(BiTree T){
        if(!T) return ;
        else {
            visited(T->data);//such as:
            /*printf("%c",T->data);*/
            PreOrder(T->lchild);
            PreOrder(T->rchild);
        }
    }
    
  • 中序遍历

    定义略

    void InOrder(BiTree T){
     if(!T) return ;
     else{
         Inorder(T->lchlid);
         visited(T->data);
         Inordeer(T->rchild);
     }
    }
    
  • 后序遍历

    定义略

    void PostOrder(BiTree T){
     if(!T) return ;
     else{
         PostOrder(T->lchild);
         PostOrder(T->rchild);
         visited(T->data);
     }
    }
    
扩展二叉树

定义:将二叉树上每个空子树用一个虚结点表示(不妨设虚结点为'#'),这样的树称为扩展二叉树

空指针的虚结点称为外部节点

  • 非递归法进行先序遍历

    void PreOrder(BiTree T){
        InitStack(S);
        while(T||!StackEmpty(S)){
            while(T){
                visited(T->data);
                Push(S,T);
                T = T->lchild;
            }
            if(!StackEmpty(S)){
                Pop(S,T);
                T = T->rchild;
            }
        }
    }
    
  • 层序遍历二叉树

    算法思路:

    1. 初始化一个空队列,用来保存已经访问过结点的孩子;
    2. 非空根指针入队;
    3. 若队列为空,则遍历结束;否则重复执行:
      1. 队头元素出队,访问;
      2. 若被访结点有左孩子,则左孩子入队;
      3. 若被访结点有右孩子,则右孩子入队;
    void LayerTraversal(BiTree T){
        InitQueue(Q);
        if(T) EnQueue(Q,T);
        while(!QueueEmpty(Q)){
            DeQueue(Q,p);
            visite(p->data);
            if(p->lchild)EnQueue(Q,p->lchild);
            if(p->rchild)EnQueue(Q,p->rchild);
        }
    }
    
  • 求二叉树节点数

方案一:

int CountNodes1(BiTree T){
 if(!T) return 0;
 else{
     n1=CountNodes1(T->lchild);
     n2=CountNodes1(T->rchild);
     return (1+n1+n2);
 }
}

方案二:对树进行先序后序中序任何一种形式的遍历,在访问操作时进行计数;

int CountNodes2(BiTree T,int &n){
 int n=0;
 if(!T) return 0;
 else{
     n++;
     CountNodes2(T->lchild,n);
     CountNodes2(T->rchild,n);
 }
}
  • 输出二叉树每个结点的层次
void Level(BiTree T,int lev){
lev=0;
if(T){
  lev++;
  printf(T->data,lev);
  Level(T->lchild,lev);
  Level(T->rchild,lev);
}
}
//二叉树的操作
InitBiTree(&T)
DestroyBiTree(&T)
CreateBiTree(&T,definition)
BiTreeEmpty(T)
BiTreeDepth(T)
Parent(T,e)
LeftChild(T,e)
RightChild(T,e)
LeftSibling(T,e)
RightSibling(T,e)
InsertChild(&T,p,LR,C)
DeleteChild(&T,p,LR)
Traverse(T)
    s
//二叉树的结构
typedef struct Node{
    char data;
    struct Node *lchild,*rchild;
}*BiTree,BiNode;,
//初始化二叉树
void InitBitree(BiTree &T){
	char ch;
    cin >> ch;
    if(ch == '#'){
        T = NULL;
    }
    else{
        T = new BiNode;
        T -> data = ch;
        InitBitree(T->lchild);
        InitBitree(T->rchild);
    }  
}

  • 二叉树的几个基本性质

    1. 在 二叉树的第\(i\)层的结点个数最多为

      \[2^{i-1} \]

    2. 深度为\(k\)的二叉树的最大结点数为

      \[2^k-1 \]

    3. 任一二叉树\(T\),如果其叶子结点数为\(n_0\), 度为2的结点数为\(n_2\),则

      \[n_0=n_2+1 \\n_0+n_1+n_2=n=2n_2+n_1+1 \]

    4. 具有\(n\)个结点的完全二叉树深度为

      \[\lceil log_2(n+1) \rceil \ 或\ \lfloor log_2n\rfloor+1 \]

    5. 如果对一个有\(n\)个结点的完全二叉树\(T\)的结点按层序(从第一层到第\([logn]+1\)层,层内从左到右从1开始编号,则对任意一个编号为\(i(1<=i<=n)\)的结点有:

      • 如果\(i=1\),则该结点是二叉树的根,无双亲;如果\(i>1\)则其双亲结点\(Parent(i)\)的编号为\([i/2]\)

      • 如果\(2i>n\),则编号为\(i\) 的结点没有左孩子,为叶子结点;否则其左孩子\(LChild(i)\)的编号为2i

      • 如果\(2i+1>n\),则编号为\(i\) 的结点没有右孩子;否则其右孩子\(RChild(i)\)的编号为\(2i+1\)

    第6章 图

    图遍历运用——迷宫问题

如何得到路径?

  • 广度遍历时,队列元素增加一个 指向“队头”元素的指针
//代码实现
typedef struct {
	int		xpos;
	int		ypos;
}PosType;//迷宫每个单元信息
typedef struct  DQNode{
	PosType			seat;
	struct  DQNode		*next, *pre;//pre用于反向指向,找得到邻结点
}DQNode,*Dqueueptr;
typedef struct {
	Dqueueptr		front;
	Dqueueptr		rear;
}DlinkQueue;
//入队列
void EnQueue(DLinkQueue &Q,PosType e){
	p=new DQNode;   
	p->seat.xpos=e.xpos;
	p->seat.ypos=e.ypos;
	p->next=NULL;
	if(!Q.rear){   //首个结点
		p->pre=NULL;//必须要写这句话
		Q.rear=p;
        Q.front=p;
	}else{
		p->pre=Q.front;  
		Q.rear->next=p;
        Q.rear=p;  
	}
//出队列 自己写一下 不销毁结点
/*因Dequeue时找出邻接点, 如果先GetHead,并且Dequeue不销毁,
这样可以建立当前结点与队首结点关系*/
void DeQueue(){
    
}
//下一个结点
PosType NextPos(PosType  cur, int v)
{
	Postype	npos;
	npos.xpos=cur.xpos+di[v];
	npos.ypos=cur.ypos+dj[v];
	return npos;
}
bool  Pass(Postype  npos)
{
	 return(0<=npos.xpos && npos.xpos<=m-1 &&
		   0<=npos.ypos && npos.ypos<=n-1 &&
	 	   maze[npos.xpos][npos.ypos]= =0 &&
         visited[npos.xpos][npos.ypos]= =FALSE)
 }
bool ShortestPath(int maze[][], int m, int n, Stack &s){
    //Stack起反向作用
	DLinkQueue Q; bool visited[m][n]; InitQueue(Q);
	for(i=0;i<m;i++)
		for(j=0;j<n;j++)visited[i][j]=FALSE;
	EnQueue(Q,(0,0)); visited[0][0]=TRUE;found=FALSE;
	while(!found&&!QueueEmpty(Q)){//广度优先遍历可以中途停止当作指标
		GetHead(Q,curp); //GetHead函数写出来一下,获得Q队首元素curp
		for(v=0;v<8 &&!found;v++){
		     npos=NextPos(curp,v);
		     if(Pass(npos)){
                 EnQueue(Q,npos);//入队列
		         visited[npos.xpos][npos.ypos]=TRUE;
		         if(npos.xpos==m-1 && npos.ypos==n-1)//!!!!!!
					found=TRUE;
		     }	
             }//for
	        DeQueue(Q,curp);
	}//while
	if(found){
		InitStack(S);
		p=Q.rear;
		while(!p){                  
		      Push(S,p->seat); //自栈顶至栈底为路径
		      p=p->pre;	
		}//while
		return TRUE;
	}//if
	else return FALSE;
}//ShortestPath

  • 总结:

    把问题抽象为图问题,怎么保存路径,销毁结点


6.4 最小生成树(MST)


  • 相关概念:

    极小连通子图

    n个结点的连通图中,包涵n个结点和n-1个边构成的连通子图

    连通图的生成树:即极小连通子图

    连通网的最小生成树:权值和最小的生成树

    求连通网最小生成树的算法

    – 克鲁斯卡尔(Kruskal)算法 复杂度:O(\(eloge\))

    – 普里姆(Prim)算法 复杂度:O( \(n^2\) )

    – 算法比较:当e(边)与\(n^2\) 差不多时,采用Prim算法快;当e远小于\(n^2\) 时,采用Kruskal算法快

  • Kruskal算法

    • 算法思想
    1. 构造只含n个结点的森林。
    1. 按权值从小到大选择边加入到森林中,并使森林不产生回路。

    2. 重复2直到森林变成一颗树

    • 算法描述
    1. \(G(V,E)\),把V={1,2,......n}看成孤立的n个连通子图。边按照权的非递减次序排列。

    2. 顺序查看边。对于第k条边(v,w),如果v,w分别属于两个连通字图\(T_1\)、T2,则用边(v、w)将T1、T2连成一个连通字图。

    3. 重复2,直至n个结点同属于一个连通图


太过复杂,本课不要求


  • Prim算法

    算法思想 复杂度O(\(n^2\))

    1. 将所有结点分为两类集合:一类是已经落在生成树上的结点集合,另一类是尚未落在生成树上的结点集合。

    2. 在图中任选一个结点\(v\)构成生成树的树根,形成生成树结点集合。

    3. 在连接两类结点的边中选出权值最小的边,将该边所连接的尚未落在生成树上的结点加入到生成树上。同时保留该边作为生成树的树枝。

    4. 重复\(3\)直至所有结点都加入生成树

    算法描述

    \[\begin{align} &1.设G=(V,E),权A[m,n],令U=\{1\}。\\ &2.if(U<V) 取min(A[i,j]),使i∈U,j∈V-U。\\ &3.将j加入U\\ &4.重复2、3,直至U=V\\ \end{align} \]


//代码实现
//利用数组记录权值然后遍历比较出最小权
void Prim(MGraph G, int v0, int adjvex[]){
	//从序号为v0的顶点出发,构造连通网G的最小生成树
	int lowcost[MAX_VERTEX_NUM];   //定义辅助数组
	for(j=0;j<G.vexnum;j++) //用邻接矩阵的v0行初始化lowcost
		if(j!=v0){lowcost[j]=G.arcs[v0][j];adjvex[j]=v0;}
	lowcost[v0]=0;   //将v0标记为红点
	for(i=1;i<G.vexnum;i++){
		k=MinEdge(lowcost, G.vexnum); //lowcost[]中非零最小值
        printf("(%d,%d),%d\n",k,adjvex[k],lowcost[k]);
		lowcost[k]= 0;	//vk变成红点
		for(j=0;j<G.vexnum;j++){  //调整紫边集
		      if(G.arcs[k][j]<lowcost[j]){
					adjvex[j]=k;//将vj的候选紫边所关联的红点改成vk
					lowcost[j]=G.arcs[k][j]; //调整vj候选紫边的权
		      }
		}
	}//Prim 

image-20220422134925602

6.5 拓扑排序

  • 要求
    • 有向无环

    • 活动顶点网络(AOV,activity on vertex )

      • 以顶点表示活动,以弧表示活动之间的优先制约关系的有向图。
    • 死锁:

      • AOV中不允许出现回路,回路意味某活动以自己的结束作为开始的先决条件。称为死锁。
    • 拓扑排序、拓扑有序序列

      • 若在有向图中从\(u\)\(v\)有一条弧,则在序列中\(u\)排在\(v\)之前,称有向图的这个操作为拓扑排序。所得序列为拓扑有序序列。若有死锁,则无法获得拓扑有序序列
  • 操作方法:

    1. 选取一个没有前驱的顶点,输出它,并从AOV中网中删除此顶点以及所有以它为尾的弧。
    2. 重复1.直至输出所有结点
  • 统计有向图邻接表各顶点的入度

  void init_indegree(ALGraph G){
  	for(i=0;i<G.vexnum;i++)indegree[i]=0;
  	for(i=0;i<G.vexnum;i++){
  		p=G.vertices[i].firstarc;
  		while(p){//第i个元素第一个指针
  			indegree[p->adjvex]++;
  			p=p->nextarc;
  		}//while
  	}//for
  } //init_indegree
  //邻接矩阵的写法
  for(i=0;i< G.vexnum;i++){
      for(j=0;j < G.vexnum;j++){
          if(!G.arcs[i][j])indegree[j]++;
      }
  }
  //与储存方式无关的写法
  for( i in V ){
      for(w = firstadjvex(G,i);w!=-1;w = nextadjvex(G,i,w))
          indegree[w]++;   
  }
     
  • 取入度为0的点\(v\)
  int getzerodegree(ALGrapg G){
  	for(i=0;i<G.vexnum;i++)
  		if(indegree[i]==0){indegree[i]=-1;return i;}
  	return –1;
  }
  • \(FirstAdj\)
int FirstAdj(ALGrapg G ,int v){
	if(G.vertex[v].firstarc)
		return G.vertex[v].firstarc->adjvex;
	else 
		return –1;
}
  • \(NextAdj\)
int NextAdj(ALGrapg G ,int v, int w){
	p=G.vertices[v].firstarc;
	while(p && p->adjvex!=w)p=p->nextarc;
	if(p && p->nextarc)
		return p->nextarc->adjvex;
	else 
		return –1;
}
  • 拓扑排序 复杂度\(O(n+e)\)
m=0;
init_indegree(G);
v=getzerodegree(G)
while(v!=-1){
	cout<<v;
    ++m;
	w=FirstAdj(G, v);
	while(w!=-1){
		indegree[w]--;//入度减少
		w=nextAdj(G, v, w);
	}
	v=getzerodegree(G);
}
if(m!=G.vexnum) cout<< "有死锁!"//理解一下这句话

//使用栈的算法
void TopologicalSort(ALGraph G){
	int count=0; InitStack(S);  
	int InDegree[MAX_VERTEX_NUM]={0};  
	for(i=0;i<G.vexnum;i++) 		
        for(p=G.vertices[i].firstarc;p;p=p->nextarc)
			InDegree[p->adjvex]++;   
	for(i=0;i<G.vexnum;i++)  
		if(InDegree[i]==0) Push(S,i); //入度为0的顶点进栈
	while(!StackEmpty(S)){ 
		Pop(S,j); cout<<G.vertices[j].data; count++;   
		for(p=G.vertices[j].firstarc; p; p=p->nextarc)
			if(!(--InDegree[p->adjvex])) Push(S,k); 
 }//end while
	if(count<G.vexnum) cout<<”该图有环” ;
} //TopologicalSort 

//深度优先遍历 得到的序列为C7C4C5C2C3C1C6正好是逆序
//仿造DFS来进行写作算法
int visited[MAX_VERTEX_NUM]]; //访问标志数组,全局数组
void DFSTraverse(Graph G){
	for(i=0;i<G.vexnum;i++) visited[i]=False; //置未访问标志
	for(v=0; v<G.vexnum; v++)   //对visited数组循环
		if(!visited[v]) DFS(G, v); 
}//DFSTraverse
void DFS_f(Graph G, int v){
	//visite(v);
    visited[v]=True;	 
    for(w=FirstAdjVex(G,v);w!=-1;w=NextAdjVex(G,v,w))
		if(!visited[w])  DFS_f(G, w);
    printf(v);//打印节点名字
}//DFS

6.6 关键路径

  • 事件:

    • 关于活动开始或完成的断言或陈述。
  • 活动边网络(AOE,activity on edge ):

    • 以弧表示活动,以顶点表示事件的有向图。弧有权值,表示活动所需的时间。
  • 源点、汇点:

    • 整个工程的起始点为源点,整个工程的结束点为汇点。一个工程的AOE是一个单源、单汇点的无环图。
  • 带权路径长度:

    • 一条路径上所有弧权值之和。
  • 关键路径、关键活动:

    • 一个工程的最短完成时间是从源点到汇点的最长带权路径,称该路径为关键路径;该路径上的活动为关键活动
  • 如何获得关键路径

设源点\(V_1\),汇点\(V_n\)

  1. \(v_{e_j}\):事件\(V_j\)可能发生的最早时刻。即从\(V_1\)\(V_j\)的最长带权路径。

\[v_{e_j}=\left\{ \begin{matrix}0&(j=1)\\ ​ max\{{v_{e_i}+w(V_i->V_j)}\} &(j=2,3......n) \end{matrix}\right.\\ \]

  1. \(v_{i_i}\):在不延误整个工期的前提下,事件\(V_i\)发生所允许的最晚时刻。等于\(ve(n)\)减去从\(Vi\)\(Vn\)的最长带权路径长度。

\[v_{l_i}=\left\{ \begin{matrix}v_{e_n}&(i=n)\\ ​ min\{{v_{l_j}-w(V_i->V_j)}\} &(j=1,2,3......m) \end{matrix}\right.\\ \]

  1. \(ee(k)\):活动\(ak(Vi->Vj)\)可能开始的最早时刻。

    \[ee(k)=ve(i) \]

  2. \(el(k)\):在不延误整个工期的前提下,活动\(ak(Vi->Vj)\)开始所允许的最晚时刻。

\[el(k)=vl(j)-w(Vi->Vj) (k=1,2,3... ...m) \]

  • 若某弧\(ak\)\(el(k)\)\(ee(k)\)相等,则\(ak\)为关键活动;否则\(el(k)-ee(k)\)为活动的余量

6.7 单源最短路径

  • 迪杰斯特拉(Dijkstra)算法
  1. \(AS[n,n]\)为有向网的邻接矩阵,\(S\)为已找到最短路径的终点的集合,其初值只有一个顶点,即源点。\(Dist[n]\)的每个分量表示当前所找到的从源点出发(经过集合S中的顶点)到各个终点的最短路径长度。其初值为\(Dist[k]=AS[i,k]\) (\(i\)为源点,\(k=0,1···n\))

  2. 选择\(u\),使得 \(dist[u]=min{Dist[w]|w!∈S,w∈V(G)}\)\(u\)为目前找到的从源点出发的最短路径的终点。将\(u\)加入\(S\)集合。

  3. 修改\(Dist\)数组中不在S中的终点对应的分量值。如果\(AS[u,w]\)为有限值,即\(u,w\)有弧存在,且 \(Dist[u]+AS[u,w]<Dist[w] 则令Dist[w]= Dist[u]+AS[u,w]\)

  4. 重复\(2)、3)\)直至求得源点到所有终点的最短路径

查找表

7.1 静态查找表

1. 二分查找
//二分查找
typedef struct{
		Datatype	data;       //元素数据
		KeyType	key;       //元素关键字
}Elemtype;   //数据元素类型
typedef struct{
	Elemtype	*elem;   //约定从下标1开始
	int		len;
}StaticSrhTable;   //顺序静态查找表类型

int BinSearch(StaticSrhTable SST,KeyType kval){
    bot=1, top=SST.len;          // 置查找范围初值
 	while(bot<=top) {
    	mid = (bot+top)/2;
    	if(SST.elem[mid].key==kval) return mid; //查找成功
    	else{
       		if (SST.elem[mid].key>kval) top = mid-1;//前半区
       		else bot = mid+1;            // 后半区
   		}
 	}
  	return 0;               // 未查找到
}
2. 分块查找
typedef struct {
	KeyType	key;
	int	stadr;
}indexItem;
typedef struct{
	indexItem	*elem;
	int	length;
}indexTable;//索引表

image-20220429141346671

//首先在索引表里面查找
state BlkInxSearch(StaticSrhTable SST, InxTab Inx, KeyType kval){
	bot = 1, top = Inx.len, blFound = FALSE;    // 置查找范围初值
	if (kval > Inx.elem[top].key) return 0;     // 越界(上限判断)
	while (bot <= top && !blFound) {
   	 	mid = (bot + top) / 2;
   	 	if (Inx.elem[mid].key == kval) {         // 索引查找成功
         	blFound = TRUE; bot = mid;
		}
    	else {
       		if (Inx.elem[mid].key > kval) top = mid - 1; // 前半区
       		else bot = mid + 1;                          // 后半区
    	}
	}   //退出循环时,bot所指的为所找的块
	bn = Inx.elem[bot].StartAdd;     //第bot块的数据记录起始地址
	if (bot < Inx.len) en = Inx.elem[bot + 1].StartAdd – 1;
	else en = SST.len;                 //第bot块的数据记录尾地址 
	for (i = bn; (i <= en) && (SST.elem[i].key != kval); i++);//无哨卡,加入越界判断
	if (i <= en) return i;
	return 0;                                          //未查找到 
} 

StartAdd 起始地址

7.2 动态查找表

1. 数据结构
ADT DynamicSearchTable{
	数据对象:D是具有相同特性的元素集合。
	数据关系:同属集合关系。
	基本操作:
	InitDSTable(&DT)
	DestroyDSTable(&DT)
	SearchDSTable(DT,kval)
	InsertDSTable(&DT,e)     
	DeleteDSTable(&DT,kval)  
	TraverseDSTable(DT,Visit())
}ADT DynamicSearchTable;
2.查找,插入,删除
Bool BinSrTree(BiTree BT, KeyType kval, BiTree &p, BiTree &f)
{/* 查找关键字为kval的记录。 若找到,则指针p指向该记录并返回TRUE;否则,返回FALSE。指针f指向p所指记录的双亲记录;若查找失败则p为空指针,f则为这个空指针的双亲。*/
	p = BT;            
	while (p) {
    		if (p->data.key == kval) return TRUE;     // 查找成功
    		else {
         	    f = p;
         	    if (p->data.key > kval) p = p->lChild;// 查找左子树
                else p = p->rChild;                       // 查找右子树
			}
	}
	return FALSE;                                       // 未查找到
}// BinSrTree 
//插入算法
Bool BinSrTree_Ins(BiTree &BT, KeyType kval)
{/* 当二叉查找树BT中不存在关键字为kval的记录时,插入之并返回TRUE;   否则,不进行插入操作并返回FALSE。*/
	f = NULL;
	if (BinSrTree(BT, kval, p, f)) return FALSE; // 不插入
	t = new BiTNode;
	t->data.key = kval; t->lChild = t->rChild = NULL;
	if (!f) BT = t;                          // 空树时为根结点
  	else  if (kval < f->data.key) 
		  f->lChild = t;                // 为左孩子
          	else f->rChild = t;                // 为右孩子
 	return TURE;
}//BinSrTree_Ins 
//删除算法
Bool BinSrTree_Del(BiTree &BT, KeyType kval) {
    f = NULL;
  	if (!BinSrTree(BT, kval, p, f)) return FALSE; // 不删除
 	if (p->lChild && p->rChild) {   //度为2,左右子树都非空
        q = p; t = p->lChild;
        while (t->rChild) { q = t; t = t->rChild;}
        p->data = t->data;    // t指向左子树中关键字最大的结点
        if (q != p) q->rChild = t->lChild;//q != p 特殊处理!!!
        else q->lChild = t->lChild; // t结点为p结点的左子树根
        free(t);
    }
  	else {   //度<=1
	    q = p;//p是删除结点
        if (!p->rChild) p = p->lChild;   // 右子树为空,挂其左子树
        else p = p->rChild;                   // 左子树为空,挂其右子树
        if(!f) BT=p;     //删除的是根结点
        else{
            if(q==f->lchild)f->lchild=p;
            else f->rchild=p; 
        }
        delete q;
   }
}// BinSrTree_Del 
  • 删除结点的处理方法
  1. 若是叶子结点,直接删除

  2. 只有一个孩子,则将其孩子直接挂到其双亲上。

  3. 有两个孩子,找左孩子中最大的一个元素,代替被删除结点,最大元素肯定只有一个孩子,按2)处理删除最大元素

3. 平衡二叉树
1.概念
  • 什么是平衡二叉树

    • 树中每个结点的左、右子树深度之差的绝对值不大于1,这种平衡状态的二叉查找树。
  • 平衡因子

    • 结点的左子树深度和右子树深度之差。

    • AVL树的平衡因子只能取-1,0,1

  • 最小子树根

    • 离插入结点最近且平衡因子不满足绝对值小于等于1的祖先结点。
  • 实现方法:平衡旋转技术

    • 四种平衡旋转方式

image-20220429151505985

image-20220429152629178

image-20220429152643819

image-20220429152705147

posted @ 2022-04-29 15:30  CarrieHEA  阅读(97)  评论(0)    收藏  举报