链表的基础知识
单向链表
分为数据域和指针域
- 指针域在C语言中是地址
- 可以存储数组下标,相对地址的概念
- 引用(js,puthon,java)
- 在相应的结构中增加一个指针域,就能串成一个链表结构
- 链表中的每个节点至少包含两个部分:数据域与指针域
- 链表中的每个节点,通过指针域的值,形成一个线性结构
- 查找节点O(n),插入节点O(1),删除节点O(1)
- 不适合快速的定位数据,适合动态的插入和删除数据的应用场景
链表的典型应用场景
场景一:操作系统内部的动态内存分配
场景二:LRU缓存淘汰算法
141. 环形链表
题目描述
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
js中的!!是什么意思呢
- js代码中有时会用到"!",这个是取反的意思,对于布尔类型会将其值true和false互换。
- 对于非布尔类型,js会将值先转换成布尔类型,而后取反。
- 字符串类型,会将空值("")转换成false,其余转换成true。
- 数字类型,会将0转换成false,其余为true。
- null undefined会转换成false。
总结:一个"!"是将对象转为布尔型并取反,两个"!!"是将取反后的布尔值再取反,相当于直接将非布尔值类型转为布尔类型值。
解题思路
1.快慢指针,设置一个慢指针pre,pre指向head,和一个快指针cur,cur指向head.next。pre每次都走一步,cur每次都走两步。如果链表有环,那么在n次追赶后,cur快指针一定能在环内某个节点上追上pre慢指针。
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
if(!head) return false;
// 申明一个慢指针pre指向head,快指针cur指向head.next
let pre = head, cur = head.next;
// 循环cur,且cur不为null或者cur.next不为null。且cur和pre不相等
while(cur && cur.next && pre !== cur){
pre = pre.next;
cur = cur.next.next;
}
// 跳出循环,说明cur为null或者cur === pre
return pre === cur;
// 如果返回值是这样去写的话,是会报错的。力扣没处理好
// return pre && pre.next
// 可以改为
// return !!pre && !!pre.next
};
2.ES6的Map数据结构,JS的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当做键。ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是"键"的范围不限于字符串,各种类型的值(包含对象)都可以当作键。也就是说,Object结构提供了"字符串--值"的对应,Map结构提供了"值--值"的对应,是一种更完善的Hash结构实现。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content');
m.get(o); // 'content'
m.has(o) // true
m.delete(o) // true
m.has(o); // false
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
if(!head) return false;
// 构造一个map对象
let map = new Map();
// 遍历链表
while(head){
// 判断当前节点是否在map对象里面,在就说明有环,返回true
if(map.has(head)) return true;
// 不在就把当前节点set进map对象内
map.set(head);
// 当前节点往后走一步
head = head.next;
}
// 跳出循环,说明链表没有环,返回false
return false;
};