一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
什么是链表?
链表是一种由一群结点组成顺序的数据结构。在最简单的情况下,每个结点由一个数据和一个指向在顺序中下一个结点的指针(既连接)而组成。 如图~
设计思路
这里我也是在visualgo上的demo中分析到的(visualgo,程序员学算法必备网站) 通过一个
class
承载所需的属性和方法
属性
- head 头部
- tail 尾部
- size 链表长度
class CreateLinkList {
private length: number;
private head!: LinkListNode;
private tail!: LinkListNode;
constructor(arr: number[]) {
const length = arr.length;
if (!length) {
throw new Error("arr is empty");
}
this.length = length;
}
size() {
return this.length;
}
getHead() {
return this.head;
}
getTail() {
return this.tail;
}
}
方法
- 创建
visualgo 动画演示
代码
class CreateLinkList {
constructor(arr: number[]) {
// 。。。
this.create(arr);
}
private create(arr: number[]) {
const length = this.length;
let curNode: LinkListNode = {
value: arr[length - 1],
};
this.tail = curNode;
if (length === 1) {
return curNode;
}
for (let i = length - 2; i >= 0; i--) {
curNode = {
value: arr[i],
next: curNode,
};
}
this.head = curNode;
}
// ...
}
- 查找 visualgo 动画演示
visualgo这里是用
value
去做的查询,本人代码设计是用index
坐标去查询 代码
// ...
class CreateLinkList {
// ...
get(index: number) {
if (index < 0 || index >= this.length) {
throw new Error('invalid index');
}
// 保留头尾的好处,查找时间复杂度为O(1);
if (index === 0) {
return this.head;
}
if (index === this.length - 1) {
return this.tail;
}
// 除头尾获取其他项时间复杂度为O(n)
let i = 0;
let temp = this.head;
while(i < index) {
if (temp.next) {
temp = temp.next;
i++;
}
}
return temp;
}
// ...
}
- 插入
visualgo 动画演示
由动画看的出来,插入需要支持三点:
1.头部前插入;2.尾部后插入;3.中间项后插入
- 在头部前插入
class CreateLinkList {
// ...
/**
* @description 在头部前插入节点
* @param value
*/
beforHeadInsert(value: number): void {
this.head = {
value,
next: this.head,
};
this.length++;
}
// ...
}
- 在尾部追加
class CreateLinkList {
// ...
/**
* @description 在尾部插入节点
* @param value
*/
lastAppend(value: number) {
this.tail.next = {
value,
};
this.length++;
}
// ...
}
- 中间项后插入
class CreateLinkList {
// ...
/**
* @description 在中间项后插入
* @param index 索引
* @param value 插入节点value
*/
insert(index: number, value: number) {
if (index < 0) {
throw new Error('invalid index');
}
// 如果index >= 最大坐标,同在尾部插入
if (index >= this.length - 1) {
this.lastAppend(value);
} else {
let i = 0;
let temp = this.head as LinkListNode;
while(i <= index) {
if (i < index) {
temp = temp.next as LinkListNode;
} else {
const t = temp.next;
const newNode = {
value,
next: t
};
temp.next = newNode;
}
i++;
}
}
}
// ...
}
- 移除
visualgo 动画演示
移除也需要支持三点: 1.移除头部节点;2.移除尾部节点;3.移除中间节点
- 移除头部节点
class CreateLinkList {
// ...
/**
* @description 删除头部节点
*/
removeHead() {
if (this.length > 1) {
this.head = this.head?.next as Required<LinkListNode>;
this.length--;
} else {
this.head = null;
this.tail = null;
this.length--;
}
}
// ...
}
- 移除节点
class CreateLinkList {
// ...
/**
* @description 删除节点
*/
remove(index: number) {
if (index < 0 || index > this.length - 1 ) {
throw new Error('invalid index');
}
if (index === 0) {
this.removeHead();
} else {
let temp = this.head as NonNullable<LinkListNode>;
let i = 0;
while(i <= index - 1) {
if (i < index - 1) {
temp = temp?.next as LinkListNode;
} else {
if (temp && temp.next && temp.next.next) {
temp.next = temp.next.next;
} else {
delete temp.next;
}
}
i++;
}
this.length--;
}
}
// ...
}
- 移除尾部节点
class CreateLinkList {
// ...
/**
* @description 删除尾部节点
*/
removeTail() {
this.remove(this.length - 1);
}
// ...
}
测试
通过使用jest
进行了一些基本的单元测试,都是通过的..
到此为止,一个基础版的单链表生成了,单测可能不够充分,可能存在一些其他的问题,有感兴趣的小伙伴可以指点一下~ 源代码