一、基础知识
链表
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的入口节点称为链表的头结点(head)。
链表类型
单链表。
双链表:可以向前查询也可以向后查询。
循环链表:链表首尾相连,可以用来解决约瑟夫环问题。
存储方式
节点在内存中散乱分布,链表通过指针域的指针链接各个节点。
定义链表节点
默认的构造函数不会初始化任何成员变量,即需要声明节点后,另写语句进行赋值。
定义链表节点及其构造函数的代码如下:(java版)
public class ListNode {
int val;
ListNode next;
// 构造函数
public ListNode() {
}
public ListNode(int val) {
this.val=val;
}
public ListNode(int val, ListNode next) {
this.val=val;
this.next=next;
}
}
链表操作
删除节点、添加节点都只涉及相邻节点的next指针,时间复杂度为O(1),并且不会影响其他节点。
但当删除的是最后一个节点时,需要从头节点遍历到倒数第二个节点通过next指针进行删除,查找的时间复杂度是O(n)。
删除节点时,在C++中最好手动释放该节点,释放这块内存;在java、python中有内存回收机制自动释放。
二、链表和数组的对比
增删 时间复杂度 | 查询 时间复杂度 | 特点 | 适用场景 | |
数组 | O(n) | O(1) | 数据量固定 | 多查询 少增删 |
链表 | O(1) | O(n) | 数据量不固定 | 少查询 多增删 |
三、双指针法在链表的应用
1. 删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
解题思路:双指针法,快慢指针间的间隔为n+1。首先设置虚拟头结点,快慢指针都从虚拟头结点开始;然后将快指针移动到第n+1个节点;将快慢指针同时向后移动,直到快指针为空,此时慢指针指向的是倒数n+1个节点,删除其后一个节点即可。
(或:快慢指针间隔为n,但移动终止条件为:将快慢指针同时向后移动,直到快指针的下一节点为空)
使用双指针,能够实现只遍历/扫描一次链表。
java代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode vitualHead = new ListNode(-1);
vitualHead.next = head;
ListNode slowIndex = vitualHead;
ListNode fastIndex = vitualHead;
for(int i=0;i<n+1;i++){
fastIndex = fastIndex.next;
}
while(fastIndex!=null){
slowIndex=slowIndex.next;
fastIndex = fastIndex.next;
}
slowIndex.next=slowIndex.next.next;
return vitualHead.next;
}
}
参考资料:
代码随想录