diff --git a/.gitignore b/.gitignore index fe260c7..198a84e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.cache/ /devbox.lock /subprojects/cpputest-4.0/ +/subprojects/packagecache/ diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..da4a8ef --- /dev/null +++ b/devbox.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.12.0/.schema/devbox.schema.json", + "packages": [ + "meson@latest", + "gcc14@latest" + ], + "shell": { + "init_hook": [ + "echo 'Welcome to the DSA Learning Zone!' > /dev/null" + ], + } +} diff --git a/libs/chapter1/chapter1.hpp b/libs/chapter1/chapter1.hpp new file mode 100644 index 0000000..ccc57c4 --- /dev/null +++ b/libs/chapter1/chapter1.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include + +bool IsUnique(const char* s, int len); +bool CheckPermutation(const char* s1, const char* s2, size_t s1_len, size_t s2_len); diff --git a/libs/chapter1/check_permutation.cpp b/libs/chapter1/check_permutation.cpp new file mode 100644 index 0000000..720832b --- /dev/null +++ b/libs/chapter1/check_permutation.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "chapter1.hpp" + +/* Prompt + Check Permutation: Given two strings, write a method to decide if + one is a permutation of the other. + */ + +bool CheckPermutation(const char* s1, const char* s2, + size_t s1_len, size_t s2_len){ + + int letters1[255] = {}; + int letters2[255] = {}; + long unsigned int indx = 0; + + if(s1_len != s2_len){ + return false; + } + + for(indx = 0; indx < s1_len; ++indx){ + int li1 = static_cast(s1[indx]); + int li2 = static_cast(s2[indx]); + letters1[li1] += 1; + letters2[li2] += 1; + } + + for(indx = 0; indx < 255; ++indx){ + if (letters1[indx] != letters2[indx]){ + return false; + } + } + + return true; +} diff --git a/libs/chapter1/is_unique.cpp b/libs/chapter1/is_unique.cpp new file mode 100644 index 0000000..108d8ab --- /dev/null +++ b/libs/chapter1/is_unique.cpp @@ -0,0 +1,21 @@ +#include +#include "chapter1.hpp" + +/* Prompt + Is Unique: Implement an algorithm to determine if a string + has all unique characters. What if you cannot use additional data + structures? +*/ + +bool IsUnique(const char* s, int len){ + int tracker[255]= {}; + + for(int i=0; i(s[i]); + tracker[tracker_index] += 1; + if (tracker[tracker_index] > 1){ + return false; + } + } + return true; +} diff --git a/libs/chapter1/meson.build b/libs/chapter1/meson.build new file mode 100644 index 0000000..490a549 --- /dev/null +++ b/libs/chapter1/meson.build @@ -0,0 +1,5 @@ + +chapter1_sources = ['is_unique.cpp', 'check_permutation.cpp'] + +chapter1_lib = static_library('chapter1', + chapter1_sources) diff --git a/libs/chapter12/chapter12.hpp b/libs/chapter12/chapter12.hpp new file mode 100644 index 0000000..592c3f5 --- /dev/null +++ b/libs/chapter12/chapter12.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +void reverse_str(char* str); diff --git a/libs/chapter12/meson.build b/libs/chapter12/meson.build new file mode 100644 index 0000000..4fcfff0 --- /dev/null +++ b/libs/chapter12/meson.build @@ -0,0 +1,4 @@ +chapter12_sources = ['reverse_string.cpp'] + +chapter12_lib = static_library('chapter12', + chapter12_sources) diff --git a/libs/chapter12/reverse_string.cpp b/libs/chapter12/reverse_string.cpp new file mode 100644 index 0000000..c6dc7e1 --- /dev/null +++ b/libs/chapter12/reverse_string.cpp @@ -0,0 +1,20 @@ +#include "chapter12.hpp" +#include +#include + +/* + Reverse String: Implement a function void reverse( char* str) in C or C++ which reverses + a null-terminated string. +*/ + +void reverse_str(char *str){ + size_t len = strlen(str); + char *tmp = reinterpret_cast(malloc(len)); + strcpy(tmp, str); + for(unsigned int offset = 0; offset < len; ++offset){ + str[offset] = tmp[len-offset-1]; + } + str[len] = '\0'; + + free(tmp); +} diff --git a/libs/chapter2/chapter2.hpp b/libs/chapter2/chapter2.hpp new file mode 100644 index 0000000..b944255 --- /dev/null +++ b/libs/chapter2/chapter2.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +// Utility functions +void remove_dups(DopeLinkedList* ll); +void reverse_linkedlist(DopeLinkedList* ll); diff --git a/libs/chapter2/meson.build b/libs/chapter2/meson.build new file mode 100644 index 0000000..ecdf910 --- /dev/null +++ b/libs/chapter2/meson.build @@ -0,0 +1,5 @@ +ch2_srcs = ['remove_dups.cpp', 'reverse.cpp'] + +chapter2_lib = static_library('chapter2', + ch2_srcs, + include_directories: libs_includes) diff --git a/libs/chapter2/remove_dups.cpp b/libs/chapter2/remove_dups.cpp new file mode 100644 index 0000000..7a0623b --- /dev/null +++ b/libs/chapter2/remove_dups.cpp @@ -0,0 +1,35 @@ +#include "chapter2.hpp" +#include + +/* +Remove Dups! +Write code to remove duplicates from an unsorted linked list. +FOLLOW UP +How would you solve this problem if a temporary buffer is not allowed? + */ + +void remove_dups(DopeLinkedList* ll){ + DopeNode* prev = ll->GetHead(); + DopeNode* node = ll->GetHead(); + DopeNode* tmp = NULL; + std::unordered_map dup_tracker; + int iter_cnt = 0; + + while(node->GetNext() != NULL){ + if(dup_tracker.count(node->GetData())){ + // remove node + prev->SetNext(node->GetNext()); + tmp = node; + node = node->GetNext(); + tmp->Reset(); + } else { + dup_tracker[node->GetData()] += 1; + node = node->GetNext(); + if (iter_cnt != 0){ + prev = prev->GetNext(); + } + } + + iter_cnt++; + } +} diff --git a/libs/chapter2/reverse.cpp b/libs/chapter2/reverse.cpp new file mode 100644 index 0000000..191789d --- /dev/null +++ b/libs/chapter2/reverse.cpp @@ -0,0 +1,32 @@ +#include "chapter2.hpp" +#include + +/* +Reverse Linked List + */ + +void reverse_linkedlist(DopeLinkedList* ll){ + DopeNode* node = ll->GetHead(); + DopeNode* prev = nullptr; + std::stack s; + int iter_cnt = 0; + s.push(node); + while(node->GetNext() != NULL){ + s.push(node); + node = node->GetNext(); + iter_cnt++; + } + s.push(node); + + prev = s.top(); + s.pop(); + ll->SetHead(prev); + while(!s.empty()){ + node = s.top(); + s.pop(); + prev->SetNext(node); + prev = node; + } + node->SetNext(nullptr); + +} diff --git a/libs/dope/bintree.hpp b/libs/dope/bintree.hpp new file mode 100644 index 0000000..b521e41 --- /dev/null +++ b/libs/dope/bintree.hpp @@ -0,0 +1,172 @@ +#pragma once + +#include + +template +class DopeBinTreeNode{ + DopeBinTreeNode* left; + DopeBinTreeNode* right; + T data; +public: + DopeBinTreeNode(T data); + ~DopeBinTreeNode(); + void SetLeft(DopeBinTreeNode* newleft){left = newleft;} + void SetRight(DopeBinTreeNode* newright){right = newright;} + DopeBinTreeNode* GetLeft(){return left;} + DopeBinTreeNode* GetRight(){return right;} + T GetData(){return data;} +}; + +template +class DopeBinTree{ + DopeBinTreeNode* root; + T* RecPreOrderTraversal(DopeBinTreeNode* node, T* out); + T* RecInOrderTraversal(DopeBinTreeNode* node, T* out); + T* RecPostOrderTraversal(DopeBinTreeNode* node, T* out); +public: + DopeBinTree(T data); + DopeBinTree(); + ~DopeBinTree(); + void insert(T data); + bool isEmpty(); + DopeBinTreeNode* getRoot(){return root;} + void RecPreOrderTraversalArray(T* array); + void RecInOrderTraversalArray(T* array); + void RecPostOrderTraversalArray(T* array); +}; + +// Implementations ___________________________________________________ +template +DopeBinTreeNode::DopeBinTreeNode(T node_data): + left(nullptr), + right(nullptr), + data(node_data) +{ + +} +template +DopeBinTreeNode::~DopeBinTreeNode(){ + delete left; + delete right; +} + +template +DopeBinTree::DopeBinTree(T data){ + root = new DopeBinTreeNode(data); +} + +template +DopeBinTree::DopeBinTree(): + root(nullptr) +{ + +} + +template +DopeBinTree::~DopeBinTree(){ + if (root != nullptr){ + delete root; + } + +} + +template +// Insert Breadth first +void DopeBinTree::insert(T data){ + DopeBinTreeNode* current_node = root; + if (root == nullptr){ + root = new DopeBinTreeNode(data); + } else { + auto new_node = new DopeBinTreeNode(data); + // Breadth first traversal + std::queue*> q; + q.push(root); + while(!q.empty()){ + current_node = q.front(); + q.pop(); + if(current_node->GetLeft()){ + q.push(current_node->GetLeft()); + } else { + current_node->SetLeft(new_node); + break; + } + if(current_node->GetRight()){ + q.push(current_node->GetRight()); + } else { + current_node->SetRight(new_node); + break; + } + } + } +} + +template +bool DopeBinTree::isEmpty(){ + if(root == nullptr){ + return true; + } else { + return false; + } +} + +template +T* DopeBinTree::RecPreOrderTraversal(DopeBinTreeNode* node, T* out){ + if (node == nullptr){ + return out--; + } + *out = node->GetData(); + out++; + out = RecPreOrderTraversal(node->GetLeft(), out); + out = RecPreOrderTraversal(node->GetRight(), out); + return out; +} + +template +T* DopeBinTree::RecInOrderTraversal(DopeBinTreeNode* node, T* out){ + if (node == nullptr){ + return out--; + } + + out = RecInOrderTraversal(node->GetLeft(), out); + *out = node->GetData(); + out++; + out = RecInOrderTraversal(node->GetRight(), out); + return out; +} + +template +T* DopeBinTree::RecPostOrderTraversal(DopeBinTreeNode* node, T* out){ + if (node == nullptr){ + return out--; + } + out = RecPostOrderTraversal(node->GetLeft(), out); + out = RecPostOrderTraversal(node->GetRight(), out); + *out = node->GetData(); + out++; + return out; +} + +template +void DopeBinTree::RecPreOrderTraversalArray(T* array){ + int index = 0; + DopeBinTreeNode* node = root; + + RecPreOrderTraversal(node, &array[index]); + +} + +template +void DopeBinTree::RecInOrderTraversalArray(T* array){ + int index = 0; + DopeBinTreeNode* node = root; + + RecInOrderTraversal(node, &array[index]); +} + +template +void DopeBinTree::RecPostOrderTraversalArray(T* array){ + int index = 0; + DopeBinTreeNode* node = root; + + RecPostOrderTraversal(node, &array[index]); +} diff --git a/libs/dope/linkedlist.cpp b/libs/dope/linkedlist.cpp new file mode 100644 index 0000000..92fc4be --- /dev/null +++ b/libs/dope/linkedlist.cpp @@ -0,0 +1,56 @@ +#include "linkedlist.hpp" + +DopeLinkedList::DopeLinkedList(){ + for(int i = 0; i < MAX_DOPE_NODES; ++i){ + nodes[i].Reset(); + } + head = &nodes[0]; +} + +DopeNode::DopeNode(int init_data){ + data = init_data; + next = NULL; + state = NodeState::UnInitialized; +} + +DopeNode::DopeNode(){ + Reset(); +} + +void DopeNode::Reset(){ + this->data = 0; + this->next = NULL; + this->state = NodeState::UnInitialized; +} + +void DopeLinkedList::AppendData(int init_data){ + DopeNode* new_node = head; + DopeNode* node = head; + // Find next free slot and allocate it for the new node + for(int i=0; i < MAX_DOPE_NODES; ++i){ + if(nodes[i].GetState() == NodeState::UnInitialized){ + new_node = &nodes[i]; + new_node->SetNext(NULL); + new_node->SetState(NodeState::Initialized); + new_node->SetData(init_data); + break; + } + } + + if(head == new_node){ + // Just set first node, nothing else to do + return; + } + + // Find last node in linked list + if ((head->GetNext() == NULL) && (head->GetState() == NodeState::Initialized)){ + // Special case, only one node so far + head->SetNext(new_node); + } else { + while(node->GetNext() != NULL){ + node = node->GetNext(); + } + node->SetNext(new_node); + } + +} diff --git a/libs/dope/linkedlist.hpp b/libs/dope/linkedlist.hpp new file mode 100644 index 0000000..5584bf1 --- /dev/null +++ b/libs/dope/linkedlist.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#define MAX_DOPE_NODES 255 + +typedef enum NodeState_E{ + UnInitialized, + Initialized +} NodeState; + +class DopeNode { + int data; + DopeNode* next; + NodeState state; +public: + DopeNode(int data); + DopeNode(); + DopeNode* GetNext(){return next;} + NodeState GetState(){return state;} + int GetData(){return data;} + void SetNext(DopeNode* node){next = node;} + void SetState(NodeState new_state){state = new_state;} + void SetData(int new_data){data = new_data;} + void Reset(); +}; + +class DopeLinkedList { + DopeNode nodes[MAX_DOPE_NODES]; + DopeNode* head; + +public: + DopeLinkedList(); + void AppendData(int init_data); + DopeNode* GetHead(){return head;} + void SetHead(DopeNode* new_head){head = new_head;} +}; diff --git a/libs/dope/meson.build b/libs/dope/meson.build new file mode 100644 index 0000000..ca02567 --- /dev/null +++ b/libs/dope/meson.build @@ -0,0 +1,3 @@ +dope_srcs = ['linkedlist.cpp'] + +dope_lib = static_library('dope', dope_srcs) diff --git a/libs/dope/queues.cpp b/libs/dope/queues.cpp new file mode 100644 index 0000000..5d296a8 --- /dev/null +++ b/libs/dope/queues.cpp @@ -0,0 +1 @@ +#include diff --git a/libs/dope/queues.hpp b/libs/dope/queues.hpp new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/libs/dope/queues.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/libs/dope/stacks.hpp b/libs/dope/stacks.hpp new file mode 100644 index 0000000..52e63d4 --- /dev/null +++ b/libs/dope/stacks.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +template +class DopeStack{ + static const size_t size = S; + T stack[S]; + T* head; + size_t occupancy; +public: + DopeStack(); + T pop(); + void push(T item); + T peek(); + size_t getOccupancy(){return occupancy;} + bool isEmpty(); +}; + +// Implementation ____________________________________________________ + +template +DopeStack::DopeStack(): + head (&stack[0]), + occupancy(0) +{ + +} + +template +T DopeStack::pop(){ + T ret = *head; + if(head != &stack[0]){ + head--; + } + + occupancy--; + return ret; +} + +template +void DopeStack::push(T item){ + if(occupancy != 0){ + head++; + } + + *head = item; + occupancy++; +} + +template +T DopeStack::peek(){ + return *head; +} + +template +bool DopeStack::isEmpty(){ + return (occupancy == 0); +} diff --git a/libs/meson.build b/libs/meson.build new file mode 100644 index 0000000..07a9a34 --- /dev/null +++ b/libs/meson.build @@ -0,0 +1,6 @@ +libs_includes = include_directories('.') + +subdir('dope') +subdir('chapter1') +subdir('chapter2') +subdir('chapter12') diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..f015ed0 --- /dev/null +++ b/meson.build @@ -0,0 +1,9 @@ +project('CrackingTheCodingInterview', 'cpp', + version: '0.1', + default_options : [ + 'warning_level=3', + ] + ) + +subdir('libs') +subdir('tests') diff --git a/subprojects/cpputest.wrap b/subprojects/cpputest.wrap new file mode 100644 index 0000000..8d9c1e2 --- /dev/null +++ b/subprojects/cpputest.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = cpputest-4.0 +source_url = https://github.com/cpputest/cpputest/archive/v4.0.tar.gz +source_filename = cpputest-4.0.tar.gz +source_hash = 0b66d20661f232d2a6af124c4455c8ccc9a4ce72d29f6ad4877eb385faaf5108 +patch_filename = cpputest_4.0-3_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/cpputest_4.0-3/get_patch +patch_hash = 75d9e997e246884df77db2c295e465f3d25a84497a337691017c86c24a0ebf93 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cpputest_4.0-3/cpputest-4.0.tar.gz +wrapdb_version = 4.0-3 + +[provide] +cpputest = cpputest_dep diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..559206b --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,11 @@ +cpputest_project = subproject('cpputest', required: true) +cpputest = cpputest_project.get_variable('cpputest_dep') + +test_exe = executable( + 'CrackingTheCodingInvterviewVerification', + sources: 'test.cpp', + dependencies: cpputest, + link_with: [chapter1_lib, chapter2_lib, chapter12_lib, dope_lib], + include_directories: libs_includes) + +test('Cracking The Coding Interview Test', test_exe) diff --git a/tests/test.cpp b/tests/test.cpp new file mode 100644 index 0000000..93882b9 --- /dev/null +++ b/tests/test.cpp @@ -0,0 +1,189 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// Chapter 1: Arrays _________________________________________________ + +TEST_GROUP(ChapterOne){}; + +TEST(ChapterOne, IsUnique){ + auto const t1 = IsUnique("aabbcc", 6); + auto const t2 = IsUnique("abcdef", 6); + CHECK_FALSE(t1); + CHECK(t2); +} + +TEST(ChapterOne, CheckPermutations){ + const auto s1 = "Hello"; + const auto s2 = "olleH"; + const auto s3 = "Holaa"; + + auto const t1 = CheckPermutation(s1, s2, strlen(s1), strlen(s2)); + auto const t2 = CheckPermutation(s1, s3, strlen(s1), strlen(s3)); + CHECK(t1); + CHECK_FALSE(t2); + +} + +// Chapter 2: Linked Lists ___________________________________________ + +TEST_GROUP(ChapterTwo){}; +TEST(ChapterTwo, RemoveDups){ + auto dll = new DopeLinkedList(); + dll->AppendData(1); + dll->AppendData(2); + dll->AppendData(2); + dll->AppendData(3); + + remove_dups(dll); + auto node = dll->GetHead(); + + CHECK_EQUAL(1, node->GetData()); + node = node->GetNext(); + CHECK_EQUAL(2, node->GetData()); + node = node->GetNext(); + CHECK_EQUAL(3, node->GetData()); + delete dll; +} + +TEST(ChapterTwo, Reverse){ + auto dll = new DopeLinkedList(); + dll->AppendData(1); + dll->AppendData(2); + dll->AppendData(3); + dll->AppendData(100); + + auto node = dll->GetHead(); + CHECK_EQUAL(1, node->GetData()); + reverse_linkedlist(dll); + node = dll->GetHead(); + CHECK_EQUAL(100, node->GetData()); + node = node->GetNext(); + CHECK_EQUAL(3, node->GetData()); + node = node->GetNext(); + CHECK_EQUAL(2, node->GetData()); + node = node->GetNext(); + CHECK_EQUAL(1, node->GetData()); + CHECK_EQUAL(nullptr, node->GetNext()); + + delete dll; +} + +// Chapter 3: Stacks and Queues ______________________________________ + +TEST_GROUP(ChapterThree){}; +TEST(ChapterThree, StackMin){ + auto stack = new DopeStack(); + stack->push(10); + stack->push(25); + stack->push(42); + stack->push(100); + stack->push(2); + + CHECK_EQUAL(2, stack->peek()); + CHECK_EQUAL(2, stack->pop()); + CHECK_EQUAL(100, stack->pop()); + CHECK_FALSE(stack->isEmpty()); + (void)stack->pop(); + (void)stack->pop(); + (void)stack->pop(); + CHECK_TRUE(stack->isEmpty()); + delete stack; +} + +// Chapter 4: Trees __________________________________________________ +TEST_GROUP(ChapterFour){}; +TEST(ChapterFour, BinaryTrees){ + auto btree = DopeBinTree(); + btree.insert(1); + btree.insert(2); + btree.insert(3); + btree.insert(4); + btree.insert(5); + btree.insert(6); + btree.insert(7); + btree.insert(8); + + + auto root = btree.getRoot(); + CHECK_EQUAL(1, root->GetData()); + CHECK_EQUAL(2, root->GetLeft()->GetData()); + CHECK_EQUAL(3, root->GetRight()->GetData()); + CHECK_EQUAL(4, root->GetLeft()->GetLeft()->GetData()); + CHECK_EQUAL(5, root->GetLeft()->GetRight()->GetData()); + CHECK_EQUAL(6, root->GetRight()->GetLeft()->GetData()); + CHECK_EQUAL(7, root->GetRight()->GetRight()->GetData()); + CHECK_EQUAL(8, root->GetLeft()->GetLeft()->GetLeft()->GetData()); + +} + +TEST(ChapterFour, TreeTraversal){ + auto btree = DopeBinTree(); + btree.insert(1); // Root + btree.insert(2); + btree.insert(3); + btree.insert(4); + btree.insert(5); + btree.insert(6); + btree.insert(7); + + int array[10] = {}; + + btree.RecPreOrderTraversalArray(array); + CHECK_EQUAL(1, array[0]); + CHECK_EQUAL(2, array[1]); + CHECK_EQUAL(4, array[2]); + CHECK_EQUAL(5, array[3]); + CHECK_EQUAL(3, array[4]); + CHECK_EQUAL(6, array[5]); + CHECK_EQUAL(7, array[6]); + + std::fill(array, array+10, 0); + + btree.RecInOrderTraversalArray(array); + CHECK_EQUAL(4, array[0]); + CHECK_EQUAL(2, array[1]); + CHECK_EQUAL(5, array[2]); + CHECK_EQUAL(1, array[3]); + CHECK_EQUAL(6, array[4]); + CHECK_EQUAL(3, array[5]); + CHECK_EQUAL(7, array[6]); + + std::fill(array, array+10, 0); + + btree.RecPostOrderTraversalArray(array); + CHECK_EQUAL(4, array[0]); + CHECK_EQUAL(5, array[1]); + CHECK_EQUAL(2, array[2]); + CHECK_EQUAL(6, array[3]); + CHECK_EQUAL(7, array[4]); + CHECK_EQUAL(3, array[5]); + CHECK_EQUAL(1, array[6]); + +} + +TEST(ChapterFour, MinHeap){ + auto btree = DopeBinTree(); + + +} + +// Chapter 12: C/C++ _________________________________________________ + +TEST_GROUP(ChapterTwelve){}; +TEST(ChapterTwelve, ReverseString){ + char s1[] = "Hello"; + reverse_str(s1); + STRCMP_EQUAL("olleH", s1); +} + +int main (int ac, char** av){ + return CommandLineTestRunner::RunAllTests(ac, av); +}