目标:
使用js实现一个链表结构。
学习资料:
链表特点
- 有序;
- 内存中无序;
- 移动、删除只需要改变前后指针;
链表属性、方法
属性:size、head、tail
方法:append、appendAt、removeAt、indexOf...
链表类型
单向链表、单向循环链表、双向链表、双向循环链表、环形链表
单向链表
先实现一个Node类,表示链表中的一个元素,包含两个属性 :链表节点的值和next指针。
class Node {
constructor(v) {
this.value = v;
this.next = null;
}
}
再实现一个linkedList类。
class LinkedList {
constructor() {
this.size = 0;
this.head = null;
}
// 链表末尾追加元素
append(){}
// 链表任意位置 添加元素
appendAt(){}
// 删除指定位置元素
removeAt(){}
// 查找元素索引
indexOf(){}
}
添加元素
append(v) {
let node = new Node(v);
if (this.head === null) {
this.head = node
} else {
let cur = this.getNode(this.size - 1)
cur.next = node
}
this.size++
}
getNode(index) {
if (index < 0 || index >= this.size) {
throw new Error('out range')
}
let cur = this.head
for (var i = 0; i < index; i++) {
cur = cur.next
}
return cur
}
在指定位置添加元素
appendAt(i, v) {
if (i < 0 || i > this.size) {
throw new Error('out range')
}
let node = new Node(v)
if (i === 0) {
node.next = this.head
this.head = node
} else {
let prev = this.getNode(i - 1)
node.next = prev.next
prev.next = node
}
this.size++
}
根据索引移除元素
removeAt(i) {
if (i < 0 || i >= this.size) {
throw new Error('out range')
}
if (i === 0) {
this.head = this.head.next
} else {
let prev = this.getNode(i - 1);
prev.next = prev.next.next
}
this.size--;
}
实现indexOf功能
indexOf(v) {
let cur = this.head
for (var i = 0; i < this.size; i++) {
if (cur.value === v) {
return i
}
cur = cur.next
}
return -1
}
完整代码
class Node {
constructor(v) {
this.value = v;
this.next = null;
}
}
class LinkedList {
constructor() {
this.size = 0;
this.head = null;
}
append(v) {
let node = new Node(v);
if (this.head === null) {
this.head = node
} else {
let cur = this.getNode(this.size - 1)
cur.next = node
}
this.size++
}
getNode(index) {
if (index < 0 || index >= this.size) {
throw new Error('out range')
}
let cur = this.head
for (var i = 0; i < index; i++) {
cur = cur.next
}
return cur
}
appendAt(i, v) {
if (i < 0 || i > this.size) {
throw new Error('out range')
}
let node = new Node(v)
if (i === 0) {
node.next = this.head
this.head = node
} else {
let prev = this.getNode(i - 1)
node.next = prev.next
prev.next = node
}
this.size++
}
removeAt(i) {
if (i < 0 || i >= this.size) {
throw new Error('out range')
}
if (i === 0) {
this.head = this.head.next
} else {
let prev = this.getNode(i - 1);
prev.next = prev.next.next
}
this.size--;
}
indexOf(v) {
let cur = this.head
for (var i = 0; i < this.size; i++) {
if (cur.value === v) {
return i
}
cur = cur.next
}
return -1
}
}
单向循环链表
class CircLinkedList extends LinkedList{
append(v){
const node = new Node(v);
if(this.head === null){
this.head = node;
node.next = this.head;
}else{
let cur = this.getNode(this.size - 1)
cur.next = node ;
node.next = this.head;
}
}
appendAt(i,v){
if (i < 0 || i > this.size) {
throw new Error('out range')
}
let node = new Node(v)
if (i === 0) {
if(this.head===null){
this.head = node
node.next = this.head
}else{
node.next = this.head
this.head = node
this.getNode(this.size - 1).next = this.head;
}
} else {
let prev = this.getNode(i - 1)
node.next = prev.next
prev.next = node
}
this.size++
}
removeAt(i) {
if (i < 0 || i >= this.size) {
throw new Error('out range')
}
if (i === 0) {
if(this.size === 1){
this.head = null
}else{
const tail = this.getNode(this.size - 1)
this.head = this.head.next;
tail.next = this.head
}
} else {
let prev = this.getNode(i - 1);
prev.next = prev.next.next
}
this.size--;
}
}
双向链表
// 双向链表
class DoubleLinkedList {
constructor() {
this.size = 0;
this.head = null;
this.tail = null
}
[Symbol.iterator]() {
let cur = this.head;
return {
next() {
if (cur) {
let res = cur
cur = cur.next;
return {
done: false, value: res.value
}
} else {
return {
done: true, value: null
}
}
}
}
}
append(v) {
let node = new DoubleNode(v);
if (this.head === null) {
this.head = node
this.tail = node
} else {
let cur = this.tail
cur.next = node
node.prev = cur
this.tail = node
}
this.size++
}
getNode(index) {
if (index < 0 || index >= this.size) {
throw new Error('out range')
}
let cur = this.head
for (var i = 0; i < index; i++) {
cur = cur.next
}
return cur
}
appendAt(i, v) {
if (i < 0 || i > this.size) {
throw new Error('out range')
}
let node = new DoubleNode(v)
if (i === 0) {
node.next = this.head
this.head.prev = node
this.head = node
} else if (i === this.size) {
node.prev = this.tail
this.tail.next = node
this.tail = node
}
else {
let prev = this.getNode(i - 1)
node.next = prev.next
node.prev = prev
node.next.prev = node
prev.next = node
}
this.size++
}
removeAt(i) {
if (i < 0 || i >= this.size) {
throw new Error('out range')
}
if (i === 0) {
this.head = this.head.next
this.head.prev = null
} else if (i === this.size - 1) {
this.tail = this.tail.prev
this.tail.next = null
} else {
let prev = this.getNode(i - 1);
prev.next = prev.next.next
prev.next.prev = prev
}
this.size--;
}
indexOf(v) {
let cur = this.head
for (var i = 0; i < this.size; i++) {
if (cur.value === v) {
return i
}
cur = cur.next
}
return -1
}
}
双向循环链表
// 双向循环链表
class DoubleCircLinkedList extends DoubleLinkedList {
append(v) {
let node = new DoubleNode(v);
if (this.head === null) {
this.head = node
this.head.prev = this.head
this.head.next = this.head
} else {
let cur = this.tail
cur.next = node
node.prev = cur
node.next = this.head
this.head.prev = node
}
this.tail = node
this.size++
}
appendAt(i, v) {
if (i < 0 || i > this.size) {
throw new Error('out range')
}
let node = new DoubleNode(v)
if (i === 0) {
node.next = this.head
this.head.prev = node
this.head = node
} else if (i === this.size - 1) {
let cur = this.tail
cur.next = node
node.prev = cur
node.next = this.head
this.head.prev = node
this.tail = node
}
else {
let prev = this.getNode(i - 1)
node.next = prev.next
node.prev = prev
node.next.prev = node
prev.next = node
}
this.size++
}
removeAt(i) {
if (i < 0 || i >= this.size) {
throw new Error('out range')
}
if (i === 0) {
this.head = this.head.next
this.head.next.prev = this.head
this.head.prev = this.tail
this.tail.next = this.head
} else if (i === this.size - 1) {
this.tail = this.tail.prev
this.tail.next = this.head
this.head.prev = this.tail
} else {
let prev = this.getNode(i - 1);
prev.next = prev.next.next
prev.next.prev = prev
}
this.size--;
}
}