javaScript数据结构与算法之双向链表的实现

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;
        }
    }
}

结语

总的来说,链表的实现只是一个基础,后面我会尝试用链表实现比较常用的一些数据结构,比如栈、队列、数组等

感谢大家的阅览,如果哪里写的有问题请大家指正,望与诸位共同进步

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值