动手刷力扣第三天——链表(力扣203,206)

文章详细介绍了链表作为一种非连续存储结构的特点,强调其在插入和删除操作上的高效性。在Python中使用deque作为链表实现,并展示了插入、访问、搜索、更新和删除元素的操作及其时间复杂度。此外,文章通过力扣题目203和206为例,讲解了如何删除链表元素和反转链表的解决方案,这两个操作的时间复杂度均为O(N)。
摘要由CSDN通过智能技术生成

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,一般用于插入与删除较为频繁的场景。

链表的每一个节点通过“指针”链接起来,每一个节点由数据(Data),指针(用来存储后一个节点的地址)组成,最开始的节点称为Head,最末尾节点的指针指向NULL。

下面我们讨论一下链表的访问,搜索,插入,删除等操作。

链表的访问与搜索:

由于链表的存储是非连续的,我们不能像数组那样采用首地址加上元偏移量计算出元素所在位置,只能挨个遍历直到找到某个节点,所以最坏的情况下我们要遍历N个元素,因此链表的访问时间复杂度为O(N)。搜索同理。

链表的访插入与删除:

同样由于链表的存储是非连续的,我们无需像数组一样改变所有元素的顺序,在插入与删除时,我们只需要修改前后元素的指针指向即可完成插入或删除操作。因此链表的插入与删除时间复杂度为O(1)。

在一定程度上,数组与链表是互补的。数组在访问和搜索上更便捷,因此更适用于读操作。链表在插入与删除上更便捷,因此更适用于写操作。我们应该根据实际情况来选择数据结构的使用。

下面是python中常用的链表操作。

# create an array
linkedlist = deque()
 
# add element    time complexity:O(1)
linkedlist .append(1)
linkedlist .append(2)
linkedlist .append(3)
print(linkedlist)    # [1,2,3]
 
# insert element    time complexity:O(N)
linkedlist.insert(2,100)
print(linkedlist)    # [1,2,100,3]
 
# access element    time complexity:O(N)
temp = linkedlist [2]
print(temp)    # 100
 
# search element    time complexity:O(N)
index = linkedlist.index(100)    # 2

# updata element    time complexity:O(N)
linkedlist[2] = 99    
print(linkedlist)    # [1,2,99,3]
 
# remove element    time complexity:O(N)
linkedlist.remove(99)
print(linkedlist)    # [1,2,3]

 
# get size
size = len(linkedlist)
print(size)    # 3
 

熟悉了链表的相关操作后我们来看看对于的简单题。力扣203移除链表元素:

 对于这道题,我们首先要定义一个虚拟节点dummy来作为新的头结点以便保存完成删除操作后的链表。然后定义一个prev指针用来表示head的前一个节点,当head指向需要删除的val值时,prev直接指向head的下一个元素(从而把当前元素删除),当head没有指向val时,ore指向head,head指向head的下一个元素从而完成遍历。循环结束后链表中的val元素全部删除,返回由dummy保存的链表即可。

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int):
        dummy = ListNode(0)         # 设置一个变量储存整个链表
        dummy.next = head           # 指向链表的头
        prev = dummy                # 为了遍历链表保存的prev变量
        while(head is not None):
            if(head.val == val):        # 当head指向需要删除的val时
                prev.next = head.next   # 指向下一个点即跳过这个数据
                head = head.next        # head也指向下一个完成遍历
            else:                       # 当head不指向val时
                prev = head             # 保留该数据
                head = head.next        # head指向下一个完成遍历
        return dummy.next               # 返回删除元素后的链表
# time complexity:O(N)     遍历了一次链表
# space complexity:O(1)    没有产生新的数据结构

力扣206反转链表:

 这道题首先我们首先定义prev指针指向链表的最后,curr指针指向链表的最开始位置,然后开始遍历链表,首先定义next指针保存curr的下一个位置以便curr往后完成遍历(如果直接反转指针curr没法按原来的顺序往下遍历),然后curr指针直接指向最后的prev(即第一个元素直接指向None变成了最后一个元素),然后prev移动到curr的位置,curr移动到next的位置(开始反转下一个元素),以此类推直到curr指向了None,此时prev(始终在curr的前一个)指向的是原来的最后一个元素(但是指针已经反转了所以他是链表的第一个元素),故可以结束循环返回prev即可。

class Solution:
    def reverseList(self, head: Optional[ListNode]):
        prev = None                 # prev指向None即链表末尾
        curr = head                 # 初始化curr指向head
        while curr is not None:     # 遍历链表
            next = curr.next        # 定义下一个点以便往后遍历
            curr.next = prev        # 下一个点指向prev(即指向最后,成功把第一个元素放到最后)
            prev = curr             # prev移动到curr
            curr = next             # curr移动到开始定义的next以完成遍历
        # 画图理解比较好理解
        return prev                 # 此时prev指向原来的最后一个元素(None之前一个)
                                    # 同时也是后来的第一个元素,因此返回prev
# time complexity:O(N)     遍历链表一次
# space complexity:O(1)    没有产生新的数据结构

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值