用TS写链表

单双链表基类

export default class SNode{
    public value:number;
    public nextN:SNode;

    constructor(val:number,nextN:SNode){
        this.value = val;
        this.nextN = nextN;
    }
}
export default class DNode{
    public value:number;
    public nextN:DNode;
    public lastN:DNode;

    constructor(val:number,nextN:DNode,lastN:DNode){
        this.value = val;
        this.nextN = nextN;
        this.lastN = lastN;
    }
}

创建单双链表

 /**
     * 创建单链表(从后插入) 
     */
    createSNodeList(arr:number[]):SNode{
        let head;
        let p;
        head = new SNode(arr[arr.length - 1],null);
        if(arr.length == 1) return head;
        for(let i = arr.length - 2;i>=0;i--){
            p = new SNode(arr[i],head);
            head = p;
        }
        return head;
    }

    /**
     * 创建单链表(从前插入)
     */
    createSNodeList1(arr:number[]):SNode{
        let head;
        let p;
        head = new SNode(arr[0],null);
        p = head;
        if(arr.length == 1) return head;
        for(let i = 1;i<arr.length;i++){
            p.nextN = new SNode(arr[i],null);
            p = p.nextN;
        }
        return head;
    }

    /**
    *创建双链表(从后插入)
    */
    createDNodeList(arr: number[]): DNode {
        let head;
        let p;
        head = new DNode(arr[arr.length - 1], null, null);
        if (arr.length == 1) return head;
        for (let i = arr.length - 2; i >= 0; i--) {
            p = new DNode(arr[i], head, null);
            head.lastN = p;
            head = p;
        }
        return head;
    }

    /**
     * 创建双链表(从前插入)
     */
    createDNodeList1(arr: number[]): DNode {
        let head;
        let p;
        head = new DNode(arr[0], null, null);
        p = head;
        if (arr.length == 1) return head;
        for (let i = 1; i < arr.length; i++) {
            p.nextN = new DNode(arr[i], null, p);
            p = p.nextN;
        }
        return head;
    }

反转单双链表

     /**
     * 反转单链表
     */
    reverseSNodeList(head: SNode) {
        let p = head;
        let p1 = p.nextN;
        p.nextN = null;
        let p2;
        while (p1 != null) {
            p2 = p1.nextN
            p1.nextN = p;
            p = p1;
            p1 = p2;
        }
        head = p;
        return head;
    }
    /**
     * 反转双链表
     */
    reverseDNodeList(head:DNode){
        let p = head;
        let p1;
        do{
           p1 = p.nextN;
           p.nextN = p.lastN;
           p.lastN = p1;
           if(p1 != null) p = p1;
        }while(p1 == null);

        head = p;
        return head;
    }

打印单双链表

    /**
     * 正序打印单双链表
     */
    printANodeList(head: SNode | DNode) {
        let p = head;
        let str = "";
        while (p != null) {
            str += p.value;
            if (head instanceof SNode) str += "->"
            else str += "<->";
            p = p.nextN;
        }
        console.log("printANodeList", str);
    }

    /**
     * 逆序打印双链表(测试用)
     */
    printDNodeList(head: DNode) {
        let p = head;
        let str = "";
        while (p.nextN != null) {
            p = p.nextN;
        }
        while (p != null) {
            str += p.value;
            str += "<->"
            p = p.lastN;
        }
        console.log("printDNodeList", str);
    }

打印两个有序单链表(无环)中相同的部分

    /**
     * 打印两个有序单链表(无环)中相同的部分 
     */
    elementsInTwoSList(head1: SNode, head2: SNode) {
        let p1 = head1;
        let p2 = head2;
        let str = "";
        while (p1 != null && p2 != null) {
            if (p1.value < p2.value) {
                p1 = p1.nextN;
            } else if (p1.value == p2.value) {
                str += p1.value;
                str += ","
                p1 = p1.nextN;
                p2 = p2.nextN;
            } else {
                p2 = p2.nextN;
            }
        }
        console.log("elementsInTwoSList", str);
    }

练习:判断一个单链表是否为回文结构 比如 1->2->3->2->1 或 1->2->3->3->2->1

    /**
     * 要求:时间复杂度为O(N),额外空间复杂度为O(N)
     * 技巧:快慢指针,慢指针来到中间位置后,利用数组存储后1/2链表节点,依次出栈进行判断
     */
    isPalindrome(head: SNode) {
        let p = head;
        let length = 0;
        let s = head;
        let f = head;
        let list = [];
        let stepsN = 1;
        while (p != null) {
            length++;
            p = p.nextN;
        }
        if (length <= 1) return true;
        let last = length % 2 == 0 ? length - 1 : length;
        while (stepsN != last) {
            f = f.nextN.nextN;
            s = s.nextN;
            stepsN += 2;
        }
        s = s.nextN;
        while (s != null) {
            list.push(s);
            s = s.nextN;
        }
        p = head;
        while (list.length > 0) {
            if (list.pop().value != p.value) return false;
            p = p.nextN;
        }
        return true;
    }

    /**
     * 要求:时间复杂度为O(N),额外空间复杂度为O(1)
     * 技巧:快慢指针,慢指针来到中间位置后,调转后1/2链表指针方向,而后依次判断。
     */
    isPalindrome1(head: SNode) {
        let p = head;
        let length = 0;
        let s = head;
        let f = head;
        let stepsN = 1;
        let isPalin = true;
        while (p != null) {
            length++;
            p = p.nextN;
        }
        if (length <= 1) return true;
        let last = length % 2 == 0 ? length - 1 : length;
        while (stepsN != last) {
            f = f.nextN.nextN;
            s = s.nextN;
            stepsN += 2;
        }
        let lastN;
        if (length % 2 == 0) s = s.nextN;
        lastN = this.reverseSNodeList(s);
        p = head;
        let p1 = lastN;
        while (p1 != null) {
            if (p1.value != p.value) {
                isPalin = false;
                break;
            }
            p1 = p1.nextN;
            p = p.nextN;
        }
        let newN;
        newN = this.reverseSNodeList(lastN) //恢复后半截的指针方向
        s.nextN = newN;
        return isPalin;
    }

练习:在单链表解决荷兰国旗问题,等于的数在中间,小于的数在左边,大于的数在右边

	/**
     * 要求:时间复杂度为O(N),空间复杂度为O(1)
     */
    holandInNodeList(head: SNode, num: number) {
        let lH; //大于区域头指针
        let lL; //大于区域尾指针
        let eH; //等于区域头指针
        let eL; //等于区域尾指针
        let sH; //小于区域头指针
        let sL; //小于区域尾指针
        let p = head;
        while (p != null) {
            if (p.value == num) {
                if (!eH) {
                    eH = p;
                    eL = p;
                } else {
                    eL.nextN = p;
                    eL = p;
                }
            } else if (p.value < num) {
                if (!sH) {
                    sH = p;
                    sL = p;
                } else {
                    sL.nextN = p;
                    sL = p;
                }
            } else {
                if (!lH) {
                    lH = p;
                    lL = p;
                } else {
                    lL.nextN = p;
                    lL = p;
                }
            }
            p = p.nextN;
        }
        lL.nextN = null;
        if (sH) {
            if (eH) {
                sL.nextN = eH;
                if (lH) eL.nextN = lH;
            } else if (lH) sL.nextN = lH;
            else sL.nextN = null;
            return sH;
        } else if (eH) {
            if (lH) eH.nextN = lH;
            else eL.nextN = null;
            return eH;
        } else {
            return lH;
        }
    }

含有随机指针的链表,即randN指针是单链表结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。

export default class DNode{
    public value:number;
    public nextN:DNode;
    public lastN:DNode;

    constructor(val:number,nextN:DNode,lastN:DNode){
        this.value = val;
        this.nextN = nextN;
        this.lastN = lastN;
    }
}

创建随机指针链表

 	/**
     * 用节点数组的方式来随机创建一个含有随机指针节点的链表
     */
    createARNodeListRandomly() {
        let list: RNode[] = [];
        let length = Math.floor(Math.random() * 10) + 1;
        let head = new RNode(Math.floor(Math.random() * 10), null, null);
        let p = head;
        while (length > 0) {
            let rNode = new RNode(Math.floor(Math.random() * 10), null, null);
            list.push(rNode);
            p.nextN = rNode;
            p = rNode;
            length--
        }
        for (let node of list) {
            if (Math.random() > 0.5) node.randN = null;
            else node.randN = list[Math.floor(Math.random() * list.length)]
        }
        return head;
    }

练习:复制一个含有随机指针节点的链表。

    /**
     * 方法一:用哈希结构Map来实现
     */
    copyARNodeList(head: RNode) {
        let map = new Map<RNode, RNode>();
        let p = head;
        let head1;
        while (p != null) {
            map.set(p, new RNode(head.value, null, null));
            p = p.nextN;
        }

        for (let node of map.keys()) {
            if (!head1) head1 = map.get(node);
            map.get(node).nextN = map.get(node.nextN);
            map.get(node).randN = map.get(node.randN);
        }

        return head1;
    }

随机产生带环的单链表

   /**创建带环随机的单链表*/
    createSNWithCircle(arr: number[]) {
        let head;
        let p;
        let circle;
        if (arr.length <= 2) return null; //有环单链表至少有三个节点
        let ran = Math.floor(Math.random() * (arr.length - 2)) + 1;//至少有一个节点在环外
        head = new DNode(arr[0], null, null);
        p = head;
        if (arr.length == 1) return head;
        for (let i = 1; i < arr.length; i++) {
            p.nextN = new DNode(arr[i], null, p);
            p = p.nextN;
            if (i == ran) {
                circle = p;
            }
        }
        p.nextN = circle;
        console.log("入环节点", circle.value);
        return head;
    }

讨论一个单链表是否有环,如果无环返回null,有环则并返回第一个入环节点(环转了一圈后又回到的第一个节点)

 	/**
     * 方法一:利用数组,需要占用O(N)的内存空间
     */
    nodeListHasCircle(head: SNode) {
        if (head == null || head.nextN == null || head.nextN.nextN == null) return null;
        let list = [];
        let p = head;
        while (p != null) {
            if (list.indexOf(p) == -1) list.push(p);
            else return p;
            p = p.nextN;
        }
        return null;
    }
	/**
     * 方法二:可用数学证明的快慢指针方法,空间复杂度O(1)
     */
    nodeListHasCircle1(head: SNode) {
        if (head == null || head.nextN == null || head.nextN.nextN == null) return null;
        let s = head.nextN;
        let f = s.nextN;
        while (s != f) {
            s = s.nextN;
            if (s == null) return null;
            f = f.nextN.nextN;
            if (f == null) return null;
        }
        f = head;
        while (s != f) {
            s = s.nextN;
            f = f.nextN;
        }
        return s;
    }

练习:求两个单链表(可能有环可能无环)是否有共同的部分并返回第一个相交的节点
讨论:可能相交的情况为二者皆有环 或 二者皆无环。其他情况皆不可能相交。

     nodeListIsInterset(head1: SNode, head2: SNode) {
        let circle1 = this.nodeListHasCircle1(head1);
        let circle2 = this.nodeListHasCircle1(head2);
        if ((!circle1 && !circle2) || (circle1 && circle2 && circle1 == circle2)) {//判定两个单链表都没有环或 二者都有环且入环节点一致
            let len1 = 0;
            let len2 = 0;
            let p1 = head1;
            let p2 = head2;
            while (p1 == circle1 && p2 == circle2) {
                if (p1 != circle1) {
                    len1++;
                    p1 = p1.nextN;
                } if (p2 != circle2) {
                    len2++;
                    p2 = p2.nextN;
                }
            }

            let off = Math.abs(len1 - len2);
            p1 = head1;
            p2 = head2;
            while (off == 0) {
                if (Math.sign(len1 - len2) == 1) {
                    p1 = p1.nextN;
                    off--;
                } else {
                    p2 = p2.nextN;
                    off--;
                }
            }
            while ((circle1 && p1 == circle1.nextN) || (!circle1 && p1 == circle1)) {
                if (p1 == p2) return p1;
                p1 = p1.nextN;
                p2 = p2.nextN;
            }

            return null;
        } else if (circle1 && circle2 && circle1 != circle2) { //二者都有环但入环节点不一致
            let p1 = circle1.nextN;
            let p2 = circle2.nextN;
            while (p1 == circle1) {
                if (p1 == p2) return p1;
                p1 = p1.nextN;
                p2 = p2.nextN;
            }
            return null;
        }
        return null;
    }
  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值