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

题目链接:203. 移除链表元素 - 力扣(Leetcode)707. 设计链表 - 力扣(Leetcode)206. 反转链表 - 力扣(Leetcode)

文章链接:代码随想录 (programmercarl.com)

链表基础

1. 定义:通过指针串联在一起的线性结构。

2. 特征:每个节点由一个数据域和一个指针域(存放指向下一个节点的指针)组成,最后一个节点的指针域指向null。链接的入口节点称为链表的头结点,也就是head。

3. 类型:单链表,双链表(两个指针域,一个指上一个指下,可向前向后查询),循环链表(链表首尾相连,可解决约瑟夫环问题)。

4. 存储方式:通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

5. 链表操作:

(1)删:ABCD四个节点,要删掉C节点,只要将B节点的next指针指向D节点即可。(在C++中还要手动释放C节点的内存,其他语言有自己的内存回收机制)。若要删除最末的节点,需要从头节点查找到第四个节点通过next指针进行删除操作。(查找的时间复杂度O(n))

(2)添:ABCD四个节点,要在BC之间加F节点,首先让B节点的next指针指向F,再让F节点的next指针指向D节点。

6. 性能对比:数组适用于数据量固定、频繁查询、较少增删的场景;链表正好相反,适用于数据量不固定、频繁增删、较少查询的场景。

203 移除链表元素

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

示例:

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

 疑点:

1. Python中的链表操作实现

需要从头节点遍历到需要操作的index的节点

2. 如何删除头结点

将头结点向后移动一位

3. 如何增添新的头结点

关键:虚拟头结点的使用技巧。

设置一个虚拟头结点,就可以按照统一的方式移除原链表的所有结点。注意,返回的时候,返回的是虚拟头结点的下一节点而不是虚拟头结点。

参考代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummy_head = ListNode(next=head) #添加一个虚拟节点
        cur = dummy_head
        while(cur.next!=None):
            if(cur.next.val == val):
                cur.next = cur.next.next #删除cur.next节点
            else:
                cur = cur.next
        return dummy_head.next

cur(current):当前节点

cur.val:当前节点的值/数据

cur.next:当前节点的指针域,指向下一个节点的指针/下一个节点

cur.next.val:下一个节点的数据域

707 设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 nextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

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

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

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

单链表

get(index)

1. 要判断index是否越界

2. 要赋予头结点

遍历完列表后要返回头结点。如果直接操作头结点,那头结点的值已改,就无法返回列表的头结点了。

3. 要从index+1开始遍历

addAtHead() & addAtTail()

可以直接用同个类里的addAtIndex()

addAtIndex() & deleteAtIndex()

1. 注意要添加/删减一个节点的空间

2. prev_node和cur_node是两个相邻的节点

3. 遍历到index后才开始操作

好像没有在增删节点后往前/往后推移节点?

# 单链表
class Node:
    
    def __init__(self, val):
        self.val = val
        self.next = None


class MyLinkedList:

    def __init__(self):
        self._head = Node(0)  # 虚拟头部节点
        self._count = 0  # 添加的节点数

    def get(self, index: int) -> int:
        """
        Get the value of the index-th node in the linked list. If the index is invalid, return -1.
        """
        if 0 <= index < self._count:
            node = self._head //从头结点开始遍历
            for _ in range(index + 1):
                node = node.next
            return node.val
        else:
            return -1

    def addAtHead(self, val: int) -> None:
        """
        Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
        """
        self.addAtIndex(0, val)

    def addAtTail(self, val: int) -> None:
        """
        Append a node of value val to the last element of the linked list.
        """
        self.addAtIndex(self._count, val)

    def addAtIndex(self, index: int, val: int) -> None:
        """
        Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
        """
        if index < 0:
            index = 0
        elif index > self._count:
            return

        # 计数累加
        self._count += 1

        add_node = Node(val)
        prev_node, current_node = None, self._head
        for _ in range(index + 1):
            prev_node, current_node = current_node, current_node.next
        else:
            prev_node.next, add_node.next = add_node, current_node

    def deleteAtIndex(self, index: int) -> None:
        """
        Delete the index-th node in the linked list, if the index is valid.
        """
        if 0 <= index < self._count:
            # 计数-1
            self._count -= 1
            prev_node, current_node = None, self._head
            for _ in range(index + 1):
                prev_node, current_node = current_node, current_node.next
            else:
                prev_node.next, current_node.next = current_node.next, None


# 双链表
# 相对于单链表, Node新增了prev属性
class Node:
    
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None


class MyLinkedList:

    def __init__(self):
        self._head, self._tail = Node(0), Node(0)  # 虚拟节点
        self._head.next, self._tail.prev = self._tail, self._head
        self._count = 0  # 添加的节点数

    def _get_node(self, index: int) -> Node:
        # 当index小于_count//2时, 使用_head查找更快, 反之_tail更快
        if index >= self._count // 2:
            # 使用prev往前找
            node = self._tail
            for _ in range(self._count - index):
                node = node.prev
        else:
            # 使用next往后找
            node = self._head   
            for _ in range(index + 1):
                node = node.next
        return node

    def get(self, index: int) -> int:
        """
        Get the value of the index-th node in the linked list. If the index is invalid, return -1.
        """
        if 0 <= index < self._count:
            node = self._get_node(index)
            return node.val
        else:
            return -1

    def addAtHead(self, val: int) -> None:
        """
        Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
        """
        self._update(self._head, self._head.next, val)

    def addAtTail(self, val: int) -> None:
        """
        Append a node of value val to the last element of the linked list.
        """
        self._update(self._tail.prev, self._tail, val)

    def addAtIndex(self, index: int, val: int) -> None:
        """
        Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
        """
        if index < 0:
            index = 0
        elif index > self._count:
            return
        node = self._get_node(index)
        self._update(node.prev, node, val)

    def _update(self, prev: Node, next: Node, val: int) -> None:
        """
            更新节点
            :param prev: 相对于更新的前一个节点
            :param next: 相对于更新的后一个节点
            :param val:  要添加的节点值
        """
        # 计数累加
        self._count += 1
        node = Node(val)
        prev.next, next.prev = node, node
        node.prev, node.next = prev, next

    def deleteAtIndex(self, index: int) -> None:
        """
        Delete the index-th node in the linked list, if the index is valid.
        """
        if 0 <= index < self._count:
            node = self._get_node(index)
            # 计数-1
            self._count -= 1
            node.prev.next, node.next.prev = node.next, node.prev

206 翻转链表

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

示例:

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

自写 

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head
        pre = None
        while cur != None:
            pre = cur
            cur = cur.next
        return pre 

双指针思路误区:

1. 这样翻转无法保存过程中的节点,只会一个覆盖一个【不,应该说没有逆转指针】

2. 没有让cur指向pre

3. 后移时不能先移cur,不然cur已经等于temp了,就无法把原来的cur给pre了

正确思路(双指针):

1. 初始化两个指针,pre和cur

2. 让cur.next指向pre【先看3.】

3. 因为指向pre后无法通过cur.next找到原本的cur.next的值,所以需要先把cur.next保存下来(用temp指针暂存)

4. 让pre和cur整体向后移一位

感想

1. 链表的基本原理是看得懂的,但实际上手还是多有疑惑。我觉得一方面是代码熟练度不高,另一方面是用Python操作链表的基本操作还接触的少,很陌生。

2. 虽然接触了数组链表这些数据结构,但实际上还不是很清楚到底应该是什么时候用。

3. 要学会画图。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值