C++_有向图_使用邻接表(链表-智能指针shared_ptr)实现

// 在C++中,实现有向图的一种常见方式是使用邻接表
// 邻接表的核心思想是,对于图中的每个节点,使用一个"链表"来存储它的邻接节点。每个链表中的节点表示从当前节点出发的边的终点。
// 下面是一个简单的实现示例,其中包含以下组件:
// Graph: 表示整个有向图。
// Node: 表示一个节点及其连接的邻接表。
// AdjNode: 表示邻接表中的一个节点。

// 在这个代码示例中,Graph类表示一个有向图。它使用了邻接表来存储每个节点的邻接边。
// 每个节点的邻接表是一个链表,使用 std::shared_ptr 来确保动态分配的内存得到妥善管理。 
// addEdge 方法用于向图中添加有向边,
// printGraph 方法用于打印整个图的结构。


// 这段代码涉及到有向图中使用邻接表实现添加边的操作。
// 我们来逐句解释代码的功能:
// auto newNode = std::make_shared<AdjNode>(destination);:
// 这行代码使用std::make_shared创建一个新的AdjNode(邻接节点)对象。AdjNode的构造函数被调用,并传入了目标节点(destination)作为参数。
// std::make_shared返回一个智能指针(std::shared_ptr),指向新创建的AdjNode对象。这种智能指针负责自动管理内存,确保对象在不再需要时正确释放。

// newNode->next = nodes[source].head;:
// 这行代码将新创建的邻接节点的next指针指向源节点的当前邻接表头部
// 这样做的目的是将新创建的节点插入到邻接表的头部,即成为链表中的第一个节点。原有的链表头部节点成为新创建节点的下一个节点。

// nodes[source].head = newNode;:
// 这行代码将源节点的邻接表头部更新为新创建的邻接节点。
// 这样做的目的是将新创建的节点设为链表的头节点,从而确保新节点成为邻接表中的第一个节点。这一行与前一行代码共同实现了将新节点插入邻接表头部的操作。

// 综上所述,这段代码实现了向图的源节点的邻接表中添加一个新的邻接节点,并将其插入到邻接表的头部位置。这是向有向图中添加边的基本操作之一。

#include <iostream>
#include <vector>
#include <memory>

// 表示邻接表中的一个节点
struct AdjNode {
    int dest; // 目的节点
    std::shared_ptr<AdjNode> next; // 指向下一个邻接节点的指针

    AdjNode(int d) : dest(d), next(nullptr) {}
};

// 表示图中的一个节点
struct Node {
    std::shared_ptr<AdjNode> head; // 指向邻接表头部的指针

    Node() : head(nullptr) {}
};

// 表示整个有向图
class Graph {
public:
    // 初始化图,给定节点数
    Graph(int n) : numVertices(n), nodes(n) {}

    // 添加有向边,从源节点到目标节点
    void addEdge(int source, int destination) {
        auto newNode = std::make_shared<AdjNode>(destination);
        newNode->next = nodes[source].head;
        nodes[source].head = newNode;
    }

    // 打印整个图
    void printGraph() {
        for (int i = 0; i < numVertices; ++i) {
            std::cout << "Node " << i << ":";
            auto current = nodes[i].head;
            while (current) {
                std::cout << " -> " << current->dest;
                current = current->next;
            }
            std::cout << std::endl;
        }
    }

private:
    int numVertices; // 图中节点数
    std::vector<Node> nodes; // 存储所有节点
};

int main() {
    Graph g(5); // 创建一个有5个节点的图

    // 添加一些有向边
    g.addEdge(0, 1);
    g.addEdge(0, 4);
    g.addEdge(1, 2);
    g.addEdge(1, 3);
    g.addEdge(1, 4);
    g.addEdge(3, 4);

    // 打印整个图
    g.printGraph();

    return 0;
}

// 在C++中,实现有向图的一种常见方式是使用邻接表。
// 邻接表的核心思想是,对于图中的每个节点,使用一个"链表"来存储它的邻接节点。每个链表中的节点表示从当前节点出发的边的终点。
// 下面是一个简单的实现示例,其中包含以下组件:
// Graph: 表示整个有向图。
// Node: 表示一个节点及其连接的邻接表。
// AdjNode: 表示邻接表中的一个节点。

// 在这个代码示例中,Graph类表示一个有向图。它使用了邻接表来存储每个节点的邻接边。
// 每个节点的邻接表是一个链表,使用 std::shared_ptr 来确保动态分配的内存得到妥善管理。 
// addEdge 方法用于向图中添加有向边,
// printGraph 方法用于打印整个图的结构。


// 这段代码涉及到有向图中使用邻接表实现添加边的操作。
// 我们来逐句解释代码的功能:
// auto newNode = std::make_shared<AdjNode>(destination);:
// 这行代码使用std::make_shared创建一个新的AdjNode(邻接节点)对象。AdjNode的构造函数被调用,并传入了目标节点(destination)作为参数。
// std::make_shared返回一个智能指针(std::shared_ptr),指向新创建的AdjNode对象。这种智能指针负责自动管理内存,确保对象在不再需要时正确释放。

// newNode->next = nodes[source].head;:
// 这行代码将新创建的邻接节点的next指针指向源节点的当前邻接表头部。
// 这样做的目的是将新创建的节点插入到邻接表的头部,即成为链表中的第一个节点。原有的链表头部节点成为新创建节点的下一个节点。

// nodes[source].head = newNode;:
// 这行代码将源节点的邻接表头部更新为新创建的邻接节点。
// 这样做的目的是将新创建的节点设为链表的头节点,从而确保新节点成为邻接表中的第一个节点。这一行与前一行代码共同实现了将新节点插入邻接表头部的操作。

// 综上所述,这段代码实现了向图的源节点的邻接表中添加一个新的邻接节点,并将其插入到邻接表的头部位置。这是向有向图中添加边的基本操作之一。

 --

智能指针 shared_ptr 是 C++ 标准库提供的一种智能指针类型,用于管理动态分配的资源,特别是在动态内存分配和释放方面非常有用。它可以确保在没有引用时释放资源,避免内存泄漏,并能够安全地共享资源的所有权。

std::shared_ptr 是 C++11 引入的智能指针类型之一,旨在提供一种自动化的内存管理方式。与原生指针相比,它具有自动管理内存的优势,可以避免内存泄漏和重复释放等问题。


下面是 shared_ptr 的一些关键特点:
1. 自动内存管理: shared_ptr 可以自动管理动态分配的内存资源的生命周期。当没有指向资源的 shared_ptr 时,资源会被自动释放,无需手动调用 delete
2. 引用计数: shared_ptr 通过引用计数来跟踪资源的使用情况。每当创建一个指向资源的 shared_ptr,计数器就会增加;当 shared_ptr 被销毁时,计数器减少。当计数器为零时,资源会被释放。
3. 多个指针共享资源: 多个 shared_ptr 可以指向同一块内存资源,它们共享对资源的访问和所有权。这使得在程序中传递和共享资源变得更加简单和安全。
4. 线程安全: shared_ptr 的引用计数是线程安全的,因此可以在多线程环境中安全地使用。
5. 循环引用解决方案: shared_ptr 能够检测并解决循环引用的问题,当存在循环引用时,通过使用 weak_ptr 来打破循环引用,防止内存泄漏。
总之,智能指针 shared_ptr 提供了一种方便而安全的方式来管理动态分配的资源,它是现代 C++ 编程中的重要工具之一。

#include <iostream>
#include <memory> // 引入 shared_ptr 头文件

int main() {
    // 创建一个 shared_ptr 指向一个新的 int 对象,并初始化值为 42
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);

    // 打印共享指针指向的值
    std::cout << "Value: " << *ptr1 << std::endl;

    // 打印当前引用计数(应该是 1,因为只有 ptr1 引用该对象)
    std::cout << "Reference count: " << ptr1.use_count() << std::endl;

    {
        // 在一个新的作用域中创建另一个 shared_ptr
        std::shared_ptr<int> ptr2 = ptr1; // ptr2 和 ptr1 共享同一个 int 对象

        // 打印新的引用计数(应该是 2,因为 ptr1 和 ptr2 都引用同一个对象)
        std::cout << "Reference count: " << ptr1.use_count() << std::endl;

        // ptr2 离开作用域时,会自动释放对对象的引用,但对象不会被销毁,因为 ptr1 仍然持有该对象
    }

    // 在作用域结束后,打印引用计数(应该是 1,因为 ptr2 已被销毁)
    std::cout << "Reference count after scope: " << ptr1.use_count() << std::endl;

    // 当 ptr1 离开作用域时,内存将自动释放
    return 0;
}

在这个例子中:

  • 我们首先创建了一个 shared_ptr,并使用 std::make_shared 创建了一个新的整数对象并初始化值为 42。

  • 然后我们打印了当前 shared_ptr 的引用计数和指向的值。

  • 接着,我们在一个新的作用域中创建了另一个 shared_ptr 指向相同的整数对象。此时,引用计数变为 2,因为有两个指针引用同一个对象。

  • 当作用域结束后,ptr2 离开作用域,被销毁,引用计数又变回 1。

  • 最后,当 ptr1 也离开作用域时,整数对象将被销毁,因为没有其他指针引用它。

这个例子展示了 std::shared_ptr 的基本用法以及它如何自动管理内存。

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值