链表:JavaScript定义链表&203移除链表元素&707设计链表&206反转链表🎁之我爱JS😛

101 阅读3分钟

定义结点

class ListNode {  // 结点类
  val; // 存结点的数据 属性
  next = null; // 指向下一个结点的指针
  constructor(value){ // 构造函数
    this.val = value;
    this.next = null;
  }
}

203移除链表元素

203.移除链表元素 image.png 重点为头结点移除,技巧为使用虚拟头结点

  • 不用虚拟头结点,直接单独处理删除头结点-->**头指针head后移 **
  • 头结点head前面加一个虚拟头结点,直接用和删除之后元素一样的方法,删除头结点,最后返回虚拟头结点下一个
  • C++要自己释放内存,JS有内存回收不用自己释放

本质:只能删除指针指的下一个(必须知道前面一个) image.png 技巧:直接判断这种情况,处理临界值 image.png 首先肯定要设虚拟头结点,明确cur指向正在判断的结点,pre指向它的前一个,有两种情况:

  1. 正在判断的cur,是要删的:越过要删的,cur指向要删的下一个,继续判断
  2. 正在判断的cur,不是要删的:pre/cur都往后移

虚拟头结点(重要)

方便操作

/**
 * 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} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    let dhead = new ListNode(0, head) //虚拟头结点,指向头结点
    let pre = dhead //前指针
    let cur = pre.next //指向正在判断的
    while (pre && cur) { //直接根据图中临界情况,都不为空
        if (cur.val === val) { //正在判断的是要删的值
            pre.next = cur.next //越过要删的
            cur = pre.next //正在判断的值,更新为下一个
        } else { //正在判断的不是要删的
            pre = cur //直接都往前移
            cur = cur.next
        }
    }
    return dhead.next //返回真正的头结点
};

707.设计链表

设计链表 原则:可以对指针的下一个进行操作 技巧:用index=0这个特殊的点去验证,一定自己画图

JS题解

首先先理解题目的意思! image.png 因为最开始初始化时size = 0,头结点对应的应该是虚拟头

  • 不是链表所有操作都需要虚拟头结点,增删元素时用虚头,不用考虑头结点
class ListNode { //链表结点类
    constructor (val, next) { //构造函数
        this.val = (val === undefined ? 0 :val)
        this.next = (next === undefined ? null : next)
    }
}

var MyLinkedList = function() { //链表的构造函数
    // 链表的属性
    this.size = 0, //大小
    this.dhead = new ListNode(0) //虚拟头结点,对应size为0
    // 下面在原型中加入链表的方法
};

/** 
 * 获取index结点值的函数
 * @param {number} index
 * @return {number}
 */
//找第index个元素(0 ~ n-1),索引无效,则返回-1
MyLinkedList.prototype.get = function(index) {
    if(index > this.size - 1 || index < 0) { //越界:索引无效直接return-1
        return -1
    }
    let cur = this.dhead //从头
    while (index >= 0) {
        cur = cur.next //向后找
        index--
    }
    return cur.val //找到返回值
};

/** 
 * @param {number} val
 * @return {void}
 */

//第一个前面加值val结点,变为头结点
MyLinkedList.prototype.addAtHead = function(val) {
    //直接加到头结点下一个
    let newNode = new ListNode(val, null) //定义新节点
    newNode.next = this.dhead.next //一定要按顺序来
    this.dhead.next = newNode
    this.size++ //加入之后大小+1
};

/** 
 * @param {number} val
 * @return {void}
 */
// 在链表最后加值val结点
MyLinkedList.prototype.addAtTail = function(val) {
    // 找最后一个
    let cur = this.dhead;
    while (cur.next !== null) {
        cur = cur.next
    }
    // 插入链表
    let newNode = new ListNode(val, null)
    cur.next = newNode
    this.size++ //记得大小+1
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
// 第 index 个节点之前添加值为 val  的节点
// 如果index = 链表的长度,插到尾结点
// 如果index 大于 链表的长度,则返回空
// 如果index 小于0,则在头部插入节点--相当于index = 0
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if (index > this.size) return  // 不插直接return
    else if (index < 0) this.addAtHead(val)  // 插在头,相当于index = 0
    else if (index == this.size) this.addAtTail(val) //插到尾
    else{
        let cur = this.dhead // index=0时在dhead后面插
        while (index--) {
            cur = cur.next
        }
        let newNode = new ListNode(val, null) // 要插入的新节点
        newNode.next = cur.next // 插入
        cur.next = newNode
        this.size++
    }
}; 

/** 
 * @param {number} index
 * @return {void}
 */
//删除第index个结点
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if (index > this.size - 1 || index < 0) return; //越界直接return
    let cur = this.dhead
    while (index--) { //找
        cur = cur.next
    }
    //删
    cur.next = cur.next.next
    this.size--
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * var obj = new MyLinkedList()
 * var param_1 = obj.get(index)
 * obj.addAtHead(val)
 * obj.addAtTail(val)
 * obj.addAtIndex(index,val)
 * obj.deleteAtIndex(index)
 */

206.反转链表

206.反转链表 image.png

(重点)双指针法

注意:cur初始为head,pre为它前面一个,初始化为null因为第一次反转操作就可以直接让尾指向空 image.png 首先tmpcur的下一个,再进行反转,反转完之后,指针前移 终止条件是cur指向null不用再反转了,最后头结点为pre

/**
 * 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) {
    let cur = head
    let pre = null
    let tmp = null
    while (cur) {
        tmp = cur.next
        cur.next = pre
        pre = cur
        cur = tmp
    }
    return pre
};