数据结构(6)链表之双向循环链表

136 阅读4分钟

双向循环链表元素以及双向循环链表的结构

image.png

        //双向循环链表元素的结构
        class DoubleCircularLinkListNode{
            constructor(data){
                this.previous=null;
                this.data=data;
                this.next=null;
            }
        }
        class DoubleCircularLinkList{
            constructor(){
                //空的双向循环链表结构
                this.head=null;
                this.tail=null;
                this.length=0;
            }
        }

双向循环链表操作方法代码思路

在链表末尾添加元素

在空链表末尾添加元素

image.png

在非空链表末尾添加元素

image.png

在链表指定位置插入元素

需要先判断插入的位置是否合法,不符合法则函数直接返回false,符合法则就创建新节点,再根据情况分类插入。

 //先判断位置是否合法
                if (position < 0 || position > this.length || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
                //位置合法执行插入操作
                //创建新节点
                let newnode = new DoubleListNode(element);

在非空链表头部插入元素

在空链表头部插入元素和在空链表尾部插入元素情况相同,都是让头部和尾部都指向新节点,将代码复用即可

image.png

在链表中间任意位置插入元素

在链表尾部位置插入,直接调用append方法,传入需要插入的数据在append方法中创建新节点。

在链表中间任意位置插入元素需要先找到指定的任意位置的前一个节点

image.png

移除链表指定位置元素

需要先判断插入的位置是否合法,不符合法则函数直接返回false,符合法则再根据情况分类移除。

//需要先判断指定的位置是否合法,this.length-1表示链表最后一个节点的索引
                if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
当在空链表中移除元素会直接返回false

移除链表头部位置元素

image.png

移除链表尾部位置元素

image.png

移除链表中间任意位置元素

image.png

双向循环链表常见操作封装代码

        //双向循环链表元素的结构
        class DoubleCircularLinkListNode {
            constructor(data) {
                this.previous = null;
                this.data = data;
                this.next = null;
            }
        }
        class DoubleCircularLinkList {
            constructor() {
                //空的双向循环链表结构
                this.head = null;
                this.tail = null;
                this.length = 0;
            }
            //链表末尾添加元素
            append(element) {
                //创建新节点
                let newnode = new DoubleCircularLinkListNode(element);
                if (this.length == 0) {
                    //在空链表末尾添加元素,将头节点和尾节点指向新节点,新节点的next指向新节点,新节点的previous指向新节点
                    this.head = newnode;
                    this.tail = newnode;
                    newnode.next = newnode;
                    newnode.previous = newnode;
                } else {
                    //在非空链表末尾添加元素,需要将新节点连接到链表中再断开原来的指向
                    //将新节点连接到链表中,新节点的previous指向尾节点,新节点的next指向头节点
                    newnode.previous = this.tail;
                    newnode.next = this.head;
                    //断开原来的指向,尾节点的next指向新节点,头节点的previous指向新节点
                    this.tail.next = newnode;
                    this.head.previous = newnode;
                    this.tail = newnode;
                }
                //最后链表长度加1
                this.length++;
            }
            //在链表指定位置插入元素
            insert(position, element) {
                //先判断位置是否合法
                if (position < 0 || position > this.length || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
                //位置合法执行插入操作
                //创建新节点
                let newnode = new DoubleCircularLinkListNode(element);
                if (position == 0) {
                    if (this.length == 0) {
                        //在空链表头部插入元素和在空链表尾部插入元素情况相同,将头节点和尾节点指向新节点,新节点的next指向新节点,新节点的previous指向新节点
                        this.head = newnode;
                        this.tail = newnode;
                        newnode.next = newnode;
                        newnode.previous = newnode;
                    } else {
                        newnode.next = this.head;
                        newnode.previous = this.tail;
                        this.head.previous = newnode;
                        this.tail.next = newnode;
                        this.head = newnode;
                    }
                    //最后链表长度加1
                    this.length++;
                } else if (position == this.length) {
                    // 在链表尾部位置插入,直接调用append方法
                    this.append(element);
                } else {
                    //在链表中间任意位置插入元素,需要先找到指定的任意位置的前一个节点
                    let index = 0;
                    let current = this.head;
                    while (index < position - 1) {
                        current = current.next;
                        index++;
                    }
                    //循环结束就是index=position-1,index就是指定的任意位置,current就是指定的任意位置的前一个节点
                    //先将新节点连接到链表中
                    newnode.previous = current;
                    newnode.next = current.next;
                    //在断开原来的指向
                    current.next = newnode;
                    newnode.next.previous = newnode;
                    //最后链表长度加1
                    this.length++;
                }
            }
            //移除链表指定位置元素
            removeAt(position) {
                //需要先判断指定的位置是否合法,this.length-1表示链表最后一个节点的索引
                if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
                //指定位置合法
                // 定义变量, 保存信息
                let current = this.head;
                let index = 0;
                let pre = null;//pre用于保存移除的指定位置的节点
                if (position == 0) {
                    //移除头部的节点
                    if (this.length == 1) {
                        //链表中只有一个节点,就让链表的头部和尾部都指向空
                        pre = this.head;//移除的就是头部节点
                        this.head = null;
                        this.tail = null;
                    } else {
                        //链表不只有一个元素,让头部节点先指向头部节点的next,再让头部节点的previous指向空
                        pre = this.head;//移除的就是头部节点
                        this.head = this.head.next;//让头部节点先指向头部节点的next
                        this.head.previous = this.tail;//让新的头部节点的previous指向尾部节点
                        this.tail.next = this.head;//让尾节点的next指向新的头节点
                    }
                    //最后链表长度都需要减1
                    this.length--;
                } else if (position == this.length - 1) {
                    //移除尾部的节点
                    pre = this.tail;//移除的就是尾部节点
                    this.tail = this.tail.previous;//让尾节点先指向尾节点的previous
                    this.tail.next = this.head;//让新的尾节点的next指向头节点
                    this.head.previous = this.tail;//让头节点的previous指向新的尾节点
                    //最后链表长度都需要减1
                    this.length--;
                } else {
                    //移除任意位置的节点,需要先找到指定的任意位置的前一个节点
                    while (index < position - 1) {
                        current = current.next;
                        index++;
                    }
                    //循环结束current就是指定的任意位置的前一个节点
                    pre = current.next;//pre用于保存移除的指定位置的节点就是current.next
                    current.next = pre.next;//指定位置的节点的前一个节点的next指向指定的任意位置的后一个节点
                    current.next.previous = current;//指定位置的节点的后一个节点的previous指向指定的任意位置的前一个节点
                    //最后链表长度都需要减1
                    this.length--;
                }
                //无论执行哪一个条件语句,都需要返回删除的数据
                return pre.data;
            }
            //查找指定元素的位置
            indexOf(ele) {
                let current = this.head;
                let index = 0;
                //先遍历链表的所有元素,当索引大于等于链表的长度时while循环就结束表示遍历完了链表所有元素
                while (index < this.length) {
                    //当前遍历到的链表元素和指定元素相等就返回当前索引,不相等就继续遍历让current变量指向当前遍历道德元素的下一个元素,索引加1.
                    if (current.data == ele) {
                        return index;
                    } else {
                        current = current.next;
                        index++;
                    }
                }
                //当遍历完链表中所有元素都没有与指定元素相等的就返回-1
                return -1;
            }
            //移除指定元素
            remove(el){
                //找到指定元素的索引
                let index=this.indexOf(el);
                //根据索引移除指定元素
                return this.removeAt(index);
            }
            //正向遍历链表转换为字符串
            toAfterString(){
                let current=this.head;
                let index=0;
                let arr=[];//用于保存链表遍历到的每一个元素
                //遍历链表
                while(index<this.length){
                    arr.push(current.data);//current就是当前遍历到的元素
                    current=current.next;//保存当前遍历到的元素的下一个元素
                    index++;
                }
                return arr.join("-");//讲数组中的元素转换为字符串返回
            }
            //反向遍历链表转换为字符串
            toBeforeString(){
                let current=this.tail;
                let index=this.length-1;
                let arr=[];//用于保存链表遍历到的每一个元素
                //遍历链表
                while(index>=0){
                    arr.push(current.data);//current就是当前遍历到的元素
                    current=current.previous;//保存当前遍历到的元素的下一个元素
                    index--;
                }
                return arr.join("-");//讲数组中的元素转换为字符串返回
            }
            //如果链表中不包含任何元素,返回`true`,如果链表长度大于0则返回`false`
            isEmpty(){
                //length为0做布尔判定是false执行else语句
                if(this.length){
                    return true;
                }else{
                    return false;
                }
            }
            //返回链表包含的元素个数。与数组的`length`属性类似
            size(){
                return this.length;
            }
        }