链表理论基础
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。
链表的定义:
//链表的定义
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;}
}
删除节点:
删除D节点,只要将C节点的next指针指向E节点就可以了。Java、Python就有自己的内存回收机制,就不用自己手动释放了。
203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::代码随想录
//链表最重要的是head
//添加dummyHead, dummyHead.next指向原head
//新建一个指针ListNode cur用来遍历
//最后返回dummyHead.next
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
while(cur.next != null) {
if(cur.next.val == val) {
cur.next = cur.next.next;
}
else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:代码随想录
//考察链表理解和操作,以及java
//利用dummyHead
//加入结点时要注意新结点先指向原来的点
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) {this.val = val;}
}
class MyLinkedList {
//属性
int size;
ListNode dummyHead;
//初始化链表
public MyLinkedList() {
size = 0;
dummyHead = new ListNode(0);
}
public int get(int index) {
//判断index是否合法
if(index < 0 || index >= size) {
return -1;
}
ListNode cur = dummyHead;
//for(i)循环和cur.next一起遍历移动cur。包含一个虚拟头节点,所以查找第 index+1 个节点
for(int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
// 如果index = 0,那么新插入的节点为链表的新头节点。
// 如果index = size,则说明是新插入的节点为链表的尾结点
// 如果index > size,则返回空
// 如果index < 0,则置为0,作为链表的新头节点。
public void addAtIndex(int index, int val) {
if(index > size) {
return;
}
if(index < 0) {
index = 0;
}
size++;
ListNode cur = dummyHead;
for(int i = 0; i < index; i++) {
cur = cur.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = cur.next;
cur.next = toAdd;
}
public void deleteAtIndex(int index) {
if(index >= size || index < 0) {
return;
}
size--;
if (index == 0) {
dummyHead = dummyHead.next;
return;
}
ListNode cur = dummyHead;
for(int i = 0; i < index; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
}
}
206.反转链表
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:代码随想录
//实际上是双指针的思想
//注意next要在while循环里面定义成cur.next。
//否则在while循环里面模拟next移动使用next = next.next会导致null pointer。
//注意while循环结束后的值。这里每轮循环是「翻转箭头」+「三人行」,最后翻转箭头后还需要继续走一次。
//所以要返回Prev 而不是cur.
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null, cur = head;
while(cur != null) {
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
}
本文介绍了链表的基础知识,包括其内存分布特性和与数组的区别。接着详细讨论了如何删除链表中的节点,并以LeetCode题目203.移除链表元素、707.设计链表和206.反转链表为例,阐述链表操作的难点和解决策略,强调了虚拟头结点在链表问题中的重要性。每部分都提供了相应的题目链接、文章讲解和视频教程资源。
1373

被折叠的 条评论
为什么被折叠?



