问题描述
我是数据结构的新手。在学习它时,我无法理解这两种链表实现之间的区别。
在我最常看到的地方,每个人都在使用下面提到的链表的第一个实现。
两个类:
class Node{
int data;
int value;
Node next;
Node(int data){
this.data = data;
this.next = null;
}
Node(int data,Node next){
this.next = next;
this.data = data;
}
}
class LinkedList{
Node head;
LinkedList(Node head){
this.head = head;
}
LinkedList(){
this.head = null;
}
void insert(int data){
//it will insert at the end
//logic
}
//Other methods.
}
但这是我的问题,为什么我们需要两个类来实现它。假设我使用以下实现:
class Node{
int data;
int value;
Node next;
Node(int data){
this.data = data;
this.next = null;
}
Node(int data,Node next){
this.next = next;
this.data = data;
}
void appendToTail(int d){
Node node = new Node(4);
Node traversal = this;
while(traversal.next!=null){
traversal = traversal.next;
}
traversal.next = n;
}
}
现在我可以在一个班级里用它做所有的事情不是吗?请告诉我这种方法有什么问题,因为它使人非常困惑。
解决方法
在第二个版本中,Node
类也用作列表的头部。但问题是,如果您有一个 Node
实例,您无法判断它当前是否是列表的头部。
为什么重要?
那么考虑一下当你在列表的开头添加一个新元素时会发生什么;例如
Node list1 = new Node(1,null);
Node list2 = new Node(2,list1);
现在,list1
可以是一个列表,也可以是另一个列表的一部分……或者两者兼而有之。仅通过查看 Node
对象本身的表示无法知道是哪种情况。
事实上,不难举出一些例子,其中错误可能导致列表进入不再“类似列表”的状态。
list2.next = list1; // creates a cycle!
相比之下,在 LinkedList
和 Node
类型不同的版本中,角色之间有明显的区别。然后,如果您将 Node
类设为 LinkedList
的私有内部类,则后者可以管理数据结构以防止形成不需要的结构(例如循环)。
现在我可以在一个班级用它做所有事情不是吗?
这是真的。
但真正的重点是,通过两个类(以及适当使用其他 Java 语言功能),您可以创建一个抽象,该抽象将行为始终像一个适当的列表。
请告诉我这种方法有什么问题,因为它使人非常困惑。
确实如此。明智地使用抽象可以减少混淆。通过在 LinkedList
类的抽象边界后面隐藏列表数据结构和操作它们的方法,您可以更轻松地编写可以安全使用列表的代码。
一类方法看起来相当有限,不是吗?使用链表的好处是可以轻松访问它的头部和尾部。拥有链表可以显着提高运行时间,而不是遍历每个节点来查找尾部。说到删除,这绝对是数据结构课程中的一个关键主题,二类方法比一类方法实用得多。在二类方法中,您可以根据需要迭代并重新分配头或尾。在一类方法中,无论您首先创建哪个节点都意味着一切,并且由于您无法定义头部或尾部,因此在删除发生后跟踪所有节点变得非常具有挑战性。 TLDR:二类方法允许更容易的访问,进而更快的运行时间。