- Linked List Cycle(环形链表)-Number141
思路:
利用快慢指针,快指针每次走两步,慢指针每次走一步,如果链表中存在环,那么快慢指针总会相遇
代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast)
return true;
}
return false;
}
}
- Linked List Cycle II (求环的入口)-Number142
思路:
在判断出环之后,将快指针指向链表头,慢指针指向相遇点
快慢指针每次移动一个指针,再次相遇点就是环的入口处,设链表头部到环的入口长度为a
,环的长度为b
,环的入口到相遇点的长度为c
:则f=2s=s+nb
,s=nb=a+c+(n-1)b
,b=a+c
,即慢指针从相遇点继续前进a
步就是环的入口
代码:
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
fast = head;
while(fast!=slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
- Merge Two Sorted Lists-Number21
思路:
利用递归的思想,当某个链表为空了,就返回另一个。然后核心还是比较当前两个节点值大小,如果 l1 的小,那么对于 l1 的下一个节点和 l2 调用递归函数,将返回值赋值给 l1.next,然后返回 l1;否则就对于 l2 的下一个节点和 l1 调用递归函数,将返回值赋值给 l2.next,然后返回 l2
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null)
return l2;
if(l2==null)
return l1;
if(l1.val<l2.val){
l1.next=mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next=mergeTwoLists(l1,l2.next);
return l2;
}
}
}
- Merge k Sorted Lists-Number23
思路:
如果利用Merge Two Sorted Lists的思路来解决,先两两合并再与下一个链表合并,这样的话时间复杂度是很高的,所以在这里我们利用分治法来解决。
不断的对k进行划分,k划分称k/2,再不停的划分,直到划分成只有一个链表或两个链表的任务。代码中的k是通过 (n+1)/2 计算的,这里为啥要加1呢,这是为了当n为奇数的时候,k能始终从后半段开始,比如当 n=5 时,那么此时 k=3,则0和3合并,1和4合并,最中间的2空出来。当n是偶数的时候,加1也不会有影响,比如当 n=4 时,此时 k=2,那么0和2合并,1和3合并。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length == 0)
return null;
int length = lists.length; //链表长度
while(length>1){
int k=(length+1)/2;
for(int i=0;i<length/2;i++){
lists[i] = mergeTwoLists(lists[i],lists[i+k]);
}
length = k;
}
return lists[0];
}
public ListNode mergeTwoLists(ListNode lists1, ListNode lists2){
ListNode temp = new ListNode(-1);
ListNode current = temp;
while(lists1!=null && lists2!=null){
if(lists1.val<lists2.val){
current.next = lists1;
lists1 = lists1.next;
}else{
current.next = lists2;
lists2= lists2.next;
}
current = current.next;
}
if(lists1==null)
current.next = lists2;
if(lists2==null)
current.next = lists1;
return temp.next;
}
}
- Reverse Linked List(反转链表)-Number 206
思路:
链表中的主要考点在于指针,所以当需要反转链表的时候,我们只需要改变指针的指向。
迭代法:引入三个指针,prev记录上一个节点,next记录下一个节点,head表示当前指针
递归法:head.next.next = head
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null; // 记录前一个节点
ListNode next = null; //记录后一个节点
while(head!=null){
next = head.next;
head.next = pre; //改变指针的方向
pre = head;
head = next;
}
return pre;
}
}
- Remove-nth Node From End of List(删除链表的倒数第k个元素)-number19
思路:
利用经典的双指针,定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动,从第k步开始,第二个指针也从链表的头指针开始遍历,由于两个指针的距离保持在k-1,当第一个指针到达链表的尾节点时,第二个指针刚好指向倒数第k个节点。
利用快慢指针的步数差来进行巧妙的计算
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head;
ListNode slow = head;
ListNode back = null; //用来记录被删除的节点的前一个节点
if(head == null)
return null;
for(int i=0;i<n-1;i++){ //快指针先走到第k-1的位置
fast = fast.next;
}
while(fast!=null && fast.next!=null){
fast = fast.next;
back = slow;
slow = slow.next;
}
if(back == null)
return slow.next;
else{
back.next = back.next.next;
return head;
}
}
}
在此题的基础上,进行变形,删除链表的中间节点,则可以设置快慢指针,快指针每次走两步,慢指针每次走一步,当快指针到达链尾的时候,慢指针到达中点位置。