系列文章导图:
查看所有系列文章: xiaozhuanlan.com/leetcode_tc
Linked List
1. 知识点
1.1 概念
链表想必大家都不陌生,链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该节点还有一个元素和一个指向另一条链表的引用。
1.2 链表常见操作
1.2.1 插入
1.2.2 删除
1.3 双向链表
简单介绍一个双向链表,与上面单链表不同的是,双向链表既有前置节点,也有后置节点。
1.4 时间复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| prepend | O(1) | 头部增加元素 |
| append | O(1) | 尾部增加元素 |
| lookup | O(N) | 查找 |
| insert | O(1) | 插入 |
| delete | O(1) | 删除 |
上表中总结,只有查找的时间复杂度是O(N),因为查找需要从头节点依次往下找。其余操作都是O(1),上面也简单介绍插入和删除,只需要动2次Next指针,即O(1)的时间复杂度。
2. 面试题
算法代码实现使用python实现,如果不了解python,则使用自己喜欢的语言实现即可, 完整代码地址github.com/CrystalSkyZ…,完整代码里可以自己进行用例调试。
2.1 206. Reverse Linked List
leetcode link 206 leetcode第206题,反转一个单链表。 题目要求: Reverse a singly linked list.
Example: Input: 1->2->3->4->5->NULL Output: 5->4->3->2->1->NULL
题目要求是反转一个单链表,思路的相对简单,即将每一个指针的Next指向它的前驱节点即可。这类链表题目在面试过程之中主要考察的是代码实现能力,思维相对简单,所以在面试之前一定要有所准备,这样才能紧张的面试过程中快速精炼的写出代码。
python实现:
class Solution:
def reverseList(self, head)
"""
:type head: ListNode
:rtype: ListNode
"""
cur, prev = head, None
while cur:
cur.next, prev, cur = prev, cur, cur.next
return prev
要实现每一个指针指向前置节点,则需要2个节点,一个是当前节点(cur),一个是前置节点(prev),然后一直循环cur节点,再就是对前置节点和当前节点进行赋值操作。
2.2 24. Swap Nodes in Pairs
leetcode link 24 leetcode第24题,反转相邻节点。 题目要求: Given a linked list, swap every two adjacent nodes and return its head.
Example: Given 1->2->3->4, you should return the list as 2->1->4->3.
题目是要求反转相邻节点,思路是循环整个链表,将相邻节点进行反转。思路简单,但是怎么让代码显得很精炼了。
python实现:
class Solution:
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev, prev.next = self, head
while prev.next and prev.next.next:
a = prev.next
b = a.next
prev.next, b.next, a.next = b, a, b.next
prev = a
return self.next
首先需要有三个节点,2个相邻节点和后一个节点。第一行代码如果不熟悉python的可能看不懂,其实是将类Solution对象自己赋值prev, prev.next则表示next是类Solution的一个属性,prev.next才是表示head节点。
需要提醒的是链表长度是偶数的话,是刚好全部2个相邻节点反转,如果是奇数则最后一个节点保持不变。
python多变量赋值有时候确实是晦涩难懂,它是一次性赋值,即先算好等号右边的所有值,然后一次性赋给左边。我们可以先简单枚举一下 1->2->3->4,如果需要反转1和2,则它的下一个状态是什么。先算好右边的值: b=2 a=1 b.next=3, 赋值给左边,则: prev.next=2 b.next=1 a.next=3 赋值之后: b=2 a=1 b.next=1 a.next=3。变成了 2->1->3->4, 可以简单理解为赋值之后,prev.next 就是 b, b.next=1=a=1, a.next=3 串起来就是 2->1->3->4
下面给出1->2->3->4->5具体一次while循环的打印,方便大家更加理解,详细请看完整代码地址https://github.com/CrystalSkyZ/leetcode_tc:
print(a.val, a.next.val, b.val, b.next.val, pre.next.val, pre.next.next.val)
# (1, 2, 2, 3, 1, 2)
# 进行赋值:
pre.next, b.next, a.next = b, a, b.next
print(a.val, a.next.val, b.val, b.next.val, pre.next.val, pre.next.next.val)
# (1, 3, 2, 1, 2, 1)
print(linear.printLiner(self.next)) # 遍历整个链表
# [2, 1, 3, 4, 5]
pre = a
print(pre.val, a.val, self.next.val, self.next.next.val)
# (1, 1, 2, 1)
2.3 141. Linked List Cycle
leetcode link 141 题目要求: Given a linked list, determine if it has a cycle in it.
Example:
题目要求判断一个链表是否有环,通常有几种思路解法:
-
暴力解法, 一直遍历循环看下一个节点是否为null, 如果是环的话则会一直死循环,可以判断一个时间,比如1s或0.5s,则结束。这种解法只是一种思路,实际面试中肯定不是一种好的解法。
-
Set判重,思路跟思路一一样,一直遍历循环,区别就是每一次遍历,在当前节点留下记号,用set存储起来,如果下一个节点在set里则证明有环,否则无环。时间复杂度是O(N),但是空间复杂度由于用了set,则是O(N)。
-
龟兔赛跑,则有一个快指针和一个慢指针,快指针每次走2步,慢指针每次走1步,如果有环的情况下,则2者肯定会相遇,即判断有环。这种思路方式如果没有事先去刷过这个题,是比较难想到的。
代码实现:
- Set判重:
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
exist = set()
while head:
if head in exist:
return True
exist.add(head)
head = head.next
return False
- 龟兔赛跑:
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
fast = slow = head
while slow and fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
3. 思考题
leetcode上还有几题跟上面讲的类似,可以先去做一下,下一篇再一起交流解法和代码实例:
如果是国内地址的话,则把路由换成https://leetcode-cn.com/
更多精彩文章请关注公众号 天澄的技术笔记 (ChengTian_Tech)