theme: smartblue
141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
方法一:map
var hasCycle = function(head) {
let map = new Map();
while(head){
if(!map.has(head)){
map.set(head, true)
}else{
return true;
}
head = head.next;
}
return false
};
方法二:快慢指针
var hasCycle = function(head) {
let slow = head;
let fast = head;
while(fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
return true
}
}
return false
};
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的链表节点
解释: 链表中有一个环,其尾部连接到第二个节点。
这道题和上面的区别就是:这题要求的是入环的第一个节点!!!我们设置的快慢指针不是相遇的节点不一定是在入环的第一个节点。
方法一:map
var detectCycle = function(head) {
let map = new Map();
while(head){
if(map.has(head)){
return head;
}else{
map.set(head,true)
}
head = head.next
}
return null
};
方法二:快慢指针
var detectCycle = function(head) {
if (head === null) {
return null;
}
let fast = head;
let slow = head;
while(fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
fast = head;
//快指针指向头节点,然后每次快慢指针各走一步直到相遇,相遇的节点就是入环节点
while (fast !== slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null
};
原理:
- 假设从起点到入环节点的距离为 L
- 环的周长为 C
- 两指针相遇时相对于头节点的距离为 X
设fast 的速度为 2 slow 的速度为 1
两指针同时出发 fast指针行驶的距离应该为slow指针的两倍
L+X+nc = 2(L+X)
推导一下 L = C-X +(N-1)C
所以俩个指针,一个从起点出发,一个从相遇点出发(走的路程相同),速度都是1。
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
var threeSum = function(nums) {
let len = nums.length;
let res = [];
if(len < 3 || nums === null){return res}
// 先对nums排序 时间复杂度nlogn
nums.sort((a,b) => a-b);
for(let i =0; i < len;i++){
//如果nums[i]>0,最小的数字都>0,没必要再往下找了
if(nums[i] > 0) break;
// 我们是先确定最小的数,如果这个最小数已经出现过了没必要再算一遍
// -4 -1 -1 0 1 2
if(i > 0 && nums[i-1] === nums[i]) continue;
// 开始确定第二第三个数
let left = i+1, right = len - 1;
while(left < right){
const sum = nums[i] + nums[left] + nums[right];
if(sum === 0){
res.push([nums[i],nums[left],nums[right]]);
//中间数字去重 -1 1 1 0
while(left < right && nums[left] === nums[left+1]){left++}
//后面数字去重
while(left < right && nums[right] === nums[right-1]){right--}
left++;
right--;
}else if(sum < 0){
left++;
}else{
right--;
}
}
}
return res
};
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

var maxArea = function(height) {
let max = 0;
for(let i = 0, j = height.length - 1; i < j;){
let minHeight = height[i] < height[j] ? height[i++] : height[j--];
let res = (j - i + 1) * minHeight;
max = Math.max(res,max)
}
return max
};
160. 相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
var getIntersectionNode = function(headA, headB) {
let pA = headA;
let pB = headB;
while(pA !== pB){
pA = pA === null ? headB : pA.next;
pB = pB === null ? headA : pB.next
}
return pA
};
876. 链表的中间结点
给定一个头结点为 `head` 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
var middleNode = function(head) {
let slow = head;
let fast = head;
while(fast && fast.next){
slow = slow.next;
fast = fast.next.next
}
return slow
};