数据结构与算法(二)之单向链表和双向链表(偏向JS)

数据结构(二)

说明:本文基于哔哩哔哩视频【JavaScript数据结构与算法】整理

链表(linked List)
  • 类似于数组(数组在有些语言叫链表)

  • 插入删除性能高

  • 相较于数组的优点: 内存不必要是一整块的内存空间(无序,没有下标),大小可以无限延伸

  • 链表内的元素由 元素本身和指向下一个元素的指针【引用或者连接】组成;

  • 相当于 火车,火车头+车厢+连接,每一节车厢和连接处相当链表中的元素
    在这里插入图片描述

  • 常见的操作
  • append(element):向列表尾部添加一个新的项

  • insert(position,element):向列表的特定位置插入一个新的项。

  • remove(element):从列表中移除一项。

  • indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。

  • removeAt(position):从列表的特定位置移除一项。

  • isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。

  • size():返回链表包含的元素个数。与数组的length属性类似。

  • toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。

  • 操作的代码实现

 // 链表的封装(类似数组)
        function linkedList() {
            // node 保存节点信息
            function Node(ele) {
                this.element = ele;
                this.next = null;
            }
            // 属性
            this.head = null;
            this.length = 0;

            // 方法
            linkedList.prototype.append = function(data) {
                var newNode = new Node(data);
                if (this.head == null) {
                    this.head = newNode;
                } else {
                    var current = this.head;
                    while (current.next) {
                        current = current.next;
                    }
                    current.next = newNode;
                }
                this.length += 1; // ++ 大多数语言无法识别
            };
            // 转化为字符串
            linkedList.prototype.toString = function() {
                var current = this.head;
                var listString = "";
                while (current) {
                    listString += " " + current.element;
                    current = current.next;
                }
                return listString.slice(1);
            };
            // 插入
            linkedList.prototype.insert = function(position, data) {
                // 越界判断  界限:>0,length
                if (position < 0 || position > this.length) return false;

                var newnode = new Node(data);
                var current = this.head;

                index = 0;
                if (position == 0) {
                    newnode.next = current;
                    this.head = newnode;
                } else {
                    // 运算符的优先级,先比较再计算,    相当于while 的最后一步,index++
                    while (index++ < position) {
                        privious = current;
                        current = current.next;
                    }
                    // 指针
                    newnode.next = current;
                    privious.next = newnode;
                }
                this.length += 1;
                return true;
            };
            // 获取某个位置的值
            linkedList.prototype.get = function(position) {
                if (position < 0 || position >= this.length) return null;

                var current = this.head;
                var index = 0;
                // 找到特定的位置
                while (index++ < position) {
                    current = current.next;
                }
                return current.element;
            };
            // 获取索引
            linkedList.prototype.indexOf = function(ele) {
                var current = this.head;
                index = 0;
                while (current) {
                    if (current.element == ele) {
                        return index;
                    }
                    index += 1;
                    current = current.next;
                }
                return -1;
            };
            // 修改数据
            linkedList.prototype.update = function(position, data) {
                if (position < 0 || position >= this.length) return null;
                var current = this.head;
                var index = 0;
                while (index++ < position) {
                    current = current.next;
                }
                current.element = data;
            };
            // 删除某个位置的值
            linkedList.prototype.removeAt = function(position) {
                if (position < 0 || position >= this.length) return null;

                var previous = null;
                var current = this.head;
                var previous = null;
                if (position == 0) {
                    this.head = current.next;
                } else {
                    var index = 0;
                    while (index++ < position) {
                        previous = current;
                        current = current.next;
                    }
                    previous.next = current.next;
                }
                this.length -= 1
                return current.element
            };
            // 删除某个值
            linkedList.prototype.remove = function(data) {
                const index = this.indexOf(data)
                return this.removeAt(index)
            };
            // 判断是否为空
            linkedList.prototype.isEmpty = function(data) {
                return this.length == 0
            }
            // 获取链表的长度
            linkedList.prototype.size = function(data) {
                return this.length
            }
        }
		// 测试
        var link = new linkedList();
        link.append("sdfg");
        link.append("sdf是g");
        link.append("sd出生地fg");
        link.toString(); // linkedList {head: Node, length: 1}head: Node {node: "sdfg", next: null}length: 1
        console.log(link, "link");

        link.insert(1, "123");
        link.insert(3, "456");
        // console.log(link.get(0), "get");
        console.log(link.indexOf("123"), "indexof");
        link.update(2, "mk");
        console.log(link, "up");
        console.log(link.removeAt(2), "removeat");
        console.log(link.remove('123'), "remove");
        console.log(link.isEmpty('123'), "isEmpty");
        console.log(link.size('123'), "size");

双向链表(使用较多)

  • 可以从两个方向遍历的链表

  • 缺点:内存空间占用较大,需要操作的指针有四个,相对复杂
    来自网络
    在这里插入图片描述

  • 操作:同 单向链表,不同的如下

  • forwardString()正向遍历 从后往前

  • backwardString()反向遍历

  • 相关代码的实现

   // 双向链表
        function DoublyLinkedList() {
            // 内部类:相当于火车的车厢
            function Node(data) {
                this.data = data;
                this.pre = null;
                this.next = null;
            }

            // 大类 双向链表类 的属性
            this.head = null;
            this.tail = null;
            this.length = 0;

            // 双向链表 操作
            DoublyLinkedList.prototype.append = function(data) {
                // 思路
                // 先以传入的元素为基础创建节点,获取这个节点之前的节点pre,让 pre的 next指针指向 这个节点

                const newnode = new Node(data);

                if (this.length == 0) {
                    this.head = newnode;
                    this.tail = newnode;
                } else {
                    newnode.pre = this.tail;
                    this.tail.next = newnode;
                    this.tail = newnode;
                }
                this.length += 1;
            };
            DoublyLinkedList.prototype.forwardString = function() {
                var current = this.tail;
                var res = "";
                while (current) {
                    res += current.data + " ";
                    current = current.pre;
                }
                return res;
            };
            DoublyLinkedList.prototype.backwardString = function() {
                // 向后遍历
                var current = this.head;
                var res = "";
                while (current) {
                    res += current.data + " ";
                    current = current.next;
                }
                return res;
            };
            DoublyLinkedList.prototype.toString = function() {
                this.forwardString();
            };
            DoublyLinkedList.prototype.insert = function(position, data) {
                // 越界判断
                if (position < 0 || position > this.length) return false;
                // 创建节点
                var newnode = new Node(data);

                if (this.length == 0) {
                    this.head = newnode;
                    this.tail = newnode;
                } else {
                    if (position == 0) {
                        this.head.pre = newnode;
                        newnode.next = this.head;
                        this, (head = newnode);
                    } else if (position == this.length) {
                        newnode.pre = this.tail;
                        this.tail.next = newnode;
                        this.tail = newnode;
                    } else {
                        var current = this.head;
                        index = 0;
                        // 找位置
                        while (index++ < position) {
                            current = current.next;
                        }
                        // 找到位置
                        // 修改指针
                        newnode.next = current;
                        newnode.pre = current.pre;
                        // current.pre.next  拿到的是 原来 的pre,不是变成newnode之后的 pre
                        current.pre.next = newnode;
                        current.pre = newnode;
                    }
                }
                this.length += 1;
            };
            DoublyLinkedList.prototype.get = function(position) {
                if (position < 0 || position >= this.length) return false;
                // 较高效率的做法是 二分之后作对比,然后决定顺序还是倒序遍历
                var current = this.head;
                index = 0;
                while (index++ < position) {
                    current = current.next;
                }
                return current.data;
            };
            DoublyLinkedList.prototype.indexOf = function(data) {
                var current = this.head;
                index = 0;
                while (current) {
                    if (current.data == data) {
                        return index;
                    }
                    current = current.next;
                    index++;
                }
                return -1;
            };
            DoublyLinkedList.prototype.update = function(position, data) {
                if (position < 0 || position >= this.length) return false;
                var current = this.head;
                var newData = new Node(data);
                index = 0;
                while (index++ < position) {
                    current = current.next;
                }
                current.data = newData;
                return true;
            };
            DoublyLinkedList.prototype.removeAt = function(position) {
                if (position < 0 || position >= this.length) return null;
                var current = this.head;
                if (this.length == 1) {
                    this.head = thia.tail = null;
                } else {
                    if (position == 0) {
                        this.head.next.pre = null; // 如果删除1 位置的,则,让head 指向为空,并且,重新赋值,指向为删除后的下一个元素
                        this.head = this.head.next;
                    } else if (position == this.length - 1) {
                        this.tail.pre.next = null;
                        this.tail = this.tail.pre;
                    } else {
                        var index = 0;
                        // 找位置
                        while (index++ < position) {
                            current = current.next;
                        }
                        // 找到了
                        current.pre.next = current.next;
                        current.next.pre = current.pre;
                    }
                    this.length -= 1;
                    return current.data;
                }
            };
            DoublyLinkedList.prototype.remove = function(data) {
                var pos = this.indexOf(data);
                return this.removeAt(pos);
            };
            DoublyLinkedList.prototype.isEmpty = function(data) {
                return this.length == 0;
            };
            DoublyLinkedList.prototype.size = function(data) {
                return this.length;
            };
        }
     const doublel = new DoublyLinkedList();
        doublel.append("123");
        doublel.append("456");
        doublel.append("789");
        console.log(doublel, "append");
        console.log(doublel.forwardString(), "forwardString");// 789 456 123  forwardString

        console.log(doublel.backwardString(), "backwardString");// 178 123 456 789  backwardString
        doublel.insert(1, "aaa");
        console.log(doublel, "insert");
        console.log(doublel.get(0), "get");
        console.log(doublel.indexOf("123"), "indexOf");
        doublel.update(0, "mmm");
        console.log(doublel, "up");
        console.log(doublel.removeAt(0), "remove at");
        doublel.remove("456");
        console.log(doublel, "remove ");
        console.log(doublel.isEmpty(), "isEmpty ");
        console.log(doublel.size(), "size ");

其他数据结构可访问以下地址:

数据结构与算法(一)之数组,队列,栈(偏向JS)
数据结构与算法(三)之集合,字典

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值