leetcode本周刷题路线:
203. 移除链表元素(简单):
题目描述:
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.value == val的节点,并返回新的头节点。
示例:
解题思路:
设置两个指针 pre 和 cur ,其中 cur 指针用于检测当前节点的值是否与 val相等,若相等则通过 pre 指针将 cur 所在的节点删除 。在链表的 head 前添加一个值为 -1的节点并将原来的 head 指向新添加的节点处, pre 指向 head 处,cur 指向 pre.next 处。pre 与 cur 同时开始遍历链表直至 cur == NULL ,遍历完成则程序运行结束。
删除节点的步骤:
pre.next = cur.next;
cur = cur.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 removeElements(ListNode head, int val) {
ListNode cur, pre;
head = new ListNode(-1, head);
pre = head;
cur = pre.next;
while(cur != null){
if(cur.val == val){
pre.next = cur.next;
cur = cur.next;
continue;
}
pre = pre.next;
cur = pre.next;
}
return head.next;
}
}
206. 反转链表(简单):
题目描述:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例:
解题思路:头插法反转链表
新建一个链表头节点 newhead 、指针 p 和 temp ,其中 p 指针指向 head,temp 指针用于保存 p 所在节点的下一节点的地址。p 指针从 head 处遍历链表,指针 p 指向某一结点,就将该节点添加至newhead后方。当 p == null 时,程序运行结束。
头插法的步骤:
p.next = newhead.next;
newhead.next = p;
解题代码:
/**
* 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 reverseList(ListNode head) {
ListNode newhead = new ListNode(), p, temp;
p = head;
while(p != null){
temp = p.next;
p.next = newhead.next;
newhead.next = p;
p = temp;
}
return newhead.next;
}
}
24.两两交换链表中的节点(中等):
题目描述:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内的值的情况下完成本题(即只能进行节点交换)。
示例:
解题思路:
设置三个指针 pre、p、q ,pre 指向一个为设置值的节点且 pre.next = head,指针 p、q、head 同时指向指针 pre 所指处。将指针 p、q 两两指向链表中相邻节点( p 在 q前),交换指针p、q 所在位置节点的位置后,通过指针 pre 将交换节点后的部分连接起来。
指针 p、 q 所指节点交换并与链表链接步骤:
p.next = q.next;
q.next = p;
pre.next = q;
解题代码:
/**
* 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 swapPairs(ListNode head) {
ListNode pre = new ListNode(), p, q;
System.out.print(pre.val);
pre.next = head;
p = q = head = pre;
while(p.next != null){
p = pre.next;
q = p.next;
if(q == null)
break;
p.next = q.next;
q.next = p;
pre.next = q;
pre = p;
}
return head.next;
}
}
19. 删除链表的倒数第N个结点(中等):
题目描述:
给你一个链表,删除链表的倒数第n个结点,并且返回链表的头节点。
示例:
解题思路:双指针法
在链表表头前添加一个值域为 -1 的节点,将原来指向链表头部的指针 head 指向新添加的节点,设置指针 p1、p2并指向指针 head 所指节点处。先让指针 p1 移动 n 步后,再让指针 p1、p2同时向后移动,此时指针 p1 与 指针 p2 的距离固定为 n,每次移动距离均为1,当指针 p1 移动到链表最后一个节点时,p2当前位置即为所要删除位置节点的前一个节点(也即链表倒数第 n 个节点),执行 p2.next = p2.next.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) {
ListNode p1, p2;
head = new ListNode(-1, head);
p1 = p2 = head;
while(n-- != 0)
p1 = p1.next;
while(p1.next != null){
p1 = p1.next;
p2 = p2.next;
}
p2.next = p2.next.next;
return head.next;
}
}
160. 相交链表(简单):
题目描述:
给你两个单链表的头节点 headA 和 headB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
示例:
解题思路:
设置两个指针 p1, p2,分别指向 headA,headB,当指针 p1、p2 遍历完当前链表后,交换两个指针所指的链表,并从其头节点重新遍历,消除指针 p1、p2 移动的距离差使指针 p1、p2 在第二次经过两链表交点时走过的距离相等。
解题代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null)
return null;
ListNode p1, p2;
p1 = headA;
p2 = headB;
while(p1 != p2){
p1 = p1 == null ? headB : p1.next;
p2 = p2 == null ? headA : p2.next;
}
return p1;
}
}
142.环形链表 II(中等):
题目描述:
给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回null
示例:
解题思路:快慢指针
设置两个指针,分别为 fast 和 slow,指针 fast 一次移动两步,指针 slow 一次移动一步,若该指针 fast 最终会指向 null ,则该链表无环;若该链表有环则指针 fast 和指针 slow 必定会在环内相遇且指针 fast 已在环内循环了至少一次,此时指针 fast 移动的距离是指针 slow 移动距离的两倍。
设链表表头到入环点的距离为 a,入环点到快慢指针相遇点的距离为 b,快慢指针相遇点到下一次入环点的距离为 c。可得:指针 fast 移动的距离为 a + b + c + b, 指针 slow 移动的距离为 a + b。又因为指针 fast 移动的距离是指针 slow 的两倍,故可列出等式 a + b + c + b = 2(a + b),易得 a = c,即,此时再设置一个指针 temp 从链表表头和指针 slow 同时向后遍历,最后必定相交于入环点。
解题代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null)
return null;
ListNode slow = head, fast = head;
boolean hasCycle = false;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
hasCycle = true;
break;
}
}
if (hasCycle) {
ListNode temp = head;
while (slow != temp) {
slow = slow.next;
temp = temp.next;
}
return temp;
}
else
return null;
}
}
学习小结:
链表是比较常见的数据结构之一,也是一些更高级的数据结构的基础。熟练掌握链表对于往后算法和数据结构的学习至关重要,同时,与链表相关的操作必定离不开指针,因此熟悉指针的原理和作用也同样重要。在本周对于链表的学习中学习到了许多只针对操作方法,例如 ” 双指针 “、” 快慢指针 “等,其中 ” 快慢指针 ‘ 这一操作十分的巧妙,让我印象深刻,让我对链表和指针都有了更深层次的理。“ 环形链表 II ” 这道题是我做的第一道通过数学思想来理解的算法题,其中就用到了 ” 快慢指针 “ ,为了理解这道题还是花了点时间的,虽然很难很费脑子,但理解这道题的一瞬间的成就感和满足感是无与伦比的,这就是算法的乐趣吧!