数据结构/数据结构与算法实验三 图的相关算法实现

简介: 数据结构/数据结构与算法实验三 图的相关算法实现

1.实验题目

1.【功能1】建立一个无向图。

2.【功能2】按深度优先遍历该无向图,输出遍历序列。

3.【功能3】按广度优先遍历该无向图,输出遍历序列。

2.实验要求

1、无向图以邻接矩阵或邻接表作为存储结构

2、主程序测试数据

3.算法思路

1.类的设计

这次实验可以设计出一个邻接表作为图的存储结构。因为题目要求图的边没有权值,所以,我们可以对课本上的邻接表作一些适当简化。在设计图的边类adjlistnetworkarc类时,可以去掉边权域,仅保留顶点域和指针域。在设计图类adjlistdirnetwork时,可以去掉对无穷大的值的定义。定义这两个类模板时,少了权值这一参数,仅保留数据域元素类型这一参数。但是,定义顶点结点adjlistnetwokvex时,无向图与带权有向图在形式上没有区别。

2.输入图

输入图分为3步:输入顶点个数,输入各个顶点的数据域,输入边。

输入顶点的个数后,完成了对图、顶点、边的初始化和构造。

输入边的同时,调用adjlistdirnetwork中添加边的函数,对V1结点添加(V1,V2)的边,对V2结点添加(V2,V1)的边。

可利用#键,提示输入已完成。在输入过程中,适当利用cin语句读取掉不需要的左括号、右括号、逗号等,并将数字按字符读取完成后转换回对应数字。

输入边完成后,可以自行定义一个输出功能,显示各个结点自己的数据域,并显示出与其邻接的所有结点。这一过程封装在adjlistdirnetwork类的函数中。

3.深度优先遍历

对于非连通图,先对每个顶点设置未访问标志,再从每一个未被访问的顶点出发进行深度优先搜索。深度优先搜索时,访问结点v,标记该结点已经被访问。再取邻接表上v的第一个邻接结点w。若顶点w不存在,退回到点v,结束。若顶点w存在且未被访问,则访问该结点。访问v在邻接表上下一个未被访问的结点。

4.广度优先遍历

对于非连通图,先对每个顶点设置未访问标志,再从每一个未被访问的顶点开始进行广度优先搜索。广度优先搜索时,先访问结点v,标记v已经被访问。将v入队列。当队列为空时结束。否则,队头顶点出队列v,取顶点v的第一个邻接顶点w,若w不存在,则将队头顶点出队列v。否则,若顶点w未被访问,则访问顶点w,标记w已被访问,将w入队列。否则,访问顶点v在邻接表上下一个未被访问的结点。

4.输出演示

1.先输入结点个数。

2.在结点个数输入完成后,根据提示可输入各个结点的数据域

3.在将各个结点数据域输入后,可以(a,b)的形式输入弧,将顶点a与顶点b连结在一起。

4.以#表示输入终止。此时,我们可以将这张图以邻接表的形式存储,看到其结点与结点之间的逻辑结构。

5.我们可以看到图的深度优先遍历序列与广度优先遍历序列

5.源代码

adjlistdirnetwork.h

#pragma once
#define DEFAULT_SIZE 100
#define DEFAULT_INFI 32767
#define status int
#define UNVISITED 0
#define VISITED 1
#include"adjlistnetworkvex.h"
#include"adjlistnetworkarc.h"
#include<iostream>
#include"seqqueue.h"
#include<stdio.h>
using namespace std;
template<class ElemType>
class adjlistdirnetwork
{
protected:
  mutable status* tag;
  int vexnum, vexmaxnum, arcnum;
  adjlistnetworkvex<ElemType>* vextable;
public:
/*  adjlistdirnetwork(ElemType es[], int vertexNum, int vertexMaxNum = DEFAULT_SIZE) {
    if (vertexMaxNum < 0)
      cerr<<"允许的顶点的最大数目不能为负!";
    if (vertexMaxNum < vertexNum)
      cerr<<"顶点数目不能大于允许的顶点的最大数目!";
    vexnum = vertexNum;
    vexmaxnum = vertexMaxNum;
    arcnum = 0;
    tag = new status[vexmaxnum];
    vextable = new adjlistnetworkvex<ElemType>[vexmaxnum];
    for (int v = 0; v < vexnum; v++) {
      tag[v] = UNVISITED;
      vextable[v].data = es[v];
      vextable[v].firstarc = NULL;
    }
  }
  */
  ~adjlistdirnetwork() {//析构函数
    delete [] vextable;
    delete []tag;
  }
  adjlistdirnetwork(int vertexMaxNum=DEFAULT_SIZE) {//默认构造函数
    if (vertexMaxNum < 0)//抛出异常
      cerr<<"允许顶点的最大数目不能为负!";
    vexnum = 0;
    vexmaxnum = vertexMaxNum;
    arcnum = 0;
    tag = new status[vexmaxnum];
    vextable = new adjlistnetworkvex<ElemType>[vexmaxnum];//构造顶点
    for (int v = 0; v < vexmaxnum; v++) 
      tag[v] = UNVISITED;//将是否访问的标志初始化
  }
  void setvexnum(int val) {
    vexnum = val;
  }
  void insertarc(int v1, int v2) {
    if (v1 < 0 || v1 >= vexnum)
      cerr << "v1不合法!";
    if (v2 < 0 || v2 >= vexnum)
      cerr << "v2不合法!";
    if (v1 == v2)
      cerr << "v1不能等于v2!";//判断不合法情况
    adjlistnetworkarc* p, * q;
    p = vextable[v1].getfirstarc();
    vextable[v1].setfirstarc(v2,p);//v1顶点的邻接表里连一条(v1,v2)的边
    arcnum++;
    q = vextable[v2].getfirstarc();
    vextable[v2].setfirstarc(v1,q);//v2顶点的邻接表里连一条(v2,v1)的边
    arcnum++;
  }
  adjlistnetworkvex<ElemType>* getvex(int i) {
    return &vextable[i];//因为类被封装了找到这个图对应的顶点表
  }
  void show() {//将图以邻接表的形式展示
    adjlistnetworkarc* p;
    for (int i = 0; i < vexnum; i++) {
      cout << vextable[i].getdata();
      p = vextable[i].getfirstarc();
      while (p != NULL) {
        cout<<"("<<vextable[i].getdata()<<"," <<vextable[p->getadjvex()].getdata()<<")";
        p = p->getnextarc();
      }
      cout << endl;
    }
  }
  void settag(int v, status _tag) {
    tag[v] = _tag;//改变v顶点的标志
  }
  void DFS(int v) {//深度优先搜索
    ElemType e;
    settag(v, VISITED);
    cout<<vextable[v].getdata();
    adjlistnetworkarc* w=vextable[v].getfirstarc();
    while(w!=NULL){
      if (tag[w->getadjvex()] == UNVISITED) 
        DFS(w->getadjvex());
      w = w->getnextarc();
    }
  }
  void DFSTraverse() {//完整的深度优先遍历过程
    int v;
    for (v = 0; v < vexnum; v++)
      settag(v, UNVISITED);
    for (v = 0; v < vexnum; v++)
      if (tag[v] == UNVISITED)
        DFS(v);
  }
  void resettag() {//在进行另一次遍历之前,先把是否访问的标记重置
    for (int i = 0; i < vexnum; i++)
      tag[i] = UNVISITED;
  }
  void BFS(int v) {//广度优先搜索
    seqqueue<int> q;
    int u;
    adjlistnetworkarc* w=NULL;
    settag(v, VISITED);
    cout << vextable[v].getdata();
    q.enqueue(v);
    while (!q.isempty()) {
      q.delqueue(u);
      w = vextable[u].getfirstarc();
      while (w != NULL) {
        if (tag[w->getadjvex()] == UNVISITED) {
          cout << vextable[w->getadjvex()].getdata();
          settag(w->getadjvex(), VISITED);
          q.enqueue(w->getadjvex());
        }
        w = w->getnextarc();
      }
    }
  }
  void BFSTraverse() {//完整的广度优先遍历
    int v;
    for (v = 0; v < vexnum; v++)
      settag(v, UNVISITED);
    for (v = 0; v < vexnum; v++)
      if (tag[v] == UNVISITED)
        BFS(v);
  }
};

adjlistnetworkarc.h

#pragma once
#include"Windows.h"
struct adjlistnetworkarc {
protected:
  int adjvex;
  adjlistnetworkarc* nextarc;
public:
  int getadjvex() {
    return adjvex;
  }
  adjlistnetworkarc* getnextarc() {
    return nextarc;
  }
  void setnextarc(adjlistnetworkarc* _nextarc) {
    nextarc = _nextarc;
  }
  //因为边类被封装了,所以要写getter/setter
  adjlistnetworkarc() {//默认的无参构造函数
    adjvex = -1;
    nextarc = NULL;
  }
  adjlistnetworkarc(int v, adjlistnetworkarc* next = NULL) {
    adjvex = v;//边的一段指向顶点v
    nextarc = next;
  }
};

adjlistnetworkvex.h

#pragma once
#define DEFAULT_SIZE 100
#define status int
#include"adjlistnetworkarc.h"
#include<iostream>
using namespace std;
template<typename ElemType>
class adjlistnetworkvex
{
protected:
  ElemType data;
  adjlistnetworkarc* firstarc;
public:
  adjlistnetworkvex() {
    firstarc = NULL;//默认的无参构造函数
  }
/*  ~adjlistnetworkvex() {
    if (firstarc != NULL) {
      adjlistnetworkarc* p = firstarc;
      while (p ->getnextarc()!= NULL) {
        firstarc->setnextarc(p->getnextarc());
        delete p;
        p = firstarc;
      }
    }
  }*/
  adjlistnetworkvex(ElemType val, adjlistnetworkarc* adj=NULL) {//构造函数
    data = val;
    firstarc = adj;
  }
  void setfirstarc(int v, adjlistnetworkarc* next = NULL) {
    firstarc = new adjlistnetworkarc(v, next);
  }
  void setdata(ElemType val) {
    data = val;
  }
  adjlistnetworkarc* getfirstarc() {
    return firstarc;
  }
  ElemType getdata() {
    return data;
  }
  //因为类被封装了,所以要写getter/setter
};

sequeue.h

#pragma once
#define DEFAULT_SIZE 100
#define OVER_FLOW 0
#define SUCCESS 1
#define UNDER_FLOW 0
#define status int
template<class ElemType>
class seqqueue {
protected:
  int front, rear;
  int maxSize;
  ElemType* elems;
public:
  seqqueue(int size = DEFAULT_SIZE) {
    maxSize = size;
    if (elems != NULL) delete []elems;
    elems = new ElemType[maxSize];
    rear = front = 0;
  }
  virtual ~seqqueue() {
    delete[]elems;
  }
  bool isempty() {
    return rear == front;
  }
  status enqueue(const ElemType e) {
    if ((rear + 1) % maxSize == front)
      return OVER_FLOW;
    else {
      elems[rear] = e;
      rear = (rear + 1) % maxSize;
      return SUCCESS;
    }
  }
  status delqueue(ElemType& e) {
    if (!isempty()) {
      e = elems[front];
      front = (front + 1) % maxSize;
      return SUCCESS;
    }
    else
      return UNDER_FLOW;
  }
  status gethead(ElemType& e) const {
    if (!isempty()) {
      e = elems[front];
      return SUCCESS;
    }
    else
      return UNDER_FLOW;
  }
};

main.cpp

#include<iostream>
#include<cstring>
#include<stdio.h>
#include"adjlistdirnetwork.h"
#define DEFAULT_SIZE 100
using namespace std;
int main() {
  adjlistdirnetwork<string> adj(DEFAULT_SIZE);
  cout << "请输入结点个数:";
  int vexcount;
  int v1, v2;
  string vexname;
  char ch=' ';
  cin >> vexcount;
  adj.setvexnum(vexcount);
  for (int i = 0; i < vexcount; i++) {
    printf_s("\n请输入第%d个结点的数据域\n", i+1);
    cin >> vexname;
    adj.getvex(i)->setdata(vexname);
  }
  cout << "请输入弧(a,b),以#表示输入终止" << endl;
  cin >> ch;//读取左括号(
  while (ch != '#') {
    cin >> ch;//以字符的形式读取顶点结点
    v1 = int(ch - '0')-1;//将字符转换成数字。
    //因为人从1开始数数,电脑从0开始数数,所以-1
    cin >> ch;//读取逗号,
    cin >> ch;//以字符的形式读取另一个顶点结点
    v2 = int(ch - '0')-1;
    cin >> ch;//读取右括号)
    cout << "请输入弧(a,b),以#表示输入终止" << endl;
    cin >> ch;//读取下一个左括号(或终止符号#
    adj.insertarc(v1, v2);//插入从v1到v2的边和从v2到v1的边
  }
  cout <<  "\n输出该图:\n";
  adj.show();
  cout << "\n深度优先遍历序列为:";
  adj.DFSTraverse();
  cout << "\n广度优先遍历序列为:";
  adj.BFSTraverse();
  cout << "\n运行完成";
}


目录
相关文章
|
4天前
|
机器学习/深度学习 存储 算法
数据结构与算法 动态规划(启发式搜索、遗传算法、强化学习待完善)
数据结构与算法 动态规划(启发式搜索、遗传算法、强化学习待完善)
11 1
|
4天前
|
存储 算法
数据结构与算法 图
数据结构与算法 图
5 0
|
7天前
|
搜索推荐 C语言
【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)
【C语言/数据结构】排序(归并排序|计数排序|排序算法复杂度)
11 0
|
7天前
|
存储 算法
Leetcode 30天高效刷数据结构和算法 Day1 两数之和 —— 无序数组
给定一个无序整数数组和目标值,找出数组中和为目标值的两个数的下标。要求不重复且可按任意顺序返回。示例:输入nums = [2,7,11,15], target = 9,输出[0,1]。暴力解法时间复杂度O(n²),优化解法利用哈希表实现,时间复杂度O(n)。
19 0
|
14天前
|
存储 机器学习/深度学习 算法
|
18天前
|
存储 算法 Python
程序设计的艺术:算法与数据结构的魅力
程序设计的艺术:算法与数据结构的魅力
8 0
|
18天前
|
存储 算法
数据结构开篇(普普通通浅浅聊数据结构)什么是数据结构 、什么是算法、重要性、如何学好数据结构呢
数据结构开篇(普普通通浅浅聊数据结构)什么是数据结构 、什么是算法、重要性、如何学好数据结构呢
|
26天前
|
存储 人工智能 算法
有哪些数据结构与算法是程序员必须要掌握的?——“数据结构与算法”
有哪些数据结构与算法是程序员必须要掌握的?——“数据结构与算法”
|
28天前
|
存储 算法 Serverless
数据结构期末复习(六)查找算法
数据结构期末复习(六)查找算法
11 0
|
28天前
|
存储 人工智能 算法
数据结构期末复习(1)数据结构和算法 线性表
数据结构期末复习(1)数据结构和算法 线性表
16 0