检查链表<Node<T>>是不是包含某个节点
Posted
技术标签:
【中文标题】检查链表<Node<T>>是不是包含某个节点【英文标题】:Check if Linked list<Node<T>> contains a certain node检查链表<Node<T>>是否包含某个节点 【发布时间】:2018-12-01 21:07:19 【问题描述】:我正在使用 c# 实现一个 Graph,我想检查我是否两次插入了相同的边,以便在执行该操作时抛出异常。
我的班级名称是 Graph
这是我的 _adjacencyList 声明
protected virtual Dictionary<T, LinkedList<Node<T>>> _adjacencyList get; set;
这是我的节点类
class Node<T> where T : IComparable<T>
public double speed get; set;
public double time get; set;
public double distance get; set;
public T source get; set;
public T destenation get; set;
public Node()
public Node(T SOURCE, T DESTENATION, double SPEED, double DISTANCE)
this.source = SOURCE;
this.destenation = DESTENATION;
this.speed = SPEED;
this.distance = DISTANCE;
this.time = this.distance / this.speed;
这是我的 addEdge 函数,它采用源顶点和目标顶点 以及边缘的“权重”
public void addEdge(T source, T Destenation, double speed, double Distance)
if (_adjacencyList.Count <= 0)
throw new InvalidOperationException("addEdge: There are no Vertices in Graph.\n");
else
if (_adjacencyList.ContainsKey(source) && _adjacencyList.ContainsKey(Destenation))
var sourceEdge = new Node<T>(source, Destenation, speed, Distance);
var destenationEdge = new Node<T>(Destenation, source, speed, Distance);
if (_adjacencyList[source].Contains(sourceEdge) || _adjacencyList[Destenation].Contains(destenationEdge))
throw new InvalidOperationException("addEdge: Edge already exists in Graph.\n");
else
_adjacencyList[source].AddLast(sourceEdge);
_adjacencyList[Destenation].AddLast(destenationEdge);
++_edgeCount;
else
throw new NullReferenceException("addEdge : Source or Destenation Vetrtex Don't Exist in Graph.\n");
当我在 main 中编写此代码时,它不会抛出“Edge 已存在于 Graph 中”的异常。
Graph<int> g = new Graph<int>();
g.addVertex(1);
g.addVertex(2);
g.addVertex(3);
g.addVertex(4);
g.addEdge(1,2,15.0,60.0);//Multiple Edge
g.addEdge(1, 2, 15.0, 60.0);//Multiple Edge
g.addEdge(1, 3, 5.0, 40.0);
g.addEdge(2,3,1.0,10.0);
g.addEdge(4,1,2.0,8.0);
我的实现有什么问题以及如何解决?
【问题讨论】:
【参考方案1】:发生这种情况是因为您忘记覆盖类 Node
的 Equals
方法。
你需要类似下面的实现:
public class Edge<T>
public double Speed get;
public double Time get;
public double Distance get;
public T Source get;
public T Destination get;
public Edge(T source, T destination, double speed, double distance)
if (source == null) throw new ArgumentNullException(nameof(source));
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (Math.Abs(speed) < 1E-9) throw new ArgumentException("speed must greater than zero", nameof(speed));
if (Math.Abs(distance) < 1E-9) throw new ArgumentException("distance must greater than zero", nameof(speed));
Source = source;
Destination = destination;
Speed = speed;
Distance = distance;
Time = Distance / Speed;
public override bool Equals(object obj)
if (!(obj is Edge<T> objAsEdgeT))
return false;
return Math.Abs(Speed - objAsNodeT.Speed) < 1E-9
&& Math.Abs(Time - objAsNodeT.Time) < 1E-9
&& Source.Equals(objAsNodeT.Source)
&& Destination.Equals(objAsNodeT.Destination);
public override int GetHashCode()
unchecked
int hash = 13;
hash = (hash*7) + Speed.GetHashCode();
hash = (hash*7) + Time.GetHashCode();
hash = (hash*7) + Source.GetHashCode();
hash = (hash*7) + Destination.GetHashCode();
return hash;
一些注意事项:
命名至关重要。Node
类本质上代表一条边。所以Edge
是一个更合适的类名。反过来想想,对于一个人来说阅读和真正理解一段与图节点相关的代码会有多困难,我们选择的名字是边。
尝试使用常见的编码样式,以使您的代码更具可读性。例如,对于我们使用Pascal Case 的属性。
在这种情况下,您不需要公共设置器。
您不需要默认构造函数。有人打电话给new Edge<int>()
是什么意思?更不用说你会得到一个例外,因为所有属性都会获得相应的默认值(双精度 -> 0),并且除法距离/速度将导致除法为零......
在构造函数内部,我们必须验证我们得到的值是否有意义。否则,我们最多只会有一个处于无意义状态的对象。没有节点我们就没有边!所以null
对于源和目标都不是有效值。此外distance
和speed
应该大于零。即使speed
有某种意义,distance
和speed
的划分也毫无意义——更不用说例外了……
【讨论】:
【参考方案2】:原因确实是你没有实现Node类的Equals方法,我这里也解释一下原因。
为了理解为什么需要 Equals 方法,您需要了解 LinkedList 类是如何工作的,这非常简单,您只需在其中添加并稍后删除类型为 Node 的对象。到目前为止一切都很好,但是当您在这段代码中使用这个对象时if (_adjacencyList[source].Contains(sourceEdge) ...)
throw new InvalidOperationException("addEdge: Edge already exists in Graph.\n");
您调用方法 Contains。现在,您的 LinkedList 对象必须查看它保存的数据并尝试比较给定条目是否已经在列表中,不幸的是,没有提到如何执行此操作,因此它不知道该怎么做。好在创建 LinkedLists 的人想到了这一点并说:让我们有一个通用的方法来检查任何数据类型的两个对象是否相等,这就是著名的 Equals 方法诞生的原因。
现在你有权说,等一下,不是每个类都默认定义了 Equals 吗?好吧,您是绝对正确的,但也有点错误,Equals 方法的默认实现对我们没有好处,因为它检查对象引用并比较它们。即使您使用相同的数据创建 2 个对象,它们也会有不同的引用,并且对它们的 Equals 方法会失败(显然)。
继续链表的故事,链表将使用 Equals 方法的默认实现,该方法失败,这就是为什么你会错过多重边缘情况。
【讨论】:
既然您首先解决了原因部分,那么您绝对值得 +1。 How to check if a graph node is in destination in LinkedList? 呢?以上是关于检查链表<Node<T>>是不是包含某个节点的主要内容,如果未能解决你的问题,请参考以下文章