输入一个链表,从尾到头打印链表每个节点的值。
可以从头到尾遍历一遍链表,将节点放入栈中,然后依次取出打印(后入先出)。
优化就是借助“递归”,先向下查找再打印输出,也可实现这种“后入先出”。可以类比二叉树的后序遍历。
用 JS 实现了简单实现了链表这种数据结构,这不是重点。
重点在printFromTailToHead
函数。
class Node {
/**
* 节点构造函数
* @param {*} value
* @param {Node} next
*/
constructor(value, next) {
this.value = value;
this.next = next;
}
}
class List {
constructor() {
this.head = new Node(null, null);
}
/**
* 从0开始计算,找到包括head在内的位于index的节点
* @param {Number} index
*/
find(index) {
let current = this.head;
for (let i = 0; i < index; ++i) {
current = current.next;
}
return current;
}
/**
* 向index位置插入元素
* @param {*} value
* @param {Number} index
*/
insert(value, index) {
const prev = this.find(index);
const next = new Node(value, prev.next);
prev.next = next;
}
}
/**
* 逆序打印链表
* @param {Node} node
*/
function printFromTailToHead(node) {
if (node.next) {
printFromTailToHead(node.next);
}
node.value && console.log(node.value);
}
/**
* 以下是测试代码
*/
let list = new List();
list.insert("a", 0);
list.insert("b", 1);
list.insert("c", 2);
printFromTailToHead(list.head);
给定单向链表的头指针和一个结点指针,定义一个函数在 $O(1)$ 时间删除该结点。
正常的做法肯定是在 $O(N)$ 时间内删除节点。而这么过分的要求,显然是通过“重新赋值”才能做到。
比如要删除节点 a,那么就将 a.next 的 value 和 next 赋值给节点 a,然后删除 a.next。
表面“看起来”像是删除了节点 a,其实是将其后节点的信息转移到了它自己身上。
除此之外,对于最后一个节点,还是要退化成 $O(N)$ 的复杂度。而整体分析一下复杂度:
$$ O(T) = (O(N) + O(1) * (n - 1)) / n = O(1) $$