标题【数据结构与算法】链表及相关问题
前言
链表(Linked list)相关算法题在面试中很常见,本文将详细介绍链表相对复杂的操作,并用Java语言实现,其中包括:建立带环链表(区别于循环链表)、判断链表是否带环,求得环入口结点及环的长度;判断两个链表是否相交、求得交点;查找倒数第K个结点;反转链表。在LeetCode和剑指Offer中出现的链表问题,基本基于这几个操作。(反正我是一文搞懂啦~)其他简单的操作实现在文后链接中。
学习目的:掌握链表相关操作,熟练使用双指针法(快慢指针法)
一. 带环单链表相关问题
1. 带环单链表建立
看过的解决带环链表问题的文章,都只讨论怎样求得环入口结点和环长度,博主这种渣渣就在想怎么不讲怎么建立带环链表,都没有办法验证……于是自己写了。
代码:
/**
* @description 建立一个带环单链表
* @param head
*/
public void createLoopList(Node<E> head) {
int i;
Node<E> current = head;
Node<E> target = null;
for (i = 0; i < 10; i++) {
Node<E> temp = new Node(i);
temp.next = null;
current.next = temp;
this.size++;
if (i == 6)
target = temp;//指定这个结点为环的第一个结点
current = temp;
}
current.next = target; //链表末端指向中间的一个结点,这样就创建了一个带环单链表
}
2. 判断链表是否带环
算法思想:图中是一个带环单链表,这里要区分,循环链表并非这里所讨论的带环单链表。这里判断链表是否带环采用的便是的双指针法,之后也会常用到此方法,应用双指针的算法时间复杂度为O(n),空间复杂度为O(1)。双指针法,即设置快慢指针,在判断是否带环时,我们让快指针每次走两个步长(即fast=fast->next->next),慢指针每次走一个步长(即slow=slow->next),这样一来,如果链表带环,则快指针早晚会经过不懈努力再次追赶上慢指针,并且两指针一定在环上相遇。
p.s.:也可以通过设置标志位的方法,但是空间复杂度较高。
代码:
/**
* @return
* @description 判断单链表是否有环(双指针法)--时间复杂度O(n);空间复杂度O(1)[设置标志位空间复杂度大]
*/
public boolean hasLoop() {
Node<E> pre = head.next;
Node<E> post = head.next;
if (pre == null)
return false;
while (pre != null && pre.next != null && pre.next.next != null) {
post = post.next; //慢指针走一步
pre = pre.next.next; //快指针走两步
if (pre == post)
return true;
}
return false;
}
3. 求环入口结点
算法思想:找到环的入口结点,仍然使用双指针法,我们设入口结点到头结点距离为e,快慢指针相遇点到头结点距离为m,设环的长度为L,快指针与慢指针相遇时,慢指针走s步,则快指针走2s步,且快指针追上慢指针是在走了n次环的长度之后,则有: s = e + m s=e+m s