leetcode链表题总结(python版)

在总结链表题前,先记录一个误区:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
tmp = ListNode(0)
res = temp

问题是:tmp和res是代表什么?两个相同链表的头结点?指向两个相同链表的头结点?

解答:tmp和res指向的是存储 哑节点0 的位置,此时tmp和res存储的是同一内存地址,可以理解成tmp和res是两个指针。当对res进行操作时,会在res中存储内容(节点)即为这个内存放内容。而res不断移动,tmp还是指向原来的 哑节点的位置。

但是我们之前用python列表时,情况如下

a = [1, 2]
b = a
b += [3]
print(a) #a = [1, 2]
print(b) #b = [1, 2, 3]

为什么改变b却不能改变a呢?那是因为列表是可变对象,对于可变对象,地址是不可以共享的,也就是a,b指向的地址是不一样的。

总结:在python中,不可变对象是共享的(如节点对象),创建可变对象永远是分配新地址(如列表)。

常见的链表题错误

在quick移两个指针的时候,要这样写,要不quick为None时,quick.next会报错。多使用哑节点。

while quick and quick.next:
    quick = quick.next.next

链表题中,能用一个指针用一个指针,能用两个不用三个,而且都要建立哑节点,并对需要的指针进行初始化。

如删除链表重复的节点的题目。

在链表题中,常用的函数有:

def to_kth_node(head, k):#head为第一个结点,k为第k个结点
    k -= 1
    p = head
    while k > 0 and p:
        p = p.next
        k -= 1
    return p

接下来进入链表的题目,做题顺序:2、19、21、23、24、25、61、83、82、86、92

【leetcode2两数相加】

题目:给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

思路:这道题的关键在于进位问题,即当两个数相加大于10,需要向高位进一位。因此,设置进位变量为flag。

但是这道题要想完全做对的关键在于,l1和l2不一样长,而且要判断while循环完后是否有进位的情况。自己在写完常规思路以后一定要想一下是否满足特殊情况,把特例测试一下。

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

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        if l1 is None:
            return l2
        if l2 is None:
            return l1
        # tmp是暂存(temporal)
        tmp = ListNode(0)  # tmp指向哑节点
        # res是重置(reset)
        res = tmp  # res指向哑节点(此时和tmp指向同一位置)
        # flag 进位标志
        flag = 0  # 初始化 (只可能是0或1)
        while l1 or l2:  # l1或l2不为空就持续执行
            tmp_sum = 0  # 链表节点值的和
            if l1:  # 如果l1不为空,把l1的某个节点值的和赋给tmp_sum
                tmp_sum = l1.val  # 把l1的某个节点的值赋给tmp_sum
                l1 = l1.next
            if l2:  # 如果l2不为空,把l2中和l1对应的节点的值加到tmp_sum
                tmp_sum += l2.val
                l2 = l2.next  # 指向下一个节点,为下一次的加和做准备
            tmp_res = ((tmp_sum + flag) % 10)  # 个位数字
            flag = ((tmp_sum + flag) // 10)  # 进位的数
            res.next = ListNode(tmp_res) #将个位数字变为节点
            res = res.next  # res后移
            #!!!设置很巧妙,要注意
            if flag:  # 如果flag不为0,就是对应位置相加后有进位
                res.next = ListNode(1)  # res的下一节点设为1
        res = tmp.next  # 找到头结点,并赋给res
        del tmp  # 删除tmp变量
        return res  # 返回res链表(链表返回只需要返回头节点)    

复杂度分析

时间复杂度:O(max(m,n)),其中m表示l1链表的长度,n表示l2链表的长度

空间复杂度:O(max(m,n)),新链表的长度最多为max(m,n)+1

总结:1、请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来找到表头,而使用哑节点,可以很快找到表头,因为此时res已经在链表尾部了。2、在写代码的过程中,考虑一下特殊情况,如下表

参考:leetcode题解https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-leetcode/

哑节点除了能快速找到新链表表头以外,还能简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部

【Leetcode19删除列表的倒数第N个节点】

题目:给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

思路:本质是删除题,属于链表的基本操作。当时想到的方法是用两次遍历,第一次遍历先知道链表一共有多少个节点,第二次遍历找到需要删除节点的前一节点。该题我写了三个版本的代码,累死个人。

代码1:没有用到哑节点,把删除分成三种情况,头节点,中间节点,尾节点,两次遍历

代码2:用到哑节点,把删除分成两种情况,尾节点和其它节点,两次遍历

代码3:用到哑节点两个指针一次遍历:一次遍历的原因是让两个指针之间间隔n个结点,这样让一个指针到尾节点结束,另一个指针即为要删除结点的上一结点。特别注意的是两个指针间隔n个结点,之前一直弄错成n-1了!!!

贴出代码:

代码1:没有用到哑节点,把删除分成三种情况,头节点,中间节点,尾节点,两次遍历

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        p = head
        tmp = p
        listnode_n = 0
        while p:
            listnode_n += 1
            p = p.next
        p = tmp
        if n == 1: #删除尾节点的情况
            if listnode_n == 1:
                return None
            else:
                for i in range(listnode_n-2):
                    p = p.next
    
  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值