链表常见问题总结(包含:获取倒数第k个元素、获取中间位置的元素、判断链表是否存在环及其环的长度)
反转链表(simple难度)
https://leetcode-cn.com/problems/reverse-linked-list/
与本题相同的题目:
下面方法思路及代码实现:
作者:wang_ni_ma
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-
来源:力扣(LeetCode)
class Solution {
public ListNode reverseList(ListNode head) {
//申请节点,pre和 cur,pre指向null
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
//记录当前节点的下一个节点
tmp = cur.next;
//然后将当前节点指向pre
cur.next = pre;
//pre和cur节点都前进一位
pre = cur;
cur = tmp;
}
return pre;
}
}
作者:wang_ni_ma
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-fan-zhuan-lian-biao-by-user74/
来源:力扣(LeetCode)
class Solution {
public ListNode reverseList(ListNode head) {
//递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
//这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
//这里请配合图片理解
//如果链表是 1->2->3->4->5,那么此时的cur就是5
//而head是4,head的下一个是5,下下一个是空
//所以head.next.next 就是5->4
head.next.next = head;
//防止链表循环,需要将head.next设置为空
head.next = null;
//每层递归函数都返回cur,也就是最后一个节点
return cur;
}
}
作者:wang_ni_ma
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-fan-zhuan-lian-biao-by-user74/
来源:力扣(LeetCode)
下面方法思路及代码实现:
作者:jyd
链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/solution/jian-zhi-offer-24-fan-zhuan-lian-biao-die-dai-di-2/
来源:力扣(LeetCode)
class Solution {
public ListNode reverseList(ListNode head) {
return recur(head, null); // 调用递归并返回
}
private ListNode recur(ListNode cur, ListNode pre) {
if (cur == null) return pre; // 终止条件
ListNode res = recur(cur.next, cur); // 递归后继节点
cur.next = pre; // 修改节点引用指向
return res; // 返回反转链表的头节点
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/solution/jian-zhi-offer-24-fan-zhuan-lian-biao-die-dai-di-2/
来源:力扣(LeetCode)
回文链表(simple难度)
https://leetcode-cn.com/problems/palindrome-linked-list/
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<Integer>();
// 将链表的值复制到数组中
ListNode currentNode = head;
while (currentNode != null) {
vals.add(currentNode.val);
currentNode = currentNode.next;
}
// 使用双指针判断是否回文
int front = 0;
int back = vals.size() - 1;
while (front < back) {
//因为List限定了为Integer类型,所以get()方法的返回值为Integer类型,所以需要用equals比较
if (!vals.get(front).equals(vals.get(back))) {
return false;
}
front++;
back--;
}
return true;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/hui-wen-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
方法二:递归
本方法思路和代码来源:
作者:sdwwld
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/di-gui-zhan-deng-3chong-jie-jue-fang-shi-zui-hao-d/
来源:力扣(LeetCode)
对链表逆序打印可以这样写:
private void printListNode(ListNode head) {
if (head == null)
return;
printListNode(head.next);
System.out.println(head.val);
}
作者:sdwwld
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/di-gui-zhan-deng-3chong-jie-jue-fang-shi-zui-hao-d/
来源:力扣(LeetCode)
也就是说最先打印的是链表的尾结点,他是从后往前打印的,看到这里是不是有灵感了,接下来对上面的代码进行改造一下
ListNode temp;
public boolean isPalindrome(ListNode head) {
temp = head;
return check(head);
}
private boolean check(ListNode head) {
if (head == null)
return true;
boolean res = check(head.next) && (temp.val == head.val);
temp = temp.next;
return res;
}
作者:sdwwld
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/di-gui-zhan-deng-3chong-jie-jue-fang-shi-zui-hao-d/
来源:力扣(LeetCode)
方法三:快慢指针(反转链表后半部分以后再还原)
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}
// 找到前半部分链表的尾节点并反转后半部分链表
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHalfStart = reverseList(firstHalfEnd.next);
// 判断是否回文
ListNode p1 = head;
ListNode p2 = secondHalfStart;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) {
result = false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
}
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/hui-wen-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
方法四:快慢指针(不复原链表,快慢指针在寻找中间节点的过程中直接反转链表前半部分,找到中间节点之后直接从中间向两边开始比较)
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode pre = null;
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
ListNode temp = slow.next;
if(pre != null) {
slow.next = pre;
}
pre = slow;
fast = fast.next.next;
slow = temp;
}
if(fast != null) slow = slow.next;//若链表为奇数个节点,则执行此步跨过最中间那个节点。
while(slow != null){
if(slow.val != pre.val) return false;
slow = slow.next;
pre = pre.next;
}
return true;
}
}
合并两个有序链表(simple难度)
https://leetcode-cn.com/problems/merge-two-sorted-lists/
与本题相同题目:
<方法一>:迭代(伪头节点法)
本方法思路及解法参考了《剑指offer25.合并两个排序的链表》的题解
作者:jyd
链接:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/solution/mian-shi-ti-25-he-bing-liang-ge-pai-xu-de-lian-b-2/
来源:力扣(LeetCode)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dum = new ListNode(0), cur = dum;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
cur.next = l1;
l1 = l1.next;
}
else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 != null ? l1 : l2;
return dum.next;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/solution/mian-shi-ti-25-he-bing-liang-ge-pai-xu-de-lian-b-2/
来源:力扣(LeetCode)
<方法二>:递归
本方法思路及代码实现:
作者:z1m
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/yi-kan-jiu-hui-yi-xie-jiu-fei-xiang-jie-di-gui-by-/
来源:力扣(LeetCode)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
else if (l2 == null) {
return l1;
}
else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
作者:z1m
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/yi-kan-jiu-hui-yi-xie-jiu-fei-xiang-jie-di-gui-by-/
来源:力扣(LeetCode)
环形链表(simple难度)
https://leetcode-cn.com/problems/linked-list-cycle/
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<ListNode>();
while (head != null) {
if (!seen.add(head)) {
return true;
}
head = head.next;
}
return false;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
环形链表Ⅱ(medium难度)
https://leetcode-cn.com/problems/linked-list-cycle-ii/
本方法思路和代码来源:
作者:jyd
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/
来源:力扣(LeetCode)
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
while (true) {
if (fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return fast;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/
来源:力扣(LeetCode)
相交链表(simple难度)
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
与本题相同的题目:
<方法一>:双指针(链表拼接)
本方法思路和代码来源:
作者:jyd
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/intersection-of-two-linked-lists-shuang-zhi-zhen-l/
来源:力扣(LeetCode)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode ha = headA, hb = headB;
while (ha != hb) {
ha = ha != null ? ha.next : headB;
hb = hb != null ? hb.next : headA;
}
return ha;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/intersection-of-two-linked-lists-shuang-zhi-zhen-l/
来源:力扣(LeetCode)
<方法二>:哈希表
本方法思路和代码来源:
作者:力扣官方题解
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/xiang-jiao-lian-biao-by-leetcode/
来源:力扣(LeetCode)
删除链表的倒数第N个结点(medium难度)
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
<方法一>:双指针(不用伪头节点)
本方法思路及代码来源:
作者:HIT_whc
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/san-zhi-zhen-jie-fa-bu-yong-wei-tou-jie-yw6nb/
来源:力扣(LeetCode)
整体思路:
设前指针为pre,后指针为cur。让前指针pre先移动n步,然后创建一个指针temp保存后指针cur所指节点的前一个节点。temp,cur,pre三个指针共同移动,直到前指针pre指向null为止。
此时后指针cur指向的是待删除的节点,temp节点指向待删除节点的前一个节点。令temp.next = cur.next,即可删除待删除节点。如下图所示:
特殊情况:
若n = 链表长度,则在前指针pre移动n步后,pre已经指向了null,此时直接返回head.next即可。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//空链表返回null
if(head == null){
return null;
}
//双指针,让pre指针先走n步,之后cur和pre指针一起走
ListNode pre = head;
ListNode cur = head;
while(n>0){
pre = pre.next;
n--;
}
//若删除第一个节点,即链表长度为n,删除倒数第n个结点,则pre指针先走n步后会直接变成null
//此时直接返回head.next
if(pre == null){
return head.next;
}
//当pre.next为null时,cur.next指向待删除的节点
while(pre.next != null){
pre = pre.next;
cur = cur.next;
}
cur.next = cur.next.next;
return head;
}
}
作者:HIT_whc
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/san-zhi-zhen-jie-fa-bu-yong-wei-tou-jie-yw6nb/
来源:力扣(LeetCode)
<方法二>:伪头节点法
本方法思路及代码来源:
作者:guanpengchn
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/
来源:力扣(LeetCode)
/**
* 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 pre = new ListNode(0);
pre.next = head;
ListNode start = pre, end = pre;
while(n != 0) {
start = start.next;
n--;
}
while(start.next != null) {
start = start.next;
end = end.next;
}
end.next = end.next.next;
return pre.next;
}
}
作者:guanpengchn
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/
来源:力扣(LeetCode)
使用哑节点(伪头节点)的好处:
在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的next指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。
例如,在这道题中,如果我们要删除节点y,我们需要知道节点y的前驱节点x,并将x的指针指向y的后继节点。但由于头节点不存在前驱节点,因此我们需要在删除头节点时进行特殊判断。但如果我们添加了哑节点,那么头节点的前驱节点就是哑节点本身,此时我们就只需要考虑通用的情况即可。