數據結構回顧(七) 圖(有向圖/無向圖)的基本操作(C/C++實現)

是大二上學期的數據結構與算法實驗題,代碼架構是老師給出的,具體實現是自己做的。當時用的教材是嚴蔚敏教授的《數據結構》。後面兩個代碼(stack.cpp,CirQueue.cpp)是老師給的,爲了調試方便也給貼一下。
主要實現瞭如下操作:

	cout<<"    1---------無向圖的創建                   2----------有向圖的創建"<<endl;
	cout<<"    3---------無向圖的廣度與深度遍歷         4----------有向圖的廣度與深度遍歷"<<endl;
	cout<<"    5---------無向圖判斷是否爲樹             8----------有向圖判斷路徑是否連通  "<<endl;
	cout<<"    7---------無向圖判斷路徑是否連通        "<<endl;
#include <string>
#include <iostream>
#include "stack.cpp"
#include "CirQueue.cpp"
#define MAX 20
using namespace std;
struct ArcNode//邊數據
{
	int adjvex;//該弧指向結點的位置  用於有向圖
	int weight;//權值
	struct ArcNode *nextarc;//指向下一條邊
};
template <class T>
struct VNode{//節點數據
	T data;//節點值
	ArcNode *firstarc;//指向邊
};
template <class T>
class ALGraph{
private:
	VNode<T> vertices[MAX];//節點數組
	int vexNum, arcNum;//存儲節點數與邊數
	int visited[MAX];//訪問標誌位
	int  graph_type ;   //圖的標誌  (  0爲無向圖   1爲有向圖   )
	int vex_degree[MAX];//記錄每個節點的度+1  爲判斷路徑作準備  
public:
	ALGraph()    //構造函數 
	{

	}
	~ALGraph(){}                     //析構函數,釋放鄰接表中各邊表結點的存儲空間
	void createDG();  //無向圖的創建操作算法 
	
	int locateVex(T v);
	int firstAdjVex(int v);//找某頂點的第1個鄰接頂點
	int nextAdjVex(int v, int w);//頂點相對於一鄰接頂點的下一鄰接頂點操作
	void DFS(int v);//從下標爲v的頂點出發對圖進行深度優先遍歷
	void DFSTraverse();  //深度優先遍歷圖
	void BFS(int v,CirQueue<int> & que);//從下標爲v的頂點出發對圖進行廣度優先遍歷
	void BFSTraverse();//廣度優先遍歷圖的算法
	void createHDG();  //有向圖的創建操作算法
	bool IsTree();//判斷一個無向圖G是否爲一棵樹
	bool DFStree(int& v,int &);//輔助判斷
	bool hasPath(T v, T w);// 判斷有向圖是否存在由頂點vi到vj的路徑
	void hPath(int v, int w,int &);// 判斷有向圖是否存在由頂點vi到vj的路徑

};


template <class T>
void  ALGraph<T>::createHDG()  //有向圖的創建操作算法
{
	//...


	this->graph_type=0;//有向圖
	T v1,v2;//元素數據
	int i,j;//計數器
	ArcNode *p;//邊數據
	
	int flag=1;//必要時的標示位
	cout<<"請輸入有向圖的節點數與邊數:"<<endl;
	cin>>this->vexNum>>this->arcNum;
	for(i=0;i<this->vexNum ;i++)//節點輸入
	{
		cout<<"請輸入第 "<<i+1<<" 個節點數據:"<<endl;
		cin>>this->vertices[i].data;	
		this->vertices[i].firstarc=NULL;
	}
	for(i=0;i<this->arcNum ;i++)
	{

		if(flag==0)
		{
			cout<<"請確認輸入邊的起始點正確!"<<endl;
			system("pause");
			cout<<"請重輸!"<<endl;
			i--;
		
		}

		cout<<"請輸入第 "<<i+1<<" 條邊:"<<endl;
		cout<<"請輸入起點";
		cin>>v1;
		cout<<"請輸入終點";
		cin>>v2;
		p=new ArcNode ;
		p->adjvex=this->locateVex(v2);//存儲下標位置
		flag=0;
		for(j=0;j<this->vexNum;j++)//搜索
		{
			if(v1==this->vertices[j].data)//掛鏈     注意判斷條件不要錯了   是v1啊啊啊啊
			{
				flag=1;
				p->nextarc=this->vertices[j].firstarc;
				this->vertices[j].firstarc=p;
				break;		
			}		
		}
		if(flag==0 && i>0)
		{
			cout<<"請確認輸入邊的起始點正確!"<<endl;
			system("pause");
			cout<<"請重輸!"<<endl;
			i--;
		
		}
	}
	cout<<"輸入完畢!"<<endl;

}


template <class T>
bool  ALGraph<T>::IsTree()//判斷一個無向圖G是否爲一棵樹     
{	
	//基於迴路指向的判斷
	//原理   判斷任意一個節點是否有到其他所有節點的連通路徑   若不滿足則判斷這不是一棵樹  
	//若滿足則進一步利用類握手定理(假設 節點數:m   邊數:n    在簡單無向圖中有:m=n   簡單有向圖:m=n   這是樹與圖的臨界點 )判斷節點與邊的關係而得到其是否爲一棵樹   
	//可利用該原理進行進一步的判斷若其爲圖含有有幾個子圖  


	//連通情況下 在無向圖種若滿足 2m>n則爲樹  有向圖中若滿足m>n 則爲樹

	//int  m=this->vertices[]
	int begin=0;//判斷該節點是否與其他所有節點都連通(此處選擇第一個節點進行判斷)
	int i;
	for(i=0;i<this->vexNum;i++)
	{
		if(!this->hasPath(this->vertices[begin].data,this->vertices[i].data))//若有一個不連通則可判斷這不是一棵樹
		{
			return false;	
		}
		
	}
	cout<<"都連通!"<<endl;
	if(this->vexNum>this->arcNum  )
	{
		return true;		
	}
	else
	{
		return false;

	}

	
	
}


template <class T>
void  ALGraph<T>::hPath(int v, int w,int &flag)// 判斷有向圖是否存在由頂點vi到vj的路徑  輔助函數
{
	if(v==w)
	{
		flag=1;
		return ;
	}	
	else
	{
		visited[v]=1;	
		ArcNode *p=vertices[v].firstarc;
		if(p)
		{ 
			while(p)
			{		
				
				if(!visited[p->adjvex]  & !flag)
				{
					hPath(p->adjvex , w,flag);
				}
				p=p->nextarc;
			}
		}
	}

}
template <class T>
bool  ALGraph<T>::hasPath(T a, T b)// 判斷有向圖是否存在由頂點vi到vj的路徑
{
	//...
	int v,w;
	int flag=0;
	v=locateVex(a);
	w=locateVex(b);
	if(v<0||w<0 )
	{
		cout<<"沒有該路徑!"<<endl;
		return false;
	
	}
	for(int i=0;i<this->vexNum ;i++)

	{
		this->visited[i]=0;	
	}
	hPath(v ,  w,flag);
	if(flag)
	{
		return true;
	
	}
	else
	{
		return false;
	}

}

template <class T>
void ALGraph<T>::createDG()  //無向圖的創建操作算法 
{
	this->graph_type=0;//無向圖
	T v1, v2;
	int i, j, k;
	ArcNode *p;
	cout << "輸入結點的總數:";
	cin >> vexNum;
	cout << "輸入邊的總數:";
	cin >> arcNum;
	for (i = 0; i<vexNum; ++i)
	{
		cout << "輸入第" << i + 1 << "個結點的數據:";
		cin >> vertices[i].data;
		vertices[i].firstarc = NULL;
	}
	for (k = 0; k<arcNum; ++k)
	{
		cout << "輸入第" << k + 1 << "邊:";
		cout << "輸入起始結點:";
		cin >> v1;
		cout << "           輸入終止結點:";
		cin >> v2;
		if ((i = locateVex(v1)) == -1){ cout << "Error!"; return; }
		if ((j = locateVex(v2)) == -1){ cout << "Error!"; return; }
		p = (ArcNode *)malloc(sizeof(ArcNode));
		p->adjvex = j;
		p->nextarc = vertices[i].firstarc;
		vertices[i].firstarc = p;

		p = (ArcNode *)malloc(sizeof(ArcNode));
		p->adjvex = i;
		p->nextarc = vertices[j].firstarc;
		vertices[j].firstarc = p;
	}
	
}
template <class T>
int ALGraph< T>::locateVex(T v)
{
	int i;
	for (i = 0; i < vexNum; i++)
	{
		if (vertices[i].data == v)
			return i;
	}

	return -1;
}

template <class T>
int ALGraph< T>::firstAdjVex(int v)//找某頂點的第1個鄰接頂點
{
	ArcNode *p;
	p = vertices[v].firstarc;
	if (p)
		return p->adjvex;
	else
		return -1;
}
template <class T>
int ALGraph< T>::nextAdjVex(int v, int w)//頂點相對於一鄰接頂點的下一鄰接頂點操作
{
	ArcNode *p;
	p = vertices[v].firstarc;
	while (p&&p->adjvex != w)
		p = p->nextarc;
	if (p&&p->nextarc)
		return p->nextarc->adjvex;
	else  return -1;
}
template <class T>
void ALGraph< T>::DFS(int v)//從下標爲v的頂點出發對圖進行深度優先遍歷
{

	
	cout << vertices[v].data;
	cout << "  ";
	visited[v] = 1;	
	ArcNode *p=this->vertices[v].firstarc;
	if(!p)
	return;
	while(p)
	{
		if (!visited[v])
		{
			DFS(p->adjvex );
			
		}
		p=p->nextarc ;


	}
}
template <class T>
void ALGraph< T>::DFSTraverse()  //深度優先遍歷圖
{
	int i;
	for (i = 0; i<vexNum; i++)
		visited[i] = 0;
	for (i = 0; i < vexNum; i++)
	{
		
		if (!visited[i])
			DFS(i);

	}

}
template <class T>
void ALGraph< T>::BFS(int v,CirQueue<int>&  que)//從下標爲v的頂點出發對圖進行廣度優先遍歷
{
	int u, w;
	if(!visited[v])
	{
		cout << vertices[v].data;
		cout << "  ";
		visited[v] = 1;
	}
	ArcNode *p=this->vertices[v].firstarc;
	if(!p)
		return;	
	while (p)
	{		
		w=p->adjvex ;		
		if(!visited[w])
		{
			cout << vertices[w].data;
			cout << "  ";
			visited[w] = 1;
		}
		que.EnQueue(w);
		p=p->nextarc ;		
	}
	
	w=que.DeQueue ();
	if(!visited[w])
	{
		BFS(w, que);
	}
}
template <class T>
void ALGraph< T>::BFSTraverse()//廣度優先遍歷圖的算法
{
	int v;
	CirQueue<int>  que;
	for (v = 0; v<vexNum; v++)
		visited[v] = 0;
	for (v = 0; v<vexNum; v++)
	if (!visited[v])
		BFS(v,que);
}
void manu_choice(ALGraph<char> &hgraph,ALGraph<char> &graph)
{

	cout<<"                   歡迎來到圖的操作"<<endl;
	cout<<"    1---------無向圖的創建                   2----------有向圖的創建"<<endl;
	cout<<"    3---------無向圖的廣度與深度遍歷         4----------有向圖的廣度與深度遍歷"<<endl;
	cout<<"    5---------無向圖判斷是否爲樹             8----------有向圖判斷路徑是否連通  "<<endl;
	cout<<"    7---------無向圖判斷路徑是否連通         9---------清屏"<<endl;
	cout<<"    10---------退出                            "<<endl;

	int choice;
	char a,b;
	while(1)
	{
		fflush(stdin);
		cout<<"    請輸入菜單:";
		cin>>choice;
		if(!cin)//異常輸入處理
		{
			cin.clear();
			while(cin.get()!='\n')
				continue;

		}
		switch(choice)
		{
		case 1:
			cout<<"無向圖的創建"<<endl;
			graph.createDG();
			break;
		case 2:
			cout<<"有向圖的創建"<<endl;
			hgraph.createHDG ();
			break;
		case 3:
			cout << "深度優先遍歷圖的結果爲:";
			graph.DFSTraverse();  //遞歸深度優先遍歷
			cout << "\n廣度優先遍歷圖的結果爲:" ;
			graph.BFSTraverse();//廣度優先遍歷圖的算法
			cout << endl;
			break;
		case 4:
			cout << "深度優先遍歷圖的結果爲:";
			hgraph.DFSTraverse();  //遞歸深度優先遍歷
			cout << "\n廣度優先遍歷圖的結果爲:" ;
			hgraph.BFSTraverse();//廣度優先遍歷圖的算法
			cout << endl;
			break;
		case 5:
			if(graph.IsTree ())
			{
				cout<<"這是一顆樹"<<endl;

			}
			else
			{
				cout<<"這不是一棵樹"<<endl;

			}
			break;
	
		case 7:
			cout << "輸入兩個元素判斷其是否連通:"<< endl ;

			cin>>a>>b;
			if(graph.hasPath (a,b))
			{
				cout<<a<<" "<<b<<" 間有路徑!"<<endl;

			}
			else
			{
				cout<<a<<" "<<b<<" 間沒有路徑!"<<endl;
			}
			break;
		case 8:
			cout << "輸入兩個元素判斷其是否連通:"<< endl ;
		
			cin>>a>>b;
			if(hgraph.hasPath (a,b))
			{
				cout<<a<<" "<<b<<" 間有路徑!"<<endl;

			}
			else
			{
				cout<<a<<" "<<b<<" 間沒有路徑!"<<endl;
			}
			break;
		case 9:
			system("cls");
			manu_choice(hgraph,graph);
			break;
		case 10:
			exit(0);
		default:cout<<"錯誤輸入!"<<endl;



		}
		//system("pause");
	}
}
void main()
{
	ALGraph<char> graph;	
	ALGraph<char> hgraph;
	manu_choice(hgraph ,graph);
}

CirQueue.cpp

//CirQueue.h
const int QueueSize = 100;  //定義存儲隊列元素的數組的最大長度

template <class T>        //定義模板類CirQueue
class CirQueue
{
public:
	CirQueue();        //構造函數,置空隊
	~CirQueue();               //析構函數
	void EnQueue(T x);           //將元素x入隊
	T DeQueue();                //將隊頭元素出隊
	T GetQueue();               //取隊頭元素(並不刪除)
	bool Empty();               //判斷隊列是否爲空
private:
	T data[QueueSize];           //存放隊列元素的數組
	int front, rear;    //隊頭和隊尾指針,分別指向隊頭元素的前一個位置和隊尾元素的位置
};

template <class T>
CirQueue<T>::CirQueue()
{
	front = rear = 0;
}

/*
* 前置條件:隊列已存在
* 輸    入:無
* 功    能:銷燬隊列
* 輸    出:無
* 後置條件:釋放隊列所佔用的存儲空間
*/

template <class T>
CirQueue<T>::~CirQueue()
{

}

/*
* 前置條件:隊列已存在
* 輸    入:元素值x
* 功    能:在隊尾插入一個元素
* 輸    出:如果插入不成功,拋出異常
* 後置條件:如果插入成功,隊尾增加了一個元素
*/

template <class T>
void CirQueue<T>::EnQueue(T x)
{
	if ((rear + 1) % QueueSize == front) throw "上溢";
	rear = (rear + 1) % QueueSize;   //隊尾指針在循環意義下加1
	data[rear] = x;                //在隊尾處插入元素
}

/*
* 前置條件:隊列已存在
* 輸    入:無
* 功    能:刪除隊頭元素
* 輸    出:如果刪除成功,返回被刪元素值,否則,拋出刪除異常
* 後置條件:如果刪除成功,隊頭減少了一個元素
*/

template <class T>
T CirQueue<T>::DeQueue()
{
	if (rear == front) throw "下溢";
	front = (front + 1) % QueueSize;    //隊頭指針在循環意義下加1
	return data[front];             //讀取並返回出隊前的隊頭元素,注意隊頭指針
}
//指向隊頭元素的前一個位置
/*
* 前置條件:隊列已存在
* 輸    入:無
* 功    能:讀取隊頭元素
* 輸    出:若隊列不空,返回隊頭元素
* 後置條件:隊列不變
*/

template <class T>
T CirQueue<T>::GetQueue()
{
	int i;
	if (rear == front) throw "下溢";
	i = (front + 1) % QueueSize;  //注意不要給隊頭指針賦值
	return data[i];
}

/*
* 前置條件:隊列已存在
* 輸    入:無
* 功    能:判斷隊列是否爲空
* 輸    出:如果隊列爲空,返回1,否則,返回0
* 後置條件:隊列不變
*/

template <class T>
bool CirQueue<T>::Empty()
{
	if (front == rear)
		return 1;
	else
		return 0;
}

stack.cpp

template<class T>
class Stack
{
private:
	T *base;
	int top;
	int size;
public:
	//棧初始化
	bool InitStack(int i)
	{
		size = i;
		base = new T[size];
		if (!base)
			return false;
		top = 0;
		return true;
	}
	//判斷是否爲空
	bool IsEmpty()
	{
		return(top == 0);
	}
	//判斷是否滿
	bool IsFull()
	{
		return(top == size);
	}
	//雙倍擴容函數
	bool DoubleSpace()
	{
		T *oldBase = base;
		base = new T[size *= 2];
		if (!base)
			return false;
		for (int i = 0; i<top; i++)
		{
			base[i] = oldBase[i];
		}
		delete[]oldBase;
		return true;
	}
	//入棧
	bool Push(T e)
	{
		if (IsFull())
		{
			if (!DoubleSpace())
				return false;
		}
		base[top++] = e;
		return true;
	}
	//出棧
	bool Pop(T &e)
	{
		if (IsEmpty())
			return false;
		e = base[--top];
		return true;
	}

	//取棧頂元素
	T getTop()
	{
		return base[top-1];
	}
	int stack_search(T data)
	{
		for(int i=0;i<top;i++)
		{
			if(data==this->base[i])
			{
				return i;
			
			}
		}
		return o;
	
	}
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章