最新数据结构与算法:终于可以用三种语言(C,C#,JavaScript(4),2024年最新讲的真详细

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

在C#文件夹中可以找到“Graph0.cs”,这个文件中包含着深度优先遍历、广度优先遍历等程序中的所有图类程序,现在,我们就要在这个类中补充新的方法。
首先复制这个类到Graph.cs,然后用C#建立一个Windows应用程序,然后在资源管理器中添加这个类,这个类和在深度优先遍历中的类完全一致,但去掉了命名空间说明,这样,这个类就可以使用在其他工程中了。

首先是再次熟悉这个类中的变量定义:

private int[,] A         //邻接矩阵
private string[] V       //顶点矩阵
private int[] Visited    //顶点访问表
private TreeNode[] T     //遍历生成树
private int num          //顶点个数
private int ConnComp     //连通分量

找到这个类中的最后一个方法:DSFTraverse(),然后开始在这个方法后补充新方法:DFS(),由于算法和C语言完全一致,此处算法问题不在介绍。

private void BFS(int N)
{
            int n;
            Queue<int> Q = new Queue<int>();
            Q.Enqueue(N);
            Visited[N] = 1; 
            while (Q.Count != 0)
            {
                n = Q.Dequeue();
                for (int i = 0; i < num; i++)
                    if (A[n, i] == 1 && Visited[i] == 0)
                    {
                        T[n].Nodes.Add(T[i]);  
                        Visited[i] = 1; 
                        Q.Enqueue(i);
                    }
            }
}


这个方法可以从第N个顶点开始遍历,同前面涉及的问题一样,考虑到多次遍历、以及多连通分量的图,我们还要补充下面的方法:

        public int BFSTraverse()
        {
            int i;
            ConnComp = 0;
            for (i = 0; i < num; i++)
            {
                T[i] = new TreeNode(V[i]);
                Visited[i] = 0;
            }
            for (i = 0; i < num; i++)
                if (Visited[i] == 0)
                {
                    BFS(i);
                    ConnComp++;
                }
            return ConnComp; 
        }


补充完类Graph中两个方法补充后、就可以进行界面设计,设计界面如下:

在这里插入图片描述
根据图1的界面设计,则广度优先遍历程序中连通分量为1的图在button1下,于是有:

private void button1\_Click(object sender, EventArgs e)
{
            int m;
            int[,] A = {
                            {0, 1, 1, 0, 0, 0, 0, 0},
                            {1, 0, 0, 1, 1, 0, 0, 0},
                            {1, 0, 0, 0, 0, 1, 1, 0},
                            {0, 1, 0, 0, 0, 0, 0, 1},
                            {0, 1, 0, 0, 0, 0, 0, 1},
                            {0, 0, 1, 0, 0, 0, 1, 0},
                            {0, 0, 1, 0, 0, 1, 0, 0},
                            {0, 0, 0, 1, 1, 0, 0, 0}
                         };
            string[] V = { "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8" };
            Graph G = new Graph(8);
            G.Arc = A; G.Vertex = V;
            m = G.BFSTraverse(); 
            treeView1.Nodes.Clear();
            treeView1.Nodes.Add(G.DFSResult);
            textBox1.Text = "该图连接分量为" + m.ToString(); 
}


由于类设计中、广泛使用了原有的代码,所以这段程序看起来和深度优先遍历的测试代码差别很小。同理,在有多个连通分量的情况下,在button2下的代码是:

        private void button2\_Click(object sender, EventArgs e)
        {
            int m;
            int[,] A = {
                        {0, 1, 1, 0, 0, 0, 0},
                        {1, 0, 0, 1, 0, 0, 0},
                        {1, 0, 0, 1, 0, 0, 0},
                        {0, 1, 1, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0, 1, 1},
                        {0, 0, 0, 0, 1, 0, 1},
                        {0, 0, 0, 0, 1, 1, 0}
                       };
            string[] V = { "A", "B", "C", "D", "E", "F", "G" };
            Graph G = new Graph(7);
            G.Arc = A; G.Vertex = V;
            m = G.BFSTraverse(); 
            treeView1.Nodes.Clear();
            G.AddInTreeView(treeView1);
            textBox1.Text = "该图连接分量为" + m.ToString(); 
        }


请自行补充button3下的代码。

程序运行结果就是:
在这里插入图片描述图的广度优先遍历到此结束。通过上述编程我们可以发现:大量使用已有的代码,可以大大简化编程的复杂程度。

问题:

我们在C#的程序中、并没有使用类似C语言那样的技术:在数据文件中保存图的数据,这首先是基于我们对C#的使用方式造成的,C#最重要的应用场合是连接数据库服务器和前端的用户浏览器,这个场合下C#提供一个正确的运算类就足够了,其数据要来自于数据库,而结果要给到浏览器上的程序。浏览器下的程序就是JavaScript,这样的情况下C#不做数据文件读取、而要做的是数据库上数据读取,至于送到JavaScript,这个对C#、就要通过一种叫WebService的技术,而在JavaScript上、则要用到一种叫Ajax技术读写这些数据,而这些都是下学期的重要实验任务。

JavaScript语言实现图的广度优先遍历、并显示广度优先遍历生成树

对JavaScript而言,是没有队列类的,尽管数组的类型直接泛型,但仅有栈而无队列。我们需要最低代价完成一个队列系统,所以要再次查看JavaScript数组的所有方法和属性:

其中:FF: Firefox, IE: Internet Explorer

在这里插入图片描述
而这个对象提供的属性,则如下表:FF: Firefox, IE: Internet Explorer

在这里插入图片描述
回顾栈和队列的差异,一个是先进后出、一个是先进先出,查找上述数组的方法,有个方法是reverse(),含义是颠倒数组元素的次序,很显然:

如果进队列是数组的push()操作,那么出队列则就是颠倒数组次序、然后pop()操作,有这个思路,按这个算法构造队列类就是:

		function Queue()
		{
		this.Q=new Array();
		this.EnQueue=function(E)
			{
			this.Q.push(E);
			}
		this.DeQueue=function()
			{
			var E;
			this.Q=this.Q.reverse();
			E=this.Q.pop();
			this.Q=this.Q.reverse();
			return E;
			}
		this.Count=function()
			{
			return this.Q.length;
			}
		}


一定注意这个类的第13行,颠倒次序出栈后一定要再次颠倒这个数组的次序,保证进栈数据的次序。这样,我们就用最小代价完成了一个队列系统,然后补充多次进出队列的测试网页,就是:

<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
	<title>一个调用Ext类库的模板页面</title>
		<script type="text/javascript" src="Queue.js"></script>
		<script type="text/javascript" src="ext-3.0.0/adapter/ext/ext-base.js"></script>
  		<script type="text/javascript" src="ext-3.0.0/ext-all.js"></script>
   		<link rel="stylesheet" type="text/css" href="ext-3.0.0/resources/css/ext-all.css" />  
	</head>
	<body bgcolor="#FFFFFF">
	<div id="hello"></div>
	<script type="text/javascript">
		function fun()
		{
		var Q=new Queue();
		Q.EnQueue(1);
		Q.EnQueue(2);
		Q.EnQueue(3);
		while(Q.Count()>0)
			{
			document.write(Q.DeQueue()+'<br>');
			}
		Q.EnQueue(4);
		Q.EnQueue(5);
		while(Q.Count()>0)
			{
			document.write(Q.DeQueue()+'<br>');
			}
	     }
 	Ext.onReady(fun);
	</script>
	</body>
</html>


注意第5行一定要引用Queue.js这个文件,否则程序无法运行。

补充广度优先遍历程序

根据广度优先遍历的算法、以及表1的队列对象,不难写出广度优先遍历程序,但写以前我们要回顾深度优先遍历函数的入口参数:

A[][]: 邻接矩阵
vCount: 顶点个数
m: 进入遍历的顶点编号
Visited[] :顶点访问状态表
T[]: Ext.tree.TreeNode对象数组,遍历结果树

我们回顾这些的原因是:我们新的遍历函数、也要尽量和旧的方法使用的参数一致,这样就对后续的编程提供了大量的方便。如果意义相近的方法、其函数入口参数差异很大、这样对后续的编程造成很多困惑。

//A[][]: 邻接矩阵
//vCount: 顶点个数
//m: 进入遍历的顶点编号
//Visited[] :顶点访问状态表
//T[]: Ext.tree.TreeNode对象数组,遍历结果树
function BFS(A,vCount,m,Visited,T)
{
		var i,n;
		var Q=new Queue();
		Q.EnQueue(m);
		Visited[m]=1;
		while(Q.Count()>0)
			{
                n = Q.DeQueue();
        	        for (i = 0; i <vCount; i++)
                	    if (A[n][i] == 1 && Visited[i] == 0)
	                    {
        	                T[n].appendChild(T[i]);
	                        Visited[i] = 1; 
	                        Q.EnQueue(i);
	                    }			
			}
}


表3 JavaScript语言图的广度优先遍历,见工程B0.html

该函数算法不在介绍,程序原理和C、C#没什么差别。

从深度优先遍历网页补充广度优先遍历程序

从深度优先遍历网页G8.html复制文件到B0.html,在F3区域的邻接矩阵编辑窗口补充命令按钮“广度优先遍历”,就是表4.
对这个表中的程序,注意是一个程序框架,而不是全部。现在就要在合适的位置补充广度优先遍历的初始化程序。

var grid=new Ext.grid.EditorGridPanel({
			renderTo:"GRID",
			title:"图的邻接矩阵编辑",
			height:400,
			width:400,
			cm:colM,
			store:gstore,
			tbar: [ 
			{ 
			text: "深度优先遍历图",   
			handler: function()
					{ 
					//已有的深度遍历代码
					} 
			},
			{
			text:"广度优先遍历图",
			handler: function()
					{
			          //以下写进遍历的代码
					} 
			}
			] 
});


注意表4,其第20行就是补充广度优先遍历程序的地方,这程序本质就是给BFS()准备合适的数据、并初始化、然后调用BFS()函数,所以这地方和深度优先遍历的代码是一致的,于是有:

text:"广度优先遍历图",
handler: function()
{
//以下写进遍历的代码
		var m=gstore.getCount();
		var n=gstore.getAt(m-1).get('row')+1;
		var Visited=Array();
		var A=Array();
		var i,j;
		for(i=0;i<n;i++)
			{
			Visited[i]=0;
			A[i]=Array();
			T[i]=new Ext.tree.TreeNode({id:vstore.getAt(i).get('id'),text:vstore.getAt(i).get('V')});
			}
		for(i=0;i<m;i++)
			{
			var r=gstore.getAt(i).get('row');
			var c=gstore.getAt(i).get('col');
			var v=gstore.getAt(i).get('Value');
			A[r][c]=v;
			}
			var Concom=0;
		for(i=0;i<n;i++)
			if(Visited[i]==0) 
				{
				BFS(A,n,i,Visited,T);Concom++;
				}
				var TR=new Ext.tree.TreeNode({id:10000,text:'广度优先遍历树,连通分量'+Concom});
		for(i=0;i<n;i++)
				if(T[i].parentNode==null)
							TR.appendChild(T[i]);
		treeView1.setRootNode(TR);
} 
}


和前面深度优先遍历的程序完全一致,仅仅是调用了不同的遍历函数。

遍历网页的进一步修改和完善:构造图类

从B0.html这个网页程序看,首先在两个遍历的命令按钮程序上有大量重复代码,其次是有关图的计算,其邻接矩阵、顶点矩阵、顶点访问状态矩阵、遍历函数等都是分离的变量和函数,而没有构成一个类、从而也就没有图的对象,这样对后续的编程也造成很多不利。

为此,我们要构造一个JavaScript的图类,整体参照C#。

对任何一个语言的类编程而言,都存在数据如何进入对象、以及数据如何从对象里给出这两个基本问题,在使用Ext过程中,我们熟悉了大量的Ext对象属性获得方法,那么我们这里也将按同样的方法来构造类,详细的介绍参见json教程。以下类名称是Graph,其中G是属性参数:

function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
}


<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
	<title>一个调用Ext类库的模板页面</title>
		<script type="text/javascript" src="G0.js"></script>
		<script type="text/javascript" src="ext-3.0.0/adapter/ext/ext-base.js"></script>
  		<script type="text/javascript" src="ext-3.0.0/ext-all.js"></script>
   		<link rel="stylesheet" type="text/css" href="ext-3.0.0/resources/css/ext-all.css" />  
	</head>
	<body bgcolor="#FFFFFF">
	<div id="hello"></div>
	<script type="text/javascript">
		function fun()
		{
		var G=new Graph({
			      A:[[1,2,3],[4,5,6],[7,8,9]],V:['A','B','C'],Visited:[0,0,0]
			      });
	     }
 	     Ext.onReady(fun);
	</script>
	</body>
</html>


注意第16行,其中构造函数的参数里:

{A:[[1,2,3],[4,5,6],[7,8,9]],V:['A','B','C'],Visited:[0,0,0]}

整体构成对象G,进入类后,进入表5程序后,由第3到第5行的程序赋值给对象相应的属性。再次参照表5程序,其中的this,对应在表6的程序是G,广义上,实例化的对象就是表5中的this。

有了上述分析,我们就可以在表5的程序中加入一个公共方法,用来获得属性中V数组的内容,代码就是:

function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
this.VName=function()
	{
	var i;
	for(i=0;i<this.num;i++)
		document.write(this.V[i]);
	}
}


这样写的方法类似是C#中的public void VName(),这样的写法可以在实例对象中引用这样方法,如:

<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
	<title>一个调用Ext类库的模板页面</title>
		<script type="text/javascript" src="G1.js"></script>
		<script type="text/javascript" src="ext-3.0.0/adapter/ext/ext-base.js"></script>
  		<script type="text/javascript" src="ext-3.0.0/ext-all.js"></script>
   		<link rel="stylesheet" type="text/css" href="ext-3.0.0/resources/css/ext-all.css" />  
	</head>
	<body bgcolor="#FFFFFF">
	<script type="text/javascript">
	function fun()
	{
	 var G=new Graph({
			      A:[[1,2,3],[4,5,6],[7,8,9]],
			      V:['A','B','C'],
			Visited:[0,0,0],
			    num:3
			});
		G.VName();
	}
 	Ext.onReady(fun);
	</script>
	</body>
</html>


上述过程完成后,可以加入一个求V数组中每行平均值的方法,涉及到求平均值,首先我们需要一个求指定行和的函数,这个函数定义成私有的,如同表9的程序中的Sum(),私有函数定义和普通的JavaScript函数完全一致。

但在实际使用中,错误首先在第17行,表示this.num是没有定义。

造成这样的结果,主要是私有的函数Sum()并不包含在对象中,这点和C#是完全不一样,所以私有函数中要引用对象的数据,要首先获得该对象的实例,就是要有这样的方法:

var Ob=this;
function Sum()
{
…
for(i=0;i<Ob.num;i++)
…
}


function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
this.VName=function()
	{
	var i;
	for(i=0;i<this.num;i++)
		document.write(this.V[i]);
	}
function Sum(n)
{
var s=0,i;
for(i=0;i<this.num;i++)  //私有方法中错误引用对象数据
	s+=this.A[n][i];
return s;
}
this.AVG=function(n)
	{
	var s;
	s=Sum(n)/this.num;	
	}
}


function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
this.VName=function()
	{
	var i;
	for(i=0;i<this.num;i++)
		document.write(this.V[i]);
	}
function Sum(n)
{
var s=0,i;
for(i=0;i<this.num;i++)  //私有方法中错误引用对象数据
	s+=this.A[n][i];
return s;
}
this.AVG=function(n)
	{
	var s;
	s=Sum(n)/this.num;	
	}
}


function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
var Ob=this;
//公共方法
this.VName=function()
	{
	var i;
	for(i=0;i<this.num;i++)
		document.write(this.V[i]);
	}
//私有方法
function Sum(n)
{
var s,i;
s=0;
for(i=0;i<Ob.num;i++)
	s+=Ob.A[n][i];
return s;
}
//公共方法
this.AVG=function(n)
	{
	var a;
	a=Sum(n)/this.num;	
	return a;
	}
}


通过上述实验过程,则有两个遍历方法的图类就是:

function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
var Ob=this;
//私有方法:深度优先遍历
function DFS(m)
{
var i;
Ob.Visited[m]=1;
for(i=0;i<Ob.num;i++)
	{
	if(Ob.A[m][i]!=0&&Ob.Visited[i]!=1) 
		{
		Ob.T[m].appendChild(Ob.T[i]);


![img](https://img-blog.csdnimg.cn/img_convert/95418445d5f1bf2af49fdca1cbae59b8.png)
![img](https://img-blog.csdnimg.cn/img_convert/b5823623248daeaca345eded478774e7.png)
![img](https://img-blog.csdnimg.cn/img_convert/d1a8c0187411a9cec1ef3e8aa4249224.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

验过程,则有两个遍历方法的图类就是:



function Graph(G)
{
this.A=G.A;
this.V=G.V;
this.Visited=G.Visited;
this.num=G.num;
this.T=G.T;
var Ob=this;
//私有方法:深度优先遍历
function DFS(m)
{
var i;
Ob.Visited[m]=1;
for(i=0;i<Ob.num;i++)
{
if(Ob.A[m][i]!=0&&Ob.Visited[i]!=1)
{
Ob.T[m].appendChild(Ob.T[i]);

[外链图片转存中…(img-oLpOjAq2-1715507988082)]
[外链图片转存中…(img-v86n3kf5-1715507988082)]
[外链图片转存中…(img-tFSNCkhQ-1715507988083)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值