代码随想录算法训练营第三天|链表理论基础、203.移除链表元素、707.设计链表 、206.反转链表

链表理论基础

1.链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个数据域一个指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头节点(head)。

2.链表的类型:单链表(基础的链表类型)、双链表(每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点,所以双链表既可向前查询也可向后查询)、循环链表(链表首位相连,链表的最后一个节点的指针域指向头节点)

3.链表的存储方式:链表在内存中不是连续分布的,链表通过指针域的指针链接在内存中的各个节点,所以链表中的节点在内存中不是连续分布的,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理

4.链表的定义(python的定义链表)

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

5.链表和数组的对比:数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

203.移除链表元素

思路:

1.直接使用原来的链表来进行删除操作:移除其他节点,让节点next指针直接指向下下一个节点就可以了。移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点,所以要将头结点向后移动一位,这样就从链表中移除了一个头结点。

2.设置一个虚拟头结点在进行移除节点操作:设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。

代码:

直接使用原来的链表来进行删除操作

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        while head!=None and head.val==val: # 移除头节点操作(头节点不为空 且 头节点的val与目标val相等)
            head=head.next # 让头节点指向头节点的下一个节点(下一个节点替换头节点,成为新头节点)
        
        node=head # 移除其他节点操作
        while node!=None and node.next!=None: # 当前节点不为空(如果node为空,node.next会报空指针错误) 且 当前节点指针不为空
            if node.next.val==val: # 当前节点的下一个节点的val与目标val相等
                node.next=node.next.next # 当前节点指针(next)指向下下一个节点
            else:
                node=node.next # 当前节点变为下一个节点
        
        return head # head是整个链表的头结点。通过head,就能得到整个链表
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

设置一个虚拟头结点在进行移除节点操作 

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummy_head=ListNode(next=head) # 添加一个虚拟头节点,让dummy_head的指针指向原链表的head节点
        current = dummy_head # 定义一个临时指针用来遍历链表(如果用dummy_head(头)节点进行遍历,头节点指向的值是不断在改变的,最后就无法返回原先列表的头节点)
        while current.next!=None:
            if current.next.val==val:
                current.next=current.next.next
            else:
                current=current.next
        
        return dummy_head.next # dummy_head.next为新链表的头节点。为什么不return head,因为head有可能已经被删除了
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

707.设计链表 

思路:

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

代码:

class ListNode:
    def __init__(self,val=0,next=None):
        self.val=val
        self.next=next

class MyLinkedList:
    def __init__(self):
        self.dummy_head=ListNode() # 设置一个虚拟头节点
        self.size=0

    def get(self, index: int) -> int:
        if index<0 or index>=self.size:
            return -1
        
        current=self.dummy_head.next
        for i in range(index):
            current=current.next
        
        return current.val

    def addAtHead(self, val: int) -> None:
        self.dummy_head.next = ListNode(val, self.dummy_head.next)
        self.size += 1

    def addAtTail(self, val: int) -> None:
        current = self.dummy_head
        while current.next!=None:
            current = current.next
        current.next = ListNode(val)
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0 or index > self.size:
            return -1
        
        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: int) -> None:
        if index < 0 or index >= self.size:
            return -1
        
        current = self.dummy_head
        for i in range(index):
            current = current.next
        current.next = current.next.next
        self.size -= 1



# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
  • 时间复杂度: 涉及 index 的相关操作为 O(index), 其余为 O(1)
  • 空间复杂度: O(n)

206.反转链表

思路:

1.双指针法:首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。

2.递归法:递归法相对抽象一些,但是其实和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。双指针法写出来之后,理解如下递归写法就不难了,代码逻辑都是一样的。

代码:

双指针法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head # 后指针(指向头节点)
        pre = None # 前指针
        while cur!=None:
            temp=cur.next # 设置一个临时指针temp保存cur的下一个节点,因为接下来要改变cur->next
            cur.next = pre # 反转
            #更新pre、cur指针
            pre = cur
            cur = temp
        return pre
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

 递归法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        return self.reverse(head, None) # reverse()函数实现反转操作,将head和None值赋给两个指针
    def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
        if cur == None:
            return pre
        temp = cur.next
        cur.next = pre
        return self.reverse(temp, cur) # 进入下一层递归
  • 时间复杂度: O(n), 要递归处理链表的每个节点
  • 空间复杂度: O(n), 递归调用了 n 层栈空间
  • 23
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值