203.移除链表元素
本题要点
①未添加虚拟头节点,需要分类讨论空头指针;
②添加虚拟头节点,可以直接遍历链表。
对方法2的补充:
假设样例为[7,7,8,8] val = 7
这种情况下return head 的结果是[7,7,8,8] 而不是理想的[8,8]
原因是 我们修改的是虚拟节点的next 而当第一个节点需要删除时,我们直接跳过了head.next的修改 就会导致开头连续或单个的7还是被输出 所以我们应该返回 dummyHead.next
代码实现
对原链表进行操作
function removeElements(head: ListNode | null, val: number): ListNode | null {
// 对原链表直接进行操作
// 头节点的删除操作(特殊)
while(head !== null && head.val === val){
head = head.next
// JS不用手动进行删除节点的操作,有浏览器的回收机制
}
// 非头节点的删除操作
let cur: ListNode = null
cur = head
while(cur !== null && cur.next !== null){
if(cur.next.val === val){
cur.next = cur.next.next
// 最开始写错了,写了cur = cur.next.next
} else {
cur = cur.next
}
}
return head
};
新增一个虚拟头节点
function removeElements(head: ListNode | null, val: number): ListNode | null {
let dummyHead: ListNode = new ListNode(0, head)
let cur = dummyHead
// dummyHead为引入的虚拟头节点
while(cur !== null && cur.next !== null){
if(cur.next.val === val){
// console.log('=' + cur.next.val)
cur.next = cur.next.next
} else {
// console.log('!=' + cur.next.val)
cur = cur.next
}
}
return dummyHead.next
// 这里不直接返回head是因为有些特殊情况里
// 我们没有修改过head.next等后续链表节点指向
// 详细说明见上面思路分析部分
};
707.设计链表
本题要点
要点看代码中的注释
代码实现
class MyListNode {
public val: number;
public next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = val === undefined ? 0 : val;
this.next = next === undefined ? null : next;
}
}
class MyLinkedList {
private size: number;
private head: ListNode;
private tail: ListNode;
constructor() {
this.size = 0;
this.head = null;
this.tail = null;
}
// 抽象出新的函数
getNode(index: number):ListNode {
// 引入虚拟头节点
let dummyHead: ListNode = new ListNode(0, this.head);
// 添加指针循环链表
let cur = dummyHead;
for (let i = 0; i <= index; i++) {
// 理论上不会出现 null
cur = cur.next!;
}
return cur;
}
get(index: number): number {
// 判断index的有效性
if(index < 0 || index >= this.size){
return -1;
}
let cur = this.getNode(index);
return cur.val;
}
addAtHead(val: number): void {
let node: ListNode = new ListNode(val, this.head);
this.head = node;
this.size++;
// 如果当前还没有尾节点(也就是说当前链表为空),将尾节点也设置为node
if(!this.tail){
this.tail = node;
}
}
addAtTail(val: number): void {
let node: ListNode = new ListNode(val, null);
if(this.tail){
this.tail.next = node;
} else {
// 同上 如果没有尾节点说明链表为空,设置头节点
this.head = node;
}
this.tail = node;
this.size++;
}
addAtIndex(index: number, val: number): void {
// 首先用条件判断来写题目中给出的特殊情况
if(index === this.size){
this.addAtTail(val);
return;
}
if(index > this.size){
return;
}
if(index <= 0){
this.addAtHead(val);
return;
}
// 获取第index个节点的前一个(改变其next指向来实现插入)
let cur = this.getNode(index - 1);
let node: ListNode = new ListNode(val, cur.next);
cur.next = node;
this.size++;
}
deleteAtIndex(index: number): void {
// 判断index的有效性
if(index < 0 || index >= this.size){
return;
}
if(index === 0){
this.head = this.head.next;
if(this.size === 1){
this.tail = null;
}
this.size--;
return;
}
// 处理其他情况
let cur = this.getNode(index - 1);
cur.next = cur.next.next
if(index === this.size - 1){
this.tail = cur;
}
this.size--;
}
}
206.反转链表
本题要点
本题有两种实现方式,第一种是结合双指针的常规的写法,第二种是递归写法。
代码实现
双指针法
function reverseList(head: ListNode | null): ListNode | null {
let cur = null, pre = null, temp = null;
pre = head;
while(pre){
temp = pre.next;
pre.next = cur;
cur = pre;
pre = temp;
}
return cur
};
递归写法
function reverseList(head: ListNode | null): ListNode | null {
function reverse(pre: ListNode | null, cur: ListNode | null): ListNode | null {
if (cur === null) return pre
let temp: ListNode | null = null
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return reverse(pre, cur)
}
return reverse(null, head)
};