141 环形链表 python_leetcode刷题总结--链表问题(python)双指针法

回文链表

题目描述:请判断一个链表是否为回文链表。

输入: 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 (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个节点

题目描述:输入一个链表,输出该链表中倒数第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

环形链表2

题目描述:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 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

删除排序链表中的重复元素2

题目描述:给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中没有重复出现的数字

输入: 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->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

pre = pre.next

pre1 = pre1.next

pre.next = net

net.next = pre1

net = cur.next

return dummy.next

k个一组反转链表

题目描述:给你一个链表,每 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值