【算法专题训练】14、链表
1、链表
- 在链表中,每个节点包含指向下一个节点的指针,這些指针把节点连接成链状结构。
链表的特点: - 创建链表时事先不知道链表的长度,也无须指定链表内存大小。
- 链表节点的内存不是在创建链表时一次性地完成,而是每添加一个节点分配一次内存。
- 链表节点在内存中的地址不是连续的,如果要找到链表的第i个结点,需要从链表头结点开始不断遍历。
- 链表节点的除了有数据域,还有指针域,指向下一个节点的位置
struct ListNode
{int val;ListNode *next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode *next) : val(x), next(next) {}
};
从内存结构来区分,数据结构的关系只有两种:数组和链表。
- 数组通过元素下标来指定元素的指针位置,链表通过节点的指针域来确定链表节点位置。
2、哨兵节点
- 哨兵节点是在原先链表的头结点前添加的一个节点。
- 作用:添加了哨兵节点后的链表一定不是空链表,不用考虑链表为空的场景,可以简化链表代码实现。
- 链表操作后,返回哨兵节点的下一个节点指针。
3、链表中的双指针解法
- 双指针解法在之前的数据结构中都有使用,这种解题思路使用非常广泛。
LCR 021. 删除链表的倒数第 N 个结点
题目信息
- https://leetcode.cn/problems/SLwz0R/description/
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]示例 2:
输入:head = [1], n = 1
输出:[]示例 3:
输入:head = [1,2], n = 1
输出:[1]提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
解题思路:
- 输入一个链表的头结点,和数字n,要求删除当前链表的倒数第n个结点,并返回处理后的链表头结点
- 双指针解法,使用快指针先从链表头结点开始遍历n个结点位置,使用锚点指针
- 然后慢指针才开始从头结点开始遍历,等到快指针遍历到链表末尾了,也就是next指针为null
- 则对慢指针的后面位置的结点进行删除,当前慢指针的位置是要删除的倒数第n个结点的前一个结点位置
例子: - 链表 1,2,3,4,5; 要求删除的链表倒数第2个位置的结点4,则慢指针停止的位置是节点3位置
代码实现:
ListNode *removeNthFromEnd(ListNode *head, int n)
{ListNode *dummy = new ListNode(); // 哨兵节点指针dummy->next = head;ListNode *fast = dummy;ListNode *slow = dummy;for (int i = 0; i < n; i++){fast = fast->next;}while (fast->next != nullptr){fast = fast->next;slow = slow->next;}// 慢指针节点删除slow->next = slow->next->next;return dummy->next;
}
4、总结:
- 链表结构的特点,链表节点除了有数据域,还有指向下一个节点的指针域
- 哨兵节点,在原始链表的头结点前面插入哨兵节点,则不用考虑链表为空的情况,可以简化代码实现
- 链表的双指针解法
- 指针对象的初始化,必须使用new关键字实例化