数据结构(4)链表之单向循环链表

237 阅读5分钟

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

image.png

//链表元素的结构
        class CircularLinkListNode{
            constructor(data){
                this.data=data;
                this.next=null;
            }
        }
//单向循环链表的结构
        class CircularLinkList{
            //单向循环链表初始结构:头节点指向空,链表长度为0
            constructor(){
                this.head=null;
                this.length=0;
            }
        }

单向循环链表的常见操作封装思路

向链表末尾增加元素方法的思路

在空链表末尾增加元素

image.png

在非空链表末尾增加元素

image.png

在链表指定的位置插入新元素方法的思路

在非空链表头部插入元素

在空链表头部插入元素就和在空链表末尾增加元素情况相同,代码复用即可

image.png

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

在空链表尾部插入元素可以直接调用append方法

image.png

移除指定位置元素方法的思路

在做移除操作之前需要先判断传入的索引位置是否合法

//position移除的合法位置是在0到链表长度减1之间的正整数,
//需要保证移除元素的位置在链表中,第一个就是链表的头节点索引就是0,链表末尾节点索引就是链表长度减1
//当传入的值是小于0或者传入的值大于了链表末尾节点的索引或者不是正整数都是不合法的,做布尔判读就是false。
//或运算当前面一个表达式做布尔判断是true就不会运算后面的表达式,前面一个表达式的结果就是或运算的结果。前面一个布尔判定是false就继续运算后面的表达式,后面的表达式结果就是或运算表达式的结果。
if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
这样就不需要做空链表的移除操作情况分类,当链表是空链表时其链表长度this.length0,position < 0 || position > this.length - 1 || !Number.isInteger(position),传入的position就会大于this.length - 1的结果-1判断是true就会执行if语句函数会直接返回false,所以不需要做空链表的移除操作情况分类。

移除指定位置的元素在链表头部

image.png

image.png

移除指定位置的元素在链表头部时
1.第一种情况是链表中只有1个节点,只需要将头节点指向空然后长度减1
2.第二种情况是链表中有不止一个节点,就需要找到链表的末尾节点。然后首先重新将头节点指向原头节点的下一个节点,然后将找到的末尾节点的next指向头部节点

移除指定位置的元素在链表末尾

image.png

/*移除指定位置的元素在链表末尾时,
需要先找到链表末尾元素的前一个元素,用while循环从链表头节点开始遍历链表*/
let index=0;
let current=this.head;
while(index<position-1){
    current=current.next;
    index++;
}
/*循环结束时current就是末尾节点的前一个节点,
只需要将末尾节点的前一个节点的next指向头节点,
如此链表中就没有任何元素可以连接要移除的节点就实现了移除末尾节点,
最后链表长度减1*/
current.next=this.head;
this.length--;

移除指定位置的元素在链表中间任意位置

image.png

/*移除指定位置的元素在链表中间任意位置时,
需要先找到指定的任意位置的前一个元素,用while循环从链表头节点开始遍历链表*/
let index=0;
let current=this.head;
while(index<position-1){
    current=current.next;
    index++;
}
/*循环结束时current就是指定的任意位置要移除的节点的前一个节点,
需要将要移除的节点的前一个节点的next指向要移除的节点的后一个节点它可以用current.next.next来表示,
如此链表中就没有任何元素可以连接要移除的节点就实现了移除该节点,
最后链表长度减1*/

而移除链表末尾的节点时,要移除的末尾节点的next就是指向头节点,要移除的末尾节点的前一个节点就是current的next再next也是指向头节点。则移除指定位置的元素在链表中间任意位置和在链表末尾两者可以合并为一种情况

let index=0;
let current=this.head;
while(index<position-1){
    current=current.next;
    index++;
}
current.next=current.next.next;
this.length--;

单向循环链表的封装代码

class CircularLinkList {
            //单向循环链表初始结构:头节点指向空,链表长度为0
            constructor() {
                this.head = null;
                this.length = 0;
            }
            //1.向链表末尾增加元素
            append(element) {
                //创建新节点
                let newnode = new CircularLinkListNode(element);
                if (this.head == null) {
                    //向空链表末尾添加元素,需要将头节点指向新节点,新节点的next指向头节点
                    this.head = newnode;//将头节点指向新节点
                    newnode.next = this.head;//新节点的next指向头节点
                } else {
                    //向非空链表末尾添加元素需要找到链表末尾的节点,链表都只能从头节点开始寻找链表中的某一个节点
                    let current = this.head;
                    while (current.next != this.head) {
                        //链表末尾的节点其next指向头节点,没有找到末尾节点循环一直进行继续找末尾节点,当找到末尾节点时current.next!=this.head做布尔判定为false,while循环停止
                        current = current.next;
                    }
                    //while循环结束,current就是链表末尾节点,将末尾节点的next指向新节点,新节点的next指向头节点
                    current.next = newnode;//末尾节点的next指向新节点
                    newnode.next = this.head;//新节点的next指向头节点
                }
                //最后链表长度加1
                this.length++;
            }
            //将链表转换为字符串
            toString() {
                //需要遍历链表,将遍历到的链表中的每个元素添加到临时数组中
                let current = this.head;
                let index = 0;
                let arr = [];
                while (index < this.length) {
                    arr.push(current.data);//current就是当前遍历到的链表元素,是元素的数据添加到数组中
                    current = current.next;
                    index++;
                }
                //while循环结束,链表中的每个元素添加到了数组中,最后返回一个字符串
                return arr.join("-");
            }
            //在链表指定的位置插入新元素
            insert(position, element) {
                //需要先判断位置是否合法
                if (position < 0 || position > this.length || !Number.isInteger(position)) {
                    //位置不合法返回false
                    return false;
                }
                //位置合法,先创建新节点
                let newnode = new CircularLinkListNode(element);
                //在链表头部位置插入元素
                //定义变量,保存相关查询信息
                let current = this.head;
                let index = 0;
                if (position == 0) {
                    if (this.head == null) {
                        //在空链表头部插入元素和在空链表末尾添加元素情况相同,需要将头节点指向新节点,新节点的next指向头节点
                        this.head = newnode;//将头节点指向新节点
                        newnode.next = this.head;//新节点的next指向头节点
                    } else {
                        //在非空链表头部插入元素,需要先找到链表末尾元素,链表末尾元素的next是头节点
                        while (current.next != this.head) {
                            current = current.next;
                        }
                        //while循环结束,current就是链表末尾元素,先将新节点的next指向头节点再将末尾元素的next指向新节点,最后将头节点指向新节点
                        newnode.next = this.head;//将新节点的next指向头节点
                        current.next = newnode;//末尾元素的next指向新节点
                        this.head = newnode;//头节点指向新节点
                    }
                    //最后链表长度加一
                    this.length++;
                } else if (position == this.length) {
                    //在链表末尾插入调用append方法即可
                    this.append(element);
                } else {
                    //在链表中间的任意位置插入,需要先找到指定位置的前一个节点
                    while (index < position - 1) {
                        current = current.next;
                        index++;
                    }
                    //while循环结束,current就是指定位置的前一个节点,先将新节点的next指向指定位置的前一个节点的next,再将指定位置的前一个节点的next指向新节点
                    newnode.next = current.next;//将新节点的next指向指定位置的前一个节点的next
                    current.next = newnode;//指定位置的前一个节点的next指向新节点
                    //最后链表长度加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;//保存要移除的节点
                //位置合法,则不需要做在空链表中移除元素的情况操作
                if (position == 0) {
                    //在链表头部删除元素
                    if (this.length == 1) {
                        //链表中只有一个元素的情况只需要将头节点指向空
                        pre = this.head;//需要移除的就是头节点
                        this.head = null;
                    } else {
                        //链表中不止一个元素的情况,需要先找到末尾节点元素
                        while (current.next != this.head) {
                            current = current.next;
                        }
                        //while循环结束,current就是链表末尾元素,先将头节点指向头节点的next,再将末尾元素的next指向头节点
                        pre = this.head;//需要移除的就是头节点
                        this.head = this.head.next;//将头节点指向头节点的next
                        current.next = this.head;//将末尾元素的next指向头节点
                        
                    }
                    //最后链表长度减1
                    this.length--;
                } else {
                    //尾部移除元素与中间任意位置移除元素合并,都需要先找到移除元素的前一个节点,将移除元素的前一个节点的next指向移除元素的后一个节点
                    while (index < position - 1) {
                        current = current.next;
                        index++;
                    }
                    //循环结束current就是除元素的前一个节点
                    pre = current.next;//current.next就是要移除的元素
                    current.next = pre.next;//移除元素的前一个节点的next指向移除元素的后一个节点
                    //最后链表长度减1
                    this.length--;
                }
                //函数返回移除的元素的数据
                return pre.data;
            }
            //查找指定元素的位置
            indexOf(element) {
                //先循环遍历整个链表
                let current = this.head;
                let index = 0;
                while (index < this.length) {
                    if (current.data == element) {
                        //遍历时遇到与查找的元素相等返回索引
                        return index;
                    } else {
                        //不相等继续遍历
                        current = current.next;
                        index++;
                    }
                }
                //遍历结束都没有相等就返回-1
                return -1;
            }
            //移除指定元素
            remove(element) {
                let index = this.indexOf(element);
                return this.removeAt(index);
            }
            //如果链表中不包含任何元素,返回`true`,如果链表长度大于0则返回`false`
            isEmpty() {
                //length为0做布尔判定是false执行else语句
                if (this.length) {
                    return true;
                } else {
                    return false;
                }
            }
            //返回链表包含的元素个数。与数组的`length`属性类似
            size() {
                return this.length;
            }
        }