链表
第一题
解法
[边界] - n的取值范围是1-链表长度,边界为n=1和n=链表长度
[思路] - 要求移除倒数第n个,考虑双指针:指针1先移动n+1步,指针2再移动(指针1指向None时,指针2刚好指向倒数第n+1个结点)
[改进] - 对于边界n=链表长度时,指针1移动n+1会报错。在开头定义一个dunmmy结点
解法1
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
p = head
for _ in range(n+1):
if not p: # 边界-正数第一个结点
return head.next
p = p.next
q = head
while p:
p = p.next
q = q.next
q.next = q.next.next
return head
改进: 开头定义一个dummy结点
class Solution(object):
def removeNthFromEnd(self, head, n):
dummy = ListNode(-1)
dummy.next = head
p = dummy
for _ in range(n+1):
p = p.next
q = dummy
while p:
p = p.next
q = q.next
q.next = q.next.next
return dummy.next
第二题
解法 看到链表题,想到递归。 刚开始想用非递归写,发现有点绕。 [解法1] - 递归解法 [解法2] - 非递归,参照递归的思想
解法1 递归,截止条件-链表为空/只有一个元素。判断head和head.next是否相等,若相等,递归操作head.next;否则, 返回head
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next:
return head
if head.val == head.next.val:
return self.deleteDuplicates(head.next)
head.next = self.deleteDuplicates(head.next)
return head
解法2 非递归,将递归改造成非递归。外面套一层while, 内部p = p.next. 为代码简洁起见,引入dummy指针pNode
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteDuplicates(self, head):
if not head or not head.next:
return head
pNode = ListNode(-1)
pHead = pNode
p = head
while p:
while p.next and p.val == p.next.val:
p = p.next
pNode.next = p
pNode = pNode.next
p = p.next
return pHead.next
第三题
解法1
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next:
return head
node = self.reverseList(head.next)
head.next.next = head
head.next = None
return node
解法2 非递归解法,定义两个指针
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
pre = None
cur = head
while cur:
temp = cur.next
cur.next = pre
pre, cur = cur, temp
return pre
第四题
思路 开始写的时候发现很麻烦,好几个特殊用例(比如m=1)要考虑。所以考虑加一个dummy结点。
把要翻转的一段先进行翻转,再把它和前后的链表接起来
解法
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
dummy = ListNode(-1)
dummy.next = head
node = dummy
for _ in range(m-1):
node = node.next
pre = node.next
cur = pre.next
for _ in range(n-m):
temp = cur.next
cur.next = pre
pre, cur = cur, temp
# 先和后面串起来
node.next.next = cur
node.next = pre
return dummy.next
我们可以看出来,总共需要n-m步即可,
第一步是将结点3放到结点1的后面,
第二步将结点4放到结点1的后面。这是很有规律的操作,那么我们就说一个就行了,比如刚开始,pre指向结点1,cur指向结点2,
然后我们建立一个临时的结点t,指向结点3(注意我们用临时变量保存某个结点就是为了首先断开该结点和前面结点之间的联系,
这可以当作一个规律记下来),然后我们断开结点2和结点3,将结点2的next连到结点4上,也就是 cur->next = t->next,
再把结点3连到结点1的后面结点(即结点2)的前面,即 t->next = pre->next,最后再将原来的结点1和结点2的连接断开,
将结点1连到结点3,即 pre->next = t。这样我们就完成了将结点3取出,加入结点1的后方。
第二步将结点4取出,加入结点1的后方,也是同样的操作,这里就不多说了,请大家自己尝试下吧,参见代码如下:
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
if m == n or not head:
return head
dummy = ListNode(0)
dummy.next = head
prev = dummy
for i in range(m - 1):
prev = prev.next
#prev record the node before reverse substring
st, then = prev.next, prev.next.next
for i in range(n - m):
st.next = then.next #
then.next = prev.next
prev.next = then #this step will correctly reconnect substring
then = st.next
return dummy.next
第五题
解法
第一次遍历,记录链表长度size
有k%size个结点翻转到了前面
解法 记录长度,两次遍历 - (先把链表连成环)
① 第一次遍历时通过tail把链表变成了环 (尾节点指向头节点)
② 第二次遍历长度为size-k-1,遍历结束后p指向不需要反转部分的最后一个节点
③ 因为最后return 反转后链表的头节点(比如题目中的例子,就是返回头节点4开头的链表)
----后三行代码
node=p.next 用来记录然后return
p.next=None(题目中的例子为例,反转后尾节点是3;如果没有这句话,节点3是指向4的)
return node(return 4)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def rotateRight(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
if not head or not head.next:
return head
# Mark size and tail
size = 1
tail = head
while tail.next:
tail = tail.next
size += 1
tail.next = head
# Rotate
k = k % size
p = head
for _ in range(size-k-1):
p = p.next
node = p.next
p.next = None
return node
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
if not head or k==0:
return head
size=0;
tmp=ListNode(0)
tmp.next=head
cur=tmp
while cur.next:
size+=1
cur=cur.next
#此时cur指向最后一个结点,这里先和head接上,变成一个环
cur.next=head
#找到新的头结点的前一个结点,摘链
for i in range(size-k%size):
cur=cur.next
#此时cur指向新的头结点的前一个结点
new_head=cur.next
#摘链
cur.next=None
return new_head
第六题
[143. Reorder List(https://leetcode.com/problems/reorder-list)
题目思路是把链表拆成两个长度相等的链表,然后将后面的链表翻转,重新连起来。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
if not head or not head.next or not head.next.next:
return
slow=fast=head
while fast.next and fast.next.next:
slow=slow.next
fast=fast.next.next
node1=head
node2=slow.next
slow.next=None
dummy=ListNode(-1)
dummy.next=node2
p=node2.next
node2.next=None
while p:
temp=p
p=p.next
temp.next=dummy.next
dummy.next=temp
node2=dummy.next
p1=node1
p2=node2
while p2:
temp1=p1.next
temp2=p2.next
p1.next=p2
p2.next=temp1
p1=temp1
p2=temp2
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
if not head or not head.next or not head.next.next:
return
slow=fast=head
while fast.next and fast.next.next:
slow=slow.next
fast=fast.next.next
node1=head
node2=slow.next
slow.next=None
node2=self.reverseList(node2)
p1=node1
p2=node2
while p2:
temp1=p1.next
temp2=p2.next
p1.next=p2
p2.next=temp1
p1=temp1
p2=temp2
def reverseList(self,head):
pre=None
cur=head
while cur:
temp=cur.next
cur.next=pre
pre,cur=cur,temp
return pre
第七题
解法 法1 - 非递归,逐次比较 法2 - 递归
解法1 非递归
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy=ListNode(-1)
p=dummy
while l1 and l2:
if l1.val<l2.val:
p.next=l1
l1=l1.next
else:
p.next=l2
l2=l2.next
#融合后链表的下一位,当前位置刚刚赋值
p=p.next
#把剩余的链表排在后面
p.next=l1 or l2
#返回融合后链表从第二个对象开始,第一个对象是自己创建的ListNode(-1)
return dummy.next
解法2 递归
class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
if not l1:
return l2
if not l2:
return l1
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
第八题
160. Intersection of Two Linked Lists
解法 1) 可以从尾部遍历链表就好了(当然不可能) 2) 知道两个链表的长度差,
长链表的先走几步就好了 我们可以用两个指针分别遍历两个链表,当其中一个为空时,
令其指向另一个链表的头部(类似长度差)
p= headA
q = headB
lenA=lenB=0
while p:
p= p.next
lenA +=1
while q:
q= q.next
lenB +=1
p=headA
q= headB
if lenA>lenB:
for i in range(lenA-lenB):
p= p.next
else:
for i in range(lenB-lenA):
q= q.next
while p!=q:
p=p.next
q= q.next
return p
假如长度不一致(比如例子),那短链表会先遍历完,然后会指向长链表的头。
而长的遍历完一次,继续向下则指向短链表的头。这时候,他们会同时到达相等的节点(如果没有就一起遍历到结束)。
也就是(长链表+短链表)的长度等于(短链表+长链表)的长度。所以有相同节点时候会遇到。很神奇!
p和q是两个指针。跳出while循环有两种方式,一个是两个链表都遍历完了,另一个是找到共同节点。
解法
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
p1 = headA
p2 = headB
while p1 != p2:
if p1:
p1 = p1.next
else:
p1 = headB
if p2:
p2 = p2.next
else:
p2 = headA
return p1
第九题
创建两个节点,第一个慢节点单步走,第二个快节点两步走,如果没有环,则快节点会首先走到链表尾,退出循环,返回False。如果存在环,则快节点会再第二圈或者第n圈的地方追上慢节点,直到两者相等,则返回True
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
slow=fast=head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
return True
return False
第十题
解题思路:
设置一个dummy头结点,然后将原链表中的节点一个个拆下来,按升序拼接到新的链表中。
优化)可以根据尾节点进行优化,如果要插入的节点比尾节点还大的话,就不用从头开始找插入的位置了。
# 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:
dummy = ListNode(-1)
tail = dummy
while head:
# First check the tail node, if tail.val > head.val ——> reset
if tail and tail.val > head.val:
tail = dummy
while tail.next and tail.next.val < head.val:
tail = tail.next
# Insert
tail.next, head.next, head = head, tail.next, head.next
return dummy.next
第十一题
138. Copy List with Random Pointer
题目描述
给定一链表 - 每个结点包含一个额外的random指针,指向任何链表中的结点或None。 返回链表的深拷贝。
例子
略
思想 两种解法。难点在于如何定位random指针。 法1 - 首先复制next指针,将其复制到已知链表的next后面;这样待求链表的random指向的结点 = 已知链表的random指向的结点的next。 法2 - 建立已知链表和待求链表相应结点的一个hash。
"""
# Definition for a Node.
class Node:
def __init__(self, val, next, random):
self.val = val
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if head is None:
return None
#add clones to the chain
n = head
while n:
clone = Node(n.val,None,None)
next_node = n.next
clone.next = next_node
n.next = clone
n = clone.next
#update random pointers
n = head
while n:
clone = n.next
if n.random:
clone.random = n.random.next
n = clone.next
#extract clones & restore original chain
n = head
res = Node(0,None,None)
clone_head = res
while n:
next_original_node = n.next.next
#extract clones
clone = n.next
clone_head.next = clone
#restore original chain
n.next = next_original_node
# move forward
n = next_original_node
clone_head = clone_head.next
return res.next
第十二题
142. Linked List Cycle II
题目描述
给定单链表,返回链表中环的起始结点。如果没有环,返回None。
思路:快慢指针相遇点到环入口的距离 = 链表起始点到环入口的距离
- 求环的长度
先判断是否是环。假设环长为L,不是环的长度为M,在环中的N处相遇。
那么fast走了M+K1L+N,slow走了M+K2L+N。fast=2slow,M+K1L+N=2*(M+K2*L+N),N=(K1-K2)*L-M。
可以看到从N出发再走M就到了环的起始点。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next:
return None
slow=fast=head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
fast=head
while slow!=fast:
slow=slow.next
fast=fast.next
return slow
return None
第十三题
148. Sort List
题目描述
链表排序,要求时间复杂度O(nlogn),空间复杂度O(1)
例子 Example 1:
Input: 4->2->1->3 Output: 1->2->3->4
Example 2:
Input: -1->5->3->4->0 Output: -1->0->3->4->5
思想 联想几个时间复杂度O(nlogn)的排序:堆排序(麻烦),快排(要移位,还要交换),归并排序(找中点,可以用快慢指针找中点)
class Solution(object):
def sortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next:
return head
fast = slow = pre = head
while fast and fast.next: # Mark: pre
pre = slow
slow = slow.next
fast = fast.next.next
pre.next = None
left = self.sortList(head)
right = self.sortList(slow)
return self.merge(left, right)
def merge(self, left, right):
dummy = ListNode(-1)
p = dummy
while left and right:
if left.val < right.val:
p.next = left
left = left.next
else:
p.next = right
right = right.next
p = p.next
if left:
p.next = left
else:
p.next = right
return dummy.next
第十四题
题目描述
设计一种LRU的数据结构,支持下面两个操作:get和put。 get(key) - 如果key在缓存中,返回key对应的value(总是正数),否则返回-1 put(key, value) - 设置值;如果key不存在,插入值。当缓存达到容量时,它应该在插入新项前使最近最少使用的项无效。 最好O(1)的时间复杂度
如果一个数据长时间没有使用,且又有新的数据加入,那么应该将最长时间没有使用的数据去除
思想 首先,涉及查询,考虑用字典来存储。 难点 - 如何找到哪个节点最久没被使用? 定义一个双向链表,尾部的节点最久没被使用。每次访问某节点时,删除该节点,并把该节点放在头部。
注意双向链表的head和tail没有数据。
解法 双向链表的插入和删除
class LRUCache(object):
class Node(object):
def __init__(self, key, value):
self.key = key
self.value = value
self.next, self.prev = None, None
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.size = 0
self.dic = {} # self.dic[key] = Node(key, value)
self.head, self.tail = self.Node(-1,-1) , self.Node(-1,-1)
self.head.next, self.tail.prev = self.tail, self.head
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
node.prev, node.next = None, None
def insertNode(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev, self.head.next = node, node
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.dic:
return -1
else:
node = self.dic[key]
self.removeNode(node)
self.insertNode(node)
return node.value
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if key in self.dic:
node = self.dic[key]
self.removeNode(node)
self.insertNode(node)
node.value = value
else:
# Remove the tailest node
if self.size == self.capacity:
remove = self.tail.prev
self.removeNode(remove)
del self.dic[remove.key]
self.size -= 1
# Insert the headest node
node = self.Node(key, value)
self.insertNode(node)
self.dic[key] = node
self.size += 1
双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
class Node():
def __init__(self,data=None):
self.data=data
self.pre=None
self.next=None
"""
初始化双向链表
"""
def __init__(self):
head=Node()
tail=Node()
self.head=head
self.tail=tail
self.head.next=self.tail
self.tail.pre=self.head
def __len__(self):
length=0
node=self.head
while node.next!=self.tail:
length+=1
node=node.next
return length
def append(self,data):
node=Node(data)
pre=self.tail.pre
pre.next=node
node.pre=pre
self.tail.pre=node
node.next=self.tail
return node
def get(self,index):
length=len(self)
if index>=0:
index=index
else:
index=length+index
if index>=length or index<0:
return None
node=self.head.next
while index:
node=node.next
index-=1
return node
def set(self,index,data):
node=self.get(index)
if node:
node.data=data
return node
def insert(self,index,data):
length=len(self)
if abs(index+1)>length:
return False
index =index if index>=0 else index+1+length
next_node=self.get(index)
if next_node:
node=Node(data)
pre_node=next_node.pre
pre_node.next=node
node.pre=pre_node
node.next=next_node
next_node.pre=node
return node
def delete(self, index):
node = self.get(index)
if node:
node.pre.next = node.next
node.next.pre = node.pre
return True
return False
def clear(self):
self.head.next = self.tail
self.tail.pre = self.head