检查链接列表是否是回文

时间:2010-11-09 23:47:21

标签: c++ algorithm data-structures linked-list

考虑一个链接列表,其节点是字符,因此列表代表一个字符串。你如何写一个递归例程来检查字符串是否是回文序列 所述函数在处理字符串中间的字符时开始展开堆栈?

例如,假设我的字符串是“女士”。我的递归函数看起来像:

bool isPalin(const node *startnode, const node *currentnode, const node *midpoint, ...);

currentnode->data == 'd'时,堆栈必须展开。

我被问到这个问题的面试;目前我无法想到这个问题有什么用处,除非是一个非常难的谜题。

第一个想法:非常明显(如果不优雅)的方式是:

  1. 首先计算列表的中点。
  2. 如果 currentnode 在“midpoint 之前”,请将前者手动推入堆栈。这可以通过维持一个计数器来决定。
  3. 否则,在递归的每一步中展开手动维护的堆栈,并与当前字符进行比较。
  4. 有更好的想法或新的见解吗?

10 个答案:

答案 0 :(得分:9)

“链表”是指std::list

template <typename BiDiIterator>
bool isPalindrome(BiDiIterator first, BiDiIterator last) {
    if (first == last) return true;
    --last;
    if (first == last) return true;
    if (*first != *last) return false;
    return isPalindrome(++first, last); // tail recursion FTW
}

isPalindrome(mylist.begin(), mylist.end());

我已经使用了这样一个事实:它可以从头开始迭代,也可以从头开始。目前尚不清楚这是否由问题给出。

使用单链表可以运行两个迭代器,一个快速,一个慢。在每次通话时,将快速两次递增,将慢一次递增一次。当快速的一个到达列表的末尾时,慢速的一个到达中点(嗯,+ / - 1并考虑奇数长度和偶数长度列表)。此时,退出比较字符值的递归。 Θ(n)运行时和内存使用的复杂性(不是尾递归)。

我会写代码,但现在是时候在英国睡觉了。

[编辑:早上全部

template <typename FwdIterator>
std::pair<FwdIterator, bool> isPalindrome(FwdIterator slow, FwdIterator fast, FwdIterator last) {
    if (fast == last) return std::make_pair(slow, true);
    ++fast;
    if (fast == last) return std::make_pair(++slow, true);
    ++fast;
    FwdIterator next = slow;
    std::pair<FwdIterator, bool> result = isPalindrome(++next, fast, last);
    if (result.second == false) return result;
    if (*slow != *(result.first)) return std::make_pair(slow, false);
    ++(result.first);
    return result;
}

...

isPalindrome(mylist.begin(), mylist.begin(), mylist.end()).second;

如果由于某些奇怪的原因,您的链表没有提供迭代器,那么希望使用if (fast->next == 0)fast = fast->next等的等效代码是显而易见的。当然,您可以使用包装器整理用户界面。

我认为如果您允许临时修改列表,可以避免额外存储,方法是在下降时将列表反转为“慢”,然后在提升时再次将其反转。这样,您不需要在递归调用中存储slow的副本:相反,您可以返回一个额外的指针供调用者遵循。不过,我不打算打扰。

答案 1 :(得分:4)

Modulo棘手的细节这个很容易。

首先,通过调用递归移动一个指针只需一步但其他两步来找到中点。当两步指针到达结束时,一步指针位于中间。棘手的事情:即使是奇怪的长度列表。

然后备份(从递归调用返回),并在每次返回时向后移动midpointer前进一步。只需将该节点的内容与下降期间可用作例程参数的内容进行比较。

干杯&amp;第h。,

答案 2 :(得分:3)

如果您确实喜欢使用堆栈,这是使用非确定性下推自动机的计算理论中的常见练习。我的想法是将每个字符串推入堆栈和每个字符串,分支关闭,一个分支跳过一个字符(如果它是一个奇怪的回文)并从堆栈中弹出每个字符串,同时将其与列表的其余部分中的字符串进行比较,另一个分支执行相同操作而不跳过初始char(如果它是一个偶数回文),第三个继续向堆栈添加元素(并使用下一个char递归地再次开始分支)。这三个分支可以通过递归地将堆栈的当前状态传递给每个分支来表示。

在伪代码中:

function isPalin(* start, * end, stack){
  if checkPalin(start, end, stack):
    return true;

  stack.push(*start);
  if checkPalin(start, end, stack):
    return true;

  if (start == end)
    return false;

  return isPalin(start.next, end, stack);
}

function checkPalin(* start, * end, stack){
  while (stack is not empty && start != end){
    start = start.next;
    if (*start != stack.pop())
      return false;
  }

  return (stack is empty && start == end);
}

答案 3 :(得分:1)

列表是否双重关联?然后是传入开始和结束节点的问题,比较它们指向的内容。如果它们不同,则返回false。如果它们是相同的,请使用start + 1和end-1递归调用自己,直到start&gt;端。

答案 4 :(得分:1)

这就是我想的问题

bool isPalindrom(node* head)
{
   if(!head) return true;

   node* left = head;
   node* mid = head;

   return cmp(left, mid, head);
}

bool cmp(node*& left, node*& mid, node* n)
{
   node* next = n->next;   

   if(next == 0)
   {
      node* lprev = left;
      left = left->next;
      return lprev->data == n->data;      
   }

   mid = mid->next;
   if(next->next == 0)
   {
      node* lprev = left;
      left = left->next->next;
      return lprev->data == next->data && lprev->next->data == n->data;
   }

   if(!cmp(left, mid, next->next)) return false;

   if(left == mid) return true;

   if(left->data != next->data) return false;

   left = left->next;

   if(left == mid) return true;

   if(left->data != n->data) return false;

   left = left->next;

   return true;
}

答案 5 :(得分:1)

在Java中,此解决方案将比较已读取的字符串与递归的字符串。它不是最好的解决方案,即使它是O(n)它的S(n ^ 2),它应该(至少)使用StringBuffer来减少所有连接。

它使用包装类将字符串的右侧与布尔值一起传回。

的优点:

  1. 从头到尾只有一个传递到列表中。
  2. 它不需要事先知道列表长度
  3. 不需要额外的数据结构
  4. 缺点:

    1. 使用内存负载S(n ^ 2)
    2. 以低效的方式连接字符串
    3. 递归解决方案,慢。
    4. 代码:

      boolean palindrome(Node n){
          RightSide v = palindromeRec(“”, n);
          return v.palindrome;
      }
      
      class RightSide{
          boolean palindrome;
          String right;
      }
      
      private RightSide palindromeRec(String read, Node n){
          RightSide v = new RightSide();
      
          if(n == null){
              v.palindrome = false;
              v.right = “”;
              return v;
          }
      
          v = palindromeRec(n.value + read, n.next);
      
          if(v.palindrome) 
              return v;
          else if(read.equals(v.right) || (n.value+read).equals(v.right)){
              v.palindrome = true;
              return v;
          }
      
          v.right = n.value + v.right;
          v.palindrome = false;
      
          return v;
      }
      

答案 6 :(得分:1)

  1. 查找总字符串的长度
  2. 获取具有中间(中间)位置的节点
  3. 在该节点上打破列表
  4. 逆转上半场
  5. 现在进行字符串比较

    包括&#34; stdafx.h&#34;

    包括&#34; LinkedList.h&#34;

  6. 链表::链表()     {         head = nullptr;         count = 0;     }

    void LinkedList :: AddItem(char * data) {     Node node = new Node;     node-&gt; Data =(void )malloc(strlen(data)+ 1);

    strcpy((char*)node->Data, data);
    node->Data = data;
    node->Next = nullptr;
    count++;
    
    if(head == nullptr)
    {
        head = node;        
        head->Next = nullptr;
        return;
    }
    
    Node *temp = head;
    
    while(temp->Next!=nullptr)
    {
        temp = temp->Next;
    }
    
    temp->Next = node;
    

    }

    void LinkedList :: TraverseList() {     节点* temp = head;

    while(temp !=nullptr)
    {
        printf("%s \n", temp->Data);
        temp = temp->Next;
    }
    

    }

    Node * LinkedList :: Reverse() {     if(!head ||!(head-&gt; Next))     {         回头;     }

    Node* temp = head;
    
    Node* tempN = head->Next;
    
    Node* prev = nullptr;
    
    while(tempN)
    {
        temp->Next = prev;
    
        prev= temp;
        temp = tempN;
    
        tempN = temp->Next;
    }
    
    temp->Next = prev;
    head = temp;
    return temp;
    

    }

    bool LinkedList :: IsPalindrome() {     int len = 0;     节点* temp = head;

    while(temp)
    {
        len = len + strlen((char*)temp->Data);
        temp = temp->Next;
    }
    
    printf("total string length is %d \n", len);
    
    int i =0;
    int mid1 = 0;
    
    temp = head;
    
    while (i < len/2)
    {
        int templen = strlen((char*)temp->Data);        
    
        if(i + strlen((char*)temp->Data) < (len /2))
        {
            i = i + strlen((char*)temp->Data);
            temp = temp->Next;
        }
        else
        {
            while(i < len/2)
            {
                mid1++;
                i++;
            }
    
            break;
        }
    }
    
    printf("len:%d, i:%d, mid1:%d mid2:%d \n",len, i, mid1, len-mid1);
    
    Node* secondHalf = temp->Next;
    temp->Next = nullptr;
    Node *firstHalf = Reverse();
    
    char* str1 = (char*)malloc(sizeof(char) * mid1 + 1);
    char* str2 = (char*)malloc(sizeof(char) * mid1 + 1);
    
    memcpy(str1, (char*)firstHalf->Data, mid1); 
    str1[mid1] = '\0';
    
    int slen = strlen((char*)temp->Data);
    
    if(slen > mid1)
    {
        memcpy(str2, (char*)firstHalf->Data + mid1, slen-mid1);
        str2[slen-mid1] = '\0';
    }
    else
    {
        str2[0] = '\0';
    }   
    
    printf("%s, %s", str1, str2);
    str1 = strrev(str1);
    
    if(!*str2)
    {
        str2 = (char*)secondHalf->Data;
        secondHalf = secondHalf->Next;
    }
    
    if(*str2 && len%2 == 1)
    {
        str2++;
        if(!*str2)
        {
            str2 = (char*)secondHalf->Data;
            secondHalf = secondHalf->Next;
        }
    }
    
    while(*str1 && *str2)
    {
        if(*str1 != *str2)
        {
            return false;
        }
    
        str1++;
        str2++;
    
        if(!*str1)
        {   
            retry:
            firstHalf = firstHalf->Next;
            if(firstHalf)
            {
                str1 = (char*) malloc(strlen((char*)firstHalf->Data) + 1);
                strcpy(str1,(char*)firstHalf->Data);                
                str1 = strrev(str1);
            }
    
            if(!*str1 && firstHalf)
            {
                goto retry;
            }
        }
    
        if(!*str2)
        {
            retrySecondHalf:
            temp = secondHalf;
            if(temp)
            {
                str2 = (char*)temp->Data;           
                secondHalf = secondHalf->Next;
            }
    
            if(!*str2 && secondHalf)
            {
                goto retrySecondHalf;
            }
        }
    }
    
    if(*str1 || *str2)
    {
        return false;
    }
    
    return true;
    

    }

    int _tmain(int argc,_TCHAR * argv []) {     LinkedList * list = new LinkedList();

    list->AddItem("01234");
    list->AddItem("");
    list->AddItem("56");
    list->AddItem("789");
    list->AddItem("1"); 
    list->AddItem("9");
    list->AddItem("");
    list->AddItem("876543210");
    
    printf("Is pallindrome: %d \n", list->IsPalindrome());
    
    return 0;
    

    }

答案 7 :(得分:0)

首先,迭代到列表的末尾,并将指向最后一个节点的指针存储为end。然后将指向第一个节点的指针存储为start

然后,调用一个函数并提供这些值。该功能将:

  1. 测试start == end(他们是否指向同一链接)。如果是这样,它将立即返回true。 (列表中的奇数项目和中间项目是唯一剩下的项目。)
  2. 然后,它会查看startend所代表的值。如果它们不相等,它将立即返回false。 (不是回文。)
  3. 否则,它会改变start以指向下一个链接(大概是start = start->next)。
  4. 如果start == end,立即返回true(处理列表中偶数链接的大小写)。
  5. 找到end之前的链接并将end设置为link i = start; while (i->next != end) i = i->next; end = i;
  6. 递归,为startend提供新值。

答案 8 :(得分:0)

以下是递归代码,其中node将数据作为整数,只需将其替换为char。它在O(n)时间内运行,使用除隐式使用大小为O(n)的堆栈之外的常量空间。其中,n是linkedlist中的节点数。

package linkedList;
class LinkedList {
    class LinkedListNode {
        public int data;
        public LinkedListNode next;
        public LinkedListNode (int d) {
            data = d;
            next = null;
        }
    }

    class PalinResult {
        public boolean done;
        public LinkedListNode forward;

        public PalinResult (LinkedListNode n) {
            forward = n;
            done = false;
        }
    }

    LinkedListNode root;

    public LinkedList () {
        root = null;
    }

    public LinkedListNode getRoot(){
        return root;
    }

    public boolean add(int d) {
        LinkedListNode t = new LinkedListNode (d);
        if (root == null) {
            root = t;
            return true;
        }

        LinkedListNode curr = root;
        while (curr.next != null) {
            curr = curr.next;
        }

        curr.next = t;
        return true;
    }

    /*
     * Takes O(n time)
     */
    public boolean checkPalindrome() {
        PalinResult res = new PalinResult (root);
        return     checkPalindromeRecur(root, res);
    }

    private boolean checkPalindromeRecur(LinkedListNode curr, PalinResult res) {
        if (curr == null) 
            return true;
        else {
            boolean ret = checkPalindromeRecur(curr.next, res);

            if (!ret || (res.done))
                return ret;

            if (curr == res.forward)
                res.done = true;

            if (curr.data == res.forward.data)
                ret = true;
            else
                ret = false;

            res.forward = res.forward.next;
            return ret;
        }
    }

    public static void main(String args[]){
        LinkedList l = new LinkedList();
        l.add(1);
        l.add(4);
        l.add(1);

        System.out.println(l.checkPalindrome());
    }
}

答案 9 :(得分:-1)

所以(我粗略的想法 - 请让我知道) 我们也可以

1)计算LL的长度;
2)适当确定中点
//(长度为5,中点为3,但长度为4,中点为2) 3)当处于中点时 - 将LL从中点反转到LL的结尾;
4)将头部数据与新的中点数据进行比较,直到头部ref重复到中间并且新的中间ref迭代到NULL。