In: Computer Science
Write a recursive function to check if a string whose each character is stored in a separate node in a doubly linked list, is a palindrome. Use the code available from DoublyLinkedList.hpp on our Github.
// | |
// Doubly-linked list with 2 dummy nodes | |
// | |
#pragma once | |
#include <stdexcept> | |
template<typename T> | |
struct Node { | |
T data; | |
Node<T>* next; | |
Node<T>* prev; | |
Node() = delete; // Intentionally no default constructor | |
Node( const T & element ) : data( element ), next( nullptr ), prev( nullptr ) {} | |
}; | |
template<typename T> | |
class DoublyLinkedList { | |
private: | |
Node<T>* head; | |
Node<T>* tail; | |
public: | |
// Constructors | |
DoublyLinkedList(); | |
DoublyLinkedList(const DoublyLinkedList&); | |
DoublyLinkedList& operator=(const DoublyLinkedList&); // assignment operator | |
~DoublyLinkedList(); // destructor | |
// Getters / Setters | |
bool empty(); | |
int size() = delete; // INTENTIONALLY NOT IMPLEMENTED !! | |
void append( const T& ); | |
void prepend( const T& ); | |
void insertAfter( Node<T>*, const T& ); | |
void remove( Node<T>* ); | |
void pop_front(); // remove element at front of list | |
void pop_back(); // remove element at back of list | |
T& front(); // return list's front element | |
T& back(); // return list's back element | |
void clear(); | |
}; | |
template<typename T> | |
DoublyLinkedList<T>::DoublyLinkedList() { | |
head = new Node<T>( T() ); // create dummy nodes | |
tail = new Node<T>( T() ); | |
head->next = tail; // have them point to each other | |
tail->prev = head; | |
} | |
template<typename T> | |
bool DoublyLinkedList<T>::empty() { | |
return head->next == tail; | |
} | |
template<typename T> | |
void DoublyLinkedList<T>::append( const T& newData ) { | |
Node<T> * newNode = new Node<T>( newData ); // create new node | |
newNode->prev = tail->prev; | |
newNode->next = tail; | |
tail->prev->next = newNode; | |
tail->prev = newNode; | |
} | |
template<typename T> | |
void DoublyLinkedList<T>::prepend( const T& newData ) { | |
Node<T> * newNode = new Node<T>( newData ); // create new node | |
Node<T> * firstNode = head->next; | |
newNode->next = head->next; | |
newNode->prev = head; | |
head->next = newNode; | |
firstNode->prev = newNode; | |
} | |
template<typename T> | |
void DoublyLinkedList<T>::insertAfter(Node<T>* curNode, const T& newData) { | |
if (curNode == tail) { | |
// Can't insert after dummy tail | |
return; | |
} | |
// Construct new node | |
Node<T>* newNode = new Node<T>( newData ); | |
Node<T>* sucNode = curNode->next; | |
newNode->next = sucNode; | |
newNode->prev = curNode; | |
curNode->next = newNode; | |
sucNode->prev = newNode; | |
} | |
template<typename T> | |
void DoublyLinkedList<T>::remove(Node<T>* curNode) { | |
if( empty() ) throw std::length_error( "empty list" ); | |
if (curNode == head || curNode == tail) { | |
// Dummy nodes cannot be removed | |
return; | |
} | |
Node<T>* sucNode = curNode->next; | |
Node<T>* predNode = curNode->prev; | |
// Successor node is never null | |
sucNode->prev = predNode; | |
// Predecessor node is never null | |
predNode->next = sucNode; | |
} | |
template <typename T> | |
void DoublyLinkedList<T>::pop_front() { | |
remove(head->next); | |
} | |
template <typename T> | |
void DoublyLinkedList<T>::pop_back() { | |
remove(tail->prev); | |
} | |
template <typename T> | |
T& DoublyLinkedList<T>::front() { | |
if( empty() ) throw std::length_error( "empty list" ); | |
return head->next->data; | |
} | |
template <typename T> | |
T& DoublyLinkedList<T>::back() { | |
if( empty() ) throw std::length_error( "empty list" ); | |
return tail->prev->data; | |
} | |
template<typename T> | |
void DoublyLinkedList<T>::clear() { | |
while( !empty() ) | |
pop_front(); | |
} | |
template<typename T> | |
DoublyLinkedList<T>::~DoublyLinkedList() { | |
clear(); | |
delete head; // remove the dummy nodes | |
delete tail; | |
} | |
template <typename T> | |
DoublyLinkedList<T>::DoublyLinkedList( const DoublyLinkedList<T> & original ) : DoublyLinkedList() { | |
// Walk the original list adding copies of the elements to this list maintaining order | |
for( Node<T> * position = original.head->next; position != original.tail; position = position->next ) { | |
append( position->data ); | |
} | |
} | |
template <typename T> | |
DoublyLinkedList<T> & DoublyLinkedList<T>::operator=( const DoublyLinkedList<T> & rhs ) { | |
if( this != &rhs ) // avoid self assignment | |
{ | |
// Release the contents of this list first | |
clear(); // An optimization might be possible by reusing already allocated nodes | |
// Walk the right hand side list adding copies of the elements to this list maintaining order | |
for( Node<T> * position = rhs.head->next; position != rhs.tail; position = position->next ) { | |
append( position->data ); | |
} | |
} | |
return *this; | |
} |
Think like, you are adding function/s to this class code. Also write a helper function.
A string is a palindrome if its reverse is the same as the original string. For e.g. “civic” is a palindrome. The function should return bool true or false.
b) Why do we have to write two functions for the above recursive implementation ? How many functions do we have to write for the singly linked list we created in Simple Singly Linked List Example.cpp code posted on Titanium ?
Function to check if a doubly linkedList is Palindrome or not
bool Palindrome(struct Node *left)
{
if (left == NULL)
return true;
// Find rightmost node
struct Node *right = left;
while (right->next != NULL)
right = right->next;
while (left != right)
{
if (left->data != right->data)
return false;
left = left->next; // next is pointing to the next node
right = right->prev; //prev is pointing to the previous node
}
return true;
}
2. A Double linked list usually has two links one for the next node, one for the previous node as compared to a singly linked list which has only one link to the next node, hence two functions have been written for double linked list