diff --git a/ListNode.hpp b/ListNode.hpp new file mode 100644 index 0000000..92d40ba --- /dev/null +++ b/ListNode.hpp @@ -0,0 +1,10 @@ +#pragma once + +struct ListNode { + int val; + ListNode *next; + bool visited; + ListNode() : val(0), next(nullptr), visited(false) {} + ListNode(int x) : val(x), next(nullptr), visited(false) {} + ListNode(int x, ListNode *next) : val(x), next(next), visited(false) {} +}; diff --git a/Solution.hpp b/Solution.hpp new file mode 100644 index 0000000..f81c8a9 --- /dev/null +++ b/Solution.hpp @@ -0,0 +1,150 @@ + +void adjustChild(int32_t begin, int32_t childIndex, ListNode** array, int32_t& numEntries) { + if (childIndex < 1) { + return; + } + int32_t parentIndex = (childIndex - 1) / 2; + while ((begin + childIndex) != begin) { + if (array[begin + childIndex]->val < array[begin + parentIndex]->val) { + ListNode* temp = array[begin + childIndex]; + array[begin + childIndex] = array[begin + parentIndex]; + array[begin + parentIndex] = temp; + childIndex = parentIndex; + parentIndex = (childIndex - 1) / 2; + } else { + break; + } + } +} + +void adjustRoot(int32_t begin, ListNode** array, int32_t& numEntries) { + int32_t maxIndex = numEntries-1; + if (maxIndex == 0) { + return; + } + int32_t parentIndex = 0; + + while (parentIndex < maxIndex) { + int32_t leftIndex = (parentIndex * 2) + 1; + if (leftIndex > maxIndex) { + return; + } + int32_t rightIndex = (parentIndex * 2) + 2; + rightIndex = rightIndex > maxIndex ? leftIndex : rightIndex; + bool swapped = false; + if (rightIndex != leftIndex) { + if (array[begin+rightIndex]->val < array[begin+leftIndex]->val) { + if (array[begin+rightIndex]->val < array[begin+parentIndex]->val) { + ListNode* temp = array[begin+rightIndex]; + array[begin+rightIndex] = array[begin+parentIndex]; + array[begin+parentIndex] = temp; + parentIndex = rightIndex; + swapped = true; + } + } else { + if (array[begin+leftIndex]->val < array[begin+parentIndex]->val) { + ListNode* temp = array[begin+leftIndex]; + array[begin+leftIndex] = array[begin+parentIndex]; + array[begin+parentIndex] = temp; + parentIndex = leftIndex; + swapped = true; + } + } + } else { + if (array[begin+leftIndex]->val < array[begin+parentIndex]->val) { + ListNode* temp = array[begin+leftIndex]; + array[begin+leftIndex] = array[begin+parentIndex]; + array[begin+parentIndex] = temp; + parentIndex = leftIndex; + swapped = true; + } + } + if (not swapped) { + break; + } + } +} + + +inline ListNode* popAndHeapify(ListNode** array, int32_t& numEntries) { + ListNode* t = array[0]; + if (numEntries == 1) { + numEntries = 0; + return t; + } + + array[0] = array[numEntries-1]; + --numEntries; + adjustRoot(0, array, numEntries); + return t; +} + +inline ListNode* replaceFrontAndHeapify(ListNode* t, ListNode**array, int32_t& numEntries) { + auto ret = array[0]; + array[0] = t; + adjustRoot(0, array, numEntries); + return ret; +} + +class Solution { +public: + static inline bool compareAscending(const ListNode* parent, const ListNode* child) { + if (child->val < parent->val) { + return true; + } + return false; + } + + ListNode* mergeKLists(vector& lists) { + int32_t numLists = lists.size(); + int32_t emptyLists = 0; + + if (numLists == 0) { + return nullptr; + } + + ListNode* myVector[10000]; + int32_t numEntries = 0; + + for (auto& ln : lists) { + if (ln) { + myVector[numEntries++] = ln; + ln = ln->next; + } else { + ++emptyLists; + } + } + + if (emptyLists == numLists) { + return nullptr; + } + + if (numEntries > 1) { + int32_t i = 1; + do { + adjustChild(0, i++, myVector, numEntries); + } while (i != numEntries); + } + + ListNode* sortedList = nullptr; + ListNode* sortedListTail = nullptr; + + while (numEntries != 0) { + ListNode* temp; + if (myVector[0]->next == nullptr) { + temp = popAndHeapify(myVector, numEntries); + } else { + temp = replaceFrontAndHeapify(myVector[0]->next, myVector, numEntries); + } + + if (sortedList == nullptr) { + sortedList = sortedListTail = temp; + } else { + sortedListTail->next = temp; + sortedListTail = temp; + } + + } + return sortedList; + } +}; \ No newline at end of file diff --git a/Verification.hpp b/Verification.hpp new file mode 100644 index 0000000..522b602 --- /dev/null +++ b/Verification.hpp @@ -0,0 +1,51 @@ +#include "ListNode.hpp" + +class Verification { + public: + Verification() {} + + bool verify(ListNode* userOutput, ListNode* referenceOutput, string& developer) { + ListNode* entry = userOutput; + ListNode* refEntry = referenceOutput; + + bool success = true; + + while (refEntry != nullptr) { + if (entry == nullptr || refEntry->val != entry->val) { + success = false; + break; + } + refEntry = refEntry->next; + entry = entry->next; + } + if (entry != nullptr) { + success = false; + } + + refEntry = referenceOutput; + entry = userOutput; + + if (not success) { + cout << "Expected output:" << endl << "["; + while (refEntry != nullptr) { + cout << refEntry->val; + refEntry = refEntry->next; + if (refEntry) { + cout << ","; + } + } + cout << "]" << endl; + + cout << "Program output:" << endl << "["; + while (entry != nullptr) { + cout << entry->val; + entry = entry->next; + if (entry) { + cout << ","; + } + } + cout << "]" << endl; + } + return success; + } +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0b34140 --- /dev/null +++ b/main.cpp @@ -0,0 +1,210 @@ +/****************************************************************************** + + Online C++ Compiler. + Code, Compile, Run and Debug C++ program online. +Write your code in this editor and press "Run" button to compile and execute it. + +*******************************************************************************/ +#include +#include +#include +#include +#include + +using namespace std; + +#include "ListNode.hpp" +class Solution; +class Verification; + +#include "Solution.hpp" +#include "refSolution.hpp" +#include "Verification.hpp" + + +#include +#include +#include +#define bad_news(msg) \ + do { perror(msg); exit(EXIT_FAILURE); } while (0) + +int32_t numLists = 10000; +int32_t minEntryValue = -10000; +int32_t maxEntryValue = 10000; + +int32_t minEntriesPerList = 3; +int32_t maxEntriesPerList = 10; + +bool printInput = false; + +vector vecList; +ListNode *ret = nullptr; + +ListNode *retRef = nullptr; +vector refVecList; +int64_t userTime = 0L; + +void runTestProgram(); +void runReferenceProgram(); +int64_t refTime = 0L; + +#define STACKINT64 (256*1024) +static ucontext_t uctx_main, uctx_func1, uctx_func2; +int64_t func1_stack[STACKINT64]; // 2M stack +int64_t func2_stack[STACKINT64]; // 2M stack +int64_t userStackUsageKbytes = 0L; +int64_t refStackUsageKbytes = 0L; + + + +int main(int argc, char** argv) +{ + string developer; + + if (argc == 2) { + developer = argv[1]; + } else { + developer = "Unnamed "; + } + + int32_t entrySpan = maxEntryValue - minEntryValue; + + cout << "Testing code written by " << developer << endl; + + srand(0); + + //numLists = 100; //TODO remove me! + + for (int32_t i = 0; i < numLists; ++i) { + int32_t numEntries = rand(); + numEntries %= maxEntriesPerList; + if (numEntries < minEntriesPerList) { + numEntries += minEntriesPerList; + } + + vector list; + for (int32_t j = 0; j < numEntries; ++j) { + list.push_back((rand() % entrySpan) + minEntryValue); + } + + sort(list.begin(), list.end()); + ListNode* head = nullptr; + ListNode* tail = nullptr; + + ListNode* refHead = nullptr; + ListNode* refTail = nullptr; + + for (const auto& entry : list) { + ListNode* ln = new ListNode(entry); + ListNode* rln = new ListNode(entry); + + if (head == nullptr) { + head = tail = ln; + refHead = refTail = rln; + } else { + tail->next = ln; + tail = ln; + refTail->next = rln; + refTail = rln; + } + } + vecList.push_back(head); + refVecList.push_back(refHead); + } + + if (printInput) { + for (const auto& list : vecList) { + for (ListNode* ln = list; ln != nullptr; ln = ln->next) { + cout << " " << ln->val; + } + cout << endl; + } + } + + int32_t actualMeasured = 0; + for(auto ln = vecList[0]; ln != nullptr; ln = ln->next) { + ++actualMeasured; + } + + + + + for (int64_t j = 0; j < STACKINT64; ++j) { + func1_stack[j] = 0xdeadbeefdeadbeef; + func2_stack[j] = 0xdeadbeefdeadbeef; + } + + if (getcontext(&uctx_func1) == -1) { + bad_news("Failed retrieving current context (getcontext())"); + } + uctx_func1.uc_stack.ss_sp = func1_stack; + uctx_func1.uc_stack.ss_size = sizeof(func1_stack); + uctx_func1.uc_link = &uctx_main; + makecontext(&uctx_func1, runTestProgram, 0); + + if (getcontext(&uctx_func2) == -1) { + bad_news("Failed retrieving current context (getcontext())"); + } + uctx_func2.uc_stack.ss_sp = func2_stack; + uctx_func2.uc_stack.ss_size = sizeof(func2_stack); + /* Successor context is func1() */ + uctx_func2.uc_link = &uctx_func1; + makecontext(&uctx_func2, runReferenceProgram, 0); + + cout << "Running reference program." << endl; + if (swapcontext(&uctx_main, &uctx_func2) == -1) { + bad_news("Failed calling reference program context."); + } + + + cout << endl << "======================================================" << endl << endl; + + bool success = Verification().verify(ret, retRef, developer); + + if (success) { + cout << "Time taken to execute program developed by " << developer << ": " << userTime << " usec. Stack space used : " << userStackUsageKbytes << " KBytes" << endl; + cout << "Time taken to execute reference solution " << refTime << " usec. Stack space used : " << refStackUsageKbytes << " KBytes" << endl; + } + cout << endl << "======================================================" << endl << endl; + return 0; +} + +void runTestProgram() { + + cout << "In user test code" << endl; + auto start = chrono::steady_clock::now(); + ret = Solution().mergeKLists(vecList); + auto end = chrono::steady_clock::now(); + + userTime = chrono::duration_cast(end-start).count(); + int64_t k = 0L; + for (; k < STACKINT64; ++k) { + if (func1_stack[k] != 0xdeadbeefdeadbeef) { + break; + } + } + userStackUsageKbytes = ((STACKINT64-k)*sizeof(int64_t))/1024; + if (swapcontext(&uctx_func1, &uctx_func2) == -1) { + bad_news("Failed switching over to main program context."); + } +} + +void runReferenceProgram() { + + cout << "In reference code" << endl; + auto start = chrono::steady_clock::now(); + retRef = refSolution().mergeKLists(refVecList); + auto end = chrono::steady_clock::now(); + + refTime = chrono::duration_cast(end-start).count(); + int64_t k = 0L; + for (; k < STACKINT64; ++k) { + if (func2_stack[k] != 0xdeadbeefdeadbeef) { + break; + } + } + refStackUsageKbytes = ((STACKINT64-k)*sizeof(int64_t))/1024; + if (swapcontext(&uctx_func2, &uctx_func1) == -1) { + bad_news("Failed switching over to user program context."); + } +} diff --git a/refSolution.hpp b/refSolution.hpp new file mode 100644 index 0000000..bd3d842 --- /dev/null +++ b/refSolution.hpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +class refSolution { +public: + #define MAXPOSITIVEVAL 10000 + #define MAXVALRANGE ((2*MAXPOSITIVEVAL)+1) //from -10000 to +10000 + #define SUBRANGEVAL 256 // Track whether any buckets grouped by SUBRANGEVAL have non-null LinkNode* + + ListNode* mergeKLists(vector& lists) { + int16_t numLists = lists.size(); + + ListNode* hashHead[MAXVALRANGE]; + ListNode* hashTail[MAXVALRANGE]; + int32_t hashNodeCount[MAXVALRANGE]; + + memset(hashHead, 0, sizeof(ListNode *)*MAXVALRANGE); + memset(hashTail, 0, sizeof(ListNode *)*MAXVALRANGE); + + if (numLists == 0) { + return nullptr; + } + + int32_t numNodes = 0; + int32_t rangeCounters[(MAXVALRANGE/SUBRANGEVAL)+1]; + memset(rangeCounters, 0, sizeof(int32_t)*((MAXVALRANGE/SUBRANGEVAL)+1)); + memset(hashNodeCount, 0, sizeof(int32_t)*(MAXVALRANGE)); + + for (auto ln : lists) { + while (ln) { + ++numNodes; + ListNode* next = ln->next; + int16_t val = ln->val; + ++rangeCounters[(val+MAXPOSITIVEVAL)/SUBRANGEVAL]; + if (hashHead[val+MAXPOSITIVEVAL] == nullptr) { + hashHead[val+MAXPOSITIVEVAL] = hashTail[val+MAXPOSITIVEVAL] = ln; + } else { + hashTail[val+MAXPOSITIVEVAL]->next = ln; + hashTail[val+MAXPOSITIVEVAL] = ln; + } + ++hashNodeCount[val+MAXPOSITIVEVAL]; + ln->next = nullptr; + assert(ln->visited == false); + ln->visited = true; + ln = next; + } + } + + ListNode* sortedList = nullptr; + ListNode* sortedListTail = nullptr; + + for (int16_t i = 0; i < MAXVALRANGE;) { + if (rangeCounters[i/SUBRANGEVAL] == 0) { + i += SUBRANGEVAL; + continue; + } + if (hashHead[i]) { + if (sortedList == nullptr) { + sortedList = hashHead[i]; + sortedListTail = hashTail[i]; + } else { + sortedListTail->next = hashHead[i]; + sortedListTail = hashTail[i]; + } + numNodes -= hashNodeCount[i]; + } + ++i; + if (numNodes == 0) { + break; + } + } + + return sortedList; + } +}; \ No newline at end of file