【问题标题】:Finding palindromes in a linked list在链表中查找回文
【发布时间】:2011-08-13 07:38:06
【问题描述】:

这是一个面试问题(再次)。

给定一个单连通链表,找出最大的回文数 在列表中。 (你可以假设回文的长度是偶数)

我采用的第一种方法是使用堆栈——我们从一开始就遍历列表并不断推入字母。每当我们发现堆栈顶部的字母与链表上的下一个字母相同时,开始弹出(并增加链表指针)并设置匹配的字母数。在我们发现不匹配后,将您从堆栈中弹出的所有字母推回,并继续您的推入和弹出操作。这种方法的最坏情况复杂度将是 O(n2),例如当链表只是一个相同字母的字符串时。

为了提高空间和时间复杂度(通过一些常数因素),我建议将链表复制到一个数组中,并在数组中找到最大的回文数,这又需要 O(n2) 时间复杂度和 O(n)空间复杂度。

有什么更好的方法可以帮助我吗? :(

【问题讨论】:

  • 如果重复请告诉我..
  • 你可以看看stackoverflow.com/questions/7043778/…或者stackoverflow.com/questions/1115001/…但是我不会说它是重复的,因为这两个没有谈论链表
  • “(你可以假设回文的长度是偶数)”你从来不用这个?为什么你认为这很重要?
  • “推回你从堆栈中弹出的所有字母” - 这不是那么简单,而且你的堆栈需要 O(n) 空间。
  • @Ricky:即使大小的回文意味着下一个要匹配的字符必须与堆栈顶部的字符相同。如果它是一个奇数大小的回文,将会有更多的组合需要检查(例如,假设堆栈顶部的元素是奇数回文的中间元素,所以弹出最顶部的元素,然后开始检查堆栈和字符串的剩余部分。

标签: algorithm linked-list palindrome


【解决方案1】:

可以想出一个空间复杂度为 O(1) 的 O(n²) 算法,如下所示:

考虑f→o→b→a→r→r→a→b

在访问时反向浏览列表。以x=fy=f.next 开头:

  • 设置x.next = null
  • f o→b→a→r→r→a→b
    ^ ^
    | \
    xy
    并检查两个列表(x 和 y)有多少链接是相等的。
  • 现在继续。 (tmp=y.next, y.next=x, x=y, y=tmp) 例如。在第二步中,它将产生f←o b→a→r→r→a→b,以及x=oy=b,现在您再次检查它是否是回文并继续:
  • f←o←b a→r→r→a→b
  • f←o←b←a r→r→a→b
  • f←o←b←a←r r→a→b 耶 :)

如果需要再次恢复列表,在O(n)内再次反转

【讨论】:

    【解决方案2】:

    这是一个经过充分分析的问题,具有 O(N) 时间复杂度。

    • 您可以反转原始字符串(比如说strstr_reversed

    那么问题转化为:在strstr_reversed中找到longest common substring

    • An O(N) approach 正在构建具有恒定时间最低共同祖先检索的后缀树 (O(N))。

    【讨论】:

      【解决方案3】:

      如果将列表复制到数组中,以下内容可能有用:由于我们只考虑偶数长度回文,我假设这种情况。但是该技术可以很容易地扩展到奇数长度回文的工作。

      我们存储的不是回文的实际长度,而是长度的一半,因此我们知道我们可以向左/向右移动多少个字符。

      考虑一下这个词:aabbabbabab。我们正在寻找最长的回文。

      a a b b a b b a b a b (spaces for readability)
      °^°                   start at this position and look to the left/right as long as possible,
       1                    we find a palindrome of length 2 (but we store "1")
                            we now have a mismatch so we move the pointer one step further
      a a b b a b b a b a b
         ^                  we see that there's no palindrome at this position, 
       1 0                  so we store "0", and move the pointer
      a a b b a b b a b a b
        ° °^° °             we have a palindrome of length 4, 
       1 0 2                so we store "2"
                            naively, we would move the pointer one step to the right,
                            but we know that the two letters before pointer were *no*
                            palindrome. This means, the two letters after pointer are
                            *no* palindrome as well. Thus, we can skip this position
      a a b b a b b a b a b
               ^            we skipped a position, since we know that there is no palindrome
       1 0 2 0 0            we find no palindrome at this position, so we set "0" and move on
      a a b b a b b a b a b
            ° ° °^° ° °     finding a palindrome of length 6,
       1 0 2 0 0 3 0 0      we store "3" and "mirror" the palindrome-length-table
      a a b b a b b a b a b
                       ^    due to the fact that the previous two positions hold "0", 
       1 0 2 0 0 3 0 0 0    we can skip 2 pointer-positions and update the table
      a a b b a b b a b a b
                         ^  now, we are done
       1 0 2 0 0 3 0 0 0 0
      

      这意味着:一旦我们找到回文位置,我们就可以推断出表格的某些部分。

      另一个例子:aaaaaab

      a a a a a a b
      °^°
       1
      
      a a a a a a b
      ° °^° °
       1 2 1        we can fill in the new "1" since we found a palindrome, thus mirroring the
                    palindrome-length-table
      a a A A a a b (capitals are just for emphasis)
           ^        at this point, we already know that there *must* be a palindrome of length
       1 2 1        at least 1, so we don't compare the two marked A's!, but start at the two 
                    lower-case a's
      

      我的观点是:一旦我们找到回文,我们就可以镜像(至少一部分)回文长度表,从而推断出有关新字符的信息。 这样,我们就可以保存比较。

      【讨论】:

        【解决方案4】:

        这是一个 O(n^2) 算法:

        1. 将列表转换为双向链表

        2. 要获得均匀长度的回文,您需要两个相同的字母相邻。 所以迭代每对相邻的字母(其中 n-1 个),并且在每次迭代中,如果字母相同,则找到中间字母是这两个字母的最大回文。

        【讨论】:

          【解决方案5】:

          我通过在 O(n) 时间内使用递归来做到这一点。 我正在这样做,

          1. 假设我们有一个源链表,现在复制整个链表 列表到其他链表,即目标链表;
          2. 现在反转目标链表;
          3. 现在检查源链表和目标链表中的数据是否相等,如果相等则为回文, 否则它们不是回文。
          4. 现在释放目标链表。

          代码:

          #include<stdio.h>
          #include<malloc.h>
          
          struct node {
              int data;
              struct node *link;
          };
          
          int append_source(struct node **source,int num) {
              struct node *temp,*r;
              temp = *source;
              if(temp == NULL) {
                  temp = (struct node *) malloc(sizeof(struct node));
                  temp->data = num;
                  temp->link = NULL;
                  *source = temp;
                  return 0;
              }
              while(temp->link != NULL) 
                  temp = temp->link;
              r = (struct node *) malloc(sizeof(struct node));
              r->data = num;
              temp->link = r;
              r->link = NULL;
              return 0;
          }
          
          int display(struct node *source) {
              struct node *temp = source;
              while(temp != NULL) {
                  printf("list data = %d\n",temp->data);
                  temp = temp->link;
              }
              return 0;
          }
          
          int copy_list(struct node **source, struct node **target) {
              struct node *sou = *source,*temp = *target,*r;
              while(sou != NULL) {
                  if(temp == NULL) {
                      temp = (struct node *) malloc(sizeof(struct node));
                      temp->data = sou->data;
                      temp->link = NULL;
                      *target = temp;
                  }
                  else {
                      while(temp->link != NULL) 
                          temp = temp->link;
                      r = (struct node *) malloc(sizeof(struct node));
                      r->data = sou->data;
                      temp->link = r;
                      r->link = NULL;
                  }
                  sou = sou->link;
              }
              return 0;
          }
          
          int reverse_list(struct node **target) {
              struct node *head = *target,*next,*cursor = NULL;
              while(head != NULL) {
                  next = head->link;
                  head->link = cursor;
                  cursor = head;
                  head = next;
              }
              (*target) = cursor;
              return 0;
          }
          
          int check_pal(struct node **source, struct node **target) {
              struct node *sou = *source,*tar = *target;
              while( (sou) && (tar) ) {
                  if(sou->data != tar->data) {
                      printf("given linked list not a palindrome\n");
                      return 0;
                  }
                  sou = sou->link;
                  tar = tar->link;
              }
              printf("given string is a palindrome\n");
              return 0;
          }
          
          int remove_list(struct node *target) {
              struct node *temp;
              while(target != NULL) {
                  temp = target;
                  target = target->link;
                  free(temp);
              }
              return 0;
          }
          
          int main()
          {
              struct node *source = NULL, *target = NULL;
              append_source(&source,1);
              append_source(&source,2);
              append_source(&source,1);
              display(source);
              copy_list(&source, &target);
              display(target);
              reverse_list(&target);
              printf("list reversed\n");
              display(target);
              check_pal(&source,&target); 
              remove_list(target);
              return 0;
          }
          

          【讨论】:

            【解决方案6】:

            首先找到链表的中点,为此遍历链表并统计节点数。

            假设节点数为 N,中点为 N/2。

            现在遍历到中点节点并开始反转链表直到结束,这可以用 O(n) 复杂度完成。

            然后比较从开始到中点的元素和从中点到最后的元素,如果它们都相等,则字符串是回文,否则中断。

            时间复杂度:- O(n)

            空间复杂度:- O(1)

            【讨论】:

            • 测试整个列表是否是回文,这不是问题。
            • 是的,你是对的。很抱歉没有正确阅读问题。
            猜你喜欢
            • 1970-01-01
            • 2012-10-22
            • 1970-01-01
            • 1970-01-01
            • 2016-11-04
            • 1970-01-01
            • 2021-10-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多