diff --git a/problems/palindrome-linked-list.cpp b/problems/palindrome-linked-list.cpp new file mode 100644 index 0000000..04f2314 --- /dev/null +++ b/problems/palindrome-linked-list.cpp @@ -0,0 +1,172 @@ +#include +#include + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +struct ListNode { + int val; + ListNode* next; + ListNode() + : val(0) + , next(nullptr) + { + } + ListNode(int x) + : val(x) + , next(nullptr) + { + } + ListNode(int x, ListNode* next) + : val(x) + , next(next) + { + } +}; + +namespace { + +struct middle_t { + ListNode* node; + std::size_t size; + + middle_t(ListNode* node, std::size_t size) + : node(node) + , size(size) + { + } +}; + +middle_t +find_middle(ListNode* head) +{ + ListNode* slow = head; + ListNode* fast = head->next; + + std::size_t size; + for (size = 0; fast != nullptr && fast->next != nullptr; size++) { + slow = slow->next; + fast = fast->next->next; + } + + return { slow, size }; +} + +ListNode* reverse_list(ListNode* tail) +{ + ListNode* previous = nullptr; + ListNode* next = nullptr; + + for (ListNode* current = tail; current != nullptr; previous = current, current = next) { + next = current->next; + current->next = previous; + } + + return previous; +} + +bool compare_lists(ListNode* left, ListNode* right, std::size_t size) +{ + for (auto i = 0u; i < size + 1; i++) { + if (left->val != right->val) { + return false; + } + left = left->next; + right = right->next; + } + + return true; +} + +} // namespace + +class Solution { +public: + bool isPalindrome(ListNode* head) + { + // find middle of the linked list + auto mid = find_middle(head); + + // reverse the tail of the linked list + auto tail = reverse_list(mid.node); + + // compare head and tail + return compare_lists(head, tail, mid.size); + } +}; + +#pragma region Testing utilities +namespace { + +ListNode* +construct_list(const std::vector& values) +{ + ListNode* head = new ListNode(values.front()); + ListNode* tail = head; + + for (std::size_t i = 1; i < values.size(); i++) { + tail->next = new ListNode(values[i]); + tail = tail->next; + } + + return head; +} + +void destroy_list(ListNode* linked_list) +{ + if (linked_list == nullptr) { + return; + } + destroy_list(linked_list->next); + delete linked_list; +} + +bool test_find_middle(const std::vector& values, int mid_value) +{ + auto linked_list = construct_list(values); + + auto mid = find_middle(linked_list); + auto result = mid.node->val == mid_value; + + destroy_list(linked_list); + return result; +} + +bool test_isPalindrome(const std::vector& values) +{ + auto linked_list = construct_list(values); + + Solution s; + auto result = s.isPalindrome(linked_list); + + destroy_list(linked_list); + return result; +} + +} // namespace +#pragma endregion // Testing utilities + +int main() +{ + // find_middle tests + assert(test_find_middle(std::vector { 1, 2, 2, 1 }, 2)); + assert(test_find_middle(std::vector { 1, 2, 3, 2, 1 }, 3)); + assert(test_find_middle(std::vector { 1, 2 }, 1)); + assert(test_find_middle(std::vector { 1 }, 1)); + + // isPalindrome tests + assert(test_isPalindrome(std::vector { 1, 2, 2, 1 })); + assert(test_isPalindrome(std::vector { 1, 2, 3, 2, 1 })); + assert(!test_isPalindrome(std::vector { 1, 2 })); + assert(test_isPalindrome(std::vector { 1 })); + + return 0; +} \ No newline at end of file