第二章 链表
链表理论基础
文章讲解: 链表理论基础
把数据结构书拿出来翻了翻,把里面的单链表、双链表和循环列表都看了看。
单链表采用头插法插入的是逆序,尾插法是顺序的。
数组和链表的对比
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
题目描述:
虚拟结点法
思路:对链表操作的一个综合练习,在练习的时候,很容易出错,也就是说,在一些细节,边界条件上要仔细考虑。
代码:
// 假设链表中的所有节点下标从 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
总结
这道题自己的思路很清晰,写的也很快,五分钟。
总结
今天的链表习题感觉唤起了对链表的一些感觉,今天把数据结构里链表的部分也进行了着重复习。复习花的时间有点久了,继续加油!