文章目录
2019/9/14:两数相加
题目链接: https://leetcode-cn.com/problems/add-two-numbers/
这题可以从两个方向去考虑,第一个是将链表转换成数字或者是列表甚至是任意形式,因为转换过后比原来在链表的基础上更好做操作,第二就是在原链表上做,虽然比较难理解,但代码更加简洁。所以我们可以先考虑第一种,先转换成数字,然后再转换回来:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
def convert(listNode):
nums = ""
if listNode is None:
return
while listNode != None:
nums = str(listNode.val) + nums
listNode = listNode.next
return int(nums)
nums1,nums2 = convert(l1),convert(l2)
num = nums1 + nums2
root = ListNode(num%10)
l3 = root
num = num // 10
while num != 0:
l3.next = ListNode(num%10)
l3 = l3.next
num = num // 10
return root
然后还可以直接用链表操作,这个是最高赞的那个代码,然后我加了一点我的注释
class Solution:
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
re = ListNode(0)
r=re # 定义一个指针
carry=0 # 进位标志位
while(l1 or l2):
x= l1.val if l1 else 0 # 非空判断
y= l2.val if l2 else 0
s=carry+x+y
carry=s//10
r.next=ListNode(s%10) # 确定下一个节点
r=r.next # 更新当前节点
if(l1!=None):l1=l1.next
if(l2!=None):l2=l2.next
if(carry>0): # 边界情况
r.next=ListNode(1)
return re.next # 返回实际起始节点
2019/9/14:反转链表
题目链接:https://leetcode-cn.com/problems/reverse-linked-list/
这题目隐隐约约在哪里看过,仔细一想好像是我之前有写过剑指offer 66中第三题同样也是链表反转,但那个要求返回一个数组,而这个是原格式,虽然依然可以用链表转数组再转链表,但那样感觉过于麻烦,然后看了下面这篇一个大佬写的特别详细的解答方案,我发现python的特性真是奇妙:
https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-zhi-chang-gui-si-lu-he-die-dai/
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
"""
:type head: ListNode
:rtype: ListNode
"""
current_node,prev = head,None # 定义一个指向当前的指针,一个空指针,prev作用是空指针变为前置指针
while current_node:
"""当前指针的下一个引用域指向前置指针,前置指针优空指针反向,当前指针指向后面保证循环进行"""
current_node.next, prev, current_node = prev, current_node, current_node.next
return prev
2019/9/15:删除链表中的节点
题目链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
这道题刚开始以为是直接考研一下链表的性质,和前面的最小栈一样,然后直接就开始写,发现不对的是没有head,我尝试自己定义了一个,嗯。。就完全走偏了,然后发现这题真实的代码只有两行,或者说一行:
class Solution:
def deleteNode(self, node):
node.val = node.next.val
node.next = node.next.next
# node.val,node.next = node.next.val,node.next.next
2019/9/15:环形链表
题目链接:https://leetcode-cn.com/problems/linked-list-cycle/
判断没有没有环,我们其实可以定义两个指针,一个快,一个慢,假如有环,那么则他们会在某一点相遇,反之不会,就如下图中的跑道,可以跑无数圈,那么快速的人总有一圈会追尾。
那么代码为:
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
fast, slow = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow: return True
return False
另外就是还有一种取巧的方式,不管链表里的值是什么,我可以将里面的内容全部覆盖掉,那么之后我只要判断循环过程中存不存在相同内容就行了,这种方法可以去看讨论里最高评论量那个。
2019/9/17:旋转链表
题目链接:https://leetcode-cn.com/problems/rotate-list/
该题需要理解的是当链表在旋转时,我们要找到旋转点的位置进行分割,然后再将分开的两段拼起来,另外如果k = 6其实就是相等于k=1;所以我们为了防止循环次数,得到链表长度n,k = n - k % n - 1.
然后第一种思路可以是找到断开的点,将其分成两段链表,将后一段保存,然后依次加入前一段中,如下图,那么代码为:
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head or not head.next: return head
# 链表个数
num = 0
p = head
while p:
num += 1
p = p.next
k = num - k % num - 1
p = head
# 找前一段链表
while k:
p = p.next
k -= 1
head1 = p.next # 通过head1保存后一段
if not head1: return head
#前一段链表最后至空
p.next = None
p = head1 # 此时P为4 —— > 5 ——> None
# 后一段链表和前一段链表连接起来
while p.next:
p = p.next
p.next = head
return head1
第二种方案就是先将链表闭合成环,找到相应的位置断开这个环,确定新的链表头和链表尾。
def rotateRight(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
# 思路:转化为循环链表,然后在合适的位置断开
if not head:
return None
tail = head
count = 1
while tail.next != None: # 遍历找到尾节点位置
tail = tail.next
count += 1
tail.next = head # 尾节点指向头,构建循环链表
ptr = head
loop = count - (k%count) # 移动步数
for i in range(loop): # 同时移动头节点和尾节点
tail = tail.next
ptr = ptr.next
tail.next = None # 断开环形链表
return ptr
2019/9/19:交替打印foobar
题目链接:https://leetcode-cn.com/problems/print-foobar-alternately
首先做这个题目之前我们需要知道两个python的内置模块,一个是queue队列,还有一个就是Semaphore或者BoundedSemaphore信号量,题目意思需要我们用两个线程去轮流调用其中两个方法,第一种方式可以用队列来实现,讲一个任务添加到队列里,执行结束后再阻塞既能完成交替操作:
import queue
class FooBar:
def __init__(self, n):
self.n = n
self.foo_queue = queue.Queue()
self.bar_queue = queue.Queue()
self.foo_queue.put('')
def foo(self, printFoo: 'Callable[[], None]') -> None:
for i in range(self.n):
# printFoo() outputs "foo". Do not change or remove this line.
self.foo_queue.get()
printFoo()
self.bar_queue.put('')
def bar(self, printBar: 'Callable[[], None]') -> None:
for i in range(self.n):
# printBar() outputs "bar". Do not change or remove this line.
self.bar_queue.get()
printBar()
self.foo_queue.put('')
第二种即是用信号量:Semaphore类和BoundedSemaphore类都是信号量类,每次有线程获得信号量的时候(即acquire())计数器-1,释放信号量时候(release())计数器+1,计数器为0的时候其它线程就被阻塞无法获得信号量。当计数器为设定好的上限的时候BoundedSemaphore就无法进行release()操作了,Semaphore没有这个限制检查。
import threading
class FooBar:
def __init__(self, n):
self.n = n
self.foo_lock = threading.Semaphore()
self.foo_lock.acquire()
self.bar_lock = threading.Semaphore()
self.bar_lock.acquire()
def foo(self, printFoo: 'Callable[[], None]') -> None:
for i in range(self.n):
# printFoo() outputs "foo". Do not change or remove this line.
printFoo()
self.bar_lock.release()
self.foo_lock.acquire()
def bar(self, printBar: 'Callable[[], None]') -> None:
for i in range(self.n):
# printBar() outputs "bar". Do not change or remove this line.
self.bar_lock.acquire()
printBar()
self.foo_lock.release()
2019/9/20:环形链表 II
题目链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/
这题基本上和之前的环形链表一样,只是中间返回值稍微改了而已,同样能和上面方法一致,这里可以考虑快慢指针和哈希表:
class Solution(object):
def detectCycle(self, head):
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast
另一种就是哈希表了:
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
visited = set()
node = head
while node is not None:
if node in visited:
return node
else:
visited.add(node)
node = node.next
return None
2019/9/21:排序链表
题目链接:https://leetcode-cn.com/problems/sort-list/
一般针对数组来讲,时间复杂度为O(nlogn)的排序算法有快排、堆排、归并、快排,而堆排空间复杂度为O(1),归并空间复杂度为O(n),但此题因为是链表,依据排序的原理,堆排序是通过不断 交换元素 实现排序的:在数组中交换两个索引的元素时间复杂度为 O(1),而链表 交换元素 本身是 O(N) 时间复杂度,所以也算是两种相反的方向吧。
另外此题代码加了点自己注释整理为:
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head # termination.
# cut the LinkedList at the mid index.
slow, fast = head, head.next # 使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后慢的所在位置就是中间位置,这样就分成了两段。
while fast and fast.next:
fast, slow = fast.next.next, slow.next # 快慢指针,快的为2,慢的为1
mid, slow.next = slow.next, None # save and cut.从中间分割,中点为奇数个中间节点或者偶数个的左边一位,然后中点的下一个引用域置空
# recursive for cutting.
left, right = self.sortList(head), self.sortList(mid) # 递归将其分成更小份
# merge `left` and `right` linked list and return it.
h = res = ListNode(0) # 做一个头指针
while left and right: # 归并排序
if left.val < right.val:
h.next, left = left, left.next
else:
h.next, right = right, right.next
h = h.next
h.next = left or right # 左右子链表可能不相同,如果还有没有走完的,比如说7 = 3 + 4,那么还有值就全部添加到尾部
return res.next # 头指针指向的下一个引用域即是我们将排好序的结果