单链表是否是回文

时间:2011-11-01 12:00:10

标签: linked-list

我有一个单链接列表。我想找到链接列表是否是Palindrome。 我已经以下面的一种方式实现了它。

bool palindromeOrNot(node *head) {
  node *tailPointer;
  node *headLocal=head;
  node *reverseList=reverseLinkedListIteratively(head);
  int response=1;

  while(headLocal != NULL && reverseList!=NULL) {
    if(headLocal->id==reverseList->id) {
      headLocal=headLocal->next;
      reverseList=reverseList->next;
    }
    else
      return false;
  }

  if(headLocal == NULL && reverseList==NULL)
    return fasle;
  else 
    return true;
}

我正在反转原始链接列表,然后比较Node by Node。如果一切都很好 然后我会返回1,否则返回0。

是否有更好的算法来解决问题。

21 个答案:

答案 0 :(得分:15)

方法1(使用堆栈)

一个简单的解决方案是使用一堆列表节点。这主要涉及三个步骤。

  1. 从头到尾遍历给定列表,并将每个访问过的节点推送到堆栈。
  2. 再次遍历列表。对于每个访问过的节点,从堆栈弹出一个节点,并将弹出节点的数据与当前访问的节点进行比较。
  3. 如果所有节点都匹配,则返回true,否则返回false。
  4. 上述方法的时间复杂度为O(n),但需要O(n)额外空间。以下方法通过不断的额外空间来解决这个问题。

    方法2(通过撤消列表)

    此方法需要O(n)时间和O(1)额外空间。

    1. 获取链接列表的中间位置。
    2. 反转链接列表的后半部分。
    3. 检查上半部分和后半部分是否相同。
    4. 通过再次反转后半部并将其附加回上半部来构建原始链接列表
    5. 方法3(使用递归)

      左右两个指针。使用递归向右和向左移动,并在每次递归调用中检查跟随。

      1. 子列表是回文。
      2. 当前左右两侧的值匹配。
      3. 如果以上两个条件均为真,则返回true。

        这个想法是使用函数调用堆栈作为容器。递归遍历直到列表的末尾。当我们从最后一个NULL返回时,我们将在最后一个节点。要与列表的第一个节点进行比较的最后一个节点。

        为了访问列表的第一个节点,我们需要在最后一次递归调用中使用列表头。因此我们也将头传递给递归函数。如果它们都匹配,我们需要比较(2,n-2)个节点。再次当递归回落到(n-2)nd节点时,我们需要从头部引用第二个节点。我们在前一次调用中前进头指针,以引用列表中的下一个节点。

        然而,识别双指针的技巧。传递单个指针和传值一样好,我们将一次又一次地传递相同的指针。我们需要传递头指针的地址,以反映父递归调用的变化。

        更多:geeksforgeeks

答案 1 :(得分:5)

Whether a single-linked list is Palindrome or not,也可以检查without reversing the linked list

可以接近递归方法,其中指针指向链表的开头,&将比较从最后一次递归返回的另一个指针。

这是伪代码:

int isPalindrome(**root,*head)
{
if(!head)
    return 1;
else
{
    int res = isPalindrome(root,head->next);

    if(res == 0)
        return 0;
    int r = (*root)->data == head->data;
    *root = (*root)->next;
    return r;
}
} 

拨打电话:isPalindrome(&root,root);

答案 2 :(得分:2)

当我们将链表与反向列表进行比较时,我们实际上只需要比较列表的前半部分。如果正常列表的前半部分与反向列表的前半部分匹配,那么正常列表的后半部分必须与反向列表的后半部分匹配。

答案 3 :(得分:1)

只有reverse个链表的一半。并开始比较。您不需要reverse整个链接列表。

答案 4 :(得分:0)

对于java,可以用这个;


boolean isListPalindrome(ListNode<Integer> l) {
    
    if(l == null || l.next == null)return true;
    
    List<Integer> list = new ArrayList<Integer>();
    List<Integer> revlist = new ArrayList<Integer>();
    
    while(l != null){
        list.add(l.value);
        revlist.add(l.value);
        l = l.next;
    }
    
    Collections.reverse(revlist);
    
    return list.equals(revlist);

}

答案 5 :(得分:0)

为什么要让它变得复杂。 因为这是一个家庭作业。我可以给你一个简单的建议。 我发现你只是比较代码中的id。 假设您的id是一个char,为什么不简单地遍历列表并将char存储在一个数组中然后检查for palindrome 。 您的方式只需反转链表一次并遍历链表两次 完全在函数中有三次遍历。

答案 6 :(得分:0)

在链接列表的开头有2个指针的情况。设置ptr1 = k = start = 1和ptr2 = n,其中n是链接列表中节点的总数。然后,我们可以从K开始,如果两者相同,则将其与n-k + 1进行比较,然后增加k并继续进行比较,直到到达链接列表的中间为止

答案 7 :(得分:0)

我已经使用递归实现了此问题的解决方案。

使用递归对链接列表进行回文检查:

// Dao class

@Dao
public interface UserDao {
    // ...    

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertAll(List<User> users);

    @Query("DELETE FROM User WHERE User.id NOT IN(:lstIDUsers)")
    void deleteOldUsers(List<Integer> lstIDUsers);
}  

答案 8 :(得分:0)

如果LinkedList中有两个字符串对象,则此逻辑(使用递归)将起作用。

public class LinkedList {
    Node head;
    String s1="",s2="";
    class Node{
        int data;
        Node next;
        Node(int d){
            this.data = d;
            this.next = null;
        }
    }
    public void append(int data){
        Node node = new Node(data);
        if(head==null){
            head = node;
        }else{
            Node cur = head;
            while(cur.next!=null) cur = cur.next;
            cur.next = node;
        }
    }
    public void palindrome(Node node){
        if(node==null){
            return;
        else
            s2+=""+node.data;
        palindrome(node.next);
        s1+=""+node.data;
    }
    public boolean isPalindrome(){
        palindrome(head);
        return s1.equals(s2);
    }
    public static void main(String ss[]){
       LinkedList  list = new LinkedList();
       list.append(10);
       list.append(25);
       list.append(10);
       System.out.println(list.isPalindrome());
   }
}

答案 9 :(得分:0)

横穿整个前半部并将其放入堆叠并横穿剩余的一半并同时从堆栈中弹出并检查两者是否相同。如果两者在整个过程中是相同的,则它是回文,否则不是。

答案 10 :(得分:0)

递归算法实现。目的只是利用自然递归堆栈。

void solve(Node *curr, Node **head, bool *ans) {
  if (!curr) return;
  solve(curr->next, head, ans);
  if ((*head)->val != curr->val)
    *ans = false;
  (*head) = (*head)->next;
}

bool is_palindrome(Node *l) {
  bool ans = true;
  solve(l, &l, &ans);
  return ans;
}

答案 11 :(得分:0)

void reverse(Node** head_ref)
{
    struct Node* prev   = NULL;
    struct Node* current = *head_ref;
    struct Node* next;
    while (current != NULL)
    {
        next  = current->next;
        current->next = prev;
        prev = current;
        current = next;
    }
    *head_ref = prev;
}

bool compair(Node *t,Node *t2){
   Node *p = t;
   Node *q = t2;
   while(q && q){
       if(p->data==q->data){
           p = p->next;
           q = q->next;
       }
       else{
           return false;
       }
   }
   if(p==NULL && q==NULL){
       return true;
   }
    return false;
}
bool isPalindrome(Node *head)
{
    //Your code here
    if(head==NULL)return true;
    Node *slow = head;
    Node *fast = head;
    Node *prevSlow;
    Node *middle = NULL;
    bool ans=true;
    if(head && head->next){
        while(slow && fast&&fast->next){
            prevSlow = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        if(fast!=NULL){
            middle = slow;
            slow = slow->next;
        }
        prevSlow->next=NULL;
        Node *secondHalf = slow;
        reverse(&secondHalf);
        ans = compair(head,secondHalf);
        if(middle!=NULL){
            prevSlow->next = middle;
            middle->next = secondHalf;
        }
        else{
            prevSlow->next = secondHalf;
        }
    }
    return ans;

}

答案 12 :(得分:0)

我正在使用堆栈来存储字符,直到列表的中间点。然后,我检查列表后半部分中的字符与堆栈顶部的对比。时间:O(n),空格:O(n / 2)

SinglyLinkedList.prototype.isPalindrome = function() {
    var slow = this.head;
    var fast = this.head;
    var stack = [];

    if(!slow) return false;

    while(fast.next != null && fast.next.next != null) {
        fast = fast.next.next
        stack.push(slow.element);
        slow = slow.next;
    }

    // check for odd or even list. if even, push the current slow to the stack.
    if(fast.next != null) {
        stack.push(slow.element);
    }

    while(slow.next) {
        slow = slow.next;
        if(stack.pop() !== slow.element) return false;
    }

    return true;
}

答案 13 :(得分:0)

用于检查输入链表的python程序是否为回文

class Node:
   def __init__(self, val):
     self.data=val
     self.next=None

def rec_palindrome(slow, fast):
  if fast == None:
      # Even number of nodes
      return 0, slow
  elif fast.next == None:
      return -1, slow
   else:
      res, ptr = rec_palindrome(slow.next, fast.next.next)
      if res == -1:
         tmp = ptr.next
         if tmp.data != slow.data:
             return 1, None
         else: 
             return 0, tmp.next 
      elif res == 1:
         return 1, None
      elif res == 0:
         if ptr.data != slow.data:
            return 1, None
         else:
            return 0, ptr.next
      else:
         return res, None 

 class LinkedList:
   def __init__(self):
     self.head = None

   def append(self, node):
     if self.head == None:
         self.head = node
     else:
         cur = self.head 
         while cur.next != None:
             cur = cur.next
         cur.next = node

   def display(self, msg):
     print(msg)
     cur = self.head
     while cur != None:
         print("%d" %cur.data, end="")
         if cur.next != None:
             print("->", end="")
         else:
             print("")
         cur = cur.next

   def is_palindrome(self):
     res, ptr = rec_palindrome(self.head, self.head)
     if res :
         print("The input list is NOT palindrome")
     else:
         print("The input list is palindrome")


if __name__ == "__main__":
  print("Pyhton program to check if the input list is palindrome or not")
  N = int(input("How many elements?"))
  llist = LinkedList()
  for i in range(N):
     val = int(input("Enter the element of node %d" %(i+1)))
     llist.append(Node(val))
  llist.display("Input Linked List")
  llist.is_palindrome()

example output:
pyhton program to check if the input list is palindrome or not
How many elements?7
Enter the element of node 112
Enter the element of node 245
Enter the element of node 389
Enter the element of node 47
Enter the element of node 589
Enter the element of node 645
Enter the element of node 712
Input Linked List
     12->45->89->7->89->45->12
The input list is palindrome

答案 14 :(得分:0)

您也可以使用数组进行检查,首先从其根遍历链接列表,并将每个节点的数据字段存储到一个数组中,然后反转该数组,然后逐个比较两个数组。以下是程序

 bool isPalindrome(Node *head)
 {
       int i=0,j,n=0,arr1[50],arr2[50];
       struct Node *current;
       current=head;
       while(current!=NULL)
       {
             arr1[i]=current->data;
             i++;
             n++;
             current=current->next;
       }
       j=0;
       for(i=n-1;i>=0;i--)
       {
           arr2[j]=arr1[i];
           j++;
       }
       for(i=0;i<n;i++)
       {
           if(arr1[i]!=arr2[i])
           {
               return false;
           }
       }
       return true;
  }

答案 15 :(得分:0)

有几种方法可以做到这一点。其中一个解决方案如下:

  1. 查找给定LinkedList中的中间节点
  2. 反向下半场
  3. 然后,将它与上半部分进行比较
  4. 该解决方案具有O(n)时间复杂度。以下是C#中的示例实现。

        // Returns length of a LinkedList
        private static int GetLength(Node head)
        {
            var length = 0;
    
            if (head == null)
                return length;
    
            var runner = head;
            while (runner != null)
            {
                length++;
                runner = runner.Next;
            }
    
            return length;
        }
    
        // Compares two LinkedLists
        private static bool Compare(Node head1, Node head2)
        {
            // Get Lengths
            var len1 = GetLength(head1);
            var len2 = GetLength(head2);
    
            if (len1 != len2)
                return false;
    
            var runner1 = head1;
            var runner2 = head2;
    
            while (runner1 != null && runner2 != null)
            {
                if (runner1.Data != runner2.Data)
                    return false;
    
                runner1 = runner1.Next;
                runner2 = runner2.Next;
            }
    
            return true;
        }
    
        // Reverse a LinkedList
        private static Node Reverse(Node head)
        {
            if (head == null)
                return null;
    
            Node prev = null;
            Node next;
            var current = head;
    
            while (current != null)
            {
                next = current.Next;
                current.Next = prev;
                prev = current;
                current = next;
            }
    
            return prev;
        }
    
        private static bool IsPalindrome(Node head)
        {
            if (head == null)
                return false;
    
            if (head.Next == null)
                return true;
    
            var slowPrev = head;
            var slow = head;
            var fast = head;
    
            while (fast != null && fast.Next != null)
            {
                fast = fast.Next.Next;
                slowPrev = slow;
                slow = slow.Next;
            }
    
            Node firstHalf;
            Node secondHalf;
            if (fast == null)
            {
                secondHalf = slow;
                slowPrev.Next = null;
                firstHalf = head;
            }
            else
            {
                secondHalf = slow.Next;
                slowPrev.Next = null;
                firstHalf = head;
            }
    
            return Compare(firstHalf, Reverse(secondHalf));
        }
    

答案 16 :(得分:0)

在java中,将值存储在字符串变量中并使用字符串构建器

反向
String s = "";
while (node != null){
s = s+ node1.value;
node = node.next;
}
StringBuilder reverseString = new StringBuilder(s);
reverseString = reverseString.reverse();
String s1 = new String(reverseString);

System.out.println(s.equals(s1));

答案 17 :(得分:0)

下面是我使用向量的实现。希望它可以帮到某人。

node.h文件如下

#ifndef node_h
#define node_h

class LinkedList
{

private:
    struct node
    {
        int data;
        node *next;
    };
    node *head;

public:
    LinkedList ();
    node *createNode (int key); 
    void appendNodeBack (int key);
    void printList ();
    bool isPalindrome (LinkedList list);

};
#endif

下面的node.cpp文件。

#include <vector>
#include "node.h"

LinkedList::LinkedList ():head(NULL) {}

LinkedList::node *LinkedList::createNode (int key)
{
    node *newNode = new node;
    newNode->data = key;
    newNode->next = NULL;
    return newNode;
}

void LinkedList::appendNodeBack (int key)
{
    node *newNode = createNode (key);

    //if tree is empty
    if (head == NULL)
    {
        head = newNode;
        return;
    }

    //if tree is not empty
    //traverse to the last node in the list
    node *temp = head;
    while (temp->next != NULL)
        temp = temp->next;

    temp->next = newNode;
}

void LinkedList::printList ()
{
    //if tree is empty
    if (head == NULL)
    {
        std::cout << "Tree is empty!\n";
        return;
    }

    //if tree not empty
    node *temp = head;
    while (temp != NULL)
    {
        std::cout << temp->data<<"-->";
        temp = temp->next;

    }
    std::cout << "NULL\n";
}

bool LinkedList::isPalindrome (LinkedList list)
{
    node *temp = head;

    unsigned int count = 0;

    //push all elements of the list in an array, and count total number of nodes
    std::vector<int> array;

    while (temp != NULL)
    {
        count++;
        array.push_back (temp->data);
        temp = temp->next;
    }

    bool check = true;

    for (unsigned int i = 0, j = array.size() -1; i < j; i++, j--)
    {
        if (array.at(i) != array.at(j))
            check = false;
    }

    return check;
}

下面的main.cpp文件。

#include <iostream>
#include "node.cpp"

int main ()
{
    LinkedList list;
    list.appendNodeBack (2);
    list.appendNodeBack (3);
    list.appendNodeBack (11);
    list.appendNodeBack (4);
    list.appendNodeBack (6);
    list.appendNodeBack (4);
    list.appendNodeBack (11);
    list.appendNodeBack (3);
    list.appendNodeBack (2);

    list.printList ();

    if (list.isPalindrome (list))
        std::cout << "List is a palindrome!\n";
    else
        std::cout << "List is NOT a palindrome!\n";

    return 0;
}

答案 18 :(得分:0)

我认为最佳解决方案是不使用额外的空间,这意味着不要使用新的反向LL ...这个想法是利用递归使用的操作堆栈...因为当递归到达时基本情况下,它会从最后一个插入的节点开始弹出堆栈,这是LL的最后一个节点...我实际上已经通过了这一步并打了一个墙...一些根和最后一个节点是如何偏移的...看我的代码

public LLNode compare(LLNode root) {
    // 
    // base case, popping opr stack,
    // this should be the last node on the linked list
    if (root.next == null) {
        System.out.printf("Poping stack: %d\n", root.data);
        return root;
    }

    // 
    // push the functions to the opr stack
    System.out.printf("pushing %d to the stack\n", root.next.data);
    LLNode last = compare(root.next);
    System.out.printf("On the way back: %d, root: %d\n", last.data, root.data);

    return root;
}

输出如下:

The list looks like this:
1 2 3 4 3 2 1
pushing 2 to the stack
pushing 3 to the stack
pushing 4 to the stack
pushing 3 to the stack
pushing 2 to the stack
pushing 1 to the stack
Poping stack: 1
On the way back: 1, root: 2
On the way back: 2, root: 3
On the way back: 3, root: 4
On the way back: 4, root: 3
On the way back: 3, root: 2
On the way back: 2, root: 1

如果你能搞清楚,请告诉我

答案 19 :(得分:0)

这是我解决这个问题的方法。为了测试它,我使用整数而不是字符,例如我使用了1,4,1,4,1而不是#34; adada&#34;。您可以将int更改为char,并且所有内容仍应正常工作

struct Node
{
    Node(int in) : data(in) {}
    int data;
    Node* next;
};

//This function will find recursively call itself until last element and than it will start    //comparing. To compare with correct element from the beginning a paramater called pos is used 
bool palindromeStart(Node* first, Node* last, size_t pos, size_t middlePos)
{
    if (last->next != NULL)
    {
        if (palindromeStart(first, last->next, pos + 1, middlePos) == false)
            return false;
    }

    size_t curPos  = middlePos - pos;
    while (curPos--)
        first = first->next;

    if (first->data != last->data)
        return false;
    return true;
}

bool isPalindrome(Node* head)
{
    Node* n1 = head;
    Node* n2 = head;
    size_t middlePos = 0;
    while (true)
    {
        if (n2 == nullptr)
        {
            --middlePos;
            break;
        }
        else if ( n2->next == nullptr)
        {
            break;
        }

        n2 = n2->next->next;
        n1 = n1->next;
        ++middlePos;
    }  // Until now I just find the middle 

    return palindromeStart(head, n1, 0, middlePos);
}

int main()
{
        Node* n = new Node(1);
        Node* n1 = new Node(4);
        Node* n2 = new Node(4);
        Node* n3 = new Node(1);
        Node* n4 = new Node(1);



        n->next = n1;
        n1->next = n2;
        n2->next = n3;
        n3->next = nullptr;
        //n3->next = n4;
        //n4->next = nullptr;

        std::cout << isPalindrome(n);


}

答案 20 :(得分:0)

我认为你可以在O(1)内存使用和相同的O(n)速度方面获得更好的解决方案。通过使用链接列表。您没有必要创建链表的反向副本。但是这种方法会破坏列表。你必须将它缝合回原位,但运行时间仍然是O(n)。

快速版本的isPalindrome的代码基本上找到链接列表的中间部分,然后逻辑上将链接列表分成两部分。它仅反转第一件并将其与另一件进行比较。坏的部分是由于第一个链表组块上的就位反转而破坏了链表。但是,您可以将列表重新拼接在一起,并且仍处于O(n)时间。

要查看的功能是isPalindromeFast。我开始但尚未完成缝合。你可以在Go play http://play.golang.org/p/3pb4hxdRIp中运行代码。

以下是Go中的完整代码。

package main

import "fmt"

type Node struct {
    value string
    next  *Node
}

func (n *Node) Next() *Node {
    return n.next
}

func (n *Node) Value() string {
    return n.value
}

func (n *Node) Length() int {
    length := 0
    linkedList := n
    for linkedList != nil {
        length += 1
        linkedList = linkedList.Next()
    }

    return length
}

func NewLinkedList(s string) *Node {
    if len(s) == 0 {
        return nil
    }

    headNode := &Node{string(s[0]), nil}
    currentNode := headNode

    for _, v := range s[1:] {
        newNode := &Node{string(v), nil}
        currentNode.next = newNode
        currentNode = newNode
    }

    return headNode
}

func PrintLinkedList(linkedList *Node) {
    for linkedList != nil {
        fmt.Println(linkedList)
        linkedList = linkedList.Next()
    }
}

func ReverseList(linkedList *Node, endPoint int) *Node {

    if endPoint == 1 {
        return linkedList
    }

    first, next, lastNode := linkedList, linkedList, linkedList
    lastNode = nil

    for i := 0; i < endPoint; i++ {
        next = first.Next()
        first.next = lastNode
        lastNode = first
        first = next

    }

    return lastNode

}

// func StitchListsBackTogether(listA, listB, listC *Node, endOfListA int) *Node {
//  listAFixed := ReverseList(listA, endOfListA)
//  listStart := listAFixed
//  for listAFixed.Next() != nil {
//      listAFixed = listAFixed.Next()
//  }
//  listAFixed.next = listB

//  return listStart

// }

func IsPalindromeFast(linkedList *Node) bool {
    // Uses O(1) space and O(n) time
    // However mutates and destroys list, so need to stitch list backtogether.  Initial implementation StitchListsBackTogether
    length := linkedList.Length()

    endOfListA := length / 2
    endOfListB := length / 2

    if length%2 == 0 {
        endOfListB += 1
    } else {
        endOfListB += 2
    }

    listA, listB := linkedList, linkedList

    for i := 0; i < endOfListB-1; i++ {
        listB = listB.Next()
    }

    listA = ReverseList(listA, endOfListA)

    for listA != nil && listB != nil {
        if listA.Value() != listB.Value() {
            return false
        }
        listA = listA.Next()
        listB = listB.Next()
    }

    return true
}

func IsPalindromeSlow(linkedList *Node) bool {
    //Uses O(1) space and O(n^2) time
    startPalidrome, endPalidrome := linkedList, linkedList

    for endPalidrome.Next() != nil {
        endPalidrome = endPalidrome.Next()
    }

    for startPalidrome != endPalidrome {

        if startPalidrome.Value() != endPalidrome.Value() {
            return false
        }

        if startPalidrome.Next() == endPalidrome {
            return true
        }

        startPalidrome = startPalidrome.Next()

        middlePalidrome := startPalidrome

        for middlePalidrome.Next() != endPalidrome {
            middlePalidrome = middlePalidrome.Next()
        }
        endPalidrome = middlePalidrome

    }

    return true
}

func main() {

    fmt.Println(IsPalindromeSlow(NewLinkedList("ttoott")))
    fmt.Println(IsPalindromeFast(NewLinkedList("ttoott")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("ttott")))
    fmt.Println(IsPalindromeFast(NewLinkedList("ttott")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("hello")))
    fmt.Println(IsPalindromeFast(NewLinkedList("hello")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("ttto")))
    fmt.Println(IsPalindromeFast(NewLinkedList("ttto")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("tt")))
    fmt.Println(IsPalindromeFast(NewLinkedList("tt")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("t")))
    fmt.Println(IsPalindromeFast(NewLinkedList("t")))
    fmt.Println("")
    fmt.Println(IsPalindromeSlow(NewLinkedList("tto3tt")))
    fmt.Println(IsPalindromeFast(NewLinkedList("tto3tt")))
    fmt.Println("")

}