如有错误,感谢不吝赐教、交流
文章目录
- 前言
- 本文涉及题目:设计链表
- 有无头结点的区别
- 头指针
- 无头结点
- 有头结点
- 为什么需要头结点呢?
- 注意:
- 单链表,本文使用Java实现
- 定义链表节点
- 定义一个链表类并初始化
- get(int index)
- addAtHead(int val)
- addAtTail(int val)
- addAtIndex(int index, int val)
- deleteAtIndex(int index)
- 完整Java代码
前言
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成;
(数据域,指针域(存放下一个节点的指针));
最后一个节点的指针指向null;
如图示例:
本文涉及题目:设计链表
leetcode707
有无头结点的区别
头指针
不管是有头结点或者是没有头结点,头指针都是指向链表的第一个节点,具体区别,看下面;
无头结点
对于没有头结点的链表,头指针指向的就是链表的第一个存储数值的节点。即head就是链表的第一个节点。
有头结点
对于有头结点的链表,头指针指向的是头结点,头结点的下一个节点是第一个存储数值的节点。即head.next才是链表的第一个节点。
为什么需要头结点呢?
对于没有头结点的链表,在操作第一个节点和剩余节点会出现不同的情况。
对于链表如图所示的不带头结点的链表,要插入一个新的节点:
原始不带头结点的链表:
在第一个节点之前插入新节点,需要修改head指针指向新的节点:
在不是第一个节点之前插入新节点,不需要修改head指针:
两种插入方式是不同的;
下面我们看看带头结点的链表插入新节点:
注意看第一个节点之前插入和剩余节点之前插入,两种情况是一样,相比不带头结点的链表,更加方便。
注意:
在实现或者做题的时候,一定要弄清楚head指向的是不是头结点,两种情况的答案完全不同。
单链表,本文使用Java实现
如上面所示的就是单链表,接下来我们看看单链表怎么去实现和使用。
定义链表节点
class ListNode {
int val; // 数值域
ListNode next; // 指针域
public ListNode() {}
public ListNode(int val) {
this.val = val;
}
}
定义一个链表类并初始化
MyLinkedList() 初始化 MyLinkedList 对象
class MyLinkedList {
// 记录有多少个节点
int size;
// 头结点
ListNode head;
public MyLinkedList() {
// 初始化链表
size = 0;
head = new ListNode(0);
}
}
get(int index)
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1
直接遍历即可返回对应位置节点的值。
public int get(int index) {
// 索引违规
if (index < 0 || index >= size) {
return -1;
}
ListNode p = head; // 遍历指针
while (index > 0) {
p = p.next;
index--;
}
return p.next.val;
}
addAtHead(int val)
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
可以参见上面的图中在头结点之后插入节点。
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next = newNode;
size++;
}
addAtTail(int val)
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
遍历到最后一个节点,直接插入。
public void addAtTail(int val) {
ListNode p = head;
// 找到尾指针
while(p.next != null) {
p = p.next;
}
ListNode newNode = new ListNode(val);
newNode.next = null;
p.next = newNode;
size++;
}
addAtIndex(int index, int val)
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
public void addAtIndex(int index, int val) {
// 违规索引
if (index < 0 || index > size) {
return;
}
ListNode newNode = new ListNode(val);
ListNode p = head;
if (index == 0) {
// 在头结点之后插入
newNode.next = head.next;
head.next = newNode;
size++;
} else {
while(index > 0) {
p = p.next;
index--;
}
newNode.next = p.next;
p.next = newNode;
size++;
}
}
deleteAtIndex(int index)
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
找到对应的索引位置,如图所示删除。
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
ListNode p = head;
if (index == 0) {
head = head.next;
size--;
return; // 这里删除之后就不执行后面的操作了
}
while(index > 0) {
p = p.next;
index--;
}
p.next = p.next.next;
size--;
}
完整Java代码
class MyLinkedList {
// 记录有多少个节点
int size;
// 头结点
ListNode head;
public MyLinkedList() {
// 初始化链表
size = 0;
head = new ListNode(0);
}
public int get(int index) {
// 索引违规
if (index < 0 || index >= size) {
return -1;
}
ListNode p = head; // 遍历指针
while (index > 0) {
p = p.next;
index--;
}
return p.next.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next = newNode;
size++;
}
public void addAtTail(int val) {
ListNode p = head;
// 找到尾指针
while(p.next != null) {
p = p.next;
}
ListNode newNode = new ListNode(val);
newNode.next = null;
p.next = newNode;
size++;
}
public void addAtIndex(int index, int val) {
// 违规索引
if (index < 0 || index > size) {
return;
}
ListNode newNode = new ListNode(val);
ListNode p = head;
if (index == 0) {
// 在头结点之后插入
newNode.next = head.next;
head.next = newNode;
size++;
} else {
while(index > 0) {
p = p.next;
index--;
}
newNode.next = p.next;
p.next = newNode;
size++;
}
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
ListNode p = head;
if (index == 0) {
head = head.next;
size--;
return; // 这里删除之后就不执行后面的操作了
}
while(index > 0) {
p = p.next;
index--;
}
p.next = p.next.next;
size--;
}
}
class ListNode {
int val;
ListNode next;
public ListNode() {}
public ListNode(int val) {
this.val = val;
}
}
ps:计划每日更新一篇博客,今日2023-04-27,日更第十一天。
昨日更新:
如何求解递归式的时间复杂度