在链表中查找回文

时间:2011-08-13 07:38:06

标签: algorithm linked-list palindrome

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

  

鉴于一个单独连接的链表,找到最大的回文   在列表中。 (你可以认为回文的长度是均匀的)

我做的第一个方法是使用堆栈 - 我们从头开始遍历列表并继续推送字母。每当我们发现堆栈顶部的字母与链表上的下一个字母相同时,开始弹出(并递增链表指针)并设置匹配字母数的计数。找到不匹配后,推回从堆栈中弹出的所有字母,然后继续推送和弹出操作。这种方法的最坏情况复杂性是O(n2),例如当链表只是一个相同字母的字符串时。

为了改善空间和时间复杂度(通过一些常数因素),我建议将链表复制到一个数组并找到数组中最大尺寸的回文,再次需要O(n2)时间复杂度和O(n)空间复杂性。

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

6 个答案:

答案 0 :(得分:5)

可以提出O(n)空间复杂度为O(1)的算法如下:

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

在访问期间浏览列表以反转链接。从x=fy=f.next开始:

  • 设置x.next = null
  • f o→b→a→r→r→a→b
    ^ ^
    |  \
    x   y
    并检查列表(x和y)相等的链接数量。
  • 现在继续。 (tmp=y.nexty.next=xx=yy=tmp) 例如。在第二步中,它会产生f←o b→a→r→r→a→bx=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 yay:)

如果您需要再次恢复列表,请在O(n)

中再次将其反转

答案 1 :(得分:4)

这是一个分析良好的问题, O(N)时间复杂度。

  • 您可以撤消原始字符串(例如strstr_reversed

然后问题转化为:在strstr_reversed中找到longest common substring

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

答案 2 :(得分:1)

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

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

考虑一下: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

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

答案 3 :(得分:0)

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

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

  2. 要有一个均匀长度的回文,你需要有两个相同的字母彼此相邻。 因此迭代每对相邻的字母(n-1)并在每次迭代时,如果字母相同,找到最大的回文,其中间字母是这两个。

答案 4 :(得分:0)

我是在O(n)时间内使用递归来完成的。 我是这样做的,

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

    #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;
    }
    

答案 5 :(得分:-2)

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

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

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

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

时间复杂度: - O(n)

空间复杂性: - O(1)