Java算法面试题(002) 如何通过一次迭代找到LinkedList的中间元素

声明:本文为本博主翻译,未经允许,严禁转载!

简介

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之后。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值