设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,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 个节点
如下图所示:从题目中我们可以条件每个节点的索引是[0 , size -1],以单链表为例进行设计。
【第一步】:我们设计一个模板,引入size变量记录链表长度,引入虚拟头节点方便进行插入删除索引等操作。
class MyLinkedList {
// 设置 链表长度
int size;
// 设置虚拟头结点
ListNode dummy;
// 构造器
public MyLinkedList() {
size = 0;
dummy = new ListNode(0);
}
/**
* 获取链表中第 index 个节点的值,如果索引无效,则返回 -1
* @param index
* @return
*/
public int get(int index) {
}
/**
* 插入头节点
* @param val
*/
public void addAtHead(int val) {
addAtIndex(0,val);
}
/**
* 插入尾节点
* @param val
*/
public void addAtTail(int val) {
addAtIndex(size,val);
}
/**
* 指定插入索引
* @param index
* @param val
*/
public void addAtIndex(int index, int val) {
}
/**
* 删除指定索引的节点,如果索引无效,则返回
* @param index
*/
public void deleteAtIndex(int index) {
}
【第二步】编写get函数
1. 判断索引是否失效,文章开头就已经介绍了索引范围为[0,size - 1];
2. 如果想得所以处的值,首先要遍历到该位置。
3. 最后输出
代码如下:
/**
* 获取链表中第 index 个节点的值,如果索引无效,则返回 -1
* @param index
* @return
*/
public int get(int index) {
// 判断索引是否失效
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = dummy;
// 循环寻找到节点
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
【第三步】编写插入函数
插头和插尾函数,可以依赖于addAtIndex。
// 插头函数
public void addAtHead(int val) {
addAtIndex(0,val);
}
// 插尾函数
public void addAtTail(int val) {
addAtIndex(size,val);
}
- 当索引index <= 0 时 插入到链表的头部
- 当index超过size 需要直接返回
- index = size 需要插入尾部
- 其他情况需要找到插入位置然后进行插入
注意:插入的顺序按照图中的1->2->3->
/**
* 指定插入索引
* @param index
* @param val
*/
public void addAtIndex(int index, int val) {
// 添加到头部
if (index <= 0) {
ListNode newNode = new ListNode(val);
newNode.next = dummy.next;
dummy.next = newNode;
size++;
return;
}
// 超过size 直接返回
if (index > size) {
return;
}
ListNode newNode = new ListNode(val);
ListNode pre = dummy;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
newNode.next = pre.next;
pre.next = newNode;
size++;
}
【第四步】编写删除函数
参考 之前写的文章 一文读懂移除链表元素_abc123mma的博客-CSDN博客
代码
package link;
class MyLinkedList {
// 设置 链表长度
int size;
// 设置虚拟头结点
ListNode dummy;
// 构造器
public MyLinkedList() {
size = 0;
dummy = new ListNode(0);
}
/**
* 获取链表中第 index 个节点的值,如果索引无效,则返回 -1
* @param index
* @return
*/
public int get(int index) {
// 判断索引是否失效
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = dummy;
// 寻找到前驱节点
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
/**
* 插入头节点
* @param val
*/
public void addAtHead(int val) {
addAtIndex(0,val);
}
/**
* 插入尾节点
* @param val
*/
public void addAtTail(int val) {
addAtIndex(size,val);
}
/**
* 指定插入索引
* @param index
* @param val
*/
public void addAtIndex(int index, int val) {
// 添加到头部
if (index <= 0) {
ListNode newNode = new ListNode(val);
newNode.next = dummy.next;
dummy.next = newNode;
size++;
return;
}
// 超过size 直接返回
if (index > size) {
return;
}
ListNode newNode = new ListNode(val);
ListNode pre = dummy;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
newNode.next = pre.next;
pre.next = newNode;
size++;
}
/**
* 删除指定索引的节点,如果索引无效,则返回
* @param index
*/
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
ListNode pre = dummy;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next = pre.next.next;
size--;
}
public static void main(String[] args) {
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
// 此时长度为1
System.out.println(linkedList.size);
linkedList.addAtTail(3);
// 此时长度为2
System.out.println(linkedList.size);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
// 此时长度为2
System.out.println(linkedList.size);
int i = linkedList.get(1);
// 返回2
System.out.println(i);
linkedList.deleteAtIndex(1); //现在链表是1-> 3
//返回3
System.out.println(linkedList.get(1));
}
}