数据结构-图(邻接多重表)(c++实现)

#include<iostream>
using namespace std;

//图的邻接多重表表示法

//邻接多重表在两个顶点有同一个边的情况下只会存储一个边结点	析构时每个节点只能释放一次

/*析构原本采用递归实现 但是递归释放完内存后存在指针填充现象
* 
	即	 delete ptr; ptr = nullptr;	后在他的上一结点处记录并未置空

	此时指针内存已经被释放并且置空 但上一结点记录ptr的值为0xdddddddd ptr != nullptr 判断不生效 出现非法访问内存引起异常

	二叉树的非递归析构也存在此现象 二叉树的非递归析构释放后的指针被重复访问可以采用递归方式规避
	
	此处采用的方法是把所有边结点统一存入数组中统一释放
*/

class node					//边结点
{
public:
	node(int _x,int _y);
	
	node();
	
	~node();

	node* one, *two;
	
	int x, y;
};

node::node(int _x,int _y) { one = nullptr; two = nullptr; x = _x; y = _y; }

node::node() { one = nullptr; two = nullptr; x = 0; y = 0; }

node::~node() { one = nullptr; two = nullptr; x = 0; y = 0; }



template<class T>	//顶点结点
class table
{
public:
	table();
	table(T _data);
	~table();

	T data;

	node* next;
};

template<class T>
table<T>::table() { data = NULL; next = nullptr;}

template<class T>
table<T>::table(T _data) { data = _data; next = nullptr; }

template<class T>
table<T>::~table() { data = NULL; next = nullptr; }


template<class T>
class map
{
public:
	map(T _data[],int _len);
	~map();

	void print();

private:

	void remove(table<T>* M);		//清空边结点

	table<T>* M;					//顶点表

	int len;						//表长

	int side;						//边的数量

};


template<class T>						//邻接多重表构建图
map<T>::map(T _data[], int _len)
{
	//开辟空间
	
	len = _len - 1;

	M = new table<T>[len];

	for (int i = 0; i < len; i++) { M[i].data = _data[i]; }

	//添加边
	int num;
	cout << "输入边的数量:";
	cin >> num;

	side = num;

	if (num < 0 || num > len * (len - 1) / 2) { cout << "数量有误!" << endl; return; }

	for (int _i = 0; _i < num; _i++)
	{
		int i, j;
		cout << "输入边在数组中的起始位置:";
		cin >> i;
		cout << "输入边在数组中的结束位置:";
		cin >> j;

		//判断下标是否在数组中存在 -- 此处省略

		node* p = new node(i, j);

		if (M[i].next == nullptr) { M[i].next = p; }
		else
		{
			node* temp = M[i].next;
			bool flag = true;
			while (flag)
			{
				if (temp->x == i)							//边的起始位置为当前结点
				{
					if (temp->one != nullptr) { temp = temp->one; }
					else { flag = false; }
				}
				else										//边的结束位置为当前结点
				{
					if (temp->two != nullptr) { temp = temp->two; }
					else { flag = false; }
				}
			}
			if (temp->x == i) { temp->one = p; }			//建立的边以此结点为起始位置
			else { temp->two = p; }							//建立的边以此结点为结束位置
										
		}

		if (M[j].next == nullptr) { M[j].next = p; }
		else
		{
			node* temp = M[j].next;
			bool flag = true;
			while (flag)
			{
				if (temp->y == j)							//边的起始位置为当前结点
				{
					if (temp->two != nullptr) { temp = temp->two; }
					else { flag = false; }
				}
				else										//边的结束位置为当前结点
				{
					if (temp->one != nullptr) { temp = temp->one; }
					else { flag = false; }
				}
			}
			if (temp->x == i) { temp->one = p; }			//建立的边以此结点为起始位置
			else { temp->two = p; }							//建立的边以此节点为结束位置
		}
	}
}

template<class T>
void map<T>::print()
{
	for (int i = 0; i < len; i++)
	{
		cout << M[i].data << "\t\t";
		
		if (M[i].next != nullptr) 
		{ 
			node* temp = M[i].next; 
			bool flag = true;

			while (flag)
			{
				if (temp->x == i)
				{
					cout << temp->x << " " << temp->y << " - ";
					if (temp->one != nullptr) { temp = temp->one; }
					else { flag = false; }
				}
				else 
				{
					cout << temp->x << " " << temp->y << " - ";
					if (temp->two != nullptr) { temp = temp->two; }
					else { flag = false; }
				}
			}
			cout << endl;
		}
	}
}

template<class T>			//析构
map<T>::~map() 
{
	remove(M);
	delete[]M;
	M = nullptr;
	len = 0;
	side = 0;
}

template<class T>										//清空边结点
void map<T>::remove(table<T>* M)
{
	node** arr = new node*[side];								//存储边结点指针的数组

	int num = 0;

	for (int i = 0; i < len; i++)		
	{
		if (M[i].next != nullptr)
		{
			node* temp = M[i].next;
			bool flag = true;
			while (flag)
			{
				if (temp->x == i)							//x == i 时 该边结点为访问元素所发出的边
				{											//因为边结点是共用的 只记录结点发出的或者接收的即可 -- 此处记录发出的
															//遍历数组中是否存在该边	--	存在则不置入数组 反之置入
					bool ret = true;
					for (int j = 0; j < num; j++) 
					{ 
						if (arr[j]->x == temp->x && arr[j]->y == temp->y) { ret = false; }//该边在数组中存在标志置为false
					}

					if (ret == true) { arr[num] = temp; num++; }				//边不存在 存入数组中数组元素个数+1

					if (temp->one != nullptr) { temp = temp->one; }				//表结点有后继边 继续深入
					else { flag = false; }										//此边结点无后继边 循环结束
				}
				else 
				{
					if (temp->two != nullptr) { temp = temp->two; }				//不为此结点发出的边 进入下一边结点
					else { flag = false; }										//此边结点无后后继边 循环结束
				}
			}
		}
	}

	//测试代码 查看数组中的结点是否为为需要释放的边结点

	/*for (int i = 0; i < num; i++) { cout << arr[i]->x << " " << arr[i]->y << "  "; }	
	cout << endl;
	*/

	delete[]arr;
	arr = nullptr;
}

对任意给定的(顶点数不小于20,边数不少于30,的类型可以是有向、无向、有向网、无向网),能够输入的顶点和边(或弧)的信息,并存储到相应存储结构(邻接矩阵、邻接表、十字链表、邻接多重表,任选其中两种类型),对自己所创建的完成以下操作: 对无向求每个顶点的度,或对有向求每个顶点的入度和出度(5分) 完成插入顶点和边(或弧)的功能(5分) 完成删除顶点和边(或弧)的功能(5分) 两种存储结构的转换(5分),如果其中一种存储结构为十字链表或邻接多重表则增加5分。 输出的深度优先遍历序列或广度优先遍历序列(5分) 求的深度优先或广度优先的生成树(或生成森林)(存储结构为孩子-兄弟链表),并对生成树进行遍历(15分) 判断的连通性,输出连通分量的个数(5分) 判断中是否存在环,无向5分,有向10分 给出顶点u和v,判断u到v是否存在路径(5分) 10、求顶点u到v的一条简单路径(10分) 11、求顶点u到v的所有简单路径(15分) 12、求顶点u到v的最短路径(10分) 13、求顶点u到其余各顶点的最短路径(15分) 14、求任两个顶点之间的最短路径(15分) 15、求最小生成树(15分) 16、对于有一个源点和一个汇点的有向网,求关键路径(20分) 编程环境可以是C、VC++、JAVA,每位同学从上述题目中选择100分的题目,注意,必须选择第1-6题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心若雪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值