声明:本文为本博主翻译,未经允许,严禁转载!
简介
Java和非Java程序员在电话面试中经常被问及如何仅一次遍历查找到LinkedList的中间元素。这个问题类似于检查回文或者计算阶乘,面试官有时也会要求编写代码。为了回答这个问题,候选人必须熟悉LinkedList数据结构,即在单LinkedList的情况下,链表的每个节点都包含数据和指针,它是下一个链表节点的地址,单链表的最后一个元素指向null。由于为了找到链表的中间元素,你需要找到LinkedList的长度,这需要对链表节点进行计数直到最后一个元素为止。让这个数据结构面试问题有趣的是,你需要在一次遍历中找到LinkedList的中间元素,而你不知道LinkedList的长度。这是考验候选人逻辑能力的地方:他是否熟悉空间和时间的权衡等等。
如果你仔细想想,你可以通过使用两个指针来解决这个问题,就像我在上一篇关于如何在Java中查找单一链表的长度的文章中提到的那样。通过使用两个指针,其中一个每次迭代增一,另外一个每两次迭代增。当第一个指针指向链表的末尾时,第二个指针将指向链表的中间节点。
实际上,这两个指针方法可以解决多个相似的问题,如何在一个迭代中从链表中找到倒数第3个元素,或者如何在链表中找到倒数第N个元素。在这个Java编程教程中,我们将看到一个Java程序,它在一个迭代中查找链接列表的中间元素。
这里是完整的Java程序来查找Java中的链表的中间节点。记住LinkedList类是我们的自定义类,不要把这个类与Java中流行的Collection类java.util.LinkedList混淆。在这个Java程序中,我们的类LinkedList表示一个包含节点集合并具有头部和尾部的链表数据结构。每个节点都包含数据和地址部分。 LinkedListTest类的主要方法是用来模拟问题的,在这里我们创建了Linked List,并在其上添加了一些元素,然后遍历它们在Java中一次遍历链接列表的中间元素。
通过一次LinkedList迭代查找中间元素的Java程序
/**
* Java program to find middle element of linked list in one pass. In order to
* find middle element of linked list we need to find length first but since we
* can only traverse linked list one time, we will use two pointers one which we
* will increment on each iteration while other which will be incremented every
* second iteration. so when first pointer will point to the end of linked list,
* second will be pointing to the middle element of linked list
*
* @author
*/
public class LinkedListTest {
public static void main(String[] args) {
// creating LinkedList with 5 elements including head
LinkedList list = new LinkedList();
list.add(new LinkedList.Node("1"));
list.add(new LinkedList.Node("2"));
list.add(new LinkedList.Node("3"));
list.add(new LinkedList.Node("4"));
list.add(new LinkedList.Node("5"));
LinkedList.Node head = list.head();
// finding middle element of LinkedList in a single pass
LinkedList.Node current = head;
LinkedList.Node middle = head;
int length = 0;
while (current.next() != null) {
length++;
if (length % 2 == 0) {
middle = middle.next();
}
current = current.next();
}
length++;
System.out.println("length of LinkedList: " + length);
System.out.println("middle element of LinkedList : " + middle);
}
}
class LinkedList {
private Node head;
private Node tail;
/**
* Constructs an empty list
*/
public LinkedList() {
head = null;
tail = null;
}
public Node head() {
return head;
}
public void add(Node node) {
if (head == null) {
head = node;
tail = node;
return;
}
tail.next = node;
tail = node;
}
public static class Node {
private Node next;
private String data;
public Node(String data) {
this.data = data;
}
public String data() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node next() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public String toString() {
return this.data;
}
}
}
原文链接
How to find middle element of LinkedList in Java in one pass in Java
译者备注
1. 译文中实现与原实现有些差异,原文中在链表初始化时自动创建了一个无关节点,相关实现也不容易理解,这块在上文中译者进行了优化,感兴趣的可以对比一下原文的实现。
2. 你应该搞清楚迭代一次是什么意思。如果面试官说你不能循环两次而只需要使用一个循环,那么你可以使用两个指针来解决这个问题。在两个指针方法中,你有两个指针,快指针和慢指针。在每个步骤中,快速指针移动两个节点,而慢速指针仅移动一个节点。所以,当快速指针指向最后一个节点,即下一个节点为空时,慢速指针将指向链表的中间节点。
3. 这里再举一个类似的案例,可以使用本文的算法解决: 例如,假设你有一个链表a1 - > a2 - > ... - > an - > b1 - > b2 - > ... - > bn,将其重新排列为a1→b1→a2→b2→b→a→bn。你不知道链表的长度(但是你要知道长度是偶数)。你可以有一个指针p1(快速指针)为每次移动两个元素,而p2(慢速指针)每次移动一个元素。当p1到达链表的末尾时,p2将处于中点。然后,将p1移回前面并开始组织的元素。在每次迭代中,p2选择一个元素并将其插入到p1之后。