链表理论基础
链表理论基础
链表的类型:单链表,双链表,循环链表
链表的定义:
class ListNode:
def __init__(self, val = 0, next = None):
self.val = val
self.next = next
链表的增删:O(1),查询:O(n)
数组的增删:O(n),查询:O(1)
leetcode 203. 移除链表元素
链表移除元素:找到这个节点的前一个节点,让前一个节点指向这个节点的下一个节点
# 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 removeElements(self, head, val):
"""
:type head: ListNode
:type val: int
:rtype: ListNode
"""
# 虚拟头结点
dummyhead = ListNode()
dummyhead.next = head # 让虚拟头结点指向链表的头结点
cur = dummyhead
while cur.next != None:
if cur.next.val == val: # 注意只能删cur.next不能删cur,因为找不到cur的前一个节点
cur.next = cur.next.next
else:
cur = cur.next
return dummyhead.next # 注意返回的是虚拟头结点的下一个节点
# 而不是head,因为原链表的head可能已经被我们删除了
leetcode 707. 设计链表
链表的增删改查
需要注意的地方:
- 需要自己定义一个链表类,dummyhead作为成员变量,定义一个num记录链表的元素个数,后面调用dummyhead和num时,都需要使用self.dummyhead和self.num,在成员函数里调用别的成员函数时也需要加上self.
- while循环的截止条件里的index,在查询操作时可以等于0,因为此时找的就是index对应的节点,而增删操作时只能大于0,因为增删找的是index对应节点的前一个节点,这样才能把index对应的节点删掉
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.dummyhead = ListNode()
self.num = 0
def get(self, index):
if index < 0 or index > self.num:
return -1
else:
cur = self.dummyhead
while cur.next is not None and index >= 0: # 这里包含等号是因为就是要找index对应的元素
cur = cur.next # 而不是index的上一个元素
index -= 1
return cur.val
def addAtHead(self, val):
node = ListNode(val)
tmp = self.dummyhead.next # 注意,需要用tmp来保存真正的头结点
self.dummyhead.next = node
node.next = tmp
self.num += 1
def addAtTail(self, val):
node = ListNode(val)
cur = self.dummyhead
while cur.next is not None:
cur = cur.next
cur.next = node
self.num += 1
def addAtIndex(self, index, val):
node = ListNode(val)
if index == self.num:
self.addAtTail(val)
return
if index < 0:
self.addAtHead(val)
return
if index > self.num:
return
cur = self.dummyhead
while cur.next is not None and index > 0: # 不包含等号找的是index的上一个元素
cur = cur.next
index -= 1
tmp = cur.next
cur.next = node
node.next = tmp
self.num += 1
def deleteAtIndex(self, index):
if 0 <= index < self.num:
cur = self.dummyhead
while cur.next is not None and index > 0:
cur = cur.next
index -= 1
cur.next = cur.next.next
self.num -= 1
else:
return
leetcode 206. 反转链表
需要注意的地方:
- 如何实现反转链表?其实就是依次让节点指向它的前一个节点
- 所以考虑双指针法,cur用来代表当前节点,pre用来代表cur的前一个节点,令 cur.next = pre,就可以实现翻转方向的操作
- cur初始化为原链表的头结点head,pre是cur的前一个节点,由于翻转后,原始的头结点会变成尾结点,尾结点指向null,所以给pre初始化为null
- 令cur.next = pre后,cur指向pre,此时cur和它原本的下一个节点之间的连接断掉了,相当于cur与之后的链表都失去了连接,所以必须先用一个临时指针temp去保存cur.next,然后才能进行翻转的操作
- 每次翻转完, 都需要让两个指针右移,所以让pre = cur, cur = temp
- 最后cur指向null,而pre就是新链表的头结点
# 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 reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 双指针法
cur = head
pre = None
while cur is not None:
temp = cur.next # 要用临时指针保留cur.next的值 不然就找不到下一个节点了
cur.next = pre # 翻转方向
pre = cur
cur = temp
return pre # pre最后会成为新的头结点
# 递归法
def reverse(cur, pre):
if cur == None: # 递归的终止条件
return pre # 返回反转后的头结点
temp = cur.next # 同样需要保存cur.next,不然就找不到了
cur.next = pre
return reverse(temp, cur) # 递归调用自身,令cur和pre右移,注意这里要return!不然输出结果是空值
return reverse(head, None) # 初始化