链表专题:JS 实现原理与高频算法题总结

文章目录

  • [链表专题:JS 实现原理与高频算法题总结](#链表专题:JS 实现原理与高频算法题总结)
  • 1.使用js语法写链表的注意点
    • 1.1链表的结构体
    • [1.2 使用js连成一个链表](#1.2 使用js连成一个链表)
    • [1.3 头节点 head 的含义](#1.3 头节点 head 的含义)
    • [1.4 JS 中没有指针,但有对象引用](#1.4 JS 中没有指针,但有对象引用)
    • [1.5 js中变量保存的是节点引用,不是节点副本](#1.5 js中变量保存的是节点引用,不是节点副本)
    • [1.6 创建一个链表](#1.6 创建一个链表)
    • [1.7 链表题常见注意点:节点比较、dummy 虚拟头节点](#1.7 链表题常见注意点:节点比较、dummy 虚拟头节点)
  • 2.链表高频题实战
    • [2.1 力扣 160:相交链表](#2.1 力扣 160:相交链表)
    • [2.2 力扣 206:反转链表](#2.2 力扣 206:反转链表)
    • [2.3 力扣 21:合并两个有序链表](#2.3 力扣 21:合并两个有序链表)
    • [2.4 力扣 19:删除链表的倒数第 N 个节点](#2.4 力扣 19:删除链表的倒数第 N 个节点)
    • [2.5 力扣 25:K 个一组翻转链表](#2.5 力扣 25:K 个一组翻转链表)

链表专题:JS 实现原理与高频算法题总结

1.使用js语法写链表的注意点

1.1链表的结构体

在 c 语言中,使用结构体 struct 实现

c 复制代码
struct ListNode {
    int val;
    struct ListNode* next;
};

在 js 中,链表节点其实就是对象

JS 里的链表节点 = 一个带 val 和 next 的对象

javascript 复制代码
function ListNode(val, next) {
      this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
}
  • 如果不写 next=null,默认就是 undefined

其中的一个节点

javascript 复制代码
const node1 = new ListNode(1)
console.log(node1)

{ val: 1, next: null }

1.2 使用js连成一个链表

javascript 复制代码
const node1 = new ListNode(1)
const node2 = new ListNode(2)
const node3 = new ListNode(3)

node1.next = node2
node2.next = node3
javascript 复制代码
node1 -> node2 -> node3 -> null

此时打印 node1,也就是表头

javascript 复制代码
console.log(node1)

ListNode {
  val: 1,
  next: ListNode { val: 2, next: ListNode { val: 3, next: null } }
}

可以想象简化为,ListNode表示的是链表节点类的实例
{
  val: 1,
  next: {
    val: 2,
    next: {
      val: 3,
      next: null
    }
  }
}

1.3 头节点 head 的含义

head 是链表入口,不一定永远是原来的第一个节点。head 本身只是一个变量,它保存的是第一个节点对象的引用,并不是整条链表本身。

javascript 复制代码
const head = node1
//如果头结点要换来换去就用 let

1.4 JS 中没有指针,但有对象引用

复制代码
let p = head

//遍历链表
let p = head
while (p !== null) {
  console.log(p.val)
  p = p.next
}

1.5 js中变量保存的是节点引用,不是节点副本

复制代码
let p = head

指的是 p 和 head 都指向都一个节点对象

javascript 复制代码
// 链表是 1-2-3
// node1 是头结点
let p=node1;
p=p.next;
console.log(p.val);
console.log(node1.val);

输出结果:

复制代码
2
1

修改了节点的 next,才会改变链表

复制代码
p.next=null
//直接从头结点那里断开了

1.6 创建一个链表

javascript 复制代码
function ListNode(val, next) {
      this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
}

const head = new ListNode(1)
head.next = new ListNode(2)
head.next.next = new ListNode(3)

1.7 链表题常见注意点:节点比较、dummy 虚拟头节点

比较节点要用 ===,这是比较是否是同一个节点对象,而不是比较值

多用 dummy 虚拟头节点

javascript 复制代码
const dummy = new ListNode(0)
let cur = dummy

return dummy.next

好处是用老判断"头节点是不是空""第一个节点怎么连"。

2.链表高频题实战

2.1 力扣 160:相交链表

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getIntersectionNode = function(headA, headB) {
    // 得到 A 链表的长度  时间复杂度是 o(n)
    // 得到 B 链表的长度  时间复杂度是 o(n)
    // 做差得到长度差,然后再一起移动,直到一个走到空还是没找到,即不存在,否则仍然存在
    // 整体的时间复杂度是 o(n)

    let tempA=headA;
    let tempB=headB;
    let lengthA=0;
    let lengthB=0;
    let chazhi=0;
    let pA=headA;
    let pB=headB;
    while(tempA!==null)
    {
        lengthA++;
        tempA=tempA.next;
    }
    while(tempB!==null)
    {
        lengthB++;
        tempB=tempB.next;
    }
    console.log(lengthA);
    if(lengthA>lengthB)
    {
        chazhi=lengthA-lengthB;
        while(chazhi--)
        {
            pA=pA.next;
        }
        while(pB!==null)
        {
            if(pA===pB) return pA;
            else{
                pA=pA.next;
                pB=pB.next;
            }
        }
    }else
    {
        chazhi=lengthB-lengthA;
        while(chazhi--)
        {
            pB=pB.next;
        }
         while(pA!==null)
        {
            if(pA===pB) return pA;
            else{
                pA=pA.next;
                pB=pB.next;
            }
        }
    }
    return null;
};

2.2 力扣 206:反转链表

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    // 定义一个 cur 定义一个 pre pre 指向 null
    //首先定义了一个 pre节点,值是链表的第一个节点,指向了 null

    // 首先得排除一下这个链表有没有两个节点
    if(head==null||head.next==null) return head;
    let pre=head;
    let cur=head.next;
    //得把头结点断开,否则双向链表了
    head.next=null;
    while(cur!==null)
    {
        let p=cur.next;
        cur.next=pre;
        pre=cur;
        cur=p;
    }
    return pre;
};

2.3 力扣 21:合并两个有序链表

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    //如果两个链表中有空的直接返回另一个即可
    if(list1===null)return list2;
    if(list2===null)return list1;

    //创建一个虚拟的头节点 dummy,两两比较插入
    let dummy=new ListNode(0,null);
    let p=dummy;
    let p1=list1;
    let p2=list2;
    while(p1!==null&&p2!==null)
    {
        if(p1.val<=p2.val)
        {
            p.next=p1;
            p=p.next;
            p1=p1.next;
        }else
        {
            p.next=p2;
            p=p.next;
            p2=p2.next;
        }
    }
    //此时有一整条链表已经加入到了新链表中,将剩余部分加回
    while(p1!=null)
    {
        p.next=p1;
        p=p.next;
        p1=p1.next;
    }
       while(p2!=null)
    {
        p.next=p2;
        p=p.next;
        p2=p2.next;
    }
    
   
    return dummy.next;
};

2.4 力扣 19:删除链表的倒数第 N 个节点

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    //一个 o(n)的方法,利用两个指针,一个先走 n 步,然后一起移动,先走的指向空之后,后走的那个指向的就是倒数第 n 个节点
    let tempN=n;
    let fast=head;
    let slow=head;
    let pre=undefined;
    while(tempN--)
    {
        fast=fast.next;
    }
    while(fast!=null)
    {
        fast=fast.next;
        pre=slow;
        slow=slow.next;
    }
    // 有可能要删除的结点就是倒数第一个节点,所以要维护一个pre
    // 如果要删除的是第一个节点,head=head.next
    if(slow===head)
    {
        head=head.next
        return head;
    } 
    if(slow.next==null)
    {
        pre.next=null;
    }else
    {
       pre.next=slow.next;
    } 
    
    return head;
};

2.5 力扣 25:K 个一组翻转链表

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var reverseKGroup = function(head, k) {
    //处理一些特殊的情况
   if (head === null || head.next === null) return head;



    // 创建一个虚拟头结点
    let dummy=new ListNode(0,null);
    dummy.next=head;
    let end=dummy.next;
    let num=1;
    //每次要连接的头和尾
    let linkhead=dummy;
    let linktail=null;
    let start=null;
    while(end!==null)
    {
        // 翻转开始的记录
        if(num===1) start=end;
        //什么时候要翻转?
        if(num===k)
        {
            //翻转要开始的地方
            //记录linktail
            linktail=end.next;
            //断开链表
            end.next=null;
            // 翻转从 start 开始,从当前的 end 结束
            let pre=null;
            let cur=start;
            while(cur!==null)
            {
                let p=cur.next;
                cur.next=pre;
                pre=cur;
                cur=p;
            }
            //翻转成功 pre 指向了翻转后的尾部
            linkhead.next=end;
            start.next=linktail;

            // 更新 linkhead
            linkhead=start;
            //更新 start 和 end
            start=linktail; //更不更新都行
            end=linktail;

            //更新 num
            num=1;
            continue;
        }
        num++;
        end=end.next;
    }
    return dummy.next;
};
相关推荐
我叫汪枫2 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch2 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI2 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn
Brendan_0012 小时前
JavaScript的Stomp.over
开发语言·javascript·ecmascript
念2342 小时前
f5 shape分析
开发语言·javascript·ecmascript
難釋懷2 小时前
Vue混入
前端·javascript·vue.js
苍穹之跃2 小时前
某量JS逆向
开发语言·javascript·ecmascript
思茂信息2 小时前
CST软件如何进行参数化扫描?
运维·开发语言·javascript·windows·ecmascript·软件工程·软件需求
訾博ZiBo2 小时前
Vue3响应式高阶用法之toRaw()
javascript·vue.js·ecmascript