想躺平又躺不平的day2

链表的一些基础小知识

链表类型

单链表
单链表结构示意图
双链表
双链表结构示意图
循环链表
在这里插入图片描述

链表操作

删除节点
删除某点即将该点上一节点的指针指向该点的下一个节点(C++需要手动释放该节点,其他语言有自己的内存回收机制无需手动释放)
链表节点删除
增加节点
增加节点即将改变上一节点的指针指向加入的节点以及加入节点的指针指向当前节点
链表节点增加

今日刷题指南

移除链表元素

# 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(next=head)
        current = dummyhead
        # 遍历所有节点,判断当前节点(current.next.val)是否需要删除,需要知道上一节点的指针(current.next)
        while current.next:
            if current.next.val == val:
                current.next = current.next.next
            else:
                current = current.next
        return dummyhead.next # 返回虚拟头节点指向的链表

一开始拿到这道题思路还是清晰的,删除当前节点等同于将上一节点指针指向下一节点,然后构造一个虚拟头节点指向头节点。但是,一敲代码人傻眼。
首先,需要明确知道的是链表的定义,如ListNode类定义那样,每一个节点包含两个属性:val(值)以及next(指向下一节点的指针)。
其次,如何遍历所有节点并且对比目标值哪些节点需要删除:

  1. 起始节点: 起始节点current即为构造的虚拟头节点,其值定义为0,指向head节点;
  2. 遍历条件: 由于删除某一节点时,我们需要知道其上一节点的指针,因此遍历条件定为current.next不为空;
  3. 上一节点:current,当前节点:current.next,当前节点值:current.next.val;
  4. 判断条件:current.next.val是否为指定值,是的话将上一节点指针current.next–>下一节点current.next.next,否则遍历至下一节点:current = current.next。

设计链表

复盘这道题目,需要深呼吸十秒钟。
将链表的各种操作封装成函数

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

class MyLinkedList(object):
    def __init__(self):
        self.dummy_head = ListNode()
        self.size = 0

    def get(self, index):  # 获取链表中下标为index的节点值(如果无效返回-1)
        """
        :type index: int
        :rtype: 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):  #将值为val的节点插入链表第一个元素前成为头节点
        """
        :type val: int
        :rtype: None
        """
        self.dummy_head.next = ListNode(val=val,next=self.dummy_head.next)
        self.size += 1

    def addAtTail(self, val):  #将值为val的节点追加为链表最后一个节点
        """
        :type val: int
        :rtype: None
        """
        current = self.dummy_head
        while current.next:
            current = current.next
        current.next = ListNode(val=val)
        self.size += 1


    def addAtIndex(self, index, val):  #将值为val的节点插入链表中下标为index的节点之前
        """
        :type index: int
        :type val: int
        :rtype: None
        """
        if index < 0 or index>self.size: #index不合法或者比链表长度大,该节点不会插入到链表中
            return
 
        current = self.dummy_head
        for i in range(index):
            current = current.next
        current.next = ListNode(val,next=current.next)
        self.size += 1

    def deleteAtIndex(self, index):  #删除链表中下标为index的节点
        """
        :type index: int
        :rtype: None
        """
        if index >= 0 and index < self.size:
            current = self.dummy_head
            for i in range(index):
                current = current.next
            current.next = current.next.next
            self.size -= 1

总结出来的规律就是:

  1. 明确当前节点需要是头节点还是虚拟头节点
  2. 最好画个示意图,演示一下遍历过程

反转链表

# 解法一:双指针法
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        current = head
        pre = None
        while current:
            tmp = current.next #临时保存下一节点(因为接下来要改变current.next)
            current.next = pre #反转
            # 更新pre、current指针
            pre = current
            current = tmp
        return pre
# 解法二:递归法
class Solution:
    def reverseList(self, head):
        return self.reverse(head, None)
    def reverse(self, cur, pre):  # 递归函数
        if cur == None:
            return pre
        temp = cur.next
        cur.next = pre
        return self.reverse(temp, cur)

我自己在做的时候,完全没有想到说用双指针,反而想着加一个虚拟头节点,但是总是会报错提示none类型无法获取next指针,感觉是加上虚拟头节点之后遍历条件的问题导致。
用双指针瞬间逻辑就比较通畅了,重点就是需要明确遍历条件、双指针的初始化以及变化过程。


本来说好晚上把这部分弄完的,赖到现在,凌晨5点14分。莽人就是晚上说好要完成但没完成的事情,通宵都要给他办咯。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值