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

73 阅读4分钟

203-移除链表元素

题目链接

【分析】

1、题干背景:需要移除链表节点的val值等于传入的val值的节点

2、这道题允许返回一个新的头节点,可以认为可返回新的链表

【疑问】

1、链表如何遍历?

通常链表遍历通常当前节点初始值设置为头节点,然后通过next指针来移动当前节点的位置

2、如何创建新的节点?

通常是通过构造函数LinkedList。这个构造函数可以自己手写

具体逻辑如下

function LinkedList(val,next){
    this.val = val === undefined ? 0 : val;
    this.next = this.next === null ? null : next;
}

3、创建新链表是否需要区分头节点的创建?

这个是需要区分的,因为头节点需要有个标志,才能后续用来返回链表的头节点

【代码实现】

var removeElements = function(head, val) {
    let nodeList = null;
    let headNode = null;
    let curNode = head;
    while(curNode){
        if(curNode.val !== val){
            // 说明需要收集
            if(nodeList===null){
                // 说明是头节点
                nodeList = new ListNode(curNode.val);
                headNode = nodeList;
            }else{
                nodeList.next = new ListNode(curNode.val);
                nodeList = nodeList.next;
            }
         }
         curNode=curNode.next;
     }
     return headNode
}

707-设计链表

题目链接

【分析】

1、题干背景:这道题就是用来考察链表的结构基础的

2、关键就是要读懂每个方法的作用,以及入参和返回值即可

3、值得注意的是头尾节点的问题,新增需要考虑,删除也同样需要考虑

4、在每次变动时,链表的长度都会发生相应的变化,这也是容易遗漏的地方

【疑问】

1、单链表需要哪些属性?

1、头节点

2、尾节点

3、链表长度

2、头部新增如何处理?

只需要创建一个新的节点,并且借助构造函数内部的head属性,将新节点的next指向原本的head节点,再将head变量指向新节点

这里需要考虑this.head为null的情况,为null说明这是第一次添加节点,那么在修改head的同时,也要考虑tail的情况

const newNode = new ListNode(1);
newNode.next = this.head;
if(this.head === null) this.tail = newNode;
this.head = newNode;

3、尾部新增如何处理?

同样创建一个新节点,借助构造函数内部的tail属性,将原本的tail的next指向新节点,并且将tail变量指向新节点即可

这里需要额外注意一点,如果this.tail为null的情况,说明当前链表还没有任何节点,所以在赋值的时候需要考虑head

const newNode = new ListNode(1);
if(this.tail){
    this.tail.next = newNode;
    this.tail = newNode;
}else{
    this.head = newNode;
    this.tail = newNode;
}

【代码实现】

这一版代码写的比较乱,后续再重新梳理一下逻辑

var MyLinkedList = function (val, next) {
  this.length = val ? 1 : 0;
  // 初始化,可能传值可能不传值
  this.head = val ? new ListNode(val) : null;
  if (this.head) {
    this.head.next = next; // 这里需要建立关联
  }

  if (next) {
    let curNode = next;
    while (curNode) {
      this.tail = curNode;
      curNode = curNode.next;
      this.length++;
    }
  } else {
    this.tail = this.head;
  }
};

/**
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function (index) {
  const node = this.getNode(index);
  return node === null ? -1 : node.val;
};
MyLinkedList.prototype.getNode = function (index) {
  // 说明这个索引的值是不存在的
  if (index >= this.length) return null;
  let curNode = this.head;
  let count = 0;
  while (curNode) {
    if (count === index) {
      return curNode;
    }
    curNode = curNode.next;
    count++;
  }
  return null;
};

/**
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function (val) {
  const node = new ListNode(val);
  // 需要考虑头节点在不在
  if (this.head) {
    node.next = this.head;
    this.head = node;
  } else {
    this.head = node;
    this.tail = node;
  }
  this.length++;
};

/**
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function (val) {
  const node = new ListNode(val);
  if (this.tail) {
    this.tail.next = node;
    this.tail = node;
  } else {
    this.head = node;
    this.tail = node;
  }
  this.length++;
};

/**
 * @param {number} index
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function (index, val) {
  if (index > this.length) return;
  else if (index === this.length) {
    this.addAtTail(val);
  } else if (index === 0) {
    this.addAtHead(val);
  } else {
    const node = this.getNode(index - 1);
    const nextNode = node.next;
    const newNode = new ListNode(val);
    newNode.next = nextNode;
    node.next = newNode;
    this.length++;
  }
};

/**
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function (index) {
  if (index >= this.length) return;
  if (index === 0) {
    // 考虑了头部
    this.head = this.head.next;
    this.tail = this.head ? this.tail : null;
  } else {
    const node = this.getNode(index - 1);
    node.next = node.next ? node.next.next : null;
    if (node.next === null) {
      this.tail = node;
    }
  }
  this.length--;
};

206-反转链表

题目链接

【分析】

1、题干背景:这道题需要将链表的顺序翻转一下

2、总体思路仍然还是围绕着修改next指针,可能容易绕晕的地方在于原next指向和新next指向的替换

3、遍历链表的顺序是从前往后,翻转后是从后往前,所以其实可以认为每次遍历的节点都需要插入到新链表的头部

【疑问】

1、如何实现节点next指针的值改变?

我们需要借助两个变量,一个是当前节点,一个上一个节点

按照顺序来说,结构为上一个节点 -> 当前节点

按照反序来说,结构为当前节点 -> 上一个节点

所以整体思路是:

原有的当前节点的next节点,作为下一次循环的当前节点

当前节点的next节点需要更新为之前存储的上一个节点

const nextNode = curNode.next;//这是下一次循环的当前节点
curNode.next = lastNode;
lastNode = curNode;
curNode = nextNode;// 这个赋值操作主要是因为while循环判断是通过curNode,所以表示下一次循环的判断条件是nextNode

【代码实现】

var reverseList = function(head) {
    let curNode = head;
    let lastNode = null;
    while(curNode){
        const node = curNode;
        curNode = curNode.next;
        node.next = lastNode;
        lastNode = node;
    }
    return lastNode;//因为翻转其实是一直在改变新链表的头节点,所以返回lastNode
};