邻接表的实现(有向邻接表)、(无向邻接表),基于C++

本文介绍了如何使用C++实现邻接表数据结构,包括有向和无向图的邻接表。邻接表相较于邻接矩阵在存储空间上更节省,特别适合边数相对顶点较少的图。文章通过代码展示了具体的实现,并给出了运行示例。

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

邻接表 

邻接矩阵的实现请看这里

是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表
邻接表的处理方法是这样的:

  • (1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
  • (2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
    例如,下图就是一个无向图的邻接表的结构。

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。 

代码实现邻接表(有向邻接表图) 基于C++

/**
 * C++: 邻接表图
 *
 * @author lph
 * 
 */

#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;

#define MAX 100
// 邻接表
class ListDG {
private: // 内部类
	// 邻接表中表对应的链表顶点
	class ENode {
	public:
		int ivex;			// 该边所指向的顶点的位置
		ENode* nextEdge;	// 指向下一条弧的指针
	};

	// 邻接表中表的顶点
	class VNode {
	public:
		char data;			//顶点信息
		ENode* firstEdge;	//指向第一条依赖该顶点的弧
	};

private: // 私有成员
	int mVexNum;			//图的顶点的数目
	int mEdgNum;			//图的边的数目
	VNode mVexs[MAX];		//用一维数组来存储邻接表的顶点

public:
	// 创建邻接表对应的图(自己输入)
	ListDG();
	// 创建邻接表对应的图(用已经提供的数据)
	ListDG(char vexs[], int vlen, char edges[][2], int elen);
	~ListDG();

	// 打印邻接表图
	void print();

private:
	// 读取一个输入字符
	char readChar();
	// 返回ch的位置
	int getPosition(char ch);
	// 将node节点链接到List的最后
	void linkLast(ENode* list, ENode* node);
};

/*
* 创建邻接表对应的图(自己输入)
*/
ListDG::ListDG() {
	char c1, c2;
	int v, e;
	int i, p1, p2;
	ENode* node1, * node2;

	// 输入顶点数和边数
	cout << "input vertex number: ";
	cin >> mVexNum;
	cout << "input edge number: ";
	cin >> mEdgNum;
	if (mVexNum < 1 || mEdgNum < 1 || (mEdgNum > (mVexNum * (mVexNum - 1)))) {
		cout << "input error: invalid parameters! " << endl;
		return;
	}

	// 初始化邻接表的顶点
	for (i = 0; i < mVexNum; ++i) {
		cout << "vertex(" << i << "):";
		mVexs[i].data = readChar();
		mVexs[i].firstEdge = nullptr;
	}

	// 初始化邻接表的边
	for (i = 0; i < mEdgNum; ++i) {
		// 读取边的起始点和结束顶点
		cout << "edge(" << i << "):";
		c1 = readChar();
		c2 = readChar();

		p1 = getPosition(c1);
		p2 = getPosition(c2);
		// 初始化node1
		node1 = new ENode();
		node1->ivex = p2;
		// 将node1链接到p1所在链表的末尾
		if (mVexs[p1].firstEdge == nullptr)
			mVexs[p1].firstEdge = node1;
		else
			linkLast(mVexs[p1].firstEdge, node1);
	}
}

/*
	创建邻接表对应的图(用已经提供的数据)
*/
ListDG::ListDG(char vexs[], int vlen, char edges[][2], int elen) {
	char c1, c2;
	int i, p1, p2;
	ENode* node1, * node2;

	// 初始化顶点数和边数
	mVexNum = vlen;
	mEdgNum = elen;
	// 初始化"邻接表"的顶点
	for (i = 0; i < mVexNum; i++)
	{
		mVexs[i].data = vexs[i];
		mVexs[i].firstEdge = NULL;
	}
	// 初始化邻接表的边
	for (i = 0; i < mEdgNum; ++i) {
		// 读取边的起始顶点和结束顶点
		c1 = edges[i][0];
		c2 = edges[i][1];

		p1 = getPosition(c1);
		p2 = getPosition(c2);
		// 初始化node1
		node1 = new ENode();
		node1->ivex = p2;
		// 将node1链接到p1所在链表的末尾
		if (mVexs[p1].firstEdge == nullptr)
			mVexs[p1].firstEdge = node1;
		else
			linkLast(mVexs[p1].firstEdge, node1);
	}
}

/*
	析构函数
*/
ListDG::~ListDG() {
	
}
/*
	将node节点连接到List最后
*/ 
void ListDG::linkLast(ENode* list, ENode* node) {
	ENode* p = list;

	while (p->nextEdge)
		p = p->nextEdge;
	p->nextEdge = node;
}

/*
	返回ch的位置
*/
int ListDG::getPosition(char ch) {
	int i;
	for (i = 0; i < mVexNum; ++i) 
		if (mVexs[i].data == ch)
			return i;
		return -1; // 失败返回-1
	
}

/*
	读取一个输入字符
*/
char ListDG::readChar() {
	char ch;
	do {
		cin >> ch;
	} while (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')));
	return ch;
}

/*
	打印邻接表图
*/
void ListDG::print() {
	int i, j;
	ENode* node;
	cout << "List Graph: " << endl;
	for (i = 0; i < mVexNum; ++i) {
		cout << i << "(" << mVexs[i].data << "):";
		node = mVexs[i].firstEdge;
		while (node != nullptr) {
			cout << node->ivex << "(" << mVexs[node->ivex].data << ") ";
			node = node->nextEdge;
		}
		cout << endl;
	}
}
int main() {
	char vexs[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
	char edges[][2] = {
		{'A', 'B'},
		{'B', 'C'},
		{'B', 'E'},
		{'B', 'F'},
		{'C', 'E'},
		{'D', 'C'},
		{'E', 'B'},
		{'E', 'D'},
		{'F', 'G'} 
	};
	int vlen = sizeof(vexs) / sizeof(vexs[0]);
	int elen = sizeof(edges) / sizeof(edges[0]);
	ListDG* pG;

	// 自定义图(输入矩阵队列)
	// pG = new ListDG();
	// 采用已经有的图
	pG = new ListDG(vexs, vlen, edges, elen);

	pG->print();
	return 0;
	
}

用已经有的数据运行代码后的结果(有向邻接表图)为:

用自己手动输入数据的结果(有向邻接表图)

邻接表的实现(无向邻接表图 :

/*
	C++: 邻接表表示的"无向图(List Undirected Graph)"
	@author lph
	
*/
#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;

constexpr auto MAX = 100;
// 邻接表;
class ListUDG {
private:
	// 内部类
	// 邻接表中表对应的链表的顶点
	class ENode {
	public:
		int ivex;		// 该边所指向的顶点的位置
		ENode* nextEdge;// 指向下一条弧的指针
	};
	// 邻接表中表对应的顶点
	class VNode {
	public:
		char data;		// 顶点信息
		ENode* firstEdge;// 指向第一条依赖该顶点的弧
	};
private:
	int mVexNum;		// 图的顶点个数
	int mEdgNum;		// 图的边的数目
	VNode mVexs[MAX];  // 一维数组存储图的顶点,也就是邻接表第一列的顶点
public:
	// 创建邻接表对应的图(自己输入)
	ListUDG();
	// 创建邻接表对应的图(用已经提供的数据)
	ListUDG(char vexs[], int vlen, char edges[][2], int elen);
	// 析构函数
	~ListUDG();
	// 打印邻接表图
	void print();
private:
	// 读取一个输入字符
	char readChar();
	// 返回ch的位置
	int getPosition(char ch);
	// 将node节点链接到list的最后
	void linkLast(ENode* list, ENode* node);
};
/*
	创建邻接表对应的图(自己输入)
*/
ListUDG::ListUDG() {
	char c1, c2;
	// int v, e;
	int i, p1, p2;
	ENode* node1, * node2;
	// 输入顶点数和边数
	cout << "input vertex number: ";
	cin >> mVexNum;
	cout << "input edge number: ";
	cin >> mEdgNum;
	if (mVexNum < 1 || mEdgNum < 1 || (mEdgNum > (mVexNum * (mVexNum - 1)))) {
	cout << "input error: invalid parameters!" << endl;
	return;
}
	// 初始化邻接表的顶点
	for (i = 0; i < mVexNum; ++i) {
		cout << "vertex(" << i << "):";
		mVexs[i].data = readChar(); // 如果mVexs在之前用的是VNode* mVexs[MAX],那么这里需要用指针mVexs[i]->data
		mVexs[i].firstEdge = nullptr;
	}
	// 初始化邻接表的边
	for (i = 0; i < mEdgNum; ++i) {
		// 读取边的起点和结束顶点
		cout << "edge(" << i << "):";
		c1 = readChar();
		c2 = readChar();

		p1 = getPosition(c1);
		p2 = getPosition(c2);
		// 初始化node1
		node1 = new ENode();
		node1->ivex = p2;
		// 将node1链接到p1所在链表的末尾
		if (mVexs[p1].firstEdge == nullptr)
			mVexs[p1].firstEdge = node1;
		else
			linkLast(mVexs[p1].firstEdge, node1);
		// 初始化node2
		node2 = new ENode();
		node2->ivex = p1;
		// 将node2链接到p2所在链表的末尾
		if (mVexs[p2].firstEdge == nullptr)
			mVexs[p2].firstEdge = node2;
		else
			linkLast(mVexs[p2].firstEdge, node2);
	}
}
/*
	创建邻接表对应的图(用已提供的数据)
*/
ListUDG::ListUDG(char vexs[], int vlen, char edges[][2], int elen) {
	char c1, c2;
	int i, p1, p2;
	ENode* node1, * node2;
	// 初始化顶点数和边数
	mVexNum = vlen;
	mEdgNum = elen;
	// 初始化邻接表的顶点
	for (i = 0; i < mVexNum; ++i) {
		mVexs[i].data = vexs[i];
		mVexs[i].firstEdge = nullptr;
	}
	// 初始化邻接表的边
	for (i = 0; i < mEdgNum; ++i) {
		// 读取边的起始顶点和结束顶点
		c1 = edges[i][0];
		c2 = edges[i][1];

		p1 = getPosition(c1);
		p2 = getPosition(c2);
		// 初始化node1
		node1 = new ENode();
		node1->ivex = p2;
		// 将node1链接到p1所在的末尾
		if (mVexs[p1].firstEdge == nullptr)
			mVexs[p1].firstEdge = node1;
		else
			linkLast(mVexs[p1].firstEdge, node1);
		// 初始化node2
		node2 = new ENode();
		node2->ivex = p1;
		// 将node2链接到p2所在链表的末尾
		if (mVexs[p2].firstEdge == nullptr)
			mVexs[p2].firstEdge = node2;
		else
			linkLast(mVexs[p2].firstEdge, node2);
	}
}
/*
	析构函数
*/
ListUDG::~ListUDG() {

}
/*
	将node节点连接到list最后
*/
void ListUDG::linkLast(ENode* list, ENode* node) {
	ENode* p = list;
	while (p->nextEdge)
		p = p->nextEdge;
	p->nextEdge = node;
}
/*
	返回ch的位置
*/
int ListUDG::getPosition(char ch) {
	int i;
	for (i = 0; i < mVexNum; ++i) 
		if (mVexs[i].data == ch)
			return i;
	return -1;
}
/*
	读取一个输入字符
*/
char ListUDG::readChar() {
	char ch;
	do {
		cin >> ch;
	} while  (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')));
	return ch;
}

/*
	打印邻接表图
*/
void ListUDG::print() {
	int i, j;
	ENode* node;
	cout << "List Graph:" << endl;
	for (i = 0; i < mVexNum; ++i) {
		cout << i << "(" << mVexs[i].data << ")";
		node = mVexs[i].firstEdge;
		while (node != nullptr) {
			cout << node->ivex << "(" << mVexs[node->ivex].data << ")";
			node = node->nextEdge;
		}
		cout << endl;
	}
}
int main(){
	char vexs[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
	char edges[][2] = {
		{'A', 'C'},
		{'A', 'D'},
		{'A', 'F'},
		{'B', 'C'},
		{'C', 'D'},
		{'E', 'G'},
		{'F', 'G'} };
	int vlen = sizeof(vexs) / sizeof(vexs[0]);
	int elen = sizeof(edges) / sizeof(edges[0]);
	ListUDG* pG;

	// 自定义"图"(输入矩阵队列)
	//pG = new ListUDG();
	// 采用已有的"图"
	pG = new ListUDG(vexs, vlen, edges, elen);

	pG->print();   // 打印图
	
	return 0;
}

打印已提供邻接表(无向邻接表图)数据的结果: 

 打印自己输入的邻接表(无向邻接表图)的数据:

邻接矩阵的实现请看这里

### C++实现邻接链表数据结构 以下是基于引用内容以及专业知识构建的一个完整的邻接链表实现方案。该示例展示了如何使用 C++ 创建图的邻接链表表示法,支持添加顶点、边的操作,并能够打印整个邻接列表。 #### 1. 定义节点和图的基本结构 为了实现邻接链表,我们需要定义两个主要类:`Node` 和 `Graph`。 - `Node` 表示图中的每一个顶点及其相邻顶点的信息。 - `Graph` 是管理这些顶点的核心容器。 ```cpp #include <iostream> #include <list> using namespace std; class Graph { int V; // 图中顶点的数量 list<int> *adj; // 指向邻接链表数组的指针 public: // 构造函数 Graph(int V) { this->V = V; adj = new list<int>[V]; } // 添加一条无向边到图中 void addEdge(int v, int w) { adj[v].push_back(w); // 将 w 加入到 v 的邻接链表中 adj[w].push_back(v); // 如果是无向图,则也需要将 v 加入到 w 的邻接链表中 } // 打印邻接链表的内容 void printGraph() { for (int i = 0; i < V; ++i) { cout << "Vertex " << i << ": "; for (auto it : adj[i]) { cout << "-> " << it; } cout << endl; } } }; ``` 上述代码实现了基本的图结构初始化方法[^3],并提供了用于添加边的功能。通过动态分配内存给邻接链表数组,我们可以在运行时灵活调整图的大小。 #### 2. 使用示例 下面是一个简单的测试程序,展示如何创建一个具有四个顶点的小型图,并为其添加几条边: ```cpp int main() { int V = 4; // 假设图有 4 个顶点 Graph g(V); // 向图中添加一些边 g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 2); g.addEdge(2, 3); // 输出最终形成的邻接链表 g.printGraph(); return 0; } ``` 执行此代码后,输出将是这样的形式: ``` Vertex 0: -> 1-> 2 Vertex 1: -> 0-> 2 Vertex 2: -> 0-> 1-> 3 Vertex 3: -> 2 ``` 这表明每个顶点与其邻居之间的连接关系已经成功建立。 #### 关键概念解析 - **邻接链表**:这是一种高效存储稀疏图的方式,在空间复杂度上优于矩阵表示法。 - **动态内存分配**:由于实际应用中的图规模可能变化很大,因此采用动态分配技术是非常必要的。 - **STL 列表**:标准模板库 (`std::list`) 提供了一个双向链表实现,非常适合用来模拟邻接链表的行为特性。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值