141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。 如果链表中存在环 ,则返回 true 。 否则,返回 false 。
提示: 链表中节点的数目范围是 [0, 104] -105 <= Node.val <= 105 pos 为 -1 或者链表中的一个 有效索引 。
题意理解: 判断一个链表是否存在环,及是否可以无限的循环访问
采用快慢指针法,就像操场跑步一样,两个人同时出发,一个人跑的快,一个人跑的慢,跑的快的那个一定会追上那个慢的人
- 假定快的是慢的的速度的二倍,及慢的走一步,快的走两步slow存慢的,quickly存快的
- 如果链表可以循环完,则快已经重点,慢的还在路上,返回false
- 当快的追上慢的时,则存在环,返回true
- 最后可以整合一下,返回快和慢是否相等即可
var hasCycle = function (head) {
if (!head) return false;
let quickly = head?.next?.next, slow = head;
while (quickly && quickly.next && slow !== quickly) {
quickly = quickly.next?.next;
slow = slow.next;
}
return slow === quickly
};
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 不允许修改 链表。
提示: 链表中节点的数目范围在范围 [0, 104] 内 -105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引
题意理解: 返回链表中第一个环节点,不存在环则返回null
- 快慢指针法,假定快的是慢的的速度的二倍,及慢的走一步,快的走两步slow存慢的,quickly存快的
- 先找到相遇的节点,及slow=quickly
- 根据推断可知,相遇节点到第入环第一个节点的距里等于首节点到入环的距里
推理过程
- 先画图更好理解。
- 设首节点到第一个入环节点的距里为a,入环到相遇节点的距里为c,相遇节点到入环的距里为c,快指针是满指针的两倍
- 慢指针的距里 a+b;快指针的距里a+n(b+c)+b,n为b在环中的圈数
- 快指针是满指针的两倍 2(a+b) = a+n(b+c)+b,约一下可得 a= (n-1)b + (n-1+1)c =(n-1)(b+c) +c (b+c为一圈)
var detectCycle = function (head) {
if (!head) return null;
let quick = head, slow = head;
while (true) {
if (!slow.next || !quick.next?.next) return null;
slow = slow.next;
quick = quick.next?.next;
if (slow === quick) {
break
}
}
while (quick && head && quick !== head) {
quick = quick.next
head = head.next
}
return quick
};
使用Set,及hash法 遍历链表,将当前节点存入set中,如果set存在则返回该节点否则存入set中 遍历结束,还不存在则返回null。
var detectCycle = function (head) {
if (!head) return null;
const set = new Set();
while(head){
if(set.has(head)){
return head
}
set.add(head)
head=head.next;
}
return null;
};
202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1,那么这个数就是快乐数。 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1: 输入:n = 19 输出:true 解释: 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1
提示: 1 <= n <= 231 - 1
题意理解: 判断一个数是否符合上述条件,是就返回true,否则返回false。
- 利用快慢指针原理,计算下一个数
- 如果是快乐数一定存在一个环就是为1的环,继续下去也为1
- 声明两个变量slow,quickly用来存储快慢数,循环slow,quickly
- 当两束相等且为1时则范湖true,否则返回false
var isHappy = function(n) {
function getNextNum(x){
let num=0;
while(x){
num+=Math.pow(x%10,2);
x=Math.floor(x/10)
}
return num;
}
let slow=n,quickly=n;
do{
slow=getNextNum(slow);
quickly=getNextNum(getNextNum(quickly));
}while(slow!==quickly && slow!==1)
return slow===1;
};
hash法及使用set原理 循环遍历计算结果,存入set中 如果当计算结果为1则返回true,否则存入当前数,set中存在当前数返回false
var isHappy = function(n) {
let set=new Set();
let num=n;
while(true){
let currentNum=0;
while(num){
currentNum+=Math.pow(num%10,2) ;
num=Math.floor(num/10)
}
if(set.has(currentNum)) return false;
set.add(currentNum);
num=currentNum;
if(currentNum===1){
return true;
}
}
};
206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
提示: 链表中节点的数目范围是 [0, 5000] -5000 <= Node.val <= 5000
提议理解: 将一个链表进行反转
- 递归反转
- 终止条件,当前节点不存在或当前节点的下个节点不存在,就返回当前节点
- 递归当前节点的下个节点
- 进行反转
var reverseList = function(head) {
if(!head || !head.next) return head;
let tail = head?.next;
let ret = reverseList(head?.next)
head.next = tail?.next;
tail.next = head;
return ret;
};
暴力法,借助变量进行反转
- 声明一个变量newHead用来存储新链表
- 遍历就链表,每次生诚新节点,新节点.next=newHead,newHead=新节点
- 返回newHead
var reverseList = function(head) {
if(!head) return null;
let newHead=null;
while(head){
let node = new ListNode(head.val)
node.next=newHead;
newHead=node;
head=head.next;
}
return newHead;
};
92. 反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
提示: 链表中节点数目为 n 1 <= n <= 500 -500 <= Node.val <= 500 1 <= left <= right <= n
题意理解: 反转位置left到right中间的链表,其余不变。数据不是从1开始,所以start.val === left获取反转开始位置不可取
- 先声明变量newHead并初始化一个新节点,以为可能从1就开始反转,就无法暂定到根节点
- 设置新节点的值newHead.next = head,以后操作新节点,设start用来存储开始位置,迭代寻找start = newHead
- 找到开始位置start,初始化一个变量pre等于开始位置start,将pre.next设为null,不影响后续使用,并将并进局部反转,使用递归
- 递归结束条件,找到反转结束位置,或者当前节点不存才或子节点不存在,返回当前节点
- pre.next指向递归的返回值
- 最后返回newHead.next
var reverseBetween = function (head, left, right) {
if(left>=right) return head;
function reverse(head, i, k) {
if (i >= k || !head || !head.next) return head;
let ne = head.next;
let res = reverse(head.next, ++i, k)
head.next = ne.next;
ne.next = head;
return res;
}
let newHead = new ListNode(-1);
newHead.next = head;
let start = newHead;
let j=left;
while (start && j>1) {
start = start.next
j--;
}
let pre = start.next;
start.next=null;
let currentReverse = reverse(pre, 1, right - left + 1)
start.next = currentReverse
return newHead.next;
};