「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
目标
- 从尾到头打印链表 leetcode-cn.com/problems/co…
- 返回倒数第 k 个节点 leetcode-cn.com/problems/kt…
- 环形链表 leetcode-cn.com/problems/li…
- K 个一组翻转链表 leetcode-cn.com/problems/re…
- 只出现一次的数字 leetcode-cn.com/problems/WG…
剑指 Offer 06. 从尾到头打印链表
简单归简单,目标既然定下,就不能因为简单而忽略;
思路清晰,代码如下
var reversePrint = function(head) {
// 用数据存储答案
const result = [];
while(head){
// 将当前值放在数组头部
result.unshift(head.val)
head = head.next;
}
// 返回数组
return result
};
面试题 02.02. 返回倒数第 k 个节点
思路:
因为链表特性,只能一步一步向链表尾部查找,且只能有头节点向尾节点寻找;怎么处理这类查找倒数第几个节点的问题呢?
第一种方法:
根据剑指 Offer 06. 从尾到头打印链表
这题的思路,我们可以将链表放在数组中;且将链表中的数据倒着放在数组中,这样直接返回数组第k-1位不就是倒数第k位节点吗?
根据上述代码书写代码如下:
var kthToLast = function(head,k){
let root = head;
let list = []
while(root){
list.unshift(root.val)
root = root.next
}
return list[k-1]
}
分析上述代码:
时间复杂度是O(n);
空间复杂度也是O(n);
有没有更优的解题思路呢?
第二种方法:
首先使用两个分别为fast和slow指针分别指向链表头部;
先让fast指针向链表尾部运行,运行多少步呢?运行k步;
当fast指针向前运行k步后,让slow指针也向前运行,这样fast和slow指针相差位k;
这是当fast指针指向head链表的尾部, slow当前所在的位置就是head到处第k位;
根据上述思路书写代码如下
var kthToLast = function(head,k){
let fast = head;
let slow = head;
while(k--){
fast = fast.next;
}
while(fast){
slow = slow.next
fast = fast.next
}
return slow.val
}
141. 环形链表
第一种方法
首先想到哈希表;遍历链表,将节点放在数组中,如果数组中意境存在该节点,表示链表有环,否则无环;
const hasCycle = function(head) {
const res = [];
while (head) {
if (res.includes(head)) {
return true;
}
res.push(head);
head = head.next;
}
return false;
};
第二种方法
快慢指针 首先使用两个分别为fast和slow指针分别指向链表头部;
fast指针每次向链表尾部走两步;
slow指针每次向链表尾部走一步;
结果:
如果fast能走到链表尾部或者fast为null 或者fast.next 为 null;链表一定没有环;
如果链表有环,哪一定有fast === slow的时候;
根据上述思路编辑代码如下:
var hasCycle = function (head) {
if (head === null || head.next === null) return false
let fast = head.next
let slow = head
while (fast !== null && fast.next !== null) {
if (fast === slow) return true
fast = fast.next.next
slow = slow.next
}
return false
}
25. K 个一组翻转链表
核心思路
反转链表;
将当前反转后的链表表头链接到上一个反转链表节点表尾;
将当前反转后的链表表尾链接下一个反转链表的表头;
pre表示链表上一个节点指针
current表示链表当前节点指针
next表示链表下一个节点指针
//K 个一组翻转链表
var reverseKGroup = function (head, k) {
let header = new ListNode(0)
header.next = head
let node = head
// 获取整个链表长度
let len = 0
while (node) {
len++
node = node.next
}
//上一节点
let pre = header
// 当前节点
let current = header.next
// 下一节点
let next = null
// 再次遍历链表,当len小于k时不需要反转链表
while (len >= k) {
//反转0-k之间的链表
for (let i = 0; i < k - 1; i++) {
// 下一节点等于当前节点的下一节点;
next = current.next
// 将开始节点的下一节点指向下一节点的下一节点,意味着删除了next这个节点
current.next = next.next
next.next = pre.next
pre.next = next
}
pre = current
current = pre.next
len -= k
}
return header.next
}
剑指 Offer II 004. 只出现一次的数字
方法1
一个比较容易想到的方法;哈希表,将出现的数字统计带哈希表中,遍历哈希表;
根据上述思路编辑代码可以轻易完成本题;代码如下:
var singleNumber = function(nums) {
const map = {};
nums.forEach(v=>{
map[v] = (map[v]||0)+1
})
return Object.keys(map).filter(v=>map[v] === 1)[0]
};
这题还有个进阶问题,要求不使用额外空间来实现吗;
这就有点难了,必须一次遍历即可知道答案;哈希表肯定是不行了;必须另向别的办法;
可以巧用#### 260. 只出现一次的数字 III的思想;260解题思路详见
将数组中的数据区分出来;
想想,其他数据出现3次,目标数据出现1次;
假设将数组中数据转为为二进制;
假设数据中出现1次的是数据为x;出现3次的数据为y;
将x,和y转换为二进制;
对于二进制任意一位;如果y在位置为1,3个y向加这时的二进制位置是不是就是3,那将这个位置再加上x在这个位置的数值;
| 10进制 | |||
|---|---|---|---|
| 5 | 1 | 0 | 1 |
| 5 | 1 | 0 | 1 |
| 5 | 1 | 0 | 1 |
| 3 | 0 | 1 | 1 |
| 二进制位之和 | 3 | 1 | 4 |
| 二进制位之和余3 | 0 | 1 | 1 |
上述图表就比较清晰了吧; 根据上述思路编辑代码如下:
var singleNumber = function(nums) {
let result = 0;
for(let i = 0 ; i < 32 ; i++){
let temp = 0;
for(let j = 0 ; j < nums.length ; j++){
// 右移i位,并且通过&1得到第i位是0还是1
temp = temp + ((nums[j] >> i) & 1);
}
if(temp %3 != 0){
//这里因为二进制位只可能是1或者0;如果为1,左移i位可以得到10进制数;
//result 累加得到是10进制数
result += (1<< i)
}
}
// 返回答案
return result;
};