代码随想录day3:链表1(移除链表元素,设计链表,与反转链表)

1. 链表理论基础

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的入口节点称为链表的头结点也就是head。

链表的类型:

  • 单链表:每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思。
  • 双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。双链表既可以向前查询也可以向后查询。

  • 循环链表:头尾相接的链表,可用于解决内瑟夫问题。图源:代码随想录

链表的存储方式::

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

  

这个链表起始点为2,终止点为7,各个节点分布在内存的不同地址空间上,通过指针串联在一起。

链表的定义:

C++版本

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

 python 版本

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

链表的操作:

  • 删除节点

只要将C节点的next指针 指向E节点就可以了。

Python有自己的内存回收机制,就不用自己手动内存中存储的D节点释放。

  • 添加节点

链表的添加以及删除都是O(1)操作,不会影响到其他节点。

但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)

链表性能分析:

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

2. 移除链表元素(leetcode 203)

由于删除头节点和删除其他节点的方式不一样,这题我们可以运用到虚拟头的办法,这样便于我们统一删除的方法,判断原有头的数据是否符合要求。我们用current和while loop来遍历我们的链表,若节点符合条件则向下遍历,若节点不符合条件则跳过该节点以删除它。

# 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)
        current = dummy_head
        while current.next:
            if current.next.val == val:
                current.next = current.next.next
            else:
                current = current.next
        return dummy_head.next

这题是本人第一次系统接触链表,感觉链表看起来规则比较复杂,但其实操作并没有想象中难,关键点是要记住他的每一个节点并不是连续的,需要通过指针来连接,并也可以通过调整指针的方式来增加或者删除节点。在写代码的过程中我们也要记住留一个dummy_head来记录他的头,由current来对后续的节点进行判断和操作。
 

3. 设计链表(leetcode 707)

这题非常考验对链表这个数据结构的理解以及基本操作方法。

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:  #注意index是从零算起的,所以等于时也会超出长度
            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:              #一直过到最后一个
            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 

        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
        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)

还记得我们在对链表进行操作时,我们的增添/删除节点的操作为O(1),但我们查询位置的操作为O(n),因此我们在操作时要有设置一个current来为我们查询需要操作的节点位置。

4. 反转链表(leetcode 206)

这题我们可以选择定义一个新的新的链表,然后一个个查询旧链表的节点,但这样非常的浪费时间(1/2*O(n^2))以及空间(新定义链表)。

因此我们可以尝试在原有链表上反转指针的位置,这样不仅节省了空间,时间上也只需要遍历一遍,即O(n)。

我们可以在这里设置两个指针,current指向head,记录当前节点并向下遍历,prev指向上个节点,目标是让current指向prev。为了在这个过程中保留current原有的指针域并向下遍历,我们用一个temp来进行储存。

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev = None
        current = head
        while current:
            temp = current.next
            current.next = prev
            prev = current
            current = temp
        return prev

参考资料:

链表_百度百科

代码随想录

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值