单链表面试问题

时间:2010-11-17 07:35:32

标签: linked-list

说我们有一个字符串:MALAYALAM,每个char都是每个节点的数据部分,所以我们将有一个大小为9的列表。我们如何知道该列表是否是回文。

约束:

  • 我们不知道列表长度。
  • 不要使用临时内存(数组或堆栈或其他列表)来获取整个列表或列表的一半。使用少量临时字符是可以接受的。
  • 列表修改是可以的,只要我们在操作结束时有原始列表。

考虑到与所有人讨论,我想到的解决方案很少。它是单链表。

谢谢& RGDS, 〜卡尔文

9 个答案:

答案 0 :(得分:3)

我的解决方案:选择快速和慢速的ptrs,反转列表的一半并与另一半进行比较。并反转反转的一半,使列表看起来像原始。我正在寻找更好的解决方案。

编辑:因为我找不到更好的溶胶。

答案 1 :(得分:2)

以下内容应在O(n)

中进行
  1. 递归到列表的末尾,保留头部指针。
  2. 从递归循环中退出时,将当前值与头部进行比较,然后将头部移动到head.next,直到字符用完或发现不匹配为止。
  3. 您可以通过保留列表长度计数来改进此解决方案,并在达到列表的一半后停止比较。

    如果字符串中的字符数大于允许的最大堆栈深度,则无效。在这种情况下,更改列表将按如下方式工作......

    找出列表的长度。

    • 继续反转链接列表中的链接,直至到达中间位置.... 一旦你到达中间,
    • 你将有一半的名单指向开头,其余的将指向最后。
    • 运行while循环直到结束并比较相应的字符并再次反转链接...

答案 2 :(得分:2)

你可以使用随机化在O(n)时间和O(1)空间中进行。

步骤1:计算字符串的哈希值,例如整个字符串的指纹。

第2步:撤消链接列表。

步骤3:计算反向字符串的哈希值。

步骤4:将链接列表反转为原始订单。

反转链表可以在O(n)时间和O(1)空间中完成,如下所示:

rev(head) {
  prev = nil;
  curr = head;
  next = head.next;
  while (curr != nil) {
    curr.next = prev;
    prev = curr;
    curr = next;
    next = curr.next;
  }
}

答案 3 :(得分:1)

查看列表一次以查明其长度

然后你可以检查字符i ==字符长度 - i是否为i = 0到length / 2

在O(n ^ 2)时间运行并使用O(1)存储

答案 4 :(得分:0)

也许你可以做一个分而治之的解决方案:

  1. 查看列表一次以确定其长度 - O(n)
  2. 再次浏览列表,将光标放在半边 - 你现在有了游标A& B - O(n)
  3. 比较A是否与B的反向:

    • 如果剩余的长度大于 一些常数,将A分成游标 通过遍历A1和A2,做同样的事情 对于B1和B2 - O(n);比较A1和 B2,A2和B1的方式相同

    • 如果剩余的长度小于 一些不变的,只是蛮力 比较它们 - 将B复制到一个数组中 比较它,然后向后看 A - O(1)

  4. 请注意,第3步应重复O(logn)次,因此,解决方案的复杂性为O(nlogn)

    更详细的复杂性分析:

    f(const) = const
    
    f(n) = f(n/2) + f(n/2) + C*n
    

    使用替换(n = 2^mf(2^m) = g(m))来解决此等式。这种递归的解决方案应该产生与n * logn相同的复杂性类。

    由于递归调用,空间复杂度为O(logn),但这似乎并未违反任何约束。但是可以修改解决方案,使其不使用递归调用,而是使用循环 - 您应该只存储递归深度和位置在该深度(假设您绘制了递归树 - 您只需要2个整数来存储位置在那棵树上知道你的下一步是什么。)

答案 5 :(得分:0)

这是一个强力算法,希望你明白这个想法。

<pseudocode>

begin = list.begin();
end = list.end();

while (begin < end) {
   previous = iterator_that_points_to(list, end);
   if (*begin != *previous)
      return false;

   begin++;
   end = previous;
}
return true;

</pseudocode>

虽然iterator_that_points_to的代码(因为命名不好而忍受我)是:

Node* iterator_that_points_to(CharList const& theList, Node* to_find)
{
   Node* iterator = theList.rootNode;
   while (iterator.next != 0) {
      if (iterator.next == to_find)
         return iterator;

      iterator = iterator->next; 
   }
   return 0; // should never happen
}

答案 6 :(得分:0)

根据他们告诉你的要求,这是他们想要的解决方案。

1)找到列表的中点。您可以通过使用两个指针并递增两个节点和第二个节点来完成此操作。当第一个指针到达结尾时,您的第二个指针将位于列表的中间节点。

2)从中间到结尾反转链表。您可以在线性时间内完成此操作。

3)现在比较两个列表。

4)通过再次反转恢复以前反转的列表。

最好的想法是编写一个函数来在线性时间内反转列表,并将其用作ispalindrome函数的帮助器。这样可以清理代码并使其更易于在白板上进行管理。

答案 7 :(得分:0)

这个看起来很酷。

假设你有一个链接列表Node1-&gt; Node2-&gt; Node3-&gt; ---&gt; Noden。

让sum1 = sum2 = 0

您只需遍历列表并计算

即可
           sum1 = (sum1+Nodei->data)*2.

           sum2 += Nodei->data*2^i.

并比较它们是否相等

如果相等

   palindrome 

其他

   not a palindrome. 

时间复杂度:O(n)

空间复杂度:O(1)

答案 8 :(得分:0)

palindrome = true; //assume initially that it is a palindrome
count = 1;
q = p; // use a temporary pointer for traversing to end
while (q->next) { q = q->next; count ++; } //count the number of elements in the list
do{
   if (p->data != q->data) {palindrome = false; break;}//compare first and last elements of the list under consideration
   p = p->next; count -  = 2; //consider a new linked list with the two end-points cut off
   q = p; for (j = 0; j < count; j++) q = q=>next; 
} while (count > 0);