链表基础知识
1. 链表的查询需要时间复杂度为O(n), 插入删除复杂度为O(1)。与数组正好相反。
2. 用python定义链表的方式:
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
3. 链表类型
单链表中的指针域只能指向节点的下一个节点。
双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。既可以向前查询也可以向后查询。
循环链表: 就是链表首尾相连。
4. 删除节点的时候,直接将current节点的指针next指向current.next.next的节点即可。在python里,多余的被删除的节点(current.next)会被内存自动回收,不用手动释放。
5. 涉及链表增删,即元素位数变动的情况,总是加上一个虚拟表头会更加方便。 dummy_head = ListNode(next=head),这个虚拟表头指向真正的表头。如果不加的话,如果删/增的是链表里的第一个元素的话就需要额外写代码处理。
203. Remove Linked List Elements 移除等于Val的元素
Given the head
of a linked list and an integer val
, remove all the nodes of the linked list that has Node.val == val
, and return the new head.
Example:
Input: head = [], val = 1 Output: []
Input: head = [7,7,7,7], val = 7 Output: []
思路:构造虚拟表头dummy_head,以防链表为空,即无ListNode时,不便统一处理。
注意!以下写法中,当current.next 为None,current就是表尾的最后一个元素,且这个元素的值是否等于val在上一个while循环里就已经被判断过了,直接跳出循环即可。while循环里,current从无实际意义的虚拟表头开始,每次判断的都是current.next,即下一个元素的值。
class Solution(object):
def removeElements(self, head, val):
"""
:type head: ListNode
:type val: int
:rtype: ListNode
"""
dummy_head = ListNode(next=head) # 构造虚拟链表头,方便增删元素
current = dummy_head # 当前链表所处元素
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy_head.next
707. Design Linked List
Example: Input ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] Output [null, null, null, null, 2, null, 3]
思路:
同样的,继续设置一个dummy_head为了方便处理增删元素的情况。同时,记录一个链表长度更加方便查询当前函数输入的index是否有意义,无意义就不用遍历整个链表了。虽然应该是并非必须。
注意!别忘了corner case,index超出了链表长度,即 if index >= self.size: return -1。index似乎默认>=0, 因为不写这个corner case,leetcode代码也能通过。
class MyLinkedList(object):
def __init__(self):
self.dummy_head = ListNode()
self.size = 0 # self.size is useful, do not forget.
def get(self, index):
"""
:type index: int
:rtype: int
"""
if index >= self.size: # corner cases: out of scope
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val):
"""
:type val: int
:rtype: None
"""
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size = self.size + 1
return self.dummy_head.next
def addAtTail(self, val):
"""
:type val: int
:rtype: None
"""
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val, None)
self.size += 1
def addAtIndex(self, index, val):
"""
:type index: int
:type val: int
:rtype: None
"""
if index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index):
"""
:type index: int
:rtype: None
"""
if index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
206. Reverse Linked List 反转链表
Example: Input: head = [1,2] Output: [2,1]
Input: head = [] Output: []
Solution 双指针法:
思路:所谓双指针就是记录当前Node cur和上一个Node pre,用temp记录当前Node的原本的下一个Node,然后把pre赋给当前Node的next,实现reverse。
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
pre = None
cur = head
while cur: #这个问题中不在乎是否有下一个链表Node,只看当前Node是否存在,只要当前的存在,一定有指向它的更前的Node。所以这里不用cur.next判断while跳出标志。
temp = cur.next # ori next of cur #如果cur已经是最后一个Node,cur.next = None罢了。
cur.next = pre # update ori next to pre
pre = cur # set pre to cur
cur = temp # set ori next of cur to the new cur
return pre
Solution 递归法
化上一解法中的while为递归的写法。
class Solution(object):
def reverseList(self, head):
return self.recursiveReverse(head, None)
def recursiveReverse(self, cur, pre):
# 跳出递归的条件
if not cur:
return pre #当前node不存在了,说明链表到尾了,return当前node的上一个为真正的链表尾巴
# 当前递归的处理
temp = cur.next
cur.next = pre
# 自调用,进入递归下一层
return self.recursiveReverse(temp, cur)
############################################################################
2. Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Input: l1 = [2,4,3], l2 = [5,6,4] Output: [7,0,8] Explanation: 342 + 465 = 807.
Example 2:
Input: l1 = [0], l2 = [0] Output: [0]
Example 3:
Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] Output: [8,9,9,9,0,0,0,1]
思路:按位相加,若超过10,则carry进1,carry被加到下一位中。
注意while循环里除了看两个列表是否被遍历到了最后一位,还要看carry != 0。因为可能有上一位相加遗留的进位。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
dummyhead = ListNode(0)
curr = dummyhead
carry = 0
while l1 != None or l2 != None or carry != 0:
l1val = l1.val if l1 else 0
l2val = l2.val if l2 else 0
columnSum = l1val + l2val + carry
carry = columnSum // 10
newNode = ListNode(columnSum % 10)
curr.next = newNode
curr = newNode
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummyhead.next