我正在阅读这本书Cracking the coding interview by Gayle Mcdowell
,并找到了第2节链接列表中某个问题的替代解决方案。特别是问题#2.1
删除重复:编写代码以从未排序的链接列表中删除重复项。如果不允许临时缓冲区,您将如何解决此问题。
Example input = [1,2,2,6,4,4,2]
Example output = [1,2,6,4]
作者在书中给出的答案如下: 基本上,它保持"指针",一个通过链表进行交互,另一个检查所有后续节点的重复:
public void deleteDups(Node head) {
Node current = head;
while (current != null) {
Node runner = current;
while (runner.next != null) {
if (runner.next.data == current.data) {
runner.next = runner.next.next;
} else {
runner = runner.next;
}
}
current = current.next;
}
}
我使用了递归,我的代码看起来有点不同,虽然(我相信)做同样的事情:
public void removeRepetites(Node head) {
if (head == null) return;
int dataToRemove = head.data;
while (head.next != null) {
if (head.next.data == dataToRemove) {
head.next = head.next.next;
} else {
removeRepetites(head.next);
head = head.next;
}
}
}
你能否发现我解决问题的方法有什么不利之处?可能是更高的O空间/时间复杂度?
谢谢!
答案 0 :(得分:1)
您的代码如何应对1,000,000个唯一项目的列表?
假设没有编译器或运行时优化,您的代码将堆栈溢出。
这就是尾递归更好地优化为循环的原因。如果你这样做,我想,它看起来就像书中的答案。
答案 1 :(得分:1)
就复杂性而言,在我看来,两个实现都会产生O(n ^ 2)复杂度,因为迭代答案涉及嵌套循环,而你的涉及一个函数在一个内部递归调用n次循环执行n次。
我发现与你的唯一问题是,由于它是递归的,它会因为对函数的递归调用而更快地填充堆栈空间。每次调用该函数时,它都会创建一个指向堆栈中函数的指针以及dataToRemove变量。
答案 2 :(得分:0)
如果您的列表未排序,则两个解决方案都不起作用,它们仅比较相邻值。我能想到的唯一解决方案是非常低效的性能O(N ^ 2)。遍历列表并删除每个节点,然后检查列表是否包含等于已删除注释的节点并将其删除,然后重新插入已删除的节点。