148. 排序链表
解: 看到这个题,让我想到了曾经做过的链表partition,那么这里也用快排的思路(看官方题解其实应该用归并最好)。
- partition的目的是根据target值的大小,把整个链表划分成三个区域,分别是小于target的区域,等于target的区域,大于target的区域。
- 链表partiton的过程就是初始化6个变量,分别代表三个链表区域的头尾节点。然后去遍历整个链表,将每个节点分别挂载到对应的区域节点上,最终将划分好的三个区域分离,然后返回三个独立的链表。由于快排需要随机target,所以还需要返回小于区和大于区的最大值和最小值
- partition函数完成后,只需要选定随机数然后让partiton去分区就好了。由于这是个链表,不能用随机下标去取值,所以需要知道这段链表的最大最小值,从这里面选一个随机数。
- 调用partiton后,会获得三个链表,让小于区和大于区的链表去递归执行选随机数分区的操作,执行完毕后将小于区的尾结点链接到等于区链表,等于区尾结点链接到大于区链表,然后返回头结点
- 以上所说三个链表都是指小于区、等于区、大于区都有值时的情况。所以链接链表的时候需要多注意判断边界条件。
function sortList(head) {
let min = Infinity
let max = -Infinity
let cur = head
while (cur) {
min = Math.min(cur.val, min)
max = Math.max(cur.val, max)
cur = cur.next
}
return getRes(head, max, min)
}
function getRes (node, maxVal, minVal) {
if (!node || !node.next) return node
// 取max-min之间的随机数
const target = ~~(Math.random() * (maxVal - minVal)) + minVal
// 快排中这里需要返回两个坐标,这里返回分区后的三段链表,以及小于区链表的值区间,大于区链表的值区间(因为取target随机数的时候需要用到)
const { smStart, eqStart, eqEnd, lgStart, smMin, smMax, lgMin, lgMax } = partitionList(node, target)
// 处理小于区链表
const smList = smStart ? getRes(smStart, smMax, smMin) : null
// 处理大于区链表
const lgList = lgStart ? getRes(lgStart, lgMax, lgMin) : null
// 处理完毕后将三个链表互相链接, 需要小于区链表的尾结点去链接后面链表
let smTail = smList
while (smTail && smTail.next) smTail = smTail.next
smTail && (smTail.next = eqStart ?? lgList)
eqEnd && (eqEnd.next = lgList)
// 返回链表头结点
return smList ?? eqStart ?? lgList
}
function partitionList(head, target) {
// 链表根据target来划分出三个区域,以下六个遍历就是这三个区域的头尾节点
let smStart,smEnd,eqStart,eqEnd,lgStart,lgEnd = null
// 快排需要选取随机数,所以需要知道链表值的区间
let smMin = Infinity
let smMax = -Infinity
let lgMin = Infinity
let lgMax = -Infinity
let curNode = head
// 遍历链表进行区域划分
while (curNode) {
if (curNode.val < target) {
smMin = Math.min(curNode.val, smMin)
smMax = Math.max(curNode.val, smMax)
smStart = smStart ?? curNode
smEnd && smEnd.next = curNode
smEnd = curNode
}
if (curNode.val === target) {
eqStart = eqStart ?? curNode
eqEnd && eqEnd.next = curNode
eqEnd = curNode
}
if (curNode.val > target) {
lgMin = Math.min(curNode.val, lgMin)
lgMax = Math.max(curNode.val, lgMax)
lgStart = lgStart ?? curNode
lgEnd && lgEnd.next = curNode
lgEnd = curNode
}
curNode = curNode.next
}
// 将划分出来的三个链表分离并返回
lgEnd && (lgEnd.next = null)
smEnd && (smEnd.next = null)
eqEnd && (eqEnd.next = null)
return { smStart, eqStart, eqEnd, lgStart, smMin, smMax, lgMin, lgMax }
}