这是我参与更文挑战的第 9 天,活动详情查看 更文挑战
这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
114. 回文链表 (palindrome-linked-list)
标签
- 链表
- 简单
题目
这里不贴题了,leetcode打开就行,题目大意:
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
基本思路
- 复制链表值到数组列表中。
- 判断是否为回文。
写法实现
var isPalindrome = function(head) {
// 准备一个数组,存链表值
let compareArr = []
// 遍历把链表值进数组
while (head !== null) {
compareArr.push(head.val)
head = head.next
}
// 判断数组回文即可
return compareArr.join('') === compareArr.reverse().join('')
}
效率高些可用双指针
var isPalindrome = function(head) {
const vals = [];
while (head !== null) {
vals.push(head.val);
head = head.next;
}
// 双指针判断回文
for (let i = 0, j = vals.length - 1; i < j; ++i, --j) {
if (vals[i] !== vals[j]) {
return false;
}
}
return true;
};
115. 排序链表 (sort-list)
标签
- 链表
- 中等
题目
这里不贴题了,leetcode打开就行,题目大意:
给你链表的头结点 head ,请将其按 升序
排列并返回排序后的链表 。
进阶:
你可以在 O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
基本思路
如果对归并排序不了解,请看我这篇 [算法拆解] 归并排序
本题正需要使用这个算法,还有我们上篇提到的分治思想
根据题目的时间复杂度要求,暴力做法肯定达不到效果,我们可以采用把这个复杂问题分解成
-
先拆分大链表
-
分的过程,直到二分到不能再二分,即递归压栈压到链只有一个结点(有序),于是在递归出栈时进行合并。
-
二分方法具体做法是找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用
快慢指针
的做法,快指针每次移动 2 步,慢指针每次移动 1 步,(跟环形链表思想类似)当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。
-
-
再合并两个有序链表
- 合的过程,合并后的结果返回给父调用,一层层向上,最后得出大问题的答案。
- 合并两个有序链表 我们之前有做过这题,所以其实,很多困难题都是由简单题合并而成的,你要做的就是拆解。而这项能力需要有长足的积累。
写法实现
我们可以先给个伪代码的思路
var sortList = function(head) {
// 二分 (递归)
l = sortList(左链) // 已排序的左链
r = sortList(右链) // 已排序的右链
merged = mergeList(l, r) // 进行合并
// 返回合并的结果给父调用
return merged
};
接下来补全逻辑,不要怕看上去代码多,其实思路很清晰
// 合并两个有序链表
var mergeTwoLists = function(l1, l2) {
// 终止条件:当两个链表都为空时,表示我们对链表已合并完成。
if (l1 === null) {
return l2
} else if (l2 === null) {
return l1
} else if (l1.val < l2.val) {
// 较小结点的 next 指针指向其余结点的合并结果。
l1.next = mergeTwoLists(l1.next, l2)
return l1
} else {
l2.next = mergeTwoLists(l1, l2.next)
return l2
}
};
var sortList = function(head) {
// 特判空
if (!head || !head.next) {
return head
}
// 快慢指针来分割链表
let [fast, slow, preSlow] = [head, head, null]
while (fast && fast.next) {
// 记录下可能断开点,就是当前 slow 位置
preSlow = slow
// 快指针2步,慢指针1步
fast = fast.next.next
slow = slow.next
}
// 要把第一个链表的尾部置空,断开
preSlow.next = null
// 当快指针走到头了,当前慢指针就是分隔点
// 递归操作继续分左右子链
let l = sortList(head)
let r = sortList(slow)
return mergeTwoLists(l, r)
};
另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧