代码随想录算法训练营day3 | 链表 | 203.移除链表元素 707.设计链表 206.反转链表

第二章 链表

链表理论基础

文章讲解: 链表理论基础

把数据结构书拿出来翻了翻,把里面的单链表、双链表和循环列表都看了看。
单链表采用头插法插入的是逆序,尾插法是顺序的。
数组和链表的对比
代码随想录算法训练营day3 | 链表 | 203.移除链表元素 707.设计链表 206.反转链表_第1张图片

203.移除链表元素

建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::203.移除链表元素

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

直接删除

思路:用p扫描整个链表,q指向p的后驱结点,当q等于target 的时候,对其删除,否则,让p直接移向下一个结点。自己思考忘了考虑当第一个结点为要删除节点的情况,直接让p指向后驱结点即可。

代码:

/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */

var removeElements = function (head, val) {
    // 删除头结点
    while (head != null && head.val === val) {
        head = head.next
    }
    let p = head
    // 删除中间结点
    while (p) {
        let q = p.next
        if (q !== null && q.val === val) {
            p.next = q.next
        } else {
            p = p.next
        }
    }
    return head
};

时间详情:76ms
内存详情:44.75MB

设置一个虚拟头节点进行删除

思路:使用一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
代码:

/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */

var removeElements = function (head, val) {
    const dummyHead = new ListNode(0,head)
    let pre = dummyHead
    while (pre.next) {
        if (pre.next.val === val) {
            pre.next =  pre.next.next
        } else {
            pre = pre.next
        }
    }
    return dummyHead.next;
    // 这里一开始返回的是head但是在通过[7,7,7,7] val=7 的时候报错了
};

总结

第一次用js写链表,emmm,先去看了别人是怎么写的,然后回来自己敲了一遍,这个题感觉不难,就是要注意对头结点的操作。刚刚复习书的时候,还有一种递归的写法和采用尾插法的写法,感觉可以再写写拓展一下思路(待填)。

707.设计链表

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:707.设计链表

题目链接:leetcode707 design-linked-list
题目描述:
代码随想录算法训练营day3 | 链表 | 203.移除链表元素 707.设计链表 206.反转链表_第2张图片

虚拟结点法

思路:对链表操作的一个综合练习,在练习的时候,很容易出错,也就是说,在一些细节,边界条件上要仔细考虑。
代码:

// 假设链表中的所有节点下标从 0 开始。
class LinkNode {
    constructor(val, next) {
        this.val = val
        this.next = next
    }
}

/**
 * 单链表 储存头尾节点 和 节点数量
 */
var MyLinkedList = function () {
    this._size = 0
    this._tail = null
    this._head = null
};


/** 
 * @param {number} index
 * @return {number}
 * 返回指定下标的结点
 */
MyLinkedList.prototype.getNode = function (index) {
    if (index < 0 || index >= this._size) {
        return null
    }
    // 创建虚拟结点
    let cur = new LinkNode(0, this._head)
    // 返回的是下标为index的结点,该题中下标从0开始,故循环条件为index>=0
    while (index >= 0) {
        cur = cur.next
        index--
    }
    return cur
};
/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function (index) {
    if (index < 0 || index >= this._size) {
        return -1
    }
    // 获取当前结点
    return this.getNode(index).val
};

/** 
 * @param {number} val
 * @return {void}
 * 采取头插法
 */
MyLinkedList.prototype.addAtHead = function (val) {
    const node = new LinkNode(val, this._head)
    this._head = node
    this._size++
    // 当尾指针为空的时候指向node
    while (!this._tail) {
        this._tail = node
    }
};

/** 
 * @param {number} val
 * @return {void}
 * 采用尾插法
 */
MyLinkedList.prototype.addAtTail = function (val) {
    const node = new LinkNode(val, null)
    this._size++
    if (this._tail) {
        this._tail.next = node;
        this._tail = node;
        return;
    }
    this._tail = node;
    this._head = node;
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function (index, val) {
    // 大于链表长度的情况
    if (index > this._size) return
    // 等于链表长度的情况
    if (index === this._size) {
        this.addAtTail(val)
        return  // 插入完成后直接返回
    }
    // 插入位置为第一个元素之前的情况
    if (index <= 0) {
        this.addAtHead(val)
        return  // 插入完成后直接返回
    }
    // 查找index-1下标的结点
    let pre = this.getNode(index - 1)
    // 创建结点
    const node = new LinkNode(val, pre.next)
    pre.next = node
    this._size++
};

/** 
 * @param {number} index
 * @return {void}
 * 删除下标为index的结点
 */
MyLinkedList.prototype.deleteAtIndex = function (index) {
    if (index < 0 || index >= this._size) return;
    if (index === 0) {
        this._head = this._head.next;
        // 如果删除的这个节点同时是尾节点,要处理尾节点
        if (index === this._size - 1) {
            this._tail = this._head
        }
        this._size--;
        return;
    }
    // 获取目标节点的上一个的节点
    const node = this.getNode(index - 1);
    node.next = node.next.next;
    // 处理尾节点
    if (index === this._size - 1) {
        this._tail = node;
    }
    this._size--;
};

总结

考察了很多基础的操作,题目不是很难,但是有很多细节的点,比如说,题目中给定的下标是从0开始的,这个就很容易错。以及在将数值插入末尾的时候,首先要判断的就是尾指针是否有效,我是没有判断直接插入就出错了,如果尾指针为null,那么就无法指向下一个结点。以及在查找结点值的时候,最好定义一个查找结点的操作,因为在插入和删除的时候,都需要查找到指定位置的前驱指针。

206.反转链表

建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:206.反转链表

题目链接:leetcode206 reverse-linked-list
题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

头插法

思路:设置一个虚拟头结点,然后进行头插法。
代码:

/**
 * 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) {
const node = new ListNode(0,null);
let p = head
while(p){
    let r = p.next
    p.next=node.next
    node.next = p
    p = r   
}
return node.next
};

执行用时: 68 ms
内存消耗: 42.9 MB

总结

这道题自己的思路很清晰,写的也很快,五分钟。

总结

今天的链表习题感觉唤起了对链表的一些感觉,今天把数据结构里链表的部分也进行了着重复习。复习花的时间有点久了,继续加油!

你可能感兴趣的:(代码随想录,算法,链表,数据结构)