单链表
单链表:链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。如图所示,我们把这个记录下个结点地址的指针叫作后继指针 next。
头节点:我们习惯性地把第一个结点
尾节点:习惯把最后一个节点叫做尾节点,指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。
链表 vs 数组
插入删除
数组:在进行数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度是 O(n)。
链表:在链表中插入或者删除一个数据,我们并不需要为了保持内存的连续性而搬移结点,因为链表的存储空间本身就不是连续的 O(1)。
链表要想随机访问第 k 个元素
数组:时间复杂度为 O(1)。
链表:链表随机访问的性能没有数组好,需要 O(n) 的时间复杂度。
与数组一样,链表也支持数据的查找、插入和删除操作。
// 头部插入节点 尾部插入节点 删除节点 打印节点值等
interface List<T> {
insertToHead: (value: T) => void; // 头部插入
insertToTail: (value: T) => void; // 尾部插入
findByValue: (value: T) => SingleNode<T> | null; // 按值查询
findByIndex: (index: number) => SingleNode<T> | null; // 按索引查询
insertToIndex: (value: T, index: number) => void; // 按索引插入
remove: (value: T) => boolean; // 删除链表的某个节点
toString:()=>string // print 节点value
}
//链表节点
class SingleNode<T> {
value: T; // tip: 静态成员不能使用泛型的类型参数。
next: SingleNode<T> | null;
constructor(value: T, next: SingleNode<T> | null = null) {
this.value = value;
this.next = null;
}
}
class SingleLinkedList<T> implements List<T> {
// 哨兵头节点
private readonly head: SingleNode<T>;
constructor() {
this.head = new SingleNode<any>(null);
}
insertToHead(value: T): void {
const newNode = new SingleNode(value);
newNode.next = this.head.next;
this.head.next = newNode;
}
insertToTail(value: T) {
const newNode = new SingleNode(value);
let p = this.head;
while (p.next !== null) {
p = p.next;
}
p.next = newNode;
}
insertToIndex(value: T, index: number) {
const newNode = new SingleNode(value);
let p = this.head;
let pos = 0;
while (p.next !== null && pos !== index) {
p = p.next;
pos++;
}
if (p.next === null) return "";
newNode.next = p.next;
p.next = newNode;
}
findByValue(value: T): SingleNode<T> | null {
let p = this.head;
while (p.next !== null) {
if (p.next.value === value) return p.next;
p = p.next;
}
return p.next;
}
findByIndex(index: number): SingleNode<T> | null {
let p = this.head;
let pos = 0;
while (p.next !== null && pos !== index) {
p = p.next;
pos++;
}
return p.next;
}
remove(value: T): boolean {
let p = this.head;
while (p.next !== null) {
if (p.next.value === value) break;
p = p.next;
}
if (p.next === null) return false;
p.next = p.next.next;
return true;
}
toString():string{
let ret: string =''
let p = this.head;
while(p.next !== null) {
ret = `${ret}-${p.next.value}`
p= p.next
}
console.log(`print-${ret}`)
return ret
}
}
const singleLinkedList = new SingleLinkedList<string>()
singleLinkedList.insertToHead('my')
singleLinkedList.insertToTail('god')
singleLinkedList.insertToIndex('great',1)
singleLinkedList.toString() // my-great-god
复制代码
虽然写程序接近 7 年的时间了,但是之前总是为了完成而完成,不去思考问什么会这样。
后续打算记录对常见算法、设计模式的理解。
Come on , let's master programmer!
reference: 俩张图片引用王争 大佬的《数据结构与算法之美》https://time.geekbang.org/column/intro/100017301?tab=catalog
评论