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

一、链表基础理论

代码随想录 (programmercarl.com)——链表基础理论

二、203. 移除链表元素

题目链接:203. 移除链表元素 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——203. 移除链表元素
视频讲解:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili

法一、原链表删除元素

需要判断是否为头节点,如果不是直接指针指向下一个节点,如果是将头节点head→next
定义cur = head而不是cur = head.next,因为需要上一个元素的指针。

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        """
        原链表删除法
        """
        cur = head

        # 判断是否为头节点,如果是头节点且头节点值为val,则删除头节点
        while cur and cur.val == val:
            head = cur.next
            cur = cur.next

        # 当前指针不是头节点,开始遍历链表
        while cur and cur.next:
            # 当前节点的下一个节点值等于val,则删除该节点
            if cur.next.val ==val:
                cur.next = cur.next.next
            else:
                # 如果不等于val,继续遍历
                cur = cur.next

        return head

法二、虚拟头节点法

需要注意最后返回dummy_head.next而不是dummy_head!

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:

        """
        虚拟节点法
        """
        # 创建虚拟头节点
        dummy_head = ListNode(next = head)

        cur = dummy_head

        # 开始从虚拟头节点遍历
        while cur.next:
            # 如果虚拟头节点下一个值等于目标值,则删除该节点
            if cur.next.val == val:
                cur.next = cur.next.next
            # 否则继续遍历
            else:
                cur = cur.next

        return dummy_head.next

三、707.设计链表

题目链接:707. 设计链表 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——707.设计链表
视频讲解:帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili

主要考察链表基本操作:获取第几个节点的值;头部插入节点;尾部插入节点;第n个节点前插入节点;删除第n个节点。

错误记录

    def addAtIndex(self, index: int, val: int) -> None:
        # 判断索引是否合法
        if index < 0 or index > self.size - 1:
            return

        cur = self.dummy_head
        for i in range(index):
            cur = cur.next

        cur.next = ListNode(val, cur.next)

        self.size += 1

判断是否合法出错,正确应该是,其中index可以含等,因此不用-1.

    def addAtIndex(self, index: int, val: int) -> None:
        # 判断索引是否合法
        if index < 0 or index > self.size:
            return

        cur = self.dummy_head
        for i in range(index):
            cur = cur.next

        cur.next = ListNode(val, cur.next)

        self.size += 1

正确代码

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

class MyLinkedList:

    def __init__(self):
        self.dummy_head = ListNode()                    # 定义一个虚拟头节点,方便增删改
        self.size = 0


    def get(self, index: int) -> int:                   # 获取第n个节点的值
        # 如果下标无效,则返回-1,可以等于size - 1,此时为链表中最后一个节点
        if index < 0 or index > self.size - 1:
            return -1

        # 获取当前节点数值,让cur指向原链表第一个,即dummy_head.next
        cur = self.dummy_head.next
        # # 遍历index
        # while index:
        #     cur = cur.next
        #     index -= 1
        
        # 遍历写法2
        for i in range(index):
            cur = cur.next
        
        return cur.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:              # 在尾部插入节点
        cur = self.dummy_head

        # 找尾部节点,cur.next为空节点,即cur.next != None
        while cur.next:
            cur = cur.next
        
        # 插入尾部节点
        cur.next = ListNode(val)

        self.size += 1


    def addAtIndex(self, index: int, val: int) -> None:  # 第n个节点前插入节点
        # 判断索引是否合法,索引可以等于列表长度,此时直接加在链表末尾
        if index < 0 or index > self.size:
            return

        cur = self.dummy_head
        # 查找第n个节点写法1
        for i in range(index):
            cur = cur.next

        # # 查找第n个节点写法2
        # while index:
        #     cur = cur.next
        #     index -= 1

        cur.next = ListNode(val, cur.next)

        self.size += 1

        
    def deleteAtIndex(self, index: int) -> None:        # 删除第n个节点
        # 判断索引是否合法,可以取最后一个节点
        if index < 0 or index > self.size:
            return
    
        cur = self.dummy_head
        # 找到当前节点
        for i in range(index):
            cur = cur.next
        
        cur.next = cur.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)

注意

在第n个节点前插入节点和删除第n个节点的操作中,cur为n-1个节点,cur.next才为第n个节点,此时才好操作,否则会出现加错位置的情况。

四、206.反转链表

题目链接:206. 反转链表 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)
视频讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

面试高频题目!!!

双指针解法

思想:定义cur和pre两个指针,cur指向pre,cur和pre向前移动,cur再指向pre,不断重复,直到结束。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        """双指针解法"""
        cur = head  # 初始化cur指针,指向head
        pre = None  # 初始化pre指针,为了使cur可以直接指向pre,所以初始化为None

        # 终止条件:cur指向None即停止
        while cur:
            temp = cur.next # 趁链表没断先保存一个cur的临时值
            cur.next = pre

            # cur和pre整体向后移动,注意先移动pre后移动cur,如果先移动cur则pre不能走到之前cur的位置
            pre = cur
            cur = temp
        
        return pre

递归解法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    """递归法"""
    # 先定义一个reverse函数,输入的值对应双指针法的两个指针
    def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
        # 终止条件:当cur为空时
        if cur == None:
            return pre
        
        temp = cur.next
        cur.next = pre

        # 移动cur和pre指针
        return self.reverse(temp, cur)       


    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 输入为双指针法cur和pre的输入
        return self.reverse(head, None)

先重点掌握双指针法,再去对照学习递归法。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我知道了。以下是对您的问题的回答: a. 动态分配内存的方法有很多,可以使用C语言的malloc函数,也可以使用C++中的new操作符,具体方法如下: C语言的malloc函数: ```c struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); ``` C++中的new操作符: ```c++ ListNode* node = new ListNode(); ``` b. 初始化单链表需要对链表节点的成员变量进行赋值,以下是一个示例: ```c++ struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; ListNode* head = NULL; ``` c. 头插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = head->next; head->next = node; } ``` 尾插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; ListNode* tail = head; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = NULL; tail->next = node; tail = node; } ``` d. 取链表某一位的值可以通过遍历链表来实现,以下是一个示例: ```c++ ListNode* p = head->next; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { cout << p->val << endl; } ``` e. 在链表的某一位进行插入操作可以通过遍历链表找到插入位置,然后插入一个新节点来实现,以下是一个示例: ```c++ ListNode* p = head; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { int x = 100; ListNode* node = new ListNode(x); node->next = p->next; p->next = node; } ``` f. 遍历链表可以通过循环遍历链表节点来实现,以下是一个示例: ```c++ ListNode* p = head->next; while (p != NULL) { cout << p->val << " "; p = p->next; } cout << endl; ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值