代码随想录 day3- 203移除链表元素 207设计链表 206反转链表

92 阅读4分钟

203.移除链表元素

题目链接

题目描述:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

初始思路,分情况讨论 删除头结点、中间结点

初始的思路是直接在原链表上进行修改:

用node变量遍历链表,如果node.next 的 val 等于要删除的目标值,则 node.next = node.next.next

同时,对需要删除的节点是头结点时进行处理和判断。

var removeElements = function(head, val) {
    while(head!=null && head.val === val){
        head = head.next
    }
    if(head === null ) return null

    let pre = head
    let cur = head.next

    while(cur){
        if(cur.val === val){
            pre.next = cur.next
        }else{
            pre = pre.next
        }
        cur = cur.next
    }

    return head
};

虚拟头节点

创建一个新的节点,指向链表的头结点,称为虚拟头结点。

虚拟头结点仅作为辅助使用,无需对头结点和非头结点进行单独处理,使用上一个解法中删除普通结点的操作,删除链表中的所有目标结点。

var removeElements = function(head, val) {
    let dummyHead = new ListNode(0,head)
    let cur = dummyHead

    while(cur.next){
        if(cur.next.val === val){
            cur.next = cur.next.next
            continue
        }
        cur = cur.next
    }

    return dummyHead.next
};

707. 设计链表

题目链接

题意:

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

初始思路:

在遇到这个问题的时候,LeetCode核心模式已经给了我几个方法的定义和规定的入类型和返回类型。 对JavaScript语言来说,这些类型都在 MyLinkedList 原型链上。 一开始我没有明确定义好节点和链表,直接在函数体中书写,不明晰也不流畅。

在看过部分解析之后:

首先发现 MyLinkedList 并没有定义其基础组成部分—— 链表节点,所以首先定义链表节点类:

function LinkNode (val,next){
    this.val = val===undefined ? -1 : val;
    this.next = next || null
}

在初始化thi.sval时,我一开始直接写的 ``this.val = val || -1; 但实际上需要注意的是,题目规定 val 可以等于 0, 所以我修改为 只有当 val 是undefined 的时候,对 val 修改初值为 -1

接下来定义链表类:

var MyLinkedList = function() {
    this.size = 0
    this.head = null
    this.tail = null
};

在第一次书写方法函数的时候,我发现很多时候都需要获取链表的长度,以及其头结点、尾结点。

同时,在 addAtIndex get deleteAtIndex 中,都需要用到 获取某个索引所对应的节点 这个方法,为了使代码简洁可读,定义一个新的 MyLinkedList 方法:

MyLinkedList.prototype.getNode = function(index){
    if(index<0 || index >= this.size) return null
    let cur = new LinkNode(50,this.head)
    while(index >= 0){
        cur = cur.next
        index --
    }

    return cur
}

这个方法接收一个数值参数作为inedx,返回当前链表的第 index 个节点。

注意,在本题中,index从0开始计算

由此,可以通过调用这个方法,更加便捷的完成其他几个函数:

MyLinkedList.prototype.get = function(index) {
    if(index < 0 || index >= this.size){
        return -1
    }
    return this.getNode(index).val
};


MyLinkedList.prototype.addAtHead = function(val) {
    let newNode = new LinkNode(val,this.head)
    this.head = newNode
    this.size ++ 
    if(!this.tail){
        this.tail = newNode
    }

};

MyLinkedList.prototype.addAtTail = function(val) {
    let newNode = new LinkNode(val,null)
    this.size ++ 

    if(this.tail){
        this.tail.next = newNode
        this.tail = newNode
        return
    }
    this.tail = newNode
    this.head = newNode
};

MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index<=0){
        this.addAtHead(val)
        return
    }else if(index>this.size){
        return
    }else if(index === this.size){
        this.addAtTail(val)
        return
    }else{
        let node = this.getNode(index-1)
        node.next = new LinkNode(val,node.next)
        this.size ++

    }
};

MyLinkedList.prototype.deleteAtIndex = function(index) {
    if(index >=0 && index<this.size){
       if(index === 0){
           this.head = this.head.next
           if(index === this.size-1){
               this.tail = this.head
           }
           this.size --
           return
       }
        let node = this.getNode(index-1)
        node.next = node.next.next
        if(index === this.size -1){
            this.tail = node
        }
        this.size--
    }else{
        return
    }
};

同时,我们需要注意,在增删链表节点的时候,对链表的属性:头结点、尾结点、长度进行及时的更新。

206. 反转链表

题目链接

题意:反转一个单链表。

image.png

示例: 输入: 1->2->3->4->5->NULL 
输出: 5->4->3->2->1->NULL

初始解题思路:

维护3个变量,分别是当前节点 node, (原链表中)当前节点的下一个节点next,当前节点的上一个节点pre

使 node.next 指向 pre, 并实时更新node, pre 和 next,从头到尾进行遍历和翻转。

var reverseList = function(head) {
    let node = head
    let pre = null
    let next
    while(node){
        next = node.next
        node.next = pre
        pre = node
        node = next
    }
    return pre
};

递归的解法

其基本思想和手段与我最开始的解题思路相同,但是是以递归的方法实现的。算是个 Trick

// 从前向后递归
var reverse = function(pre,head){
    if(!head) return pre
    let next = head.next
    head.next = pre
    pre = head

    return reverse(pre,next)
}

var reverseList = function(head) {
    return reverse(null,head)
};

var reverse = function(head){
    //到最后一个节点返回
    if(!head || !head.next) return head

    const pre = reverse(head.next)
    console.log(pre.next,head.next)
    head.next = pre.next
    pre.next = head
    return head 
}

//从后向前递归,类似于翻折、对折

var reverseList = function(head) {
    let cur = head
    while(cur && cur.next){
        cur = cur.next
    }
    //当前cur是链表的最后一个元素
    reverse(head)
    return cur
};