一、判断是否为环形链表
LeetCode141题:环形链表
解题思路 :快慢指针
设置两个指针,一个每次走一步称为慢指针,另一个每次走两步称为快指针。
当链表无环的时候,快指针会先到达链表末尾。
当链表有环的时候,两个指针走着都会在环里循环的走,终会相遇于某个节点。
代码实现
/**
* 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 == null) {
return false;
}
let fast =head, slow = head;
while(fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if(slow === fast) {
return true;
}
}
return false;
};
二、判断是否为环形链表并确定环的起点
LeetCode142题:环形链表II
解题思路:快慢指针
当有环时,
假设当慢指针走到环的起点距离为a,则快指针走了2a距离;
假设此时快指针距离环的起点为x,则当快指针再走2x的时候,慢指针走了x,此时快慢指针正好相遇。
由于x+a为一个环的长度,所以慢指针继续走a步就会到达环的起点;
将慢指针从头开始走,当指针走a步也会到达环的起点,所以这个指针跟慢指针同时走到相遇于环的起点时就能看出a的长度。
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
// 创建快慢指针
let fast = head, slow = head;
// 当快指针不为空的时候,快慢指针一直往后走
while(fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
// 当快慢指针相遇时证明有环
if(fast === slow) {
// 将快指针从头开始走,与慢指针同时走,每次走一步,当相遇时证明在环的起点
fast = head;
while(slow !== fast) {
slow = slow.next;
fast = fast.next;
}
return fast;
}
}
// 当快指针为空时证明走到链表尾了,链表无环
return null;
};
三、快乐数
LeetCode202题:快乐数
解题思路
当计算得的值无限循环时,始终变不到1的,只有不循环才可能得到1。
所以将计算的值看做链表节点,有重复值时相当于链表有环,可使用快慢指针进行判断。
快慢指针一直往后走,当快指针为1时,返回true。当链表有环时始终到不了1,要返回false。
代码实现
/**
* @param {number} n
* @return {boolean}
*/
// 获取每个位置上的数字的平方和
var getSum = function(n) {
let sum = 0;
while(n) {
sum += (n % 10) * (n % 10);
n = Math.floor(n / 10);
}
return sum;
}
var isHappy = function(n) {
let slow = n;
let fast = getSum(n);
while(fast != 1 && slow != fast) {
slow = getSum(slow);
fast = getSum(getSum(fast));
}
return fast == 1;
};
四、反转链表
LeetCode206题:反转链表
解题思路
当计算得的值无限循环时,始终变不到1的,只有不循环才可能得到1。
所以将计算的值看做链表节点,有重复值时相当于链表有环,可使用快慢指针进行判断。
快慢指针一直往后走,当快指针为1时,返回true。当链表有环时始终到不了1,要返回false。
代码实现
/**
* 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) {
let pre = null;
let cur = head;
while(cur) {
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
};
五、反转链表II(指定位置反转)
LeetCode92题:反转链表II
解题思路
将部分链表进行反转,反转后再将头和尾与原来断开的节点连起来。
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} left
* @param {number} right
* @return {ListNode}
*/
// 翻转链表
var reverse = function(head, length) {
let pre = null;
let cur = head;
while(length -- ) {
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
head.next = cur;
return pre;
}
var reverseBetween = function(head, left, right) {
// 需要翻转的链表长度
let length = right - left + 1;
// 设置一个空的子结点(当后面要用到.next时,这里就要new ListNode,否则可用null),放在链表前面,
// 当翻转链表的left为1时,用pre.next接受翻转后的链表才不会报错
let ret = new ListNode(-1, head)
let pre = ret;
let cur = head;
// 找到翻转前的节点
while(-- left) {
pre = cur;
cur = cur.next;
}
// 翻转链表后将pre.next指向翻转后的链表头
pre.next = reverse(cur, length);
return ret.next;
};
六、K个一组反转链表
LeetCode25题:K个一组反转链表
解题思路
判断有没有K个节点组成一组,如果有,则对K个节点进行反转,将指针移动到最后一个节点,再进行递归判断;若没有,则返回整个链表。
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var reverseKGroup = function(head, k) {
if(head == null) return null;
// 创建虚拟哨兵节点房在第一个节点前面
let ret = new ListNode(-1, head);
let pre = ret;
return reverseRecursion(ret, pre, k)
};
var reverseRecursion= function(ret, pre, k) {
let tail = pre;
// 判断是否有K个节点能组成一组
for(var i = 0; i < k; i++) {
if(tail && tail.next != null) {
tail = tail.next;
} else {
tail = null;
return ret.next;
}
}
// 如果有K个节点,则反转这K个节点,并将指针移到最后一个节点,当做下个k组的前一个节点,递归操作直到剩余的节点不能组成一组
if(tail != null) {
pre && (pre.next = reverse(pre.next, k));
for(var i = 0; i < k; i++) {
pre && (pre = pre.next);
}
return reverseRecursion(ret, pre, k)
}
}
// k个节点进行反转
var reverse = function(reverseHead, length) {
let pre = new ListNode(-1, reverseHead);
let cur = reverseHead;
while(cur && length--) {
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
reverseHead && (reverseHead.next = cur);
return pre;
}
七、旋转链表
LeetCode61题:旋转链表
解题思路
将链表尾部与头部连起来形成环,再找到对应的移动后的位置断开链表,返回新链表。
移动后的位置为 size - k % size
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var rotateRight = function(head, k) {
if(!head) return null;
// 获取链表的长度,并将尾部与头部连起来形成环
let size = 1, cur = head;
while(cur.next) {
cur = cur.next;
size ++;
}
cur.next = head;
// 将对应的位置断开,返回新链表
for(let i = 1; i < size-k%size; i ++) {
head = head.next;
}
let newHead = head.next;
head.next = null;
return newHead;
};
九、删除链表的倒数第 N 个结点
LeetCode19题:删除链表的倒数第 N 个结点
解题思路
设置两个指针pre、cur,cur指针先往前走n个节点,然后两个指针同时走,当cur走到链表尾时,pre指针刚好指向要删除的节点的前一个节点,然后将pre.next指向pre.next.next便删除了第n个节点。
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
if(!head) return null;
let ret = new ListNode(-1, head);
let pre = ret;
let cur = head;
// cur比pre先走n个指针,当cur走到链表尾时,pre刚好指向倒数第n个节点的前一个
//(题目中1 <= n <= size,所以不用考虑n > size的情况)
while(n --) {
cur = cur.next;
}
while(cur) {
pre = pre.next;
cur = cur.next;
}
pre.next = pre.next.next;
return ret.next;
};
十、删除排序链表中的重复元素
LeetCode83题:删除排序链表中的重复元素
解题思路
设置一个指针,一直向后走,如果当前节点是否等于后一个节点,则删除后一个节点,直到走到链表尾为止。
代码实现
/**
* 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 deleteDuplicates = function(head) {
let cur = head;
while(cur != null && cur.next != null) {
if(cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
};
十、分隔链表
LeetCode86题:分隔链表
解题思路
设置两个链表small和big,将原链表中的节点一个一个单独取出后,判断其大小,放入到对应的链表中。
当所有节点都放置完成后,将small链表的尾部链接big链表的头部,这样就形成了所求的链表。
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} x
* @return {ListNode}
*/
var partition = function(head, x) {
// 设置两个链表
let small = new ListNode();
let big = new ListNode();
// 两个链表的操作指针
let bigNode= big;
let smallNode = small;
// 设置p指向当前操作的节点
// 设置q指向p的下个节点,并将p.next设置为null使之成为单独的节点
// 判断p的大小,将p放置到对应的链表中
for(let p = head; p != null; p = q) {
q = p.next;
p.next = null;
if(p.val >= x) {
bigNode.next = p;
bigNode = bigNode.next;
} else {
smallNode.next = p;
smallNode = smallNode.next;
}
}
smallNode.next = big.next;
return small.next;
};
十、复制带随机指针的链表
LeetCode138题:复制带随机指针的链表
解题思路
将所有节点一个一个复制后插入到原节点后面,此时新节点的random指向与原节点的random指向相同,所以需要将 新节点的random指向 改为 原节点的random指向的节点的下一个节点。
所有新节点的random指向改好后,拆分链表,将链表拆为原链表和新链表。
代码实现
/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* };
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
if(!head) return null;
// 克隆每个节点,并插在原节点后面
let cur = head;
while(cur) {
let copyNode = new Node(cur.val, cur.next, cur.random);
// let copyNode = new Node(cur.val)
// copyNode.next = cur.next
// copyNode.random = cur.random;
cur.next = copyNode;
cur = copyNode.next;
}
// 将所有克隆节点的random指向对应的克隆节点
cur = head.next;
while(cur) {
(cur.random) && (cur.random = cur.random.next);
// cur向后走两步,这样写能判断cur.next是否存在
(cur = cur.next) && (cur = cur.next);
}
// 将链表分为原链表和克隆链表
// 实现方法一
// 把第一个克隆的节点储存到p、q、head中
// let p = q = head.next;
// while(q.next) {
// // 原链表:将head的next指向下下个节点
// head.next = head.next.next;
// // 新链表:将q的next指向下下个节点
// q.next = q.next.next;
// head = head.next;
// q = q.next;
// }
// // 链表尾用null
// head.next = null;
// return p;
// 实现方法二
let newHead = head.next; //新链表
let p = head; //原链表指针
// let q = p.next;
while(p) {
let q = p.next; // q指向p.next
p.next = q.next; // p.next指向下下个节点
p.next && (q.next = p.next.next); // q.next指向下下个节点
p = p.next; // p指针往后走一步
}
return newHead;
};