DFS和BFS在二叉树中的应用

DFS和BFS在二叉树中的应用


一、前言

在学习完深度优先搜索和广度优先搜索后,结合堆栈与队列的性质,在二叉树中实现深度优先遍历(即前序遍历)和广度优先遍历(即层次遍历)。共涉及一下知识点:

  • 二叉树的定义与建立(初始化,插入节点)
  • 堆栈的定义与建立(初始化,入栈,出栈)
  • 队列的定义与建立(初始化,入队,出队)
  • DFS的实现
  • BFS的实现

本篇文章综合性比较强,涉及内容比较多,请仔细学习。


二、二叉树

二叉树是树的一种,有且只有一个根节点,出根节点之外有两个互不相交的子集,称为左子树和右子树。两个子树本身又都是二叉树。

1.树的定义

需要定义一个二叉树的节点类型,包括节点值,以及节点的左孩子和右孩子。

/*定义二叉树节点类型*/
typedef struct TreeNode *BinTree;
struct TreeNode
{
    int Data;
    BinTree LeftChild;
    BinTree RightChild;
};

2.树的初始化与建立

建立一棵二叉树,首先需要分配空间,其次将节点值输入并赋值,最后创建并更新其左右子树,选择递归的方式建立。

/*建立一棵二叉树*/
BinTree BuildBinTree()
{
    int ch;
    scanf("%d",&ch);

    BinTree T;
    T=(BinTree)malloc(sizeof(struct TreeNode));
    
    if(ch==-1)//如果输入为-1,表明节点为空
    {
        T=NULL;
        return T;
    }
    else{
        T->Data=ch;
        printf("input %d LeftChild Value:",ch);
        T->LeftChild=BuildBinTree();
        printf("input %d RightChild Value:",ch);
        T->RightChild=BuildBinTree();
    }
    return T;
}

三、堆栈

栈是限定仅在表尾进行插入和删除操作的线性表。又称为先进后出的线性表。

1.栈的定义

建立一个堆栈,首先要开辟一定空间的数组,并且定义一个栈顶指针。

/*定义堆栈类型*/
typedef struct SNode *Stack;
struct SNode{
    int Data[MAXSIZE];
    int top;
};

2.栈的初始化

分配一个堆栈类型的空间,将栈顶指针指向-1

/*堆栈初始化*/
Stack initS()
{
    Stack S;
    S=(Stack)malloc(sizeof(struct SNode));
    S->top=-1;
    return S;
}

3.入栈和出栈操作

入栈首先判断堆栈是否已满,如果没满则将数据压入栈,栈顶指针向后移动一位。
出栈首先判断堆栈是否为空,如果不空则将数据弹出栈,栈顶指针向前移动一位。

/*入栈*/
void Push(Stack Ptrs,int x)
{
    if(Ptrs->top==MAXSIZE-1) printf("stack is full!\n");
    else Ptrs->Data[++(Ptrs->top)]=x;
}

/*出栈*/
int Pop(Stack Ptrs)
{
    if(Ptrs->top==-1) printf("stack is empty!");
    else return Ptrs->Data[(Ptrs->top)--];
}

四、队列

与栈相反,队列是一种先进先出的线性表。只允许在表的一端进行插入元素,在另一端删除元素。

1.队列的定义

首先开一个数组,其次设置一个队首和队尾标记。

/*定义队列类型*/
typedef struct QNode
{
    int Data[MAXSIZE];
    int front;
    int rear;
}*Queue;

2.队列的初始化

开辟空间,将队首队尾标记置0。

/*队列初始化*/
Queue initQ()
{
    Queue Q;
    Q=(Queue)malloc(sizeof(struct QNode));
    Q->front=0;
    Q->rear=0;
    return Q;
}

3.入队和出队操作

判断队列是否已满,如果不满则更新队尾标记,将数据存入队列
判断队列是否为空,如果不空则更新队首标记,将数据弹出队列

/*入队*/
void AddQ(Queue PtrQ,int x)
{
    if((PtrQ->rear+1)%MAXSIZE==PtrQ->front) printf("Queue is full!\n");
    PtrQ->rear=(PtrQ->rear+1)%MAXSIZE;
    PtrQ->Data[PtrQ->rear]=x;
}

/*出队*/
int DeleteQ(Queue PtrQ)
{
    if(PtrQ->front==PtrQ->rear) printf("Queue is empty!\n");
    else{
        PtrQ->front=(PtrQ->front+1)%MAXSIZE;
        return PtrQ->Data[PtrQ->front];
    }
}

五、DFS

深度优先搜索遍历即类似于树的先序遍历,是树的先序遍历的推广。
思路为:

  • 从根节点出发,访问根节点,将根节点入栈
  • 左子树不为空,则将根节点弹出,访问左子树节点,重复第一步操作
  • 右子树不为空,则将根节点弹出,访问右子树节点,重复第一步操作
  • 直至所有节点都访问过

具体代码实现如下:

/*深度优先搜索,在二叉树中即为前序遍历*/
void DFS(BinTree T,Stack S)
{
    if(T==NULL) return;
        
    Push(S,T->Data);
    printf("%d ",Pop(S));

    DFS(T->LeftChild,S);
    DFS(T->RightChild,S);
}

六、BFS

广度优先搜索遍历类似于树的层次遍历。
思路为:

  • 一层一层的访问二叉树
  • 首先访问根节点
  • 其次访问其左右子树节点
  • 再次对其左右子树做第一步操作
  • 直至每一层都遍历完成

具体代码如下:

/*广度优先搜索,在二叉树中即为层次遍历*/
int flag=0;//根节点标记
void BFS(BinTree T,Queue Q)
{
    if(T==NULL) return;

    if(!flag)//根节点
    {
        AddQ(Q,T->Data);
        printf("%d ",DeleteQ(Q));
        flag=1;
    }
    //左右孩子节点
    AddQ(Q,T->LeftChild->Data);
    printf("%d ",DeleteQ(Q));
    AddQ(Q,T->RightChild->Data);
    printf("%d ",DeleteQ(Q));

    BFS(T->LeftChild,Q);
    BFS(T->RightChild,Q);
}

七、代码以及运行结果

1.完整代码

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100

/*定义二叉树节点类型*/
typedef struct TreeNode *BinTree;
struct TreeNode
{
    int Data;
    BinTree LeftChild;
    BinTree RightChild;
};

/*定义堆栈类型*/
typedef struct SNode *Stack;
struct SNode{
    int Data[MAXSIZE];
    int top;
};

/*定义队列类型*/
typedef struct QNode
{
    int Data[MAXSIZE];
    int front;
    int rear;
}*Queue;

/*建立一棵二叉树*/
BinTree BuildBinTree()
{
    int ch;
    scanf("%d",&ch);

    BinTree T;
    T=(BinTree)malloc(sizeof(struct TreeNode));
    
    if(ch==-1)//如果输入为-1,表明节点为空
    {
        T=NULL;
        return T;
    }
    else{
        T->Data=ch;
        printf("input %d LeftChild Value:",ch);
        T->LeftChild=BuildBinTree();
        printf("input %d RightChild Value:",ch);
        T->RightChild=BuildBinTree();
    }
    return T;
}

/*堆栈初始化*/
Stack initS()
{
    Stack S;
    S=(Stack)malloc(sizeof(struct SNode));
    S->top=-1;
    return S;
}

/*入栈*/
void Push(Stack Ptrs,int x)
{
    if(Ptrs->top==MAXSIZE-1) printf("stack is full!\n");
    else Ptrs->Data[++(Ptrs->top)]=x;
}

/*出栈*/
int Pop(Stack Ptrs)
{
    if(Ptrs->top==-1) printf("stack is empty!");
    else return Ptrs->Data[(Ptrs->top)--];
}

/*队列初始化*/
Queue initQ()
{
    Queue Q;
    Q=(Queue)malloc(sizeof(struct QNode));
    Q->front=0;
    Q->rear=0;
    return Q;
}

/*入队*/
void AddQ(Queue PtrQ,int x)
{
    if((PtrQ->rear+1)%MAXSIZE==PtrQ->front) printf("Queue is full!\n");
    PtrQ->rear=(PtrQ->rear+1)%MAXSIZE;
    PtrQ->Data[PtrQ->rear]=x;
}

/*出队*/
int DeleteQ(Queue PtrQ)
{
    if(PtrQ->front==PtrQ->rear) printf("Queue is empty!\n");
    else{
        PtrQ->front=(PtrQ->front+1)%MAXSIZE;
        return PtrQ->Data[PtrQ->front];
    }
}

/*深度优先搜索,在二叉树中即为前序遍历*/
void DFS(BinTree T,Stack S)
{
    if(T==NULL) return;
        
    Push(S,T->Data);
    printf("%d ",Pop(S));

    DFS(T->LeftChild,S);
    DFS(T->RightChild,S);
}

/*广度优先搜索,在二叉树中即为层次遍历*/
int flag=0;
void BFS(BinTree T,Queue Q)
{
    if(T==NULL) return;

    if(!flag)//根节点
    {
        AddQ(Q,T->Data);
        printf("%d ",DeleteQ(Q));
        flag=1;
    }
    //左右孩子节点
    AddQ(Q,T->LeftChild->Data);
    printf("%d ",DeleteQ(Q));
    AddQ(Q,T->RightChild->Data);
    printf("%d ",DeleteQ(Q));

    BFS(T->LeftChild,Q);
    BFS(T->RightChild,Q);
}

int main()
{   
    Stack S=initS();
    Queue Q=initQ();

    BinTree T=BuildBinTree(T);

    printf("DFS:");
    DFS(T,S);
    printf("\n");
    printf("BFS:");
    BFS(T,Q);

    return 0;
}

2.运行结果

建立一课二叉树,如下图:
在这里插入图片描述

以下为遍历结果:
在这里插入图片描述


八、总结评价

深度优先搜索和广度优先搜索是各类算法比赛经常出现的考点,因此,对其的熟练掌握显得尤为重要。
本篇文章树为载体,结合数据结构一书中的基本知识,对DFS和BFS进行讲解和编写,不仅在于帮助大家实现对两种搜索算法的理解,更是对数据结构基础的夯实与打磨。

有问题欢迎各位大佬指出
算法系列将持续更新,欢迎关注,一起学习

  • 29
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S1XmKl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值