当前位置 博文首页 > jcLee95的博客:TypeScript算法专题 - blog5 - 单链表节点的`任

    jcLee95的博客:TypeScript算法专题 - blog5 - 单链表节点的`任

    作者:[db:作者] 时间:2021-09-18 15:50

    TypeScript数据结构与算法专题 -
    [单链表5] 单链表节点的`任意分组反转`实现

    李俊才
    CSDN:jcLee95
    邮箱:291148484@163.com

    专题目录:https://blog.csdn.net/qq_28550263/article/details/115718216


    【导读】: 在上一篇博文《单链表节点的两-两反转的实现》(点击链接进行跳转)中,我们采用了 “交换数据法”“改变结点引用关系” 两种方法来实现以2个结点为一组对链表进行分组反转。本文我们将进一步加大难度,对链表以任意指定参数k个为一组,对链表进行分组反转。

    ->点我跳转:如果你不是很理解什么是任意k结点个一组反转可以先看一下运行结果

    0. 对上一篇及之前博文代码的删改

    如果使用C语言实现,则头指针和头结点有所不同,若头指针为head则头节点是*head如果链表带有头节点则头指针指向头结点、头节点的指针域指向首元结点。也就是说C语言实现的链表中头结点可能并不是首元结点,而首元结点为其后继结点,头节点的作用是"方便插入、删除等运算的实现"。如果链表没有头节点,则头指针直接指向首元结点

    不过TypeScript中是没有指针的概念,我们也没有必要像C语言中那样区分头指针与头节点。但是与C语言中采用头节点以保持链表操作的一致性一脉相承,我们也设置一个头结点,即:

    为了“简化链表的插入和删除操作,并统一空表和非空表的操作,决定在链表的链头设置头结点。这样无论是在链表的首元结点处插入还是在链表的中间或者尾部插入,都能够统一处理。

    在删除掉实用性功能不大或者被本文包含的两两翻转结点,以及其它重复实现的功能之后,我们给出新的代码如下:

    // 这里提供一个由头指针的链表的实现方法
    
    class Lnode{
        /**
         * 链表结点类,结点是构成链表的基础单元。
         * @class Lnode
         * @constructor
         * @param next {Lnode} 指向下一结点的指针
         * @param data {string | number} 结点的数据。
         * @param empty {boolean} 结点是否为空的标记。
         */
        next: Lnode | null;              // 结点类型(Lnode)的nest也是结点类型Lnode,undefined和null是任意类型的子类型也可以不写
        data: any;
        empty:boolean;
        constructor();
        constructor(data);
        constructor(data?:any){
            if(data!=undefined){                     // 非空结点
                this.data = data;
                this.next = null;
                this.empty = false
            }else{                                   // 空结点,即值构建结点对象但实际上不存在结点
                this.data = null;
                this.next = null;
                this.empty = true;
            }
        }
    }
    
    
    export class LinkList{
        /**
         * 单链表类
         * @class LinkList
         * @constructor
         * @param head {Lnode} 实际上用于替代头结点
         * @param length {number} 链表的长度
         */
        head: Lnode;                          // 头指针,其next挂载链表的第一个结点。实际上也是一个结点
        length: number;                       // 链表所容纳结点的个数
    
        constructor();                        // 重载构建空链表
        constructor(datas:any[]);             // 重载传入一组数据作为元素构建非空链表
        constructor(datas?:any[]){
            /**初始化 */
            this.head = new Lnode();
            if(datas==undefined){             
                this.head.next = null;        // head.next 为头指针,指向首元结点
                this.length = 0;              // 初始化链表长度
            }else{
                for(let i=0; i<datas.length; i++){
                    this.append(datas[i])
                }
                this.length = datas.length;  // 指定一组数据初始化则初始后长度为这组数据元素的个数
            }
        }
    
        is_empty(){
            /**
             * 判断链表是否为空
             * 只需要判断头结点是否为空,若头结点为空则为空链表,否则不是。
             * @return {Boolean} true:链表为空; false:链表非空。
             */
            if(this.head.next == null){
                return true;
            }else{
                return false;
            }
        }
    
        top():any{
            /**
             * 获取链表头结点的数据域内容
             */
            return this.head.next.data;
        }
    
        top_node():Lnode{
            /**
             * 获取链表头结点
             */
            let node = this.head.next;
            node.next = null;
            return node;
        }
    
        clear(){
             /**
              * 清空链表,只要把头结点干废,整个链表直接就清空了
              */
            this.head.next = null;
            this.length = 0;
        }
    
        append(elm: any):void{
            /**
             * 用数据构成新结点插入单链表尾部
             * @param elm {any} 需要插入链表尾部的新结点
             */
            if(this.head.next == null){                     // 没有头结点
                this.head.next = new Lnode(elm);
                this.length = this.length + 1;
            }else{
                let pointer = this.head.next;               // 指向头节点
                while(pointer.next != null){
                    pointer = pointer.next
                }
                pointer.next = new Lnode(elm)
            }
        }
    
        append_node(node: Lnode):void{
            /**
             * 将新结点挂载到链表尾部
             * @param node {Lnode} 需要插入链表尾部的新结点
             */
            if(this.head.next == null){                     // 没有头结点
                this.head.next = node;
                this.length = this.length + 1;
            }else{
                let pointer = this.head.next;               // 指向头节点
                while(pointer.next != null){
                    pointer = pointer.next
                }
                pointer.next = node
            }
        }
    
        append_left(elm: any):void{
            /**
             * 用数据构成新结点插入单链表头部
             *  @param elm {any} 需要插入链表尾部的新结点
             */
            if(this.head == null){
                this.head.next = new Lnode(elm);                // 若为空链表,直接将链表头设置为该结点
                this.length = this.length + 1;                  // 增加结点长度
            }else{
                // 先将新结点的`next`指向原第一个结点
                let node = new Lnode(elm);
                node.next = this.head.next;
                // 再将`head`指向新的结点
                this.head.next = node;
                this.length = this.length + 1;                  // 增加结点长度
            }
        }
    
        append_node_left(node: Lnode){
            /**
             * 将一个新结点插入到链表首部
             * @param node {Lnode} 要从左侧也就是链头插入的新结点
             */
            if(this.head.next == null){
                this.head.next = node;                     // 若为空链表,直接将链表头设置为该结点
                this.length = this.length + 1;             // 增加结点长度
            }else{
                // 先将新结点的`next`指向原第一个结点
                node.next = this.head.next;
                // 再将`head`指向新的结点
                this.head.next = node;
                this.length = this.length + 1;             // 增加结点长度
            }
        }
        
        get_node(index: number):Lnode{
            /**
             * 获取索引号为`index`的结点。
             * 索引号是从数字`0`开始计算的
             * @param index {number} 索引号
             * @return node {Lnode} 索引得到地节点
             */
            if(index<0){throw "ValueError: Index must be a positive integer!"}                    // 负数索引报错
            else if(index+1 > this.length){throw "ValueError: Index exceeds the maximum value!"}  // 索引过界报错
            else{
                let pointer:Lnode = this.head.next;       // 从头结点开始
                for(let i=0; i<index;i++){
                    pointer = pointer.next;          // 逐个指向下一个结点
                };
                pointer.next = null;                 // 斩断后继
                return pointer;
            };
        };
    
        get(index: number):any{
            /**
             * 索引结点地数据
             * @param index {number} 索引号
             * @return data {any} 链表中与索引号对应地节点地数据域内容
             */
            return this.get_node(index).data
        };
    
        index(x:any):number{
            /**
             * 返回链表中第一个数据域 x 的元素的零基索引
             * @param x {any} 用于寻找索引的数据
             */
            let pointer: Lnode = this.head.next; // 从引用(指向)第一个结点开始
            let index = 0;