持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情
83. 删除排序链表中的重复元素
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,1,2,3,3] 输出:[1,2,3]
思路:由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。
具体地,我们从指针cur 指向链表的头节点,随后开始对链表进行遍历。如果当前cur 与cur.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 deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
ListNode cur = head;
while (cur.next != null) {
if (cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
234. 回文链表
示例 1:
输入: 1->2 输出: false
示例 2:
输入: 1->2->2->1 输出: true
将值复制到数组中后用双指针法,因为数组可以随机访问。
/**
* 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 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){
if(!vals.get(front).equals(vals.get(back))){
return false;
}
front++;
back--;
}
return true;
}
}
时间复杂度:O(n),其中 n指的是链表的元素个数。 空间复杂度:O(n),其中 n指的是链表的元素个数,使用了一个数组列表存放链表的元素值。
141. 环形链表
给定一个链表,判断链表中是否有环。如果链表中存在环,则返回 true 。 否则,返回 false 。
示例:
思路1:哈希表 遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。如果有被访问过的节点说明存在环,否则不存在。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
dict = {}
while head:
if head not in dict:
dict[head] = 1
else:
return True
head = head.next
return False
思路2:快慢指针 定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,两指针相遇,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
//如果我们将两个指针初始都置于 head,那么 while 循环就不会执行。
//因此,我们可以假想一个在 head 之前的虚拟节点,慢指针从虚拟节点移动一步到达 head,
//快指针从虚拟节点移动两步到达 head.next,这样我们就可以使用 while 循环了。
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;
}
}
时间复杂度:O(N),其中 N是链表中的节点数。 当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N轮。 空间复杂度:O(1)。我们只使用了两个指针的额外空间。
拓展:如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。
142. 环形链表 II
与上题类似,给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
思路:哈希表
字典记录每个节点的下标,返回索引为 环所在的位置 的链表节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
dict, i, j = {}, 0, 0
while head:
if head not in dict:#没找到环,加入字典
dict[head] = i
i+=1
else:#找到环,返回索引为 环所在的位置 的链表节点
while j < dict[head]:
head = head.next
j += 1
return head
head = head.next#继续向后搜索
return None