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

LeetCode 203.移除链表元素:

文章链接
题目链接:203.移除链表元素

看到题目后的想法

  1. 添加虚拟头节点,通过虚拟头节点遍历和删除链表节点。优点在于不需要将头节点的值进行单独判断,代码逻辑一致。
    需要注意的是:采用pre遍历删除链表节点时,循环判断条件为pre.next,所以删除节点后不需要更新pre
class Solution(object):
   def removeElements(self, head, val):
       """
       :type head: ListNode
       :type val: int
       :rtype: ListNode
       """
       new_head = ListNode(next = head)    # 添加虚拟头结点
       pre = new_head
       while pre.next != None:
           if pre.next.val == val:
               pre.next = pre.next.next    # 删除节点之后不需要迭代pre,因为pre.next被更新了
           else:
               pre = pre.next
       return new_head.next
  1. 在原始的链表上进行判断,同时将头结点单独判断。首先需要单独判断头节点,当头节点值不为 val 时遍历删除后续节点。需要注意的是,对头结点的删除操作需要使用循环,因为可能出现如下情形。
    在这里插入图片描述
class Solution(object):
   def removeElements(self, head, val):
       """
       :type head: ListNode
       :type val: int
       :rtype: ListNode
       """
       # 不添加虚拟头节点删除链表元素
       while head != None and head.val == val:  # 循环删除值为val的头节点
           head = head.next
       if head == None:    # 删除后链表为空
           return head
       # 此时head.val != val
       pre = head
       while pre.next != None:
           if pre.next.val == val:     
               pre.next = pre.next.next
           else:
               pre = pre.next
       return head
  1. 采用递归的方式删除链表的节点。分为三类,
    ① 头节点为空,直接返回头节点
    ② 头节点值为 val,将head.next链表删除节点后的头节点作为新的头节点
    ③ 头节点值不为val,将head.next链表删除节点后的头节点链接到head之后
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        # 空链表
        if head == None:
            return None
        # 递归处理
        elif head.val == val:   
            new_head = self.removeElements(head.next, val)  # 类中调用对应的函数用self.removeElements   
            head = new_head
            return head
        else:
            head.next = self.removeElements(head.next, val)
            return head

遇到的问题

  1. 对于python的链表实现不够熟悉。
# LeetCode上的链表定义
class ListNode(object):
	def __init__(self, val = 0, next = None):
		self.val = val
		self.next = next
# 创建新节点
nodep1 = ListNode()		# 创建了一个节点nodep1, 值val = 0, next = None
nodep2 = ListNode(next = head)		# nodep2节点, val = 0, next = head
  1. python的类中调用对应的函数用 self.function

LeetCode 707.设计链表:

文章链接
题目链接:707.设计链表

看到题目的想法:

1.单链表实现
① 题目所给的类为链表,所以需要先声明一个链表节点的类

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

② 采用虚拟头节点来初始化链表。同时使用了size记录链表中的节点个数

    def __init__(self):
        self.dummy_head = LinkNode()
        self.size = 0

③. 具体的类中的函数实现。需要注意的是
1)插入或删除节点时,需要同时修改 self.size
2)获取下标为n的节点,其中n从0开始。

    def get(self, index):
        """
        :type index: int
        :rtype: int
        """
        if index < 0 or index > (self.size - 1):
            return -1
        p = self.dummy_head.next
        for i in range(index):	# 可以带入index = 0的时候确定range中的是index还是多少
            p = p.next
        return p.val


    def addAtHead(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.size += 1
        self.dummy_head.next = ListNode(val = val, next = self.dummy_head.next)


    def addAtTail(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.size += 1
        p = self.dummy_head
        while p.next != None:
            p = p.next
        p.next = ListNode(val = val)


    def addAtIndex(self, index, val):
        """
        :type index: int
        :type val: int
        :rtype: None
        """
        if index < 0 or index > self.size:  # 插入元素的下标范围为[0, self.size]
            return
        self.size += 1
        pre = self.dummy_head
        for i in range(index):
            pre = pre.next
        pre.next = ListNode(val = val, next = pre.next)


    def deleteAtIndex(self, index):
        """
        :type index: int
        :rtype: None
        """
        if index < 0 or index > (self.size - 1):
            return
        self.size -= 1
        pre = self.dummy_head
        for i in range(index):	# 代入index为0时,确定range中的是index
            pre = pre.next
        pre.next = pre.next.next

2.循环双链表链表实现
对于不熟悉的人来说很容易在prev上弄错,插入和删除节点还是自己先在纸上写一遍再用代码实现。还是用的创建虚拟头节点的方式实现的。

class DListNode(object):
    def __init__(self, val=0, next=None, prev=None):
        self.val = val
        self.next = next
        self.prev = prev


class MyLinkedList(object):

    def __init__(self):
        self.dummy_head = DListNode()
        self.dummy_head.next = self.dummy_head.prev = self.dummy_head
        self.size = 0

    def get(self, index):
        """
        :type index: int
        :rtype: int
        """
        if index < 0 or index > (self.size - 1):
            return -1
        cur = self.dummy_head
        if index <= (self.size - 1) // 2:  # 前半段
            for i in range(index + 1):
                cur = cur.next
        else:  # 后半段
            for i in range(self.size - index):
                cur = cur.prev
        return cur.val

    def addAtHead(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.size += 1
        hnext = self.dummy_head.next  # 获取当前头节点的下一个节点
        newnode = DListNode(val=val, prev=self.dummy_head, next=hnext)  # 新节点
        self.dummy_head.next = newnode
        hnext.prev = newnode

    def addAtTail(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.size += 1
        tail = self.dummy_head.prev  # 获取尾节点
        newnode = DListNode(val=val, prev=tail, next=self.dummy_head)
        tail.next = newnode
        self.dummy_head.prev = newnode

    def addAtIndex(self, index, val):
        """
        :type index: int
        :type val: int
        :rtype: None
        """
        if index < 0 or index > self.size:
            return
        cur = self.dummy_head
        if index <= (self.size - 1) // 2:  # index在前半段
            for i in range(index):
                cur = cur.next  # 获取目标位置的前一个节点
            cnext = cur.next    # 目标位置的节点
            newnode = DListNode(val=val, prev=cur, next=cnext)
            cur.next = newnode
            cnext.prev = newnode
        else:
            for i in range(self.size - index):
                cur = cur.prev  # 移动到目标节点
            cprev = cur.prev    # 目标节点的前一个节点
            newnode = DListNode(val=val, prev=cprev, next=cur)
            cprev.next = newnode
            cur.prev = newnode
        self.size += 1

    def deleteAtIndex(self, index):
        """
        :type index: int
        :rtype: None
        """
        if index < 0 or index > (self.size - 1):
            return
        cur = self.dummy_head
        if index <= (self.size - 1) // 2:
            for i in range(index):
                cur = cur.next  # 找到待删除节点的前一个节点
            next = cur.next.next    # 目标位置的下一个节点
            cur.next = next
            next.prev = cur
        else:
            for i in range(self.size - index):
                cur = cur.prev  # 找到待删除节点
            prev = cur.prev    # 待删除节点的前一个节点
            prev.next = cur.next
            cur.next.prev = prev
        self.size -= 1

实现过程中遇到的困难:

① 因为对于循环双链表的节点插入删除,以及从后向前找不熟悉,导致细节出现错误。
② 学会正确的debug方式,对于出现错误的例子,在自己IDE上进行debug。对于较为复杂的错例,使用批处理的方式在本地IDE上进行处理,同时打印出运行过程中的数据,进而一步步准确定位到错误位置。

# 记录一下本次的debug代码
    def printList(self):	# 类中遍历打印链表
        pnext = self.dummy_head.next
        prev = self.dummy_head.prev
        for i in range(self.size):
            print(str(i) + ":" + str(pnext.val), end=' ')
            pnext = pnext.next
        print('\n')
        for i in range(self.size):
            print(str(self.size - 1 - i) + ":" + str(prev.val), end=' ')
            prev = prev.prev
        print('\n')
# 主函数对于复杂的输入案例进行批处理
i = 0
# list27 = []
for func_name, inputs in zip(list1, list2):
    if i == 96:	# 这个是前面打印输出后找到了错误操作的结果,能直接在错误的位置进行debug
        func = getattr(obj, func_name)  # 获取函数对象
        re = func(*inputs)  # 调用函数
    else:
        func = getattr(obj, func_name)  # 获取函数对象
        re = func(*inputs)  # 调用函数
        # if inputs[0] == 27:	# 统计操作数为27的操作前一共有多少操作
        #     list27.append(i)
        print(func_name + " " + str(inputs))
        obj.printList()
        if re is not None:
            print(re)
        i += 1
# for list3 in list27:
#     print(list3)	# 找到相同的错误函数调用前面有多少正常的函数调用(因为例子中的操作太多)

LeetCode 206.反转链表:

文章链接
题目链接:206.反转链表

看到题目后的思路:

  1. 头插法建立新链表同时删除原链表并返回新链表。但是比较浪费空间
  2. 在原链表的基础上进行反转
    ① 对链表中节点的反转:p.next = pre,但是直接反转后原链表丢失,因此在反转前pnext = p.next保存原链表
    ② 反转后链表的头节点为原链表的尾节点
# 双指针写法
class Solution(object):
    def reverseList(self, head):
        pre, p = None, head
        while p != None:
            pnext = p.next  # 存储p在原链表中的下一个节点
            p.next = pre    # 反转
            pre = p # 更新p和pre
            p = pnext
        return pre
        
# 从前往后递归写法
class Solution(object):
    def reverseList(self, head):
        return self.reverseNode(None, head)

    def reverseNode(self, pre, p):
        if p == None:
            return pre	# 为空时返回pre作为头指针
        pnext = p.next
        p.next = pre
        return self.reverseNode(p, pnext)	# 这里也要返回

# 从后往前递归写法
# 首先反转从第二个节点开始的链表,再反转第二个节点和第一个节点
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None or head.next == None:
            return head
        last = self.reverseList(head.next)   # 递归调用,反转从第二个节点开始的链表
        head.next.next = head   # 反转第二个节点
        head.next = None
        return last # last为原链表的尾节点也即反转链表的头节点

学习收获:

  1. 自己实现了循环双链表的各种操作,但是还是不是很熟悉,后续二刷三刷时要多练习
  2. 对debug的进一步掌握,通过打印输出来一步步找到错误的位置
  3. 复习了python的一些语法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值