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

代码随想录刷题第三天

链表基础知识

链表

移除链表元素 (LC 203)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例:
在这里插入图片描述

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

移除某个链表元素的时候只需要将前一个节点的next指针指向下一个元素即可。但一定要注意的是前一个节点,不是要删除的节点。如下图:
在这里插入图片描述
所以常规的代码是:

while(cur!=None and cur.next!=val):
	if cur.next.val == val:
		cur.next = cur.next.next  # 删除下一个节点
	else:
		cur = cur.next		# 无需删除,跳到下一个节点

!!! 但是,如果要删除头节点怎么办???
头结点前面没有节点了,无法按照之前的模式将上一个节点链接到下一个节点。应对这个问题有两种方法:

  • 如果头节点需要删除,则将头节点向后移动一位,直接忽略第一个节点: head = head.next
    但是,这个方法会出现两种删除节点的方法(头结点和中间节点),不统一
    在这里插入图片描述
  • 另一种方法是在头结点之前添加一个虚拟节点dummyhead(推荐)。 这样可以保证只使用一种方法删除所有可能的节点。
    在这里插入图片描述

代码实现

第一种方法:

class Solution(object):
    def removeElements(self, head, val):
    	# 移除头节点
        while (head!=None and head.val == val):
            head = head.next
        # 移除后续节点
        cur = head
        while(cur!=None and cur.next!=None):
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

第二种方法:

class Solution(object):
    def removeElements(self, head, val):
    	# 创建dummynode, dummynode链接到head
        dummynode = ListNode(0, head)        
        cur = dummynode
        while(cur!=None and cur.next!=None):
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummynode.next

设计链表 (LC 707)

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

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

示例:

输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3

本题一共需要完成五个不同的操作:
在这里插入图片描述

  1. 初始化链表
    在完成每个步骤之前,需要先初始化这个linkedlist。注意,linkedlist有两个属性: 一个dummyhead, 作为链表的第一个节点; 另一个是链表的size,用于储存链表的长度。
class MyLinkedList(object):
    def __init__(self):
        self.dummyhead = ListNode(0)
        self.size=0
  1. 获取第n个节点的值
    在执行这个操作前,需要先判断一下n是否合法,合法区间 0<=n<=size-1。如果不合法,则返回-1
    若合法,则将cur一个个右移至第n个位置,然后返回第n个位置的val
def get(self, index):
    if index < 0 or index > self.size-1:
        return -1  
    cur = self.dummyhead.next
    while(index>0):
        cur=cur.next
        index-=1
    return cur.val
  1. 头部插入节点
    先创建一个newnode, 将新节点指向cur.next, 然后将cur指针指向新的节点。一定要注意节点更新的顺序,如果先更新cur.next = newnode, 那么下一步 newnode.next 的指向无法通过 cur.next 知晓(已经更新了)。size+=1, 因为插入了一个节点。
def addAtHead(self, val):
    newnode = ListNode(val)
    cur = self.dummyhead
    newnode.next = cur.next
    cur.next = newnode
    self.size+=1
  1. 在尾部插入节点
    先将cur移动到最后一个节点,即 cur = cur.next until cur.next==None。 然后将 cur.next 指向 newnodesize+=1,因为插入了一个节点。
def addAtTail(self, val):
    newnode = ListNode(val)
    cur = self.dummyhead
    while(cur.next!=None):
        cur = cur.next
    cur.next = newnode
    self.size+=1
  1. 第n个节点前插入节点
    在执行这个操作前,需要先判断一下n是否合法,合法区间 0<=n<=sizen==size 也是合法的,因为题目要求这种情况将newnode插在尾部。如果不合法,则直接跳过这个步骤返回;如果合法,先将cur向右边移动n步, 然后使用之前的方法添加节点(newnode.next=cur.next cur.next = newnode
def addAtIndex(self, index, val):
    #注意区间,与其他情况不同,需要考虑index = size
    if index<0 or index>self.size:
        return
    newnode = ListNode(val)
    cur = self.dummyhead
    while(index>0):
        cur = cur.next
        index-=1
    newnode.next = cur.next
    cur.next = newnode
    self.size+=1
  1. 删除第n个节点
    在执行这个操作前,需要先判断一下n是否合法,合法区间 0<=n<=size-1;如果不合法,则直接跳过这个步骤返回;如果合法,先将cur向右边移动n步,然后使用 cur.next = cur.next.next 删除节点。
def deleteAtIndex(self, index):
    if index<0 or index>self.size-1:
        return
    cur = self.dummyhead
    while(index>0):
        cur = cur.next
        index-=1
    cur.next = cur.next.next
    self.size-=1

注意在链表操作时,最好添加一个dummyhead, 同时所有增加删减节点操作都需要将cur节点指向要操作节点的前一个节点

完整代码:

# 定义链表节点
class ListNode(object):
    def __init__(self, val=0, nextnode=None):
        self.val = val
        self.next = nextnode
# 定义新链表
class MyLinkedList(object):
    def __init__(self):
        self.dummyhead = ListNode(0)
        self.size=0

    def get(self, index):
        if index < 0 or index > self.size-1:
            return -1  
        cur = self.dummyhead.next
        while(index>0):
            cur=cur.next
            index-=1
        return cur.val

    def addAtHead(self, val):
        newnode = ListNode(val)
        cur = self.dummyhead
        newnode.next = cur.next
        cur.next = newnode
        self.size+=1

    def addAtTail(self, val):
        newnode = ListNode(val)
        cur = self.dummyhead
        while(cur.next!=None):
            cur = cur.next
        cur.next = newnode
        self.size+=1

    def addAtIndex(self, index, val):
        #注意区间,与其他情况不同,需要考虑index = size
        if index<0 or index>self.size:
            return
        newnode = ListNode(val)
        cur = self.dummyhead
        while(index>0):
            cur = cur.next
            index-=1
        newnode.next = cur.next
        cur.next = newnode
        self.size+=1

    def deleteAtIndex(self, index):
        if index<0 or index>self.size-1:
            return
        cur = self.dummyhead
        while(index>0):
            cur = cur.next
            index-=1
        cur.next = cur.next.next
        self.size-=1

反转链表 (LC 206)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

双指针法:(一定要掌握)
初始化:快指针cur指向head, 慢指针prev=Null。循环向后移动直到cur=Null, 返回prev。每次循环,使用一个temp = cur.next, 然后cur.next = prev,反转指向;最后向后移动prev和cur指针, pre = cur, cur = temp.

在这里插入图片描述
代码实现

class Solution(object):
    def reverseList(self, head):
        cur = head
        prev = None
        while (cur!=None):
            temp = cur.next
            cur.next = prev
            prev = cur
            cur = temp
        return prev

递归法:(附加,难理解)
在双指针法基础上改为递归,不太好理解,推荐掌握双指针即可

class Solution(object):
    def reverseList(self, head):
        # 初始化current = head 指向最左边的null
        # 类似双指针,将current.next指向prev
        return self.reverse(head, None) 

    def reverse(self, cur, prev):
        if cur == None:
            return prev  # 返回null前面一个作为新head
        temp = cur.next  # 保留下一个节点作为用于移动current
        cur.next = prev  # current.next指向前一个内容
        # 进行下一层循环,下一层current = temp, prev = current, 两个向右指针移动
        return self.reverse(temp, cur)

注意!!! 记得递归代码每个函数都需要有返回,将想输出一层层返回到上层(主函数)

总结:

今天三题的难度还好,在第二题的addAtIndex这里debug了很久,主要是没看到题目要求 n=size 的时候也属于合法区间。剩下两题难度还好,但是递归法需要再重新理解一下。继续加油!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值