javaScript数据结构与算法之双向链表的实现
引言
这篇文章写之前其实双向链表的实现已经写了好几天了,期间反复修改过,最后感觉自己写的复杂了,又删除了很多方法,只保留了一些基本的方法,最终完成了双向链表的实现
快捷通道
上一篇:javaScript数据结构与算法之单向链表的实现.
下一篇:暂无
原理
双向链表原理还是离散节点的连接,不同的是单向链表是有next指向下一个节点,而双向链表在有next的情况下,还有一个last指向上一个
完整javaScript代码
过程就不多说了,跟单向链表大同小异,只不过需要处理每个节点的next和last,不能只处理next
// 双向链表节点
function DoubleNode(value) {
let node = Object.create(null);
node.value = value; // 节点值
node.last = null; // 上一个节点
node.next = null; // 下一个节点
return node;
}
/**
* 双向链表
*/
function DoubleLinkList() {
this.head = null; // 头部节点
this.tail = null; // 尾部节点
this.length = 0; // 链表长度
/**
* 判断是不是对象
* @param {*} param 需要判断的值
* @returns true是,false不是
*/
DoubleLinkList.prototype.isObject = function (param) {
return Object.prototype.toString.call(param) === "[object Object]";
}
/**
* 向链表添加一个节点,并放到头部
* @param {*} value 节点值
*/
DoubleLinkList.prototype.push = function (value) {
let node = new DoubleNode(value);
if (!this.head && !this.tail) {
this.head = node;
this.tail = node;
} else {
if (this.length === 1) {
this.tail.last = node;
node.next = this.tail;
this.head = node;
} else {
this.head.last = node;
node.next = this.head;
this.head = node;
}
}
this.length++;
}
/**
* 弹出最后加入的节点,链表为空返回null
* @returns 节点值
*/
DoubleLinkList.prototype.pop = function () {
if (this.head) {
let node = this.head;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
node.next.last = null;
this.head = node.next;
}
this.length--;
return node.value;
} else {
return null;
}
}
/**
* 向链表添加一个节点,并放到尾部
* @param {*} value 节点值
*/
DoubleLinkList.prototype.unshift = function (value) {
let node = new DoubleNode(value);
if (!this.head && !this.tail) {
this.head = node;
this.tail = node;
} else {
if (this.length === 1) {
this.head.last = node;
node.next = this.head;
this.tail = node;
} else {
this.tail.next = node;
node.last = this.tail;
this.tail = node;
}
}
this.length++;
}
/**
* 弹出尾部的节点,链表为空返回null
* @returns 节点值
*/
DoubleLinkList.prototype.shift = function () {
if (this.tail) {
let node = this.tail;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
node.last.next = null;
this.tail = node.last;
}
this.length--;
return node.value;
} else {
return null;
}
}
/**
* 遍历
* head->tail
* @param {*} fn 回调函数
*/
DoubleLinkList.prototype.finds = function (fn) {
if (fn && Object.prototype.toString.call(fn) === "[object Function]") {
let node = this.head;
while (node) {
fn(node.value);
node = node.next;
}
} else {
throw new Error("no callback function");
}
}
/**
* 查询
* @param {*} value 要查询的值
* @param {*} all 是否查全部,false只查询第一个,true查全部并返回值数组
* @returns 节点值或节点值数组,未查到返回空值或空数组
*/
DoubleLinkList.prototype.find = function (value, all = false) {
if (!value) return null;
let node = this.head;
let nodeArr = [];
while (node) {
if (node.value === value) {
if (all) {
nodeArr.push(node.value);
} else {
return node.value;
}
}
node = node.next;
}
if (all) {
return nodeArr;
} else {
return null;
}
}
/**
* 针对节点value是对象的查询方法
* @param {*} key 节点的标识名
* @param {*} value 节点的标识值
* @param {*} all 是否查全部,false只查询第一个,true查全部并返回值数组
* @returns 节点值或节点值数组,未查到返回空值或空数组
*/
DoubleLinkList.prototype.findObj = function (key, value, all = false) {
if (!key || !value) return null;
let node = this.head;
let nodeArr = [];
while (node) {
// 如果节点的值不是对象跳过本次循环
if (!this.isObject(node.value)) {
node = node.next;
continue;
}
if (node.value[key] === value) {
if (all) {
nodeArr.push(node.value);
} else {
return node.value;
}
}
node = node.next;
}
if (all) {
return nodeArr;
} else {
return null;
}
}
}
完整typeScript代码
// 双向链表节点
class DoubleNode {
/** 节点值 */
public value: any = null;
/** 上一个节点 */
public last: DoubleNode = null;
/** 下一个节点 */
public next: DoubleNode = null;
/**
* 构造方法
* @param value 节点值
*/
constructor(value: any) {
this.value = value;
}
}
// 双向链表
class DoubleLinkList {
/** 头部节点 */
public head: DoubleNode = null;
/** 尾部节点 */
public tail: DoubleNode = null;
/** 链表长度 */
public length: number = 0;
/**
* 判断是不是对象
* @param param 需要判断的值
* @returns true是,false不是
*/
private isObject(param: any): boolean {
return Object.prototype.toString.call(param) === "[object Object]";
}
/**
* 向链表添加一个节点,并放到头部
* @param value 节点值
*/
public push(value: any): void {
let node = new DoubleNode(value);
if (!this.head && !this.tail) {
this.head = node;
this.tail = node;
} else {
if (this.length === 1) {
this.tail.last = node;
node.next = this.tail;
this.head = node;
} else {
this.head.last = node;
node.next = this.head;
this.head = node;
}
}
this.length++;
}
/**
* 弹出最后加入的节点,链表为空返回null
* @returns 节点值
*/
public pop(): any {
if (this.head) {
let node = this.head;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
node.next.last = null;
this.head = node.next;
}
this.length--;
return node.value;
} else {
return null;
}
}
/**
* 向链表添加一个节点,并放到尾部
* @param value 节点值
*/
public unshift(value: any): void {
let node = new DoubleNode(value);
if (!this.head && !this.tail) {
this.head = node;
this.tail = node;
} else {
if (this.length === 1) {
this.head.last = node;
node.next = this.head;
this.tail = node;
} else {
this.tail.next = node;
node.last = this.tail;
this.tail = node;
}
}
this.length++;
}
/**
* 弹出尾部的节点,链表为空返回null
* @returns 节点值
*/
public shift(): any {
if (this.tail) {
let node = this.tail;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
node.last.next = null;
this.tail = node.last;
}
this.length--;
return node.value;
} else {
return null;
}
}
/**
* 遍历(从head开始)
* @param fn 回调函数
*/
public finds(fn: Function): void {
if (fn && Object.prototype.toString.call(fn) === "[object Function]") {
let node = this.head;
while (node) {
fn(node.value);
node = node.next;
}
} else {
throw new Error("no callback function");
}
}
/**
* 查询
* @param value 要查询的值
* @param all 是否查全部,false只查询第一个,true查全部并返回值数组
* @returns 节点值或节点值数组,未查到返回空值或空数组
*/
public find(value: any, all: boolean = false): any | any[] | null {
if (!value) return null;
let node = this.head;
let nodeArr = [];
while (node) {
if (node.value === value) {
if (all) {
nodeArr.push(node.value);
} else {
return node.value;
}
}
node = node.next;
}
if (all) {
return nodeArr;
} else {
return null;
}
}
/**
* 针对节点value是对象的查询方法
* @param key 节点的标识名
* @param value 节点的标识值
* @param all 是否查全部,false只查询第一个,true查全部并返回值数组
* @returns 节点值或节点值数组,未查到返回空值或空数组
*/
public findObj(key: string, value: string, all: boolean = false): any | any[] | null {
if (!key || !value) return null;
let node = this.head;
let nodeArr = [];
while (node) {
// 如果节点的值不是对象跳过本次循环
if (!this.isObject(node.value)) {
node = node.next;
continue;
}
if (node.value[key] === value) {
if (all) {
nodeArr.push(node.value);
} else {
return node.value;
}
}
node = node.next;
}
if (all) {
return nodeArr;
} else {
return null;
}
}
}
结语
总的来说,链表的实现只是一个基础,后面我会尝试用链表实现比较常用的一些数据结构,比如栈、队列、数组等
感谢大家的阅览,如果哪里写的有问题请大家指正,望与诸位共同进步