题目描述:请判断一个链表是否为回文链表。
输入: 1->2
输出: false
输入: 1->2->2->1
输出: true
思路分析:(1)快慢指针:直觉上只要我们找到链表中的中间节点,然后利用一个指针从头开始遍历,一个指针从尾部开始遍历,比较两个指针所指的值是否相同就可以了,但是链表是单向的,我们无法做到从尾节点向前遍历,于是我们想到是否可以将链表从中间节点位置反转然后就可以比较了。那么这里存在一个问题,我们怎么找到一个链表的中间位置呢?利用快慢指针,一个慢指针每次走一步,一个快指针每次走两步,快指针走到尾节点,那么慢指针就是我们要找的中间节点。这里存在链表节点个数是偶数还是奇数的不同情况,但都可以统一处理。空间复杂度为O(1)。
(2)我们可以遍历链表,并将每个节点的值存储在数组中,然后就可以利用双指针一个头指针一个尾指针从数组两边向中间遍历,比较值是否相同。这样处理因为需要建立一个数组,空间复杂度为O(n),
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if not head:
return True
#定义两个快慢指针,寻找链表中间位置
slow = fast = head
while fast:
slow = slow.next
fast = fast.next.next if fast.next else fast.next#当链表中的节点是偶数的时候
#将链表中间节点之后节点反转
p, q = slow, None
while p:
q,q.next,p = p,q,p.next
#重新以head开始比较反转链表
while q:
if q.val!=head.val:
return False
q = q.next
head = head.next
return True
思路分析:(1)双指针法:指针 prev 用来表示前驱节点,指针 cur 用来遍历链表,每次循环改变将 pre->cur 的方向改变为 pre<-cur,直到遍历结束。
(2)同上一题思路(2),建立一个数组保存每个节点的值,然后创建一个新链表,
代码如下
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur, prev = head, None
while cur:
cur.next, prev, cur = prev, cur, cur.next
return prev
题目描述:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
思路分析:快慢指针法,我们让快指针先走k步,然后两个指针再一起走,知道快指针指向尾结点,这是慢指针与快指针相隔k个节点,此时慢指针的下一个节点就是我们需要的倒数第K个节点。
代码如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
fromer, latter = head, head
for _ in range(k):
fromer = fromer.next
while fromer:
fromer, latter = fromer.next, latter.next
return latter
题目描述:输入两个链表,找出它们的第一个公共节点。
思路分析:双指针法,一个指针p1从链表A的头部开始遍历,一个指针p2从链表B的头部开始遍历,到p1或者p2到达尾部的时候即p1.next=None的时候,令其转向B的头部,同理P2指向A的头部,如果两个链表有公共节点,则p1和p2一定会相遇,没有公共节点那么他们就会同时指向None,不管他们是什么,我们只需要返回他们两个指针指向的节点相等的时候的节点就可以的。
代码如下:
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1, p2 = headA, headB
while p1!=p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
return p1
题目描述:给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
思路分析:快慢指针法,如果链表存在环,那么快指针与慢指针在某个时刻会相遇,否则快指针会先指向None。
class Solution:
def hasCycle(self, head: ListNode) -> bool:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow==fast:
return True
return False
题目描述:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
思路分析:(1)正常的就是用一个指针遍历链表,然后用一个哈希表或者数组存储遍历的值,直到重复的时候,那个节点就是我们要找的入环节点,但是这样空间复杂度为O(n);
(2)我们先利用双指针法找到快慢指针相遇的节点;如何找呢?
当两者第一次相遇时,慢指针slow走了(F+a)步,快指针fast走了(F+a+b+a),所以,
2(F+a)=F+a+b+a ----->F=b
通过以上我们可以知道,在两个指针第一次相遇后,慢指针再走b=F步到入环处,然后在让一个指针从起始点走F=b也到起始点,这样这两个指针相遇点就是入环点。
# 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:
#判断链表是否有环,如果有环,找到其相遇点
def cycle(head):
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow==fast:
return fast
return None
#如果有环,初始化一个指针p,然后令p和相遇点同时移动,直到相遇
fast = cycle(head)
if fast:
p = head
while p!=fast:
p = p.next
fast = fast.next
return p
else:
return None
题目描述:给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中没有重复出现的数字
输入: 1->2->3->3->4->4->5
输出: 1->2->5
思路分析:使用双指针pre,cur,建立哑结点,pre初始化指向head,cur指向head.next,如果cur.val==cur.next.val,则说明有重复数字,记re = cur.val,然后移动cur直到cur.val!=re,然后将pre.next = cur.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
thead = ListNode(-1)
thead.next = head
pre,cur = None,thead
while cur:
pre=cur
cur=cur.next
while cur and cur.next and cur.next.val == cur.val:
re=cur.val
while cur and cur.val==re:
cur=cur.next
pre.next=cur
return thead.next
题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
输入: 1->1->2->3->3
输出: 1->2->3
思路分析:与上一题不同的是,这次只需要删除所有重复的元素,让所有元素出现一次就行。使用单指针遍历链表,如果head.val==head.next.val,就将head.next指向head.next.next.否则移动指针head,head=head.next
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if head == None or head.next == None:
return head
dummyHead,dummyHead.next = ListNode(0),head
while head != None and head.next != None:
if head.val == head.next.val:
head.next = head.next.next
else:
head = head.next
return dummyHead.next
题目描述:反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NUL
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
dummy = ListNode(-1)
dummy.next = head
pre = dummy
# 找到翻转链表部分的前一个节点, 1->2->3->4->5->NULL, m = 2, n = 4 指的是 节点值为1
for _ in range(m-1):
pre = pre.next
# 用双指针,进行链表翻转
print(pre)
node = None
cur = pre.next
for _ in range(n-m+1):
tmp = cur.next
cur.next = node
node = cur
cur = tmp
print(cur)
# 将翻转部分 和 原链表拼接
pre.next.next = cur
pre.next = node
return dummy.next
基本思路:利用两个指针cur,net表示链表相邻的两个位置,依次移动两个指针,当后一个指针所指的值小于前一个指针,再利用一个指针从头开始查找合适的插入点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
cur, net = head, head.next #初始点
dummy, dummy.next = ListNode(float("inf")), head
while net:
if net.val>cur.val:
cur = cur.next
net = net.next
else:
cur.next = net.next #将要遍历的下一个节点先储存起来
pre, pre1 = dummy, dummy.next
while pre1.val<net.val:
pre = pre.next
pre1 = pre1.next
pre.next = net
net.next = pre1
net = cur.next
return dummy.next
题目描述:给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
思路分析:迭代加反转链表,
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
class Solution:
def reverse(self, head:ListNode, tail:ListNode):
prev = tail.next
cur = head
while prev!=tail:
cur.next, prev, cur = prev, cur, cur.next
return tail, head
def reverseKgroup(self,head:ListNode, k:int):
hair = ListNode(0)
hair.next = head
pre = hair
while head:
tail = head
for i in range(k):
tail = tail.next
if not tail:
return hair.next
nxt = tail.next
head, tail = self.reverse(pre,tail)
pre.next = head
tail.next = nxt
pre = tail
head = tail.next
return hair.next