链式前向星(非递归深度优先遍历算法等)

文章介绍了链式前向星类,包括非递归深度优先和广度优先遍历算法,检测有向图或无向图中的环,以及寻找两点间的基本路径。测试程序展示了如何在用户输入下操作这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

链式前向星类(包括非递归深度优先遍历、非递归广度优先遍历、求一条包含所有结点的道路、是否有环、两点之间基本路径):

测试程序:


链式前向星类(包括非递归深度优先遍历、非递归广度优先遍历、求一条包含所有结点的道路、是否有环、两点之间基本路径):

(PS:有向图和无向图的区分靠main程序中用户输入来解决,如果是有向图输入中只有边1 4,如果是无向图输入边1 4以及边4 1)

#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <string>

using namespace std;

//链式前向星
class chain_forward_star
{
private:
    //结点数、边数、边序号(从1开始)
    int _node_number, _edge_number, _sequence_number;  
    //前结点最后出现的边序号
    int *_end_edge = new int[_node_number+1] ;         
    //边类
    struct Edge
    {
        //后结点、前结点上一次出现的边序号
        int _end_node, _last_edge;                      
    };
    //边数组
    Edge* _edge = new Edge[_node_number+1];            
public:
    //构造函数
    chain_forward_star(int node_number, int edge_number):
        _node_number(node_number),_edge_number(edge_number)
    {
        for (int x = 0; x < _node_number + 1; x++)
        {
            _end_edge[x] = -1;
        }
        _sequence_number = 1;
    }
    //默认析构函数
    ~chain_forward_star()
    {
        delete[] _end_edge;
        delete[] _edge;
    }

    //加边函数(起点,终点)
    void add_edge(int start_node, int end_node)                     
    {
        _edge[_sequence_number]._end_node = end_node;               
        _edge[_sequence_number]._last_edge = _end_edge[start_node];    
        _end_edge[start_node] = _sequence_number;                    
        _sequence_number++;                                         
    }

    //非递归深度优先遍历(遍历起点)
    void non_recursive_depth_first_traversal(int start_node) const
    {
        //初始化
        //建一个end_edge的副本进行使用
        int* temp_end_edge = new int[_node_number + 1];  
        //flag用于避免回路
        bool* flag = new bool[_node_number + 1];
        for (int x = 0; x < _node_number + 1; x++)
        {
            flag[x] = false;
            temp_end_edge[x] = _end_edge[x];
        }
        //vector用于回访前面的结点
        vector<int> node_vector;
        //算法
        flag[start_node] = true;
        node_vector.push_back(start_node);
        cout << start_node << " ";
        for (int temp_edge = temp_end_edge[start_node], temp_node = start_node; ; )
        {
            if (temp_edge == -1)
            {
                node_vector.pop_back();
                if (node_vector.empty())
                {
                    break;
                }
                temp_node = node_vector.back();
                temp_edge = temp_end_edge[temp_node];
                continue;
            }
            temp_end_edge[temp_node] = _edge[temp_edge]._last_edge;
            temp_node = _edge[temp_edge]._end_node;
            if (flag[temp_node] == true)
            {
                temp_node = node_vector.back();
                temp_edge = temp_end_edge[temp_node];
                continue;
            }
            flag[temp_node] = true;
            node_vector.push_back(temp_node);
            cout << temp_node << " ";
            temp_edge = temp_end_edge[temp_node];
        }
        //回收内存
        delete [] temp_end_edge;
        delete [] flag;
    }

    //非递归广度优先遍历
    void non_recursive_breadth_first_traversal(int start_node) const
    {
        //初始化
        queue<int> node_queue;
        bool* flag = new bool[_node_number + 1];
        for (int x = 0; x < _node_number + 1; x++)
        {
            flag[x] = false;
        }
        //算法 
        flag[start_node] = true;
        node_queue.push(start_node);
        while (!node_queue.empty()) 
        {
            int temp_node = node_queue.front();
            node_queue.pop();
            cout << temp_node << " ";

            for (int temp_edge = _end_edge[temp_node]; temp_edge!=-1; 
                temp_edge = _edge[temp_edge]._last_edge) 
            {
                int temp_end_node = _edge[temp_edge]._end_node;
                if (flag[temp_end_node] == false)
                {
                    node_queue.push(temp_end_node);
                    flag[temp_end_node] = true;
                }
            }
        }
        //回收
        delete [] flag;
    }

    //求一条包含所有结点的回路
    //采用类似之前的深度优先遍历算法,每个结点作为开始结点,一直走,找到所求即退出
    void all_node_road_search(void)
    {
        //初始化
        int* temp_end_edge = new int[_node_number + 1];
        bool* flag = new bool[_node_number + 1];
        //算法
        for (int start_node = 1; start_node <= _node_number; start_node++)
        {
            for (int x = 0; x < _node_number + 1; x++)
            {
                flag[x] = false;
                temp_end_edge[x] = _end_edge[x];
            }
            vector<int> node_vector;
            flag[start_node] = true;
            node_vector.push_back(start_node);
            for (int temp_edge = temp_end_edge[start_node], temp_node = start_node; ; )
            {
                if (temp_edge == -1)
                {
                    node_vector.pop_back();
                    if (node_vector.empty())
                    {
                        break;
                    }
                    temp_node = node_vector.back();
                    temp_edge = temp_end_edge[temp_node];
                    continue;
                }
                temp_end_edge[temp_node] = _edge[temp_edge]._last_edge;
                temp_node = _edge[temp_edge]._end_node;
                if (flag[temp_node] == true)
                {
                    break;
                }
                flag[temp_node] = true;
                node_vector.push_back(temp_node);
                temp_edge = temp_end_edge[temp_node];
            }
            //这里不一样
            if (node_vector.size() == _node_number)
            {
                cout << "一条包含所有结点的回路如下:";
                for (int x = 0; x < _node_number; x++)
                {
                    cout << node_vector[x] << " ";
                }
                //回收
                delete [] temp_end_edge;
                delete [] flag;
                return;
            }
        }
        cout << "没有找到一条包含所有结点的回路!";
        //回收
        delete [] temp_end_edge;
        delete [] flag;
        return;
    }

    //是否有环
    //采用类似之前的深度优先遍历算法,每个结点作为开始结点,遇到重复的即有环,退出函数
    void is_ring(void) const
    {
        //初始化
        int* temp_end_edge = new int[_node_number + 1];
        bool* flag = new bool[_node_number + 1];
        //算法
        for (int start_node = 1; start_node <= _node_number; start_node++)
        {
            for (int x = 0; x < _node_number + 1; x++)
            {
                flag[x] = false;
                temp_end_edge[x] = _end_edge[x];
            }
            vector<int> node_vector;
            flag[start_node] = true;
            node_vector.push_back(start_node);
            for (int temp_edge = temp_end_edge[start_node], temp_node = start_node; ; )
            {
                if (temp_edge == -1)
                {
                    node_vector.pop_back();
                    if (node_vector.empty())
                    {
                        break;
                    }
                    temp_node = node_vector.back();
                    temp_edge = temp_end_edge[temp_node];
                    continue;
                }
                temp_end_edge[temp_node] = _edge[temp_edge]._last_edge;
                temp_node = _edge[temp_edge]._end_node;
                if (flag[temp_node] == true)
                {
                    cout<< "有环!" ;
                    //回收
                    delete [] temp_end_edge;
                    delete [] flag;
                    return;
                }
                flag[temp_node] = true;
                node_vector.push_back(temp_node);
                temp_edge = temp_end_edge[temp_node];
            }
        }
        cout << "无环!";
        //回收
        delete [] temp_end_edge;
        delete [] flag;
        return;
    }

    //找两个点之间的基本路径(点不重复的路径,同时边一定也不重复)
    void basic_path_between_two_points(int start_node, int end_node)
    {
        //初始化
        int* temp_end_edge = new int[_node_number + 1];
        bool* flag = new bool[_node_number + 1];
        for (int x = 0; x < _node_number + 1; x++)
        {
            flag[x] = false;
            temp_end_edge[x] = _end_edge[x];
        }
        vector<int> node_vector;
        //算法
        flag[start_node] = true;
        node_vector.push_back(start_node);
        for (int temp_edge = temp_end_edge[start_node], temp_node = start_node; ; )
        {
            if (temp_node == end_node)
            {
                cout << start_node << "、" << end_node <<"两点间基本路径如下:";
                for (auto x : node_vector)
                {
                    cout << x << " ";
                }
                //回收
                delete [] temp_end_edge;
                delete [] flag;
                return;
            }
            if (temp_edge == -1)
            {
                node_vector.pop_back();
                if (node_vector.empty())
                {
                    break;
                }
                temp_node = node_vector.back();
                temp_edge = temp_end_edge[temp_node];
                continue;
            }
            temp_end_edge[temp_node] = _edge[temp_edge]._last_edge;
            temp_node = _edge[temp_edge]._end_node;
            if (flag[temp_node] == true)
            {
                temp_node = node_vector.back();
                temp_edge = temp_end_edge[temp_node];
                continue;
            }
            flag[temp_node] = true;
            node_vector.push_back(temp_node);
            temp_edge = temp_end_edge[temp_node];
        }
        cout << start_node << "、" << end_node << "两点间没有基本路径!";
        //回收
        delete [] temp_end_edge;
        delete [] flag;
        return;
    }
};

测试程序:

#include <iostream>

#include "chain_forward_star.h"
using namespace std;

//判断边处理函数
bool process_repeat_edge(int* temp_node_list_1, int* temp_node_list_2, 
                    int start_node, int end_node, int length)
{
    for (int x = 1; x <= length; x++)
    {
        if (temp_node_list_1[x] == start_node && temp_node_list_2[x] == end_node)
        {
            return false;
        }
    }
    return true;
}

int main()
{
    cout << "请输入结点数和边数:";
    int node_number = 0 , edge_number = 0;
    cin >> node_number >> edge_number;
    //输入结点数为0时退出
    if(node_number == 0)
    {
        cout << "错误,结点数为0!" << endl;
        return 0;
    }
    if(edge_number == 0)
    {
        cout << "错误,边数为0!" << endl;
        return 0;
    }
    if(edge_number > node_number * (node_number - 1))
    {
        cout << "错误,边数大于通过结点推断数目!" << endl;
        return 0;
    }
    //类实例化
    chain_forward_star star(node_number, edge_number);
    cout << "每排输入一条边的前结点和后结点,结点只能输入数字,从1到结点数:" << endl;
    int start_node = -1;
    int end_node = -1;
    //用于处理重复边
    int* temp_node_list_1 = new int[edge_number+1];
    int* temp_node_list_2 = new int[edge_number+1];
    //加边
    for (int x = 1; x <= edge_number; x++)
    {
        cin >> start_node >> end_node;
        //处理输入不正确的边
        if(start_node == -1 || end_node == -1 || start_node > node_number || end_node > node_number)
        {
            cout << "请输入正确格式的边!" << endl;
            return 0;
        }
        //处理重复边
        temp_node_list_1[x] = start_node;
        temp_node_list_2[x] = end_node;
        if(!process_repeat_edge(temp_node_list_1, temp_node_list_2, start_node, end_node, x-1))
        {
            cout << "错误,出现重复边!" << endl;
            return 0;
        }
        star.add_edge(start_node, end_node);
    }
    //回收
    delete [] temp_node_list_1;
    delete [] temp_node_list_2;
    cout << endl;

    cout << "非递归深度优先遍历:";
    star.non_recursive_depth_first_traversal(1);
    cout << endl;

    cout << "非递归广度优先遍历:";
    star.non_recursive_breadth_first_traversal(1);
    cout << endl;

    star.all_node_road_search();
    cout << endl;

    star.is_ring();
    cout << endl;
   
    cout<<"请输入想要查找基本路径两个结点:";
    int node_1 = -1, node_2 = -1;
    cin >> node_1 >> node_2;
    if(node_1 == -1 || node_2 == -1 || node_1 > node_number || node_2 > node_number)
    {
        cout << "请输入正确格式的边!" << endl;
        return 0;
    }
    star.basic_path_between_two_points(node_1, node_2);
    cout << endl;

    return 0;
}

/*
输入示例,第一排是7个点 8条边
7 8 
1 2
1 3
1 4
3 7
4 5
4 6
5 6
2 7
*/

### 链式前向星遍历的实现方数据结构 链式前向星是一种高效的存储稀疏矩阵的方,在图论中常用于表示邻接表形式的图。它通过数组模拟链表的方式,能够快速访问边的信息并支持高效的操作。 #### 数据结构设计 链式前向星的核心在于利用两个数组来构建邻接表: - **`head[]`**: 记录每个节点的第一条边的位置索引。 - **`edge[]`**: 存储每条边的目标节点编号以及下一条边的指针位置。 具体定义如下: ```c++ struct Edge { int to; // 边指向的节点 int next; // 下一条边的索引 }; Edge edges[MAXM]; // 边集数组,MAXM为最大可能的边数 int head[MAXN], tot = 0; // MAXN为最大可能的节点数, tot记录当前边的数量 ``` 当新增加一条从 `u` 到 `v` 的边时,可以按照以下方式更新数组[^1]: ```c++ void add_edge(int u, int v) { edges[tot].to = v; edges[tot].next = head[u]; head[u] = tot++; } ``` 此操作的时间复杂度接近 O(1),因此非常适合大规模图的数据处理场景。 #### 图的非递归深度优先遍历 (DFS) 对于基于链式前向星存储的图,可以通过栈或者显式的循环控制来进行非递归版本的 DFS 。以下是具体的实现代码: ```cpp #include <iostream> #include <vector> using namespace std; const int MAXN = 1e5 + 7; const int MAXM = 2e5 + 7; // 定义边结构体 struct Edge { int to; int next; } edges[MAXM]; int head[MAXN], tot = 0; // 添加边函数 void add_edge(int u, int v){ edges[tot].to = v; edges[tot].next = head[u]; head[u] = tot++; } bool vis[MAXN]; // 是否访问过标记数组 // 非递归DFS核心逻辑 void dfs_non_recursive(int start_node){ vector<int> stack; stack.push_back(start_node); while(!stack.empty()){ int current = stack.back(); stack.pop_back(); if (!vis[current]){ cout << current << ' '; vis[current] = true; for(int i=head[current];i!=-1;i=edges[i].next){ int neighbor = edges[i].to; if(!vis[neighbor]) stack.push_back(neighbor); } } } } ``` 上述代码展示了如何使用标准库中的容器完成一次完整的非递归深度优先搜索过程。 #### 图的非递归广度优先遍历 (BFS) 同样地,也可以采用队列代替栈的方式来执行 BFS : ```cpp queue<int> q; q.push(start_node); while(!q.empty()) { int node = q.front(); q.pop(); if(vis[node]) continue; vis[node]=true; printf("%d ",node); for(int e=head[node];~e;e=edges[e].next){ int adjNode = edges[e].to; if(!vis[adjNode]) q.push(adjNode); } } ``` 这段程序实现了基础版的广度优先扫描功能. #### 总结 以上介绍了链式前向星作为底层支撑技术下的两种主要图遍历策略——DFS 和 BFS ,它们均具备较高的性能表现,并且易于扩展到更复杂的实际应用场景之中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值