阿锐刷题日记Day03-链表01:链表API设计;移除链表元素; 反转链表

链表理论基础

建议:了解一下链接基础,以及链表和数组的区别

文章链接:(https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html)

707.设计链表

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接/文章讲解/视频讲解:(https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html)
在这里插入图片描述

const node = new LinkNode(val, this._head); // 新建节点node并让其尾部指向原链表的head
this._head = node; // 让虚拟头节点this._head的尾部指向新建节点node

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

当你实例化 MyLinkedList 类时,你会得到一个链表对象,该对象包含了 _size、_head 和 _tail 这些属性。
其中,_head 和 _tail 是链表的头节点和尾节点,它们最初都为 null。 _size 用于跟踪链表中节点的数量。

每个节点通过 LinkNode 构造函数创建,包含了一个值 val 和一个指向下一个节点的引用 next。
这样,链表对象实际上就是一个包含多个节点对象的数据结构

链表和节点都是构造函数, 也就是对象类型, 所以最终存储就是一个链表对象包含多个节点对象

使用下划线 _ 开头的变量约定为私有,但在JavaScript中,它们仍然是可以被访问和修改的。
这里的目的主要是通过命名约定来提醒开发者,而不是通过语言特性来实现真正的私有性

防止意外修改: 开发者在使用这个类的时候,看到下划线开头的变量会意识到这是类内部状态,应该避免直接修改
清晰标识: 通过命名约定,可以更清晰地区分类的内部状态和公共接口。公共接口是类暴露给外部使用的方法和属性,而以下划线开头的变量则表示内部状态
维护性: 如果你需要在类内部进行一些额外的逻辑或修改内部状态,使用私有变量可以更容易进行维护和更新,因为你知道哪些是内部状态,哪些是公共接口

// 定义链表节点类
class LinkNode {
    constructor(val, next) {
        this.val = val;
        this.next = next;
    }
}

/**
 * Initialize your data structure here.
 * 单链表 储存头尾节点 和 节点数量
 */
// 定义链表类
var MyLinkedList = function() {
    this._size = 0;       // 节点数量
    this._tail = null;    // 尾节点
    this._head = null;    // 头节点
};

/**
 * Get the value of the index-th node in the linked list. If the index is invalid, return -1. 
 * @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 -> 返回的是head
    while(index-- >= 0) {
        cur = cur.next;
    }
    return cur;
};
MyLinkedList.prototype.get = function(index) {
    if(index < 0 || index >= this._size) return -1;
    // 获取当前节点
    return this.getNode(index).val;
};

/**
 * Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    const node = new LinkNode(val, this._head); // 创造需要的节点, 值为val, 尾部指向当前head
    this._head = node; // 将链表的head改成刚才添加的node
    this._size++;
    if(!this._tail) { // 如果链表的尾节点 _tail 为空(即链表是空的)
        this._tail = node;
    }
};

/**
 * Append a node of value val to the last element of the linked list. 
 * @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;
};

/**
 * Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index > this._size) return;
    if(index <= 0) {
        this.addAtHead(val);
        return;
    }
    if(index === this._size) {
        this.addAtTail(val);
        return;
    }
    // 获取目标节点的上一个的节点
    const node = this.getNode(index - 1);
    node.next = new LinkNode(val, node.next); // 插入值为val的新建节点, 实际上就是从index位置创建一个链表对象, 起始点是新建的node
    this._size++;
};

/**
 * Delete the index-th node in the linked list, if the index is valid. 
 * @param {number} index
 * @return {void}
 */
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--;
};

// MyLinkedList.prototype.out = function() {
//     let cur = this._head;
//     const res = [];
//     while(cur) {
//         res.push(cur.val);
//         cur = cur.next;
//     }
// };
/**
 * 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)
 */

203.移除链表元素

建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。

题目链接/文章讲解/视频讲解::(https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html)

题意:删除链表中等于给定值 val 的所有节点。

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

示例 2: 输入:head = [], val = 1 输出:[]

示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
/**
 * 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) {
    dummy_head=new ListNode(0,head)
    let cur = dummy_head
    while(cur.next){
        if(cur.next.val==val){
            cur.next=cur.next.next
            continue
        }
        cur=cur.next
        
    }
    return dummy_head.next
};

206.反转链表

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

题目链接/文章讲解/视频讲解:(https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html )

题意:反转一个单链表。

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

如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。

只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表

首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点

temp=cur.next 和 cur=temp 共同实现了cur的遍历
然后cur.next=pre和pre=cur实现了两个节点之间的指向反转, 同时让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) {
    // 如果是空链表或者链表只有一个元素
    if(!head || !head.next) return head 
    let temp = null, pre=null, cur=head
    while(cur){
        temp=cur.next
        cur.next=pre
        pre=cur
        cur=temp
    }
    return pre
};

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值