记录 3 道算法题
设计链表
设计一个链表,用双向链表实现,有索引,跟数组很像。提供 get,addAtHead,addAtTail, addAtIndex,deleteAtIndex方法。分别用于获取某个index的节点的val,插入到链表的开头,插入到链表的结尾。插入到index节点的前面,删除index节点。
用到了伪节点,这样添加起来很方便。不用做很多 if 判断。
因为要排除伪节点,所以index = 0 是 this.head.next, index = this.length - 1 是 this.tail.prev
function Node(val, next = null, prev = null) {
this.next = next
this.prev = prev
this.val = val
}
function MyLinkedList() {
this.length = 0
this.head = new Node(0)
this.tail = new Node(0)
this.head.next = this.tail
this.tail.prev = this.next
}
-
get方法
因为使用了双向链表,所以可以根据开头和结尾两边找最近的。还要做判断index是否正确。
MyLinkedList.prototype.get = function(index) { // 检查index在不在范围内 if (this.length - 1 < index || index < 0) return -1 let node // 如果是中间偏后的就从后面找,如果是偏前就在前面找 if (index < this.length / 2) { node = this.head.next while(index > 0) { node = node.next index-- } } else { node = this.tail.prev index = this.length - 1 - index while(index > 0) { node = node.prev index-- } } return node.val } -
addAtHead
从头插入分为两个可能,第一是当链表里没有东西的时候,要插在 tail 的前面。链表里面有节点了,就是插入在 this.head.next 前面。
然后可以归纳为插入到 index = 0 的地方。所以可以使用 addAtIndex。但是这样的话就需要在 addAtIndex 里面判断链表的长度是不是0。
MyLinkedList.prototype.addAtHead = function (val) { this.addAtIndex(0, index) } -
addAtHead
和上面同理也可以使用 addAtIndex ,不同的是不需要考虑链表长度是不是为 0。
MyLinkedList.prototype.addAtTail = function(val) { this.addAtIndex(this.length, val) } -
addAtIndex
重头戏来了。每一个插入动作都是先生成一个节点,next指向index的节点,prev指向index的前一个节点,然后再让 index的节点和它前面的节点断开联系。所以只要找得到 index的节点就成功了。
MyLinkedList.prototype.addAtIndex = function(index, val) { // 超过了链表长度就无法插入 if (index > this.length) return let node // 根据题目要求,小于0的都插入到开头。 if (index <= 0) { // 这里就是前面说的 如果没有节点时 插入到开头的判断 if (this.length === 0) { this.head.next = new Node(val, this.head.next, this.head) this.head.next.next.prev = this.head.next this.length++ return } node = this.head.next } else if (index === this.length) { // 在如果是最后一个的索引+1,那就是插入到结尾 node = this.tail } else { // 和get一样的找到那个节点 if (index < this.length / 2) { node = this.head.next while(index > 0) { node = node.next index-- } } else { node = this.tail.prev index = this.length - 1 - index while(index > 0) { node = node.prev index-- } } } // 新节点指向 next 和 prev // 然后重置 next 和 prev 的联系 node.prev = node.prev.next = new Node(val, node, node.prev) // 记得加1 this.length++ }; -
deleteAtIndex
找到 index 的节点然后直接让 prev.next = next就行了。
MyLinkedList.prototype.deleteAtIndex = function(index) { // 前面和 get 一样的 if (this.length - 1 < index || index < 0) return let node if (index < this.length / 2) { node = this.head.next while(index > 0) { node = node.next index-- } } else { node = this.tail.prev index = this.length - 1 - index while(index > 0) { node = node.prev index-- } } // 删除节点 node.prev.next = node.next node.next.prev = node.prev this.length-- }
根据val删除链表的节点
删除节点都是一样的方法,先找到要删除节点的前一个节点。所以需要比较 node.next.val === val
因为是比较 node.next.val 所以需要先手动比较一下第一个节点 node.val
function deleteNode(head, val) {
let node = head
// 先解决第一个节点
if (node.val === val) {
return head.next
}
// 找到前一个节点
while (node?.next) {
if (node.next.val === val) {
break
} else {
node = node.next
}
}
// 如果存在就删掉
if (node.next) {
node.next = node.next.next
}
return head
}
平均分隔链表
725. 分隔链表 - 力扣(LeetCode) (leetcode-cn.com)
要求:
* 平均地将链表分成 k 组,组与组之间相差的个数不能超过 1。
* 前面的组的个数不能比后面的组小
* 分隔的节点放进数组输出
对于分组的做法。就是简单的让要分隔的节点斩断联系。node.next = null
如果分组的个数大于了链表的长度,就将链表切成一个个,然后剩余的位置push null。
怎么说也得先遍历一遍链表,得到节点个数,然后就可以除以 k,而多出来的节点则是和 k 取余。然后把取余的结果分成一次次加到前面的分组。
然后用双循环,外循环处理分组的push, 内循环处理分组的内容。要注意的点是如何在内循环获取足够数量的节点。然后斩断联系,保存下一组的开头。
处理链表永远要处理怎么保存被斩断的节点。
function splitListToParts(head, k) {
let count = 0
let node = head
// 统计节点的个数
while (node) {
node = node.next
count++
}
node = head
const output = []
// 如果分组数量大 那就拆成一个节点一组
if (k >= count) {
for (let i = 0; i < k; i++) {
if (node) {
// 保存被斩断的节点
const temp = node.next
node.next = null
output.push(node)
node = temp
} else {
// 补空位
output.push(null)
}
}
return output
}
// 平均数向下取整
let n = (count / k) | 0
// 平均分后多出来的节点数量
count = count % k
for (let i = 0; i < k; i++) {
// 用 head 标记每一组的开头,最后push进数组
head = node
// 要 n - 1,即每一组个数 - 1,
// 因为我们需要让他停在每一组最后一个节点
for (let j = 0; j < n - 1; j++) {
node = node.next
}
// 如果还有多余的次数就分配一个,给当前的组
if (count) {
node = node.next
count--
}
output.push(head)
// 先保存下一组的开头
head = node.next
// 斩断联系
node.next = null
}
return output
}