实验报告
课程:程序设计与数据结构
班级: 1623
姓名: 齐力锋
学号:2016232 成绩: 2分 指导教师:娄嘉鹏 王志强
实验日期:11月20日
密级:非密级
预习程度: 已预习
必修/选修: 必修
实验序号: 2326
实验一 :
- 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器
- 给出伪代码,产品代码,测试代码(不少于5条测试)
- 上方提交代码链接
附件提交测试截图
实验二 :
- 用十字链表实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器
- 给出伪代码,产品代码,测试代码(不少于5条测试)
- 上方提交代码链接
- 附件提交测试截图
实验三 :
- 实现PP19.9
- 给出伪代码,产品代码,测试代码(不少于5条测试)
- 上方提交代码链接
- 附件提交测试截图
实验过程及问题解决:
一、
首先,我思考了什么是邻接矩阵,该用怎么样的方法去实现邻接矩阵创建图。
我认为可以用一个一维数组存放图中所有顶点数据。用一个二维数组存放顶点间关系(边或着弧)的数据,这个二维数组称为邻接矩阵
基于此,我开始了实验一
//-------------------------------------------------- // 伪代码: // 初始化"顶点数"和"边数" // 采用已有的"图" // 初始化"顶点" // 初始化"边" //替换为二维数组 //利用 printf 格式化打印 //------------------------------------------------------- // Writen by QLF public class Matrix { private char[] DingDian; // 顶点集合 private int[][] LinjieMatrix; // 邻接矩阵 int numDingDian; //顶点数 int numedgs;//边数 public Matrix(char[] DingDian, char[][] edges) { // 初始化"顶点数"和"边数" this.numDingDian = DingDian.length; this.numedgs = edges.length; // 初始化"顶点" this.DingDian = new char[numDingDian]; for (int i = 0; i < this.DingDian.length; i++) this.DingDian[i] = DingDian[i]; // 初始化"边" LinjieMatrix = new int[numDingDian][numDingDian]; for (int i = 0; i < numedgs; i++) { // 读取边的起始顶点和结束顶点 int p1 = getPosition(edges[i][0]); int p2 = getPosition(edges[i][1]); LinjieMatrix[p1][p2] = 1; LinjieMatrix[p2][p1] = 1; } /* * * * * DingDian -- 顶点数组 * edges -- 边数组 */ } private int getPosition(char ch) { for(int i = 0; i< DingDian.length; i++) if(DingDian[i]==ch) return i; return -1; } public void print() { System.out.printf("Martix Graph:\n"); for (int i = 0; i < DingDian.length; i++) { for (int j = 0; j < DingDian.length; j++) System.out.printf("%d ", LinjieMatrix[i][j]); System.out.printf("\n"); } } public void addEdge(int start,int end,int len){ //在两个指定下标的节点之间添加一条边 LinjieMatrix[start][end] = len; LinjieMatrix[end][start] = len; numedgs++; } //在2个顶点之间删除一条边 public void removeEdge(int start,int end){ //删除两个指定下标顶点之间的边 LinjieMatrix[start][end] = 0; LinjieMatrix[end][start] = 0; numedgs--; } public static void main(String[] args) { char[] vexs = {'齐', '力', '锋', '啊', 'E', 'F', 'G'}; char[][] edges = new char[][]{ {'齐', '锋'}, {'齐', '啊'}, {'齐', 'F'}, {'力', '锋'}, {'锋', '啊'}, {'E', 'G'}, {'F', 'G'}}; Matrix pG; //-------------------------------------------------- // 伪代码: // 初始化"顶点数"和"边数" // 采用已有的"图" // 初始化"顶点" // 初始化"边" //替换为二维数组 //利用 printf 格式化打印 //------------------------------------------------------- pG = new Matrix(vexs, edges); pG.addEdge(2,3,3); pG.removeEdge(3,3); pG.print(); // 打印图 } }
我的思路为:
1.初始化"顶点数"和"边数"
2.采用已有的"图"
3.初始化"顶点"
4.初始化"边"
5.替换为二维数组
6.利用 printf 格式化打印
我的方法简而概之就是利用一维数组存储点的信息(Char类型),二维数组描述边之间的关系。
二、
总的思想:
有向图的邻接表和逆邻接表结合起来得到的
邻接表是有缺陷的,关心了出度问题,想了解入度就必须要遍历整个图才能知道
十字链表通常是实现有向图的,这次实验要求实现无向图,虽然降低了难度,但是可供学习的资源少了,有些难度
我认为,在无向图的应用中,关注的重点是顶点的话,那么邻接表是不错的选择,但如果我们更关注的是边的操作,比如对已经访问过的边做标记,或者删除某一条边等操作,邻接表就显得不那么方便了。
/** * Created by 齐力锋 on 2017/11/24. */ /* * 作者:齐力锋 * 作业为实验四2 * vexs -- 顶点数组 * edges -- 边数组 伪代码和解释在每个方法旁的注释处 */ public class OTHList { int Numvlen; // 顶点个数 int Numelen; // 边个数 VertexNode[] vertexNodeList; // 顶点数组 EdgeNode edgeNode; public OTHList(char[] vexs, char[][] edges) { this.Numvlen = vexs.length; this.Numelen = edges.length; // 初始化顶点,建立顶点表 vertexNodeList = new VertexNode[Numvlen]; for (int i = 0; i < Numvlen; i++) { vertexNodeList[i] = new VertexNode(); vertexNodeList[i].vertex = vexs[i]; vertexNodeList[i].firstIn = null; vertexNodeList[i].firstOut = null; } // 初始化边,利用头插法建立十字链表 for (int i = 0; i < Numelen; i++) { EdgeNode edgeNode1 = new EdgeNode(); EdgeNode edgeNode2 = new EdgeNode(); int haha = FindWhere(edges[i][0], vexs); int hehe = FindWhere(edges[i][1], vexs); edgeNode1.tailvex = haha; edgeNode1.headvex = hehe; edgeNode1.taillink = vertexNodeList[haha].firstOut; vertexNodeList[haha].firstOut = edgeNode1; edgeNode2.tailvex = haha; edgeNode2.headvex = hehe; edgeNode2.headlink = vertexNodeList[hehe].firstIn; vertexNodeList[hehe].firstIn = edgeNode2; } } /** * 伪代码: * 顶点表结点结构 * vertex :存储顶点信息 * firstIn :入边表头指针,指向该顶点的入边表中第一个结点 * firstOut : 出边表头指针,指向该顶点的出边表中第一个结点 */ private class VertexNode { char vertex; EdgeNode firstIn; EdgeNode firstOut; } /** * 边表结点 * tailvex 弧起点在顶点表的下标 * headvex 弧终点在顶点表的下标 * headlink 入边表指针域,指向终点相同的下一条边 * taillink 边表指针域,指向起点相同的下一条边 */ private class EdgeNode { int tailvex; int headvex; EdgeNode headlink; EdgeNode taillink; } /** * 返回char的位置 */ private int FindWhere(char ch, char[] vexs) { for (int i = 0; i < Numvlen; i++) if (vexs[i] == ch) return i; return -1; } /** * 打印邻接表 注:这里参考了一些网上的代码(有些不太会) */ public void print() { System.out.printf("邻接表:\n"); for (int i = 0; i < Numvlen; i++) { System.out.print(vertexNodeList[i].vertex + "-->"); if (vertexNodeList[i].firstOut != null) { EdgeNode mEdgeNode = new EdgeNode(); mEdgeNode = vertexNodeList[i].firstOut; System.out.print(mEdgeNode.headvex); while (mEdgeNode.taillink != null) { mEdgeNode = mEdgeNode.taillink; System.out.print(mEdgeNode.headvex); } System.out.print("\n"); } else { System.out.print("\n"); } } System.out.print("----------\n"); System.out.printf("逆邻接表:\n"); for (int i = 0; i < Numvlen; i++) { System.out.print(vertexNodeList[i].vertex + "<--"); if (vertexNodeList[i].firstIn != null) { EdgeNode mEdgeNode = new EdgeNode(); mEdgeNode = vertexNodeList[i].firstIn; System.out.print(mEdgeNode.tailvex); while (mEdgeNode.headlink != null) { mEdgeNode = mEdgeNode.headlink; System.out.print(mEdgeNode.tailvex); } System.out.print("\n"); } else { System.out.print("\n"); } } } public boolean removeSide(Object A,Object B) throws Exception { boolean result = false; int num1 = -1,num2 = -1; for(int i=0;i){ if(vertexNodeList[i].equals(A)) num1 = i; else if(vertexNodeList[i].equals(B)) num2 = i; } if(num1!=-1 && num2 != -1){ result = true; } return result; } public boolean removeNode(Object m){ int A = -1; boolean result = false; for(int i=0;i ){ if(vertexNodeList[i].equals(m)) A = i; } if(A!=-1) result = true; return result; } public boolean isEmpty(){ return (this.size()==0); } public int size(){ return vertexNodeList.length; } /** * 主函数 */ public static void main(String args[]) { // 顶点数组 char[] vexs = { '齐', '力', '锋', '陕' }; // 边数组 char[][] edges = new char[][] { { '齐', '力' }, { '齐', '锋' }, { '齐', '陕' }, { '力', '陕' }, { '锋', '陕' } }; OTHList listUDG = new OTHList(vexs, edges); listUDG.print(); } }
我的变量的解释:
tailvex是指弧起点在顶点的下标,
headvex是指弧终点在顶点表中的下标,
headlink是指入边表指针域,指向终点相同的一下条边
taillink是指出边表指针域,指向起点相同的下一条边。
顶点依然是一个一维数组,存储他的信息
其实这里可以采用链式存储结构,LinkedList,这样会方便很多,直接用remove,size,isEmpty等方法