diff --git a/c-cpp/07_linkedlist/SingleList.cpp b/c-cpp/07_linkedlist/SingleList.cpp index a43079c..5f07863 100644 --- a/c-cpp/07_linkedlist/SingleList.cpp +++ b/c-cpp/07_linkedlist/SingleList.cpp @@ -6,7 +6,7 @@ using namespace std; class CElement; /*** - * @brief 单链表容器 + * @brief */ class CSingleList { @@ -15,61 +15,61 @@ public: ~CSingleList(); /** - * @brief 插入..链表末尾插入 - * @return 成功返回非空指针,否则失败 + * @brief ..ĩβ + * @return ɹطǿָ,ʧ */ CElement* Insert(void* lpData, int iDataSize); /** - * @brief 插入..链表指定位置插入 - * @return 成功返回非空指针,否则失败 + * @brief ..ָλò + * @return ɹطǿָ,ʧ */ CElement* Insert(CElement* lpElement, void* lpData, int iDataSize); /** - * @brief 删除 + * @brief ɾ */ void Delete(CElement*); /** - * @brief 链首 + * @brief */ CElement* Begin(); /** - * @brief 下一个元素 + * @brief һԪ */ CElement* Next(); /*** - * @brief 链尾 + * @brief β */ CElement* End(); /** - * @brief 是否是空链表 - * @return 空返回TRUE,否则返回FALSE + * @brief Ƿǿ + * @return շTRUE򷵻FALSE */ bool Empty(); /** - * @brief 反转 + * @brief ת */ void Reverse(); /** - * @brief 检测环 - * @return 返回TRUE时表示链表存在环,否则不存在环. + * @brief ⻷ + * @return TRUEʱʾڻ,򲻴ڻ. */ bool CheckCircle(); /** - * @brief 合并2个有序的链表 + * @brief ϲ2 */ void Merge(CSingleList& lst, std::function); /** - * @brief 删除倒数第K个结点 + * @brief ɾK */ void DeleteLastKth(int k); /** - * @brief 求中间节点 + * @brief мڵ */ CElement* Center(); private: @@ -80,18 +80,18 @@ private: CSingleList(CSingleList const & rhs); CSingleList& operator= (CSingleList const& rhs); private: - /**头结点*/ + /**ͷ*/ CElement* m_lpHead; - /**哨兵*/ + /**ڱ*/ CElement* m_lpSentinel; - /**空结点,用于End()返回 */ + /**ս㣬End() */ CElement* m_lpNull; - /**当前结点. 枚举时使用. */ + /**ǰ. öʱʹ. */ CElement* m_lpCur; }; /*** - * @brief 单链表结点元素. + * @brief Ԫ. */ class CElement { @@ -101,11 +101,11 @@ protected: ~CElement(); public: /*** - * @brief 获取数据指针 + * @brief ȡָ */ void* GetDataPtr(); protected: - /**下一个结点*/ + /**һ*/ CElement* m_lpNext; void* m_lpData; }; @@ -113,7 +113,7 @@ protected: void CreateList(CSingleList& lst) { - //循环插入元素到链表尾 + //ѭԪصβ for(int i=1; i<10;i++) { int* p = new int(); @@ -134,15 +134,15 @@ void PrintList(CSingleList& lst) int main() { { - /// 链表的基本操作,插入/枚举/删除 + /// Ļ/ö/ɾ CSingleList lst; CElement* lpElement = NULL; CreateList(lst); - std::cout<<"枚举链表当前的元素"<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<m_lpNext = lpCurElement->m_lpNext; lpCurElement->m_lpNext = lpNewElement; - }else{//插入到指定元素的前面 + }else{//뵽ָԪصǰ CElement* lpIter = m_lpSentinel; while(NULL != lpIter) { @@ -443,11 +443,13 @@ bool CSingleList::CheckCircle() return false; } +/** + * ϲ2 +*/ void CSingleList::Merge(CSingleList& lst, std::function fnCompare) { CElement* lpL1 = Begin(); CElement* lpL2 = lst.Begin(); - CElement* lpTail = NULL; if(!fnCompare) { @@ -458,7 +460,26 @@ void CSingleList::Merge(CSingleList& lst, std::function { if(lpL1 != End()) { + /** + * Ҫȷλ + * + * 1,2; 1 <- 2, 2ϲ1 + * + * 1ԪС2еԪأѭ1д2еĵǰԪصԪ + * 1ҵĵԪλ[A]ʱ2еĵǰԪز뵽Ԫλ[A]ǰ; + * 1вλ1ĩλԪ + */ iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr()); + if(iRet < 0){ + lpL1 = Next(); + while(lpL1 != End()){ + iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr()); + if(iRet > 0){ + break; + } + lpL1 = Next(); + } + } }else{ iRet = -1; } @@ -468,17 +489,13 @@ void CSingleList::Merge(CSingleList& lst, std::function lpNewElement->m_lpData = lpL2->GetDataPtr(); if(lpL1 != End()) { - Insert(lpNewElement,lpL1, iRet <= 0); + Insert(lpNewElement,lpL1, iRet < 0); }else{ - if(NULL == lpTail) - { - lpTail = Tail(); - } + CElement* lpTail = Tail(); Insert(lpNewElement,lpTail); } } lpL2 = lst.Next(); - lpL1 = Next(); } } diff --git a/c-cpp/13_sorts/.gitkeep b/c-cpp/13_sorts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/c-cpp/13_sorts/bucket_sort.hpp b/c-cpp/13_sorts/bucket_sort.hpp new file mode 100644 index 0000000..41133ff --- /dev/null +++ b/c-cpp/13_sorts/bucket_sort.hpp @@ -0,0 +1,43 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef SORTS_BUCKET_SORT_HPP_ +#define SORTS_BUCKET_SORT_HPP_ + +#include +#include +#include +#include + +template ::value_type, + typename Compare = std::less> +void bucket_sort(IterT first, IterT last, Compare comp = Compare()) { + const T min = *std::min_element(first, last), max = *std::max_element(first, last); + const T range = max + 1 - min; + const size_t bucket_num = (range - 1) / BucketSize + 1; + + std::vector> buckets(bucket_num); + for (auto b : buckets) { + b.reserve(2 * BucketSize); + } + + for (IterT i = first; i != last; ++i) { + size_t idx = (*i - min) / BucketSize; + buckets[idx].emplace_back(*i); + } + + IterT dest = first; + for (auto b : buckets) { + std::sort(b.begin(), b.end(), comp); + std::copy(b.begin(), b.end(), dest); + dest += b.size(); + } + + return; +} + +#endif // SORTS_BUCKET_SORT_HPP_ + diff --git a/c-cpp/13_sorts/bucket_sort_test.cc b/c-cpp/13_sorts/bucket_sort_test.cc new file mode 100644 index 0000000..3199e8a --- /dev/null +++ b/c-cpp/13_sorts/bucket_sort_test.cc @@ -0,0 +1,33 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include +#include + +#include "bucket_sort.hpp" + +template > +void test_bucket_sort(Container cont, Compare comp = Compare()) { + bucket_sort(cont.begin(), cont.end(), comp); + std::transform(cont.begin(), cont.end(), std::ostream_iterator(std::cout, " "), + [](T i){ return i; }); + std::cout << std::endl; +} + +int main() { + std::vector test{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9}; + + test_bucket_sort<2>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<3>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<4>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<5>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<6>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + + return 0; +} + diff --git a/c-cpp/13_sorts/counting_sort.hpp b/c-cpp/13_sorts/counting_sort.hpp new file mode 100644 index 0000000..5e7f782 --- /dev/null +++ b/c-cpp/13_sorts/counting_sort.hpp @@ -0,0 +1,40 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef SORTS_COUNTING_SORT_HPP_ +#define SORTS_COUNTING_SORT_HPP_ + +#include +#include +#include +#include + +template ::value_type> +void counting_sort(IterT first, IterT last) { + const auto len = std::distance(first, last); + if (len < 2) { return; } + + const T max = *std::max_element(first, last); + if (max == 0) { return; } + + std::vector counter(max + 1); + for (IterT i = first; i != last; ++i) { + ++counter[*i]; + } + for (size_t i = 1; i != max + 1; ++i) { + const size_t j = max - i; + counter[j] += counter[j + 1]; // Liam Huang: count of numbers that is not less than j. + } + + std::vector temp(len); + for (IterT i = first; i != last; ++i) { + temp[len - counter[*i]] = *i; + --counter[*i]; // Liam Huang: stable for relative position. + } + std::copy(temp.begin(), temp.end(), first); +} + +#endif // SORTS_COUNTING_SORT_HPP_ + diff --git a/c-cpp/13_sorts/counting_sort_test.cc b/c-cpp/13_sorts/counting_sort_test.cc new file mode 100644 index 0000000..4e0814a --- /dev/null +++ b/c-cpp/13_sorts/counting_sort_test.cc @@ -0,0 +1,36 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include +#include + +#include "counting_sort.hpp" + +template +void test_counting_sort(Container cont) { + counting_sort(cont.begin(), cont.end()); + std::transform(cont.begin(), cont.end(), std::ostream_iterator(std::cout, " "), + [](T i){ return i; }); + std::cout << std::endl; +} + +int main() { + // Liam Huang: pi for test + const std::vector test1{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3}; + const std::vector test2{2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9}; + const std::vector test3{5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9}; + const std::vector test4{3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4}; + const std::vector test5{5, 9, 2, 3, 0, 7, 8, 1, 6, 4, 0, 6, 2, 8, 6}; + + test_counting_sort(test1); // 1 1 2 3 3 3 4 5 5 5 6 7 8 9 9 9 + test_counting_sort(test2); // 2 2 2 3 3 3 3 4 4 6 6 7 8 8 9 + test_counting_sort(test3); // 0 1 1 2 3 4 5 6 7 8 8 9 9 9 9 + test_counting_sort(test4); // 0 0 1 2 3 4 4 4 5 5 7 7 8 9 9 + test_counting_sort(test5); // 0 0 1 2 2 3 4 5 6 6 6 7 8 8 9 + + return 0; +} + diff --git a/c-cpp/15_bsearch/binary_search.c b/c-cpp/15_bsearch/binary_search.c new file mode 100644 index 0000000..1125fb5 --- /dev/null +++ b/c-cpp/15_bsearch/binary_search.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +typedef int(*bs)(int *arr, int size, int val); + +int binary_search_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (arr[mid] == val) + return mid; + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_r(arr, mid, val); + } + + return idx; +} + +int binary_search_i(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] == val) + return mid; + + if (arr[mid] < val) + low = mid + 1; + else + high = mid - 1; + } + + return -1; +} + +void iteratioin_test(bs binary_search) +{ + int arr[10] = {1, 4, 5, 9, 12, 19, 21, 28, 31, 36}; + int idx; + + idx = binary_search(arr, 10, 12); + if (idx != -1) + printf("find 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search(arr, 10, 13); + if (idx != -1) + printf("find 13 at %d\n", idx); + else + printf("13 not in arr \n"); + + idx = binary_search(arr, 10, 1); + if (idx != -1) + printf("find 1 at %d\n", idx); + else + printf("1 not in arr \n"); + + idx = binary_search(arr, 10, 36); + if (idx != -1) + printf("find 36 at %d\n", idx); + else + printf("36 not in arr \n"); + + idx = binary_search(arr, 10, 31); + if (idx != -1) + printf("find 31 at %d\n", idx); + else + printf("31 not in arr \n"); + +} + +int main() +{ + printf("=== Test iteration version:\n"); + iteratioin_test(binary_search_i); + + printf("=== Test recursive version:\n"); + iteratioin_test(binary_search_r); + + return 0; +} diff --git a/c-cpp/15_bsearch/bsearch.hpp b/c-cpp/15_bsearch/bsearch.hpp index e05f063..d55a086 100644 --- a/c-cpp/15_bsearch/bsearch.hpp +++ b/c-cpp/15_bsearch/bsearch.hpp @@ -8,19 +8,16 @@ #include #include -enum class BsearchPolicy { FIRST, LAST, UNSPECIFIED }; - // Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, // but with a bad time complexity. For better performance, iterators should meet // the RandomAccessIterator requirement. template ::value_type, - typename Compare> + typename Compare = std::less> IterT bsearch(IterT first, IterT last, ValueT target, - Compare comp, - BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + Compare comp = Compare()) { IterT result = last; while (std::distance(first, last) > 0) { IterT mid = first + std::distance(first, last) / 2; @@ -29,38 +26,12 @@ IterT bsearch(IterT first, } else if (comp(target, *mid)) { last = mid; } else { // equal - if (policy == BsearchPolicy::FIRST) { - if (mid == first or comp(*(mid - 1), *mid)) { - result = mid; - break; - } else { - last = mid; - } - } else if (policy == BsearchPolicy::LAST) { - if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) { - result = mid; - break; - } else { - first = mid + 1; - } - } else { - result = mid; - break; - } + result = mid; + break; } } return result; } -template ::value_type, - typename Compare = std::less> -IterT bsearch(IterT first, - IterT last, - ValueT target, - BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { - return bsearch(first, last, target, Compare(), policy); -} - #endif // BSEARCH_BSEARCH_HPP_ diff --git a/c-cpp/15_bsearch/bsearch_test.cc b/c-cpp/15_bsearch/bsearch_test.cc index b862f63..e552991 100644 --- a/c-cpp/15_bsearch/bsearch_test.cc +++ b/c-cpp/15_bsearch/bsearch_test.cc @@ -8,10 +8,8 @@ #include "bsearch.hpp" template -void test_bsearch(const VecT& test, - T target, - BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { - auto it = bsearch(test.begin(), test.end(), target, policy); +void test_bsearch(const VecT& test, T target) { + auto it = bsearch(test.begin(), test.end(), target); std::cout << std::distance(test.begin(), it) << std::endl; } @@ -21,17 +19,9 @@ int main() { test_bsearch(test, 8); // 14 test_bsearch(test, -1); // 14 test_bsearch(test, 0); // 0, 1 - test_bsearch(test, 0, BsearchPolicy::FIRST); // 0 - test_bsearch(test, 0, BsearchPolicy::LAST); // 1 test_bsearch(test, 4); // 5, 6 - test_bsearch(test, 4, BsearchPolicy::FIRST); // 5 - test_bsearch(test, 4, BsearchPolicy::LAST); // 6 test_bsearch(test, 5); // 7, 8, 9, 10, 11 - test_bsearch(test, 5, BsearchPolicy::FIRST); // 7 - test_bsearch(test, 5, BsearchPolicy::LAST); // 11 test_bsearch(test, 7); // 13 - test_bsearch(test, 7, BsearchPolicy::FIRST); // 13 - test_bsearch(test, 7, BsearchPolicy::LAST); // 13 return 0; } diff --git a/c-cpp/16_bsearch/.gitkeep b/c-cpp/16_bsearch/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/c-cpp/16_bsearch/bsearch_variant.c b/c-cpp/16_bsearch/bsearch_variant.c new file mode 100644 index 0000000..8827127 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_variant.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include + + +int binary_search(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] == val) + return mid; + + if (arr[mid] < val) + low = mid + 1; + else + high = mid - 1; + } + + return -1; +} + +/* + * find the first index with *val* + * + * This is a little tricky because the calculation of mid is integer based, it + * will be cast to the lower bound of an integer. + * + * In case the [low, high] range is of size 1 or 2 and arr[mid] >= val, we will + * have: + * + * mid = (low + high) / 2 = low + * high = mid - 1 = low - 1 < low, which break the loop + * + */ +int binary_search_first(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + //printf("[%d-%d] %d\n", low, high, mid); + + if (arr[mid] >= val) + high = mid - 1; + else + low = mid + 1; + } + + //printf("[%d-%d] %d\n", low, high, mid); + if (arr[low] == val) + return low; + else + return -1; +} + +int binary_search_last(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] <= val) + low = mid + 1; + else + high = mid - 1; + } + + if (arr[high] == val) + return high; + else + return -1; +} + +int binary_search_first_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + // we find *val* at mid, try first half + if (arr[mid] == val) { + idx = binary_search_first_r(arr, mid, val); + return idx != -1 ? idx : mid; + } + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_first_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_first_r(arr, mid, val); + } + + return idx; +} + +int binary_search_last_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + // we find *val* at mid, try last half + if (arr[mid] == val) { + idx = binary_search_last_r(arr+mid+1, size-mid-1, val); + if (idx != -1) + mid += idx + 1; + return mid; + } + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_last_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_last_r(arr, mid, val); + } + + return idx; +} + +int binary_search_first_bigger(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] >= val) { + if (mid == 0 || arr[mid-1] < val) + return mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + + return -1; +} + +int binary_search_first_bigger_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + if (arr[mid] >= val) { + // find one bigger than val, try first half + idx = binary_search_first_bigger_r(arr, mid, val); + if (idx == -1) + idx = mid; + } else { + // the bigger one may sit in second half + idx = binary_search_first_bigger_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } + + return idx; +} + +int binary_search_last_smaller(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] <= val) { + if (mid == 0 || arr[mid+1] > val) + return mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + + return -1; +} + +int binary_search_last_smaller_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + if (arr[mid] <= val) { + // find one smaller than val, try second half + idx = binary_search_last_smaller_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + else + idx = mid; + } else { + // the smaller one may sit in first half + idx = binary_search_last_smaller_r(arr, mid, val); + } + + return idx; +} + +int main() +{ + int arr[10] = {1, 4, 5, 9, 12, 14, 19, 19, 31, 36}; + int idx; + + printf("Test Array:\n"); + for (idx = 0; idx < 10; idx++) + printf("%8d", arr[idx]); + printf("\n"); + + idx = binary_search_first(arr, 10, 19); + if (idx != -1) + printf("first 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_first_r(arr, 10, 19); + if (idx != -1) + printf("first 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_last(arr, 10, 19); + if (idx != -1) + printf("last 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_last_r(arr, 10, 19); + if (idx != -1) + printf("last 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_first_bigger(arr, 10, 12); + if (idx != -1) + printf("first bigger 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_first_bigger_r(arr, 10, 12); + if (idx != -1) + printf("first bigger 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_last_smaller(arr, 10, 12); + if (idx != -1) + printf("last smaller 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_last_smaller_r(arr, 10, 12); + if (idx != -1) + printf("last smaller 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + return 0; +} diff --git a/c-cpp/16_bsearch/bsearch_varients.hpp b/c-cpp/16_bsearch/bsearch_varients.hpp new file mode 100644 index 0000000..4ea5336 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_varients.hpp @@ -0,0 +1,90 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_ +#define BSEARCH_BSEARCH_VARIENTS_HPP_ + +#include +#include + +enum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER }; + +// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, +// but with a bad time complexity. For better performance, iterators should meet +// the RandomAccessIterator requirement. +template ::value_type, + typename Compare> +IterT bsearch(IterT first, + IterT last, + ValueT target, + Compare comp, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + IterT result = last; + while (std::distance(first, last) > 0) { + IterT mid = first + std::distance(first, last) / 2; + if (policy == BsearchPolicy::FIRST_NOT_LESS) { + if (!comp(*mid, target)) { + if (mid == first or comp(*(mid - 1), target)) { + result = mid; + break; + } else { + last = mid; + } + } else { + first = mid + 1; + } + } else if (policy == BsearchPolicy::LAST_NOT_GREATER) { + if (comp(target, *mid)) { + last = mid; + } else { + if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } + } else { // policy == UNSPECIFIED or FIRST or LAST + if (comp(*mid, target)) { + first = mid + 1; + } else if (comp(target, *mid)) { + last = mid; + } else { // equal + if (policy == BsearchPolicy::FIRST) { + if (mid == first or comp(*(mid - 1), *mid)) { + result = mid; + break; + } else { + last = mid; + } + } else if (policy == BsearchPolicy::LAST) { + if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } else { + result = mid; + break; + } + } + } + } + return result; +} + +template ::value_type, + typename Compare = std::less> +IterT bsearch(IterT first, + IterT last, + ValueT target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + return bsearch(first, last, target, Compare(), policy); +} + +#endif // BSEARCH_BSEARCH_VARIENTS_HPP_ + diff --git a/c-cpp/16_bsearch/bsearch_varients_test.cc b/c-cpp/16_bsearch/bsearch_varients_test.cc new file mode 100644 index 0000000..34a2252 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_varients_test.cc @@ -0,0 +1,41 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include + +#include "bsearch_varients.hpp" + +template +void test_bsearch(const VecT& test, + T target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + auto it = bsearch(test.begin(), test.end(), target, policy); + std::cout << std::distance(test.begin(), it) << std::endl; +} + +int main() { + std::vector test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 8}; // std::less() + + test_bsearch(test, 8); // 14 + test_bsearch(test, -1); // 14 + test_bsearch(test, 0); // 0, 1 + test_bsearch(test, 0, BsearchPolicy::FIRST); // 0 + test_bsearch(test, 0, BsearchPolicy::LAST); // 1 + test_bsearch(test, 4); // 5, 6 + test_bsearch(test, 4, BsearchPolicy::FIRST); // 5 + test_bsearch(test, 4, BsearchPolicy::LAST); // 6 + test_bsearch(test, 5); // 7, 8, 9, 10, 11 + test_bsearch(test, 5, BsearchPolicy::FIRST); // 7 + test_bsearch(test, 5, BsearchPolicy::LAST); // 11 + test_bsearch(test, 7, BsearchPolicy::FIRST_NOT_LESS); // 13 + test_bsearch(test, 7, BsearchPolicy::LAST_NOT_GREATER); // 12 + test_bsearch(test, 7, BsearchPolicy::FIRST); // 14 + test_bsearch(test, 8); // 13 + test_bsearch(test, 8, BsearchPolicy::FIRST); // 13 + test_bsearch(test, 8, BsearchPolicy::LAST); // 13 + + return 0; +} + diff --git a/c-cpp/17_skiplist/.gitkeep b/c-cpp/17_skiplist/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/c-cpp/17_skiplist/skiplist.hpp b/c-cpp/17_skiplist/skiplist.hpp new file mode 100644 index 0000000..9cbfb45 --- /dev/null +++ b/c-cpp/17_skiplist/skiplist.hpp @@ -0,0 +1,186 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/29. + */ + +#ifndef SKIPLIST_SKIPLIST_HPP_ +#define SKIPLIST_SKIPLIST_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class skiplist { + public: + using value_type = Value; + using hash_type = std::hash; + using key_type = typename hash_type::result_type; + using size_type = size_t; + + private: + struct InternalNode { + value_type value; + const key_type key; + std::vector forwards; // pointers to successor nodes + + InternalNode(const key_type& k, const size_type lv) + : value(), key(k), forwards(lv, nullptr) {} + InternalNode(const value_type& v, const size_type lv) + : value(v), key(hash_type()(value)), forwards(lv, nullptr) {} + }; + using node_type = InternalNode; + + private: + const size_type MAX_LEVEL = 16; + const double PROBABILITY = 0.5; + const unsigned int seed = + std::chrono::system_clock::now().time_since_epoch().count(); + mutable + std::default_random_engine generator = std::default_random_engine(seed); + mutable + std::binomial_distribution distribution = + std::binomial_distribution(MAX_LEVEL - 1, PROBABILITY); + node_type* head = nullptr; + node_type* nil = nullptr; + static const value_type default_value; + + public: + skiplist() { + key_type head_key = std::numeric_limits::min(); + key_type nil_key = std::numeric_limits::max(); + head = new node_type(head_key, MAX_LEVEL); + nil = new node_type(nil_key, MAX_LEVEL); + std::fill(head->forwards.begin(), head->forwards.end(), nil); + } + skiplist(std::initializer_list init) : skiplist() { + for (const value_type& v : init) { + insert(v); + } + } + skiplist(const skiplist& other) = delete; + skiplist(skiplist&& other) : + MAX_LEVEL(std::move(other.MAX_LEVEL)), + PROBABILITY(std::move(other.PROBABILITY)), + seed(std::move(other.seed)), + generator(std::move(other.generator)), + distribution(std::move(other.distribution)), + head(other.head), + nil(other.nil) { + other.head = nullptr; + other.nil = nullptr; + } + ~skiplist() { + node_type* node = head; + while (nullptr != node and nullptr != node->forwards[0]) { + node_type* tmp = node; + node = node->forwards[0]; + delete tmp; + } + delete node; + } + skiplist& operator=(const skiplist& other) = delete; + skiplist& operator=(skiplist&& other) = delete; + + private: + inline size_type get_random_level() const { + return distribution(generator); + } + static size_type get_node_level(const node_type* node) { + return node->forwards.size(); + } + static node_type* make_node(const value_type& v, const size_type lv) { + return new node_type(v, lv); + } + /** + * @brief returns a pointer to the first node such that + * node->key == hash_type()(v) and node->value == v. + */ + node_type* get_first_equal(const value_type& v) const { + const key_type target = hash_type()(v); + node_type* x = head; + for (size_type i = get_node_level(head); i > 0; --i) { + while (x->forwards[i - 1]->key < target or + x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) { + x = x->forwards[i - 1]; + } + } + return x->forwards[0]; + } + /** + * @brief returns a collection of nodes. + * returns[i] is the pointer to the last node at level i + 1 + * such that returns[i]->key < hash_type()(v) or + * returns[i]->key == hash_type()(v) but returns[i]->value != v. + */ + std::vector get_predecessors(const value_type& v) const { + const key_type target = hash_type()(v); + std::vector results(get_node_level(head), nullptr); + node_type* x = head; + for (size_type i = get_node_level(head); i > 0; --i) { + while (x->forwards[i - 1]->key < target or + x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) { + x = x->forwards[i - 1]; + } + results[i - 1] = x; + } + return results; + } + + public: + const value_type& find(const value_type& target) { + node_type* x = get_first_equal(target); + if (nullptr != x and nil != x and x->value == target) { + return x->value; + } else { + return default_value; + } + } + void insert(const value_type& value) { + std::vector preds = get_predecessors(value); + const size_type new_node_lv = get_random_level(); + node_type* new_node = make_node(value, new_node_lv); + for (size_type i = 0; i != new_node_lv; ++i) { + new_node->forwards[i] = preds[i]->forwards[i]; + preds[i]->forwards[i] = new_node; + } + } + void erase(const value_type& value) { + std::vector preds = get_predecessors(value); + + node_type* node = preds[0]->forwards[0]; + if (node == nil or node->value != value) { return; } + + for (size_type i = 0; i != get_node_level(node); ++i) { + preds[i]->forwards[i] = node->forwards[i]; + } + delete node; + } + void print(std::ostream& os) const { + node_type* list = head->forwards[0]; + os << "{"; + + while (list != nil) { + os << "key: " << list->key << " value: " << list->value + << " level: " << get_node_level(list); + + list = list->forwards[0]; + + if (list != nil) os << " : "; + + os << "\n"; + } + os << "}\n"; + } +}; + +template +const typename skiplist::value_type skiplist::default_value = + typename skiplist::value_type(); + +#endif // SKIPLIST_SKIPLIST_HPP_ + diff --git a/c-cpp/17_skiplist/skiplist_test.cc b/c-cpp/17_skiplist/skiplist_test.cc new file mode 100644 index 0000000..ced124e --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_test.cc @@ -0,0 +1,67 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ + +#include +#include + +#include "skiplist.hpp" + +int main() { + // 1. Initialize a skip list for test + // * default constructor + // * constructor with initializer list + // * insert + skiplist ss{"1", "2", "3", "4", "5"}; + + // 1a. show + // * print + ss.print(std::cout); + std::cout << std::endl; + + // 2. move construction + // * move constructor + skiplist s(std::move(ss)); + + // 2a. show + // * print + s.print(std::cout); + std::cout << std::endl; + + // 3.a find something doesn't exist. + // * find + auto f = s.find("0"); + if (!f.empty()) { + std::cout << "Node found!\nvalue: " << f << '\n'; + } else { + std::cout << "Node NOT found!\n"; + } + + // 3.b find something does exist. + // * find + auto ff = s.find("1"); + if (!ff.empty()) { + std::cout << "Node found!\tvalue: " << ff << '\n'; + } else { + std::cout << "Node NOT found!\n"; + } + + // 4. insert() - reassign + s.insert("TEST"); + + // 4a. print() + s.print(std::cout); + std::cout << std::endl; + + // 5. erase() + s.erase("TEST"); + + // 5a. print(); + s.print(std::cout); + std::cout << std::endl; + + std::cout << "\nDone!\n"; + + return 0; + // 6. destructor +} diff --git a/c-cpp/17_skiplist/skiplist_tr.hpp b/c-cpp/17_skiplist/skiplist_tr.hpp new file mode 100644 index 0000000..9ea72b2 --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_tr.hpp @@ -0,0 +1,366 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ + +#ifndef SKIPLIST_SKIPLIST_TR_HPP_ +#define SKIPLIST_SKIPLIST_TR_HPP_ + +#ifdef LIAM_UT_DEBUG_ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace skiplist_detail { +template +struct InternalNode { + using iterator = typename std::list::iterator; + const Key key; + std::multiset values; + std::vector forwards; + + InternalNode() = delete; + explicit InternalNode(const Key& k) : key(k) {} +}; + +template +class random_level { + private: + mutable std::random_device rd; + mutable std::mt19937 gen = std::mt19937(rd()); + mutable std::binomial_distribution dist; + + public: + random_level(IntType max_level, double prob) : dist(max_level - 1, prob) {} + inline IntType operator()() const { return dist(gen); } +}; +} // namespace skiplist_detail + +enum class erase_policy { ALL, SINGLE }; + +template , + size_t Factor = 2> +class skiplist { + public: + using value_type = Value; + using size_type = size_t; + using hasher = Hash; + using hash_type = typename Hash::result_type; + using compare = std::less; + using node_type = skiplist_detail::InternalNode; + using container = std::list; + using iterator = typename container::iterator; + using const_iterator = typename container::const_iterator; + static_assert(std::is_same::value, + "STATIC ASSERT FAILED! iterator type differs."); + + private: + size_type max_lv_ = 2; + double prob_ = 0.5; + mutable skiplist_detail::random_level rl_; + container cont_; + + public: + skiplist() : rl_(max_lv_, prob_) { + init_internally(); + } + explicit skiplist(const size_type max_lv, const double prob = 0.5) + : max_lv_(max_lv), prob_(prob), rl_(max_lv_, prob_) { + init_internally(); + } + skiplist(skiplist&& other) = default; + skiplist& operator=(skiplist&& other) = default; + ~skiplist() = default; + template + skiplist(InputIt first, InputIt last) : skiplist() { + using value_type_in_iter = typename std::iterator_traits::value_type; + static_assert(std::is_same::value, + "STATIC ASSERT FAILED! Value in InputIt should be the same to value_type."); + for (InputIt i = first; i != last; ++i) { + insert(*i); + } + } + skiplist(std::initializer_list init) : skiplist(init.begin(), init.end()) {} + + private: // noncopyable + skiplist(const skiplist&) = delete; + skiplist& operator=(const skiplist&) = delete; + + private: + void init_internally() { + const hash_type tail_key = std::numeric_limits::max(); + node_type tail(tail_key); + tail.forwards.resize(max_lv_); + std::fill(tail.forwards.begin(), tail.forwards.end(), cont_.end()); + cont_.insert(cont_.begin(), std::move(tail)); + + const hash_type head_key = std::numeric_limits::min(); + node_type head(head_key); + head.forwards.resize(max_lv_); + cont_.insert(cont_.begin(), std::move(head)); + std::fill(cont_.begin()->forwards.begin(), cont_.begin()->forwards.end(), + std::next(cont_.begin())); + +#ifdef LIAM_UT_DEBUG_ + assert(cont_.begin()->key == head_key); + for (auto it : cont_.begin()->forwards) { + assert(it->key == tail_key); + } + for (auto it : std::next(cont_.begin())->forwards) { + assert(it == cont_.end()); + } + std::cerr << "UT_DEBUG: all assert in init_internally() success!\n"; +#endif + + return; + } + /** + * @brief return a const_iterator points to the last element + * such that its hash_key <= target_hash_key + */ + const_iterator find_helper(const hash_type& key) const { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "Keys contained in the list: "; + for (auto node : cont_) { + std::cerr << node.key << ' '; + } + std::cerr << '\n'; + std::cerr << "Target key: " << key << '\n'; +#endif + const_iterator iter = begin(); + for (size_type i = 0; i != max_lv_; ++i) { + size_type focus = max_lv_ - 1 - i; + // invariant: iter->key <= key + while (not compare()(key, iter->forwards[focus]->key)) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "i: " << i << " focus: " << focus << ". " + << "since iter->forwards[focus]->key[" << iter->forwards[focus]->key + << "] <= key[" << key << "], "; +#endif + iter = iter->forwards[focus]; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "step forward iter to [" << iter->key << "]\n"; +#endif + } + // result: iter->key <= key < iter->forwards[focus]->key +#ifdef LIAM_UT_DEBUG_ + std::cerr << "The following fact holds at level " << focus + << ": iter->key[" << iter->key << "] <= key[" + << key << "] < iter->forwards[focus]->key[" << iter->forwards[focus]->key + <<"].\n"; +#endif + } + return iter; + } + std::vector find_predecessors(const hash_type& key, const size_type& lv) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "Keys contained in the list: "; + for (auto node : cont_) { + std::cerr << node.key << ' '; + } + std::cerr << '\n'; + std::cerr << "Target key: " << key << '\n'; +#endif + std::vector res; + res.resize(lv + 1); + iterator iter = begin(); + for (size_type i = 0; i != max_lv_; ++i) { + size_type focus = max_lv_ - 1 - i; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "i: " << i << " focus: " << focus << ".\n"; +#endif + // invariant: iter->key < key + while (compare()(iter->forwards[focus]->key, key)) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "since iter->forwards[focus]->key[" << iter->forwards[focus]->key + << "] < key[" << key << "], "; +#endif + iter = iter->forwards[focus]; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "step forward iter to [" << iter->key << "]\n"; +#endif + } + // result: iter->key < key <= iter->forwards[focus]->key +#ifdef LIAM_UT_DEBUG_ + std::cerr << "The following fact holds at level " << focus + << ": iter->key[" << iter->key << "] < key[" << key + << "] <= iter->forwards[focus]->key[" << iter->forwards[focus]->key + <<"].\n"; +#endif + if (focus < lv + 1) { + res[focus] = iter; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "predecessor at level [" << focus + << "] has been recorded, while level upper limit is " << lv <<".\n"; +#endif + } + } + return res; + } + + public: + size_type size() const { + return cont_.size() - 2; + } + bool empty() const { + return size() == 0; + } + iterator begin() { + return cont_.begin(); + } + const_iterator begin() const { + return cont_.cbegin(); + } + const_iterator cbegin() const { + return cont_.cbegin(); + } + iterator end() { + return cont_.end(); + } + const_iterator end() const { + return cont_.cend(); + } + const_iterator cend() const { + return cont_.cend(); + } + void grow(const size_type new_max_lv) { + if (max_lv_ < new_max_lv) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "grow from [" << max_lv_ << "] to [" + << new_max_lv << "]!\n"; +#endif + max_lv_ = new_max_lv; + + iterator tail = std::prev(cont_.end()); + auto beg_tail = tail->forwards.end(); + tail->forwards.resize(max_lv_, cont_.end()); + + iterator head = cont_.begin(); + auto beg_head = head->forwards.end(); + head->forwards.resize(max_lv_, tail); + + return; + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "abandon growing!\n"; +#endif + return; + } + } + void grow() { + grow(Factor * max_lv_); + } + size_type capability() const { + return std::pow(Factor, max_lv_); + } + + public: + const_iterator find(const value_type& target) const { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "finding [" << target << "]!\n"; +#endif + const hash_type key = hasher()(target); + const_iterator iter = find_helper(key); + return (iter->key == key) ? iter : cont_.end(); + } + void insert(const value_type& target) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "inserting [" << target << "]!\n"; +#endif + if (size() > static_cast(Factor - 1) / Factor * capability()) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "size[" << size() << "], Factor[" << Factor << "], capability[" << capability() << "]!\n"; +#endif + grow(); + } + const hash_type key = hasher()(target); + const size_type lv = rl_(); + std::vector predecessors = find_predecessors(key, lv); + if (predecessors[0]->forwards[0]->key == key) { // key already in skiplist +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] already in the skiplist, insert directly!\n"; +#endif + predecessors[0]->forwards[0]->values.insert(target); + return; + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] not in the skiplist, insert a new node!\n"; +#endif + node_type node(key); + node.forwards.resize(lv + 1); + node.values.insert(target); + iterator inserted = cont_.insert(predecessors[0]->forwards[0], std::move(node)); + for (size_type i = 0; i != lv + 1; ++i) { + inserted->forwards[i] = predecessors[i]->forwards[i]; + predecessors[i]->forwards[i] = inserted; + } +#ifdef LIAM_UT_DEBUG_ + assert(inserted->forwards[0] == std::next(inserted)); +#endif + return; + } + } + void erase(const value_type& target, + const erase_policy policy = erase_policy::ALL) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "erasing [" << target << "]!\n"; +#endif + const hash_type key = hasher()(target); + std::vector predecessors = find_predecessors(key, max_lv_); + if (predecessors[0]->forwards[0]->key == key) { // hit +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] is in the skiplist!\n"; +#endif + iterator found = predecessors[0]->forwards[0]; + for (auto iter = found->values.begin(); iter != found->values.end(); ) { + if (policy == erase_policy::ALL) { + if (*iter == target) { + iter = found->values.erase(iter); + } else { + ++iter; + } + } else if (policy == erase_policy::SINGLE) { + if (*iter == target) { + found->values.erase(iter); + break; + } + } + } +#ifdef LIAM_UT_DEBUG_ + std::cerr << "target(s) removed!\n"; +#endif + if (found->values.empty()) { + const size_type lvp1 = found->forwards.size(); // lv plus 1 + for (size_type i = 0; i != lvp1; ++i) { + predecessors[i]->forwards[i] = found->forwards[i]; + } + cont_.erase(found); +#ifdef LIAM_UT_DEBUG_ + std::cerr << "empty node removed!\n"; +#endif + return; + } else { + return; + } + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] is not in the skiplist, do nothing!\n"; +#endif + return; + } + } +}; + +#endif // SKIPLIST_SKIPLIST_TR_HPP_ + diff --git a/c-cpp/17_skiplist/skiplist_tr_test.cc b/c-cpp/17_skiplist/skiplist_tr_test.cc new file mode 100644 index 0000000..15e3fa2 --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_tr_test.cc @@ -0,0 +1,107 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ +#include + +#include +#include +#include + +#include "skiplist_tr.hpp" + +int main() { + // 1. UT for skiplist_detail::random_level + skiplist_detail::random_level rl(5, 0.5); + std::map hist; + for (size_t i = 0; i != 10000; ++i) { + ++hist[rl()]; + } + for (auto p : hist) { + std::cout << p.first << ' ' << std::string(p.second / 100, '*') << '\n'; + } + + // 2. UT for skiplist(), init_internally(), size(), empty() + skiplist sl_default; + assert(sl_default.empty()); + // 2.1. UT for grow with abandon + sl_default.grow(1); + assert(sl_default.capability() == 4); + // 2.2. UT for grow + sl_default.grow(10); + assert(sl_default.capability() == 1024); + + // 3. UT for constructor of initializer_list and InputIt, init_internally(), insert(), + // find_predecessors() + skiplist sl{"hello", "world", "!"}; + assert(not sl.empty()); + assert(3 == sl.size()); + + // 4. UT for find() find_helper() + auto search = sl_default.find("nonexist"); + assert(search == sl_default.cend()); + assert(search == sl_default.end()); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + search = sl.find("nonexist"); + assert(search == sl.cend()); + assert(search == sl.end()); + + // 5. UT for insert(), find_predecessors() + // 5.1. UT for insert a already-exist item + sl.insert("hello"); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + // 5.2. UT for insert a new incoming item + search = sl.find("now exist"); + assert(search == sl.cend()); + assert(search == sl.end()); + sl.insert("now exist"); + search = sl.find("now exist"); + assert(search != sl.cend()); + assert(search != sl.end()); + + // 6. UT for erase(), find_predecessors() + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + // 6.1. UT for erase single item + sl.erase("hello", erase_policy::SINGLE); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + // 6.2. UT for erase all items + sl.erase("hello", erase_policy::ALL); + search = sl.find("hello"); + assert(search == sl.cend()); + assert(search == sl.end()); + // 6.3 UT for erase non-exist item + sl.erase("nonexist"); + + // 7. UT for insert() behind erase() + // 7.1. different word + sl.insert("world"); + search = sl.find("world"); + assert(search != sl.cend()); + assert(search != sl.end()); + assert(search->values.count("world") == 2); + // 7.1. same word, also UT for grow() + search = sl.find("hello"); + assert(search == sl.cend()); + assert(search == sl.end()); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + assert(search->values.count("hello") == 5); + + return 0; + // 8. UT for ~skiplist() +} + diff --git a/f21 b/f21 new file mode 100644 index 0000000..72ef739 --- /dev/null +++ b/f21 @@ -0,0 +1,63 @@ +// A Stack based C++ program to find next +// greater element for all array elements +// in same order as input. +#include + +using namespace std; + +/* prints element and NGE pair for all +elements of arr[] of size n */ +void printNGE(int arr[], int n) +{ + stack s; + unordered_map mp; + + /* push the first element to stack */ + s.push(arr[0]); + + + // iterate for rest of the elements + for (int i = 1; i < n; i++) { + + if (s.empty()) { + s.push(arr[i]); + continue; + } + + /* if stack is not empty, then + pop an element from stack. + If the popped element is smaller + than next, then + a) print the pair + b) keep popping while elements are + smaller and stack is not empty */ + while (s.empty() == false && s.top() < arr[i]) { + mp[s.top()] = arr[i]; + s.pop(); + } + + /* push next to stack so that we can find + next smaller for it */ + s.push(arr[i]); + } + + /* After iterating over the loop, the remaining +elements in stack do not have the next smaller +element, so print -1 for them */ + while (s.empty() == false) { + mp[s.top()] = -1; + s.pop(); + } + + for (int i=0; i " << mp[arr[i]] << endl; +} + +/* Driver program to test above functions */ +int main() +{ + int arr[] = { 11, 13, 21, 3 }; + int n = sizeof(arr) / sizeof(arr[0]); + printNGE(arr, n); + return 0; +} diff --git a/go/15_binarysearch/binarysearch.go b/go/15_binarysearch/binarysearch.go index fdd1763..f9c8ff5 100644 --- a/go/15_binarysearch/binarysearch.go +++ b/go/15_binarysearch/binarysearch.go @@ -45,3 +45,111 @@ func bs(a []int, v int, low, high int) int { return bs(a, v, mid+1, high) } } + +//查找第一个等于给定值的元素 +func BinarySearchFirst(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == 0 || a[mid-1] != v { + return mid + } else { + high = mid - 1 + } + } + } + + return -1 +} + +//查找最后一个值等于给定值的元素 +func BinarySearchLast(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == n-1 || a[mid+1] != v { + return mid + } else { + low = mid + 1 + } + } + } + + return -1 +} + +//查找第一个大于等于给定值的元素 +func BinarySearchFirstGT(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (high + low) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid != n-1 && a[mid+1] > v { + return mid + 1 + } else { + low = mid + 1 + } + } + } + + return -1 +} + +//查找最后一个小于等于给定值的元素 +func BinarySearchLastLT(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == 0 || a[mid-1] < v { + return mid - 1 + } else { + high = mid - 1 + } + } + } + + return -1 +} diff --git a/go/15_binarysearch/binarysearch_test.go b/go/15_binarysearch/binarysearch_test.go index 755fc56..2ac76a1 100644 --- a/go/15_binarysearch/binarysearch_test.go +++ b/go/15_binarysearch/binarysearch_test.go @@ -25,3 +25,79 @@ func TestBinarySearchRecursive(t *testing.T) { t.Fatal(BinarySearch(a, 4)) } } + +func TestBinarySearchFirst(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirst(a, 2) != 1 { + t.Fatal(BinarySearchFirst(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirst(a, 3) != 4 { + t.Fatal(BinarySearchFirst(a, 3)) + } +} + +func TestBinarySearchLast(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLast(a, 2) != 3 { + t.Fatal(BinarySearchLast(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLast(a, 3) != 4 { + t.Fatal(BinarySearchLast(a, 3)) + } +} + +func TestBinarySearchFirstGT(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 2) != 4 { + t.Fatal(BinarySearchFirstGT(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 1) != 1 { + t.Fatal(BinarySearchFirstGT(a, 1)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 3) != 5 { + t.Fatal(BinarySearchFirstGT(a, 3)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 4) != -1 { + t.Fatal(BinarySearchFirstGT(a, 4)) + } +} + +func TestBinarySearchLastLT(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 2) != 0 { + t.Fatal(BinarySearchLastLT(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 1) != -1 { + t.Fatal(BinarySearchLastLT(a, 1)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 3) != 3 { + t.Fatal(BinarySearchLastLT(a, 3)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 4) != 4 { + t.Fatal(BinarySearchLastLT(a, 4)) + } +} diff --git a/go/17_skiplist/skiplist.go b/go/17_skiplist/skiplist.go new file mode 100644 index 0000000..0938ada --- /dev/null +++ b/go/17_skiplist/skiplist.go @@ -0,0 +1,177 @@ +package _7_skiplist + +import ( + "fmt" + "math" + "math/rand" +) + +const ( + //最高层数 + MAX_LEVEL = 16 +) + +//跳表节点结构体 +type skipListNode struct { + //跳表保存的值 + v interface{} + //用于排序的分值 + score int + //层高 + level int + //每层前进指针 + forwards []*skipListNode +} + +//新建跳表节点 +func newSkipListNode(v interface{}, score, level int) *skipListNode { + return &skipListNode{v: v, score: score, forwards: make([]*skipListNode, level, level), level: level} +} + +//跳表结构体 +type SkipList struct { + //跳表头结点 + head *skipListNode + //跳表当前层数 + level int + //跳表长度 + length int +} + +//实例化跳表对象 +func NewSkipList() *SkipList { + //头结点,便于操作 + head := newSkipListNode(0, math.MinInt32, MAX_LEVEL) + return &SkipList{head, 1, 0} +} + +//获取跳表长度 +func (sl *SkipList) Length() int { + return sl.length +} + +//获取跳表层级 +func (sl *SkipList) Level() int { + return sl.level +} + +//插入节点到跳表中 +func (sl *SkipList) Insert(v interface{}, score int) int { + if nil == v { + return 1 + } + + //查找插入位置 + cur := sl.head + //记录每层的路径 + update := [MAX_LEVEL]*skipListNode{} + i := MAX_LEVEL - 1 + for ; i >= 0; i-- { + for nil != cur.forwards[i] { + if cur.forwards[i].v == v { + return 2 + } + if cur.forwards[i].score > score { + update[i] = cur + break + } + cur = cur.forwards[i] + } + if nil == cur.forwards[i] { + update[i] = cur + } + } + + //通过随机算法获取该节点层数 + level := 1 + for i := 1; i < MAX_LEVEL; i++ { + if rand.Int31()%7 == 1 { + level++ + } + } + + //创建一个新的跳表节点 + newNode := newSkipListNode(v, score, level) + + //原有节点连接 + for i := 0; i <= level-1; i++ { + next := update[i].forwards[i] + update[i].forwards[i] = newNode + newNode.forwards[i] = next + } + + //如果当前节点的层数大于之前跳表的层数 + //更新当前跳表层数 + if level > sl.level { + sl.level = level + } + + //更新跳表长度 + sl.length++ + + return 0 +} + +//查找 +func (sl *SkipList) Find(v interface{}, score int) *skipListNode { + if nil == v || sl.length == 0 { + return nil + } + + cur := sl.head + for i := sl.level - 1; i >= 0; i-- { + for nil != cur.forwards[i] { + if cur.forwards[i].score == score && cur.forwards[i].v == v { + return cur.forwards[i] + } else if cur.forwards[i].score > score { + break + } + cur = cur.forwards[i] + } + } + + return nil +} + +//删除节点 +func (sl *SkipList) Delete(v interface{}, score int) int { + if nil == v { + return 1 + } + + //查找前驱节点 + cur := sl.head + //记录前驱路径 + update := [MAX_LEVEL]*skipListNode{} + for i := sl.level - 1; i >= 0; i-- { + update[i] = sl.head + for nil != cur.forwards[i] { + if cur.forwards[i].score == score && cur.forwards[i].v == v { + update[i] = cur + break + } + cur = cur.forwards[i] + } + } + + cur = update[0].forwards[0] + for i := cur.level - 1; i >= 0; i-- { + if update[i] == sl.head && cur.forwards[i] == nil { + sl.level = i + } + + if nil == update[i].forwards[i] { + update[i].forwards[i] = nil + } else { + update[i].forwards[i] = update[i].forwards[i].forwards[i] + } + } + + sl.length-- + + return 0 +} + +func (sl *SkipList) String() string { + return fmt.Sprintf("level:%+v, length:%+v", sl.level, sl.length) +} diff --git a/go/17_skiplist/skiplist_test.go b/go/17_skiplist/skiplist_test.go new file mode 100644 index 0000000..3c04f36 --- /dev/null +++ b/go/17_skiplist/skiplist_test.go @@ -0,0 +1,38 @@ +package _7_skiplist + +import "testing" + +func TestSkipList(t *testing.T) { + sl := NewSkipList() + + sl.Insert("leo", 95) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + sl.Insert("jack", 88) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + sl.Insert("lily", 100) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + t.Log(sl.Find("jack", 88)) + t.Log("-----------------------------") + + sl.Delete("leo", 95) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") +} diff --git a/go/binarysearch2.go b/go/binarysearch2.go new file mode 100644 index 0000000..ef2549e --- /dev/null +++ b/go/binarysearch2.go @@ -0,0 +1,90 @@ +package binarySearch + +//查找第一个值等于给定值得元素 +func BinarySearch2(nums []int, value int) int { + length := len(nums) + start := 0 + end := length - 1 + for start <= end { + mid := (start + end) / 2 + if nums[mid] > value { + end = mid - 1 + } else if nums[mid] < value { + start = mid + 1 + } else { + for mid >= 0 { + if nums[mid-1] == value { + mid-- + } else { + return mid + } + } + } + } + return -1 +} + +// 查找最后一个值等于给定值的元素 +func BinarySearch3(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := (start + end) / 2 + if nums[mid] > value { + end = mid - 1 + } else if nums[mid] < value { + start = mid + 1 + } else { + for mid < len(nums) { + if nums[mid+1] == value { + mid++ + } else { + return mid + } + } + } + } + return -1 +} + +// 查找第一个大于等于给定值的元素 +func BinarySearch4(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := (start + end) >> 1 + if nums[mid] < value { + start = mid + 1 + } else { + for mid >= 0 { + if nums[mid-1] >= value { + mid-- + } else { + return mid + } + } + } + } + return -1 +} + +// 查找最后一个小于等于给定值的元素 +func BinarySearch5(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := (start + end) >> 1 + if nums[mid] > value { + end = mid - 1 + } else { + for mid < len(nums) { + if nums[mid+1] <= value { + mid++ + } else { + return mid + } + } + } + } + return -1 +} diff --git a/java/17_skiplist/SkipList.java b/java/17_skiplist/SkipList.java new file mode 100644 index 0000000..8e62c20 --- /dev/null +++ b/java/17_skiplist/SkipList.java @@ -0,0 +1,119 @@ +package skiplist; + +import java.util.Random; + +/** + * 跳表的一种实现方法。 + * 跳表中存储的是正整数,并且存储的是不重复的。 + * + * Author:ZHENG + */ +public class SkipList { + + private static final int MAX_LEVEL = 16; + + private int levelCount = 1; + + private Node head = new Node(); // 带头链表 + + private Random r = new Random(); + + public Node find(int value) { + Node p = head; + for (int i = levelCount - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + } + + if (p.forwards[0] != null && p.forwards[0].data == value) { + return p.forwards[0]; + } else { + return null; + } + } + + public void insert(int value) { + int level = randomLevel(); + Node newNode = new Node(); + newNode.data = value; + newNode.maxLevel = level; + Node update[] = new Node[level]; + for (int i = 0; i < level; ++i) { + update[i] = head; + } + + Node p = head; + for (int i = level - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + update[i] = p; + } + + for (int i = 0; i < level; ++i) { + newNode.forwards[i] = update[i].forwards[i]; + update[i].forwards[i] = newNode; + } + + if (levelCount < level) levelCount = level; + } + + public void delete(int value) { + Node[] update = new Node[levelCount]; + Node p = head; + for (int i = levelCount - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + update[i] = p; + } + + if (p.forwards[0] != null && p.forwards[0].data == value) { + for (int i = levelCount - 1; i >= 0; --i) { + if (update[i].forwards[i] != null && update[i].forwards[i].data == value) { + update[i].forwards[i] = update[i].forwards[i].forwards[i]; + } + } + } + } + + private int randomLevel() { + int level = 1; + for (int i = 1; i < MAX_LEVEL; ++i) { + if (r.nextInt() % 2 == 1) { + level++; + } + } + + return level; + } + + public void printAll() { + Node p = head; + while (p.forwards[0] != null) { + System.out.print(p.forwards[0] + " "); + p = p.forwards[0]; + } + System.out.println(); + } + + public class Node { + private int data = -1; + private Node forwards[] = new Node[MAX_LEVEL]; + private int maxLevel = 0; + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{ data: "); + builder.append(data); + builder.append("; levels: "); + builder.append(maxLevel); + builder.append(" }"); + + return builder.toString(); + } + } + +} diff --git a/javascript/15_binary/binaryFind.js b/javascript/15_binary/binaryFind.js new file mode 100644 index 0000000..86ae4ef --- /dev/null +++ b/javascript/15_binary/binaryFind.js @@ -0,0 +1,27 @@ +/** + * 二分查找 + * + * Author: nameczz + */ +// 数组必须有序 不存在重复 +const biaryFind = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target === sortedArr[mid]) { + return mid + } else if (target < sortedArr[mid]) { + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 +} +const arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102] +console.log(biaryFind(arr, 44)) +console.log(biaryFind(arr, 1)) +console.log(biaryFind(arr, 102)) +console.log(biaryFind(arr, 1111)) \ No newline at end of file diff --git a/javascript/16_binary/binary-find.js b/javascript/16_binary/binary-find.js new file mode 100644 index 0000000..ef055fb --- /dev/null +++ b/javascript/16_binary/binary-find.js @@ -0,0 +1,90 @@ +/** + * 二分查找 + * + * Author: nameczz + */ + + +// 查找第一个等于给定值 +const biaryFindFirst = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + + if (target < sortedArr[mid]) { + high = mid - 1 + } else if (target > sortedArr[mid]) { + low = mid + 1 + } else { + if (mid === 0 || sortedArr[mid - 1] < target) return mid + high = mid - 1 + } + } + return -1 +} + +// 查找最后一个相等的数 +const biaryFindLast = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target < sortedArr[mid]) { + high = mid - 1 + } else if (target > sortedArr[mid]) { + low = mid + 1 + } else { + if (mid === sortedArr.length - 1 || sortedArr[mid + 1] > target) return mid + low = mid + 1 + } + } + return -1 +} + +// 查找第一个大于等于给定值的元素 +const biaryFindFistBig = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target <= sortedArr[mid]) { + if (mid === 0 || sortedArr[mid - 1] < target) return mid + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 +} + +// 查找最后一个小于等于给定值的元素 +const biaryFindLastSmall = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (sortedArr[mid] > target) { + high = mid - 1 + } else { + if (mid === sortedArr.length - 1 || sortedArr[mid + 1] > target) return mid + low = mid + 1 + } + } + return -1 +} + +const arr = [1, 2, 3, 4, 4, 4, 4, 4, 6, 7, 8, 8, 9] +const first = biaryFindFirst(arr, 4) +console.log(`FindFirst: ${first}`) + +const last = biaryFindLast(arr, 4) +console.log(`FindLast: ${last}`) +const FisrtBig = biaryFindFistBig(arr, 5) +console.log(`FindFisrtBig: ${FisrtBig}`) +const LastSmall = biaryFindLastSmall(arr, 4) +console.log(`FindLastSmall: ${LastSmall}`) \ No newline at end of file diff --git a/notes/11_sorts/readme.md b/notes/11_sorts/readme.md index b23cb96..ac0b134 100644 --- a/notes/11_sorts/readme.md +++ b/notes/11_sorts/readme.md @@ -1,4 +1,4 @@ -# 排序(上) +# 排序(平方时间复杂度排序算法) | 排序算法 | 时间复杂度 | 是否基于比较 | |---------|----|----| diff --git a/notes/12_sorts/.gitkeep b/notes/12_sorts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/12_sorts/readme.md b/notes/12_sorts/readme.md new file mode 100644 index 0000000..57066e4 --- /dev/null +++ b/notes/12_sorts/readme.md @@ -0,0 +1,176 @@ +# 排序(线性对数时间复杂度排序算法) + +开篇问题:如何在 $O(n)$ 时间复杂度内寻找一个无序数组中第 K 大的元素? + +## 归并排序 + +* 归并排序使用了「分治」思想(Divide and Conquer) + * 分:把数组分成前后两部分,分别排序 + * 合:将有序的两部分合并 + +![归并排序分解图](https://static001.geekbang.org/resource/image/db/2b/db7f892d3355ef74da9cd64aa926dc2b.jpg) + +* 分治与递归 + * 分治:解决问题的处理办法 + * 递归:实现算法的手段 + * ——分治算法经常用递归来实现 +* 递归实现: + * 终止条件:区间 `[first, last)` 内不足 2 个元素 + * 递归公式:`merge_sort(first, last) = merge(merge_sort(first, mid), merge_sort(mid, last))`,其中 `mid = first + (last - first) / 2` + +C++ 实现: + +```cpp +template ::value_type, + typename BinaryPred = std::less> +void merge_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) { + const auto len = std::distance(first, last); + if (len <= 1) { return; } + auto cut = first + len / 2; + merge_sort(first, cut, comp); + merge_sort(cut, last, comp); + std::vector tmp; + tmp.reserve(len); + detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp); + std::copy(tmp.begin(), tmp.end(), first); +} +``` + +这里涉及到一个 `merge` 的过程,它的实现大致是: + +```cpp +namespace detail { +template ::value_type>> +OutputIt merge(InputIt1 first1, InputIt1 last1, + InputIt2 first2, InputIt2 last2, + OutputIt d_first, + BinaryPred comp = BinaryPred()) { + for (; first1 != last1; ++d_first) { + if (first2 == last2) { + return std::copy(first1, last1, d_first); + } + if (comp(*first2, *first1)) { + *d_first = *first2; + ++first2; + } else { + *d_first = *first1; + ++first1; + } + } + return std::copy(first2, last2, d_first); +} +} // namespace detail +``` + +![`merge` 的过程](https://static001.geekbang.org/resource/image/95/2f/95897ade4f7ad5d10af057b1d144a22f.jpg) + +### 算法分析 + +* 稳定性 + * 由于 `comp` 是严格偏序,所以 `!comp(*first2, *first1)` 时,取用 `first1` 的元素放入 `d_first` 保证了算法稳定性 +* 时间复杂度 + * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时, + * 有递推公式:$T(n) = 2T(n/2) + n$ + * 展开得 $T(n) = 2^{k}T(1) + k * n$ + * 考虑 $k$ 是递归深度,它的值是 $\log_2 n$,因此 $T(n) = n + n\log_2 n$ + * 因此,归并排序的时间复杂度为 $\Theta(n\log n)$ +* 空间复杂度 + * 一般来说,空间复杂度是 $\Theta(n)$ + +## 快速排序(quick sort,快排) + +原理: + +* 在待排序区间 `[first, last)` 中选取一个元素,称为主元(pivot,枢轴) +* 对待排序区间进行划分,使得 `[first, cut)` 中的元素满足 `comp(element, pivot)` 而 `[cut, last)` 中的元素不满足 `comp(element, pivot)` +* 对划分的两个区间,继续划分,直到区间 `[first, last)` 内不足 2 个元素 + +![快排分区示例](https://static001.geekbang.org/resource/image/4d/81/4d892c3a2e08a17f16097d07ea088a81.jpg) + +显然,这又是一个递归: + +* 终止条件:区间 `[first, last)` 内不足 2 个元素 +* 递归公式:`quick_sort(first, last) = quick_sort(first, cut) + quick_sort(cut, last)` + +```cpp +template ::value_type> +void quick_sort(IterT first, IterT last) { + if (std::distance(first, last) > 1) { + IterT prev_last = std::prev(last); + IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; }); + std::iter_swap(cut, prev_last); + quick_sort(first, cut); + quick_sort(cut, last); + } +} +``` + +> 一点优化(Liam Huang):通过将 `if` 改为 `while` 同时修改 `last` 迭代器的值,可以节省一半递归调用的开销。 + +```cpp +template ::value_type> +void quick_sort(IterT first, IterT last) { + while (std::distance(first, last) > 1) { + IterT prev_last = std::prev(last); + IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; }); + std::iter_swap(cut, prev_last); + quick_sort(cut, last); + last = cut; + } +} +``` + +如果不要求空间复杂度,分区函数实现起来很容易。 + +![非原地分区](https://static001.geekbang.org/resource/image/66/dc/6643bc3cef766f5b3e4526c332c60adc.jpg) + +若要求原地分区,则不那么容易了。下面的实现实现了原地分区函数,并且能将所有相等的主元排在一起。 + +```cpp +template ::value_type, + typename Compare = std::less> +std::pair inplace_partition(BidirIt first, + BidirIt last, + const T& pivot, + Compare comp = Compare()) { + BidirIt last_less, last_greater, first_equal, last_equal; + for (last_less = first, last_greater = first, first_equal = last; + last_greater != first_equal; ) { + if (comp(*last_greater, pivot)) { + std::iter_swap(last_greater++, last_less++); + } else if (comp(pivot, *last_greater)) { + ++last_greater; + } else { // pivot == *last_greater + std::iter_swap(last_greater, --first_equal); + } + } + const auto cnt = std::distance(first_equal, last); + std::swap_ranges(first_equal, last, last_less); + first_equal = last_less; + last_equal = first_equal + cnt; + return {first_equal, last_equal}; +} +``` + +### 算法分析 + +* 稳定性 + * 由于 `inplace_partition` 使用了大量 `std::iter_swap` 操作,所以不是稳定排序 +* 时间复杂度 + * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时, + * 有递推公式:$T(n) = 2T(n/2) + n$(假定每次分割都是均衡分割) + * 展开得 $T(n) = 2^{k}T(1) + k * n$ + * 考虑 $k$ 是递归深度,它的值是 $\log_2 n$,因此 $T(n) = n + n\log_2 n$ + * 因此,快速排序的时间复杂度为 $\Theta(n\log n)$ +* 空间复杂度 + * 一般来说,空间复杂度是 $\Theta(1)$,因此是原地排序算法 + +## 开篇问题 + +* 分区,看前半段元素数量 + * 前半段元素数量 < K,对后半段进行分区 + * 前半段元素数量 > K,对前半段进行分区 + * 前半段元素数量 = K,前半段末位元素即是所求 diff --git a/notes/13_sorts/.gitkeep b/notes/13_sorts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/13_sorts/readme.md b/notes/13_sorts/readme.md new file mode 100644 index 0000000..aa38c89 --- /dev/null +++ b/notes/13_sorts/readme.md @@ -0,0 +1,77 @@ +# 线性排序 + +## 开篇问题 + +如何按年龄给 100 万用户排序? + +## 桶排序(Bucket Sort) + +算法思想: + +* 按待排序数据的 key 分有序桶 +* 桶内排序 +* 有序桶依次输出 + +![桶排序示例](https://static001.geekbang.org/resource/image/98/ae/987564607b864255f81686829503abae.jpg) + +### 算法分析 + +* 时间复杂度 $O(n)$ + * $n$ 个元素,分 $m$ 个有序桶,每个桶里平均 $k = n / m$ 个元素 + * 桶内快排,复杂度 $O(k \log k)$,$m$ 个桶一共 $O(n \log k)$ + * 当 $m$ 接近 $n$,例如当 $k = 4$ 时,这个复杂度近似 $O(n)$ +* 使用条件 + * 数据易于分如有序桶 + * 数据在各个有序桶之间分布均匀 + * 适合外部排序——数据不全部载入磁盘 + +## 计数排序(Counting Sort) + +计数排序可以视作是桶排序的一个特殊情况: + +* 数据的取值范围很小 +* 每个分桶内的元素 key 值都一样 + +此时,由于分桶内的元素 key 值都一样,所以桶内的排序操作可以省略,以及桶的编号本身就能记录桶内元素的值。因此,算法只需遍历一遍所有的数据,统计每个取值上有多少元素即可。这个过程时间复杂度是 $O(n)$。 + +* 假设待排序的数组 `A = {2, 5, 3, 0, 2, 3, 0, 3}`,我们有计数数组 `C = {2, 0, 2, 3, 0, 1}` + +接下来,我们要对 `C` 进行计数操作,具体来说,对从下标为 1 的元素开始累加 `C[i] += C[i - 1]`。 + +* 计数累加 `C = {2, 2, 4, 7, 7, 8}` + +此时,`C` 中的元素表示「小于等于下标的元素的个数」。接下来,我们从尾至头扫描待排序数组 `A`,将其中元素依次拷贝到输出数组 `R` 的相应位置。我们注意到,`A[7] = 3` 而 `C[3] == 4` 。这意味着,待排序的数组中,包括 3 本身在内,不超过 3 的元素共有 4 个。因此,我们可以将这个 3 放置在 `R[C[3] - 1]` 的位置,而后将 `C[3]` 的计数减一——这是由于待排序数组中未处理的部分,不超过 3 的元素现在只剩下 3 个了。如此遍历整个待排序数组 `A`,即可得到排序后的结果 `R`。 + +![计数排序示例](https://static001.geekbang.org/resource/image/1d/84/1d730cb17249f8e92ef5cab53ae65784.jpg) + +### 算法分析 + +* 时间复杂度 + * $n$ 个元素,最大值是 $k$,分 $k$ 个「桶」;时间复杂度 $O(n)$ + * 桶内计数累加;时间复杂度 $O(k)$ + * 摆放元素;时间复杂度 $O(n)$ + * 当 $k < n$ 时,总体时间复杂度是 $O(n)$ +* 使用条件 + * $k < n$ + * 待排序数据的 key 是非负整数 + +## 基数排序(Radix Sort) + +基数排序适用于等长数据的排序。对于不等长数据,可以在较短的数据后面做 padding,使得数据等长。 + +* 先就 least significant digit 进行稳定排序——通常可以用桶排序或者计数排序;时间复杂度 $O(n)$ +* 而后依次向 greatest significant digit 移动,进行稳定排序 + +![基数排序示例](https://static001.geekbang.org/resource/image/df/0c/df0cdbb73bd19a2d69a52c54d8b9fc0c.jpg) + +### 算法分析 + +* 时间复杂度 + * 对每一位的排序时间复杂度是 $O(n)$ + * 总共 $k$ 位,因此总的时间复杂度是 $O(kn)$;考虑到 $k$ 是常数,因此总的时间复杂度是 $O(n)$ +* 使用条件 + * 等长数据 + +## 解答开篇 + +桶排序。 diff --git a/notes/14_sorts/.gitkeep b/notes/14_sorts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/14_sorts/readme.md b/notes/14_sorts/readme.md new file mode 100644 index 0000000..49e8d94 --- /dev/null +++ b/notes/14_sorts/readme.md @@ -0,0 +1,10 @@ +# 排序优化 + +## 如何取舍排序算法? + +* 排序规模小 —— $O(n^2)$ 的算法(通常是插排) +* 排序规模大 —— $O(n\log n)$ 的算法(通常不用归并排序) + +## 如何优化快速排序? + +参考:[谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/) diff --git a/notes/15_bsearch/.gitkeep b/notes/15_bsearch/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/15_bsearch/readme.md b/notes/15_bsearch/readme.md new file mode 100644 index 0000000..4947b03 --- /dev/null +++ b/notes/15_bsearch/readme.md @@ -0,0 +1,23 @@ +# 二分查找(上) + +## 算法描述 + +二分查找(Binary Search)也叫折半查找,是针对有序数据集合的查找算法。其描述十分简单: + +* 折半取中,判断元素与目标元素的大小关系 + * 小于——往前继续折半 + * 大于——往后继续折半 + * 等于——返回 + +关于它的复杂度分析,参见[谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/)中的相关信息。它的复杂度是 $O(\log n)$。 + +## $O(\log n)$ 的惊人之处 + +在 42 亿个数据中用二分查找一个数据,最多需要比较 32 次。 + +## 适用场景 + +* 依赖顺序表结构 +* 数据本身必须有序 +* 数据量相对比较元素的开销要足够大——不然遍历即可 +* 数据量相对内存空间不能太大——不然顺序表装不下 diff --git a/notes/16_bsearch/.gitkeep b/notes/16_bsearch/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/16_bsearch/readme.md b/notes/16_bsearch/readme.md new file mode 100644 index 0000000..541b35f --- /dev/null +++ b/notes/16_bsearch/readme.md @@ -0,0 +1,100 @@ +# 二分查找(下) + +本节课讨论二分的各种变体。实际上在针对上一节的代码中,已经实现了两个变体。本次实现四个变体: + +* 第一个等于给定值的元素 +* 最后一个等于给定值的元素 +* 第一个不小于给定值的元素 +* 最后一个不大于给定值的元素 + +```cpp +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_ +#define BSEARCH_BSEARCH_VARIENTS_HPP_ + +#include +#include + +enum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER }; + +// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, +// but with a bad time complexity. For better performance, iterators should meet +// the RandomAccessIterator requirement. +template ::value_type, + typename Compare> +IterT bsearch(IterT first, + IterT last, + ValueT target, + Compare comp, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + IterT result = last; + while (std::distance(first, last) > 0) { + IterT mid = first + std::distance(first, last) / 2; + if (policy == BsearchPolicy::FIRST_NOT_LESS) { + if (!comp(*mid, target)) { + if (mid == first or comp(*(mid - 1), target)) { + result = mid; + break; + } else { + last = mid; + } + } else { + first = mid + 1; + } + } else if (policy == BsearchPolicy::LAST_NOT_GREATER) { + if (comp(target, *mid)) { + last = mid; + } else { + if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } + } else { // policy == UNSPECIFIED or FIRST or LAST + if (comp(*mid, target)) { + first = mid + 1; + } else if (comp(target, *mid)) { + last = mid; + } else { // equal + if (policy == BsearchPolicy::FIRST) { + if (mid == first or comp(*(mid - 1), *mid)) { + result = mid; + break; + } else { + last = mid; + } + } else if (policy == BsearchPolicy::LAST) { + if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } else { + result = mid; + break; + } + } + } + } + return result; +} + +template ::value_type, + typename Compare = std::less> +IterT bsearch(IterT first, + IterT last, + ValueT target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + return bsearch(first, last, target, Compare(), policy); +} + +#endif // BSEARCH_BSEARCH_VARIENTS_HPP_ +``` diff --git a/notes/17_skiplist/.gitkeep b/notes/17_skiplist/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notes/17_skiplist/readme.md b/notes/17_skiplist/readme.md new file mode 100644 index 0000000..9d9f87a --- /dev/null +++ b/notes/17_skiplist/readme.md @@ -0,0 +1,43 @@ +# 跳表(Skip List) + +支持快速地: + +* 插入 +* 删除 +* 查找 + +某些情况下,跳表甚至可以替代红黑树(Red-Black tree)。Redis 当中的有序集合(Sorted Set)是用跳表实现的。 + +## 跳表的结构 + +跳表是对链表的改进。对于单链表来说,即使内容是有序的,查找具体某个元素的时间复杂度也要达到 $O(n)$。对于二分查找来说,由于链表不支持随机访问,根据 `first` 和 `last` 确定 `cut` 时,必须沿着链表依次迭代 `std::distance(first, last) / 2` 步;特别地,计算 `std::(first, last)` 本身,就必须沿着链表迭代才行。此时,二分查找的效率甚至退化到了 $O(n \log n)$,甚至还不如顺序遍历。 + +![单链表查找示例](https://static001.geekbang.org/resource/image/e1/6d/e18303fcedc068e5a168de04df956f6d.jpg) + +跳表的核心思想是用空间换时间,构建足够多级数的索引,来缩短查找具体值的时间开销。 + +![具有二级索引的跳表示例](https://static001.geekbang.org/resource/image/49/65/492206afe5e2fef9f683c7cff83afa65.jpg) + +例如对于一个具有 64 个有序元素的五级跳表,查找起来的过程大约如下图所示。 + +![五级跳表示例](https://static001.geekbang.org/resource/image/46/a9/46d283cd82c987153b3fe0c76dfba8a9.jpg) + +## 复杂度分析 + +对于一个每一级索引的跨度是下一级索引 $k$ 倍的跳表,每一次 `down` 操作,相当于将搜索范围缩小到「剩余的可能性的 $1 / k$」。因此,查找具体某个元素的时间复杂度大约需要 $\lfloor \log_k n\rfloor + 1$ 次操作;也就是说时间复杂度是 $O(\log n)$。 + +![跳表查询过程示例](https://static001.geekbang.org/resource/image/d0/0c/d03bef9a64a0368e6a0d23ace8bd450c.jpg) + +前面说了,跳表是一种用空间换时间的数据结构。因此它的空间复杂度一定不小。我们考虑原链表有 $n$ 个元素,那么第一级索引就有 $n / k$ 个元素,剩余的索引依次有 $n / k^2$, $n / k^3$, ..., $1$ 个元素。总共的元素个数是一个等比数列求和问题,它的值是 $\frac{n - 1}{k - 1}$。可见,不论 $k$ 是多少,跳表的空间复杂度都是 $O(n)$;但随着 $k$ 的增加,实际需要的额外节点数会下降。 + +## 高效地插入和删除 + +对于链表来说,插入或删除一个给定结点的时间复杂度是 $O(1)$。因此,对于跳表来说,插入或删除某个结点,其时间复杂度完全依赖于查找这类结点的耗时。而我们知道,在跳表中查找某个元素的时间复杂度是 $O(\log n)$。因此,在跳表中插入或删除某个结点的时间复杂度是 $O(\log n)$。 + +![在跳表中插入一个元素](https://static001.geekbang.org/resource/image/65/6c/65379f0651bc3a7cfd13ab8694c4d26c.jpg) + +## 跳表索引的动态更新 + +为了维护跳表的结构,在不断插入数据的过程中,有必要动态维护跳表的索引结构。一般来说,可以采用随机层级法。具体来说是引入一个输出整数的随机函数。当随机函数输出 $K$,则更新从第 $1$ 级至第 $K$ 级的索引。为了保证索引结构和数据规模大小的匹配,一般采用二项分布的随机函数。 + +![在跳表中插入一个元素并更新索引](https://static001.geekbang.org/resource/image/a8/a7/a861445d0b53fc842f38919365b004a7.jpg) diff --git a/php/12_sort/quicksort.php b/php/12_sort/quicksort.php new file mode 100644 index 0000000..95a60ca --- /dev/null +++ b/php/12_sort/quicksort.php @@ -0,0 +1,47 @@ += $r) return; + + $q = partition($a, $l, $r); + quickSortInternally($a, $l, $q-1); + quickSortInternally($a, $q+1, $r); +} + +function partition(&$a, $l, $r): int +{ + $pivot = $a[$r]; + $i = $l; + + for ($j = $l; $j < $r; ++$j) { + if ($a[$j] < $pivot) { + [$a[$j], $a[$i]] = [$a[$i], $a[$j]]; + ++$i; + } + } + + [$a[$r], $a[$i]] = [$a[$i], $a[$r]]; + + return $i; +} + +$a1 = [1,4,6,2,3,5,4]; +$a2 = [2, 2, 2, 2]; +$a3 = [4, 3, 2, 1]; +$a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]; +quickSort($a1); +print_r($a1); +quickSort($a2); +print_r($a2); +quickSort($a3); +print_r($a3); +quickSort($a4); +print_r($a4); \ No newline at end of file diff --git a/python/.vs/slnx.sqlite b/python/.vs/slnx.sqlite new file mode 100644 index 0000000..482c4f8 Binary files /dev/null and b/python/.vs/slnx.sqlite differ diff --git a/python/08_stack/simple_browser.py b/python/08_stack/simple_browser.py new file mode 100644 index 0000000..ff7edfe --- /dev/null +++ b/python/08_stack/simple_browser.py @@ -0,0 +1,75 @@ +""" + a simple browser realize + Author: zhenchao.zhu + 解答:我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈, + 并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。 + 当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据, + 那就说明没有页面可以点击前进按钮浏览了。 +""" + +import sys +# 引用当前文件夹下的single_linked_list +sys.path.append('linked_stack.py') +from linked_stack import LinkedStack +#from .linked_stack import LinkedStack + +class NewLinkedStack(LinkedStack): + + def is_empty(self): + return not self._top + + +class Browser(): + + def __init__(self): + self.forward_stack = NewLinkedStack() + self.back_stack = NewLinkedStack() + + def can_forward(self): + if self.back_stack.is_empty(): + return False + + return True + + def can_back(self): + if self.forward_stack.is_empty(): + return False + + return True + + def open(self, url): + print("Open new url %s" % url, end="\n") + self.forward_stack.push(url) + + def back(self): + if self.forward_stack.is_empty(): + return + + top = self.forward_stack.pop() + self.back_stack.push(top) + print("back to %s" % top, end="\n") + + def forward(self): + if self.back_stack.is_empty(): + return + + top = self.back_stack.pop() + self.forward_stack.push(top) + print("forward to %s" % top, end="\n") + + +if __name__ == '__main__': + + browser = Browser() + browser.open('a') + browser.open('b') + browser.open('c') + if browser.can_back(): + browser.back() + + if browser.can_forward(): + browser.forward() + + browser.back() + browser.back() + browser.back() diff --git a/python/17_skiplist/skip_list.py b/python/17_skiplist/skip_list.py new file mode 100644 index 0000000..52069eb --- /dev/null +++ b/python/17_skiplist/skip_list.py @@ -0,0 +1,91 @@ +""" + An implementation of skip list. + The list stores positive integers without duplicates. + + 跳表的一种实现方法。 + 跳表中储存的是正整数,并且储存的是不重复的。 + + Author: Wenru +""" + +from typing import Optional +import random + +class ListNode: + + def __init__(self, data: Optional[int] = None): + self._data = data + self._forwards = [] # Forward pointers + +class SkipList: + + _MAX_LEVEL = 16 + + def __init__(self): + self._level_count = 1 + self._head = ListNode() + self._head._forwards = [None] * type(self)._MAX_LEVEL + + def find(self, value: int) -> Optional[ListNode]: + p = self._head + for i in range(self._level_count - 1, -1, -1): # Move down a level + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] # Move along level + + return p._forwards[0] if p._forwards[0] and p._forwards[0]._data == value else None + + def insert(self, value: int): + level = self._random_level() + if self._level_count < level: self._level_count = level + new_node = ListNode(value) + new_node._forwards = [None] * level + update = [self._head] * level # update is like a list of prevs + + p = self._head + for i in range(level - 1, -1, -1): + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] + + update[i] = p # Found a prev + + for i in range(level): + new_node._forwards[i] = update[i]._forwards[i] # new_node.next = prev.next + update[i]._forwards[i] = new_node # prev.next = new_node + + def delete(self, value): + update = [None] * self._level_count + p = self._head + for i in range(self._level_count - 1, -1, -1): + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] + update[i] = p + + if p._forwards[0] and p._forwards[0]._data == value: + for i in range(self._level_count - 1, -1, -1): + if update[i]._forwards[i] and update[i]._forwards[i]._data == value: + update[i]._forwards[i] = update[i]._forwards[i]._forwards[i] # Similar to prev.next = prev.next.next + + def _random_level(self, p: float = 0.5) -> int: + level = 1 + while random.random() < p and level < type(self)._MAX_LEVEL: + level += 1 + return level + + def __repr__(self) -> str: + values = [] + p = self._head + while p._forwards[0]: + values.append(str(p._forwards[0]._data)) + p = p._forwards[0] + return "->".join(values) + + +if __name__ == "__main__": + l = SkipList() + for i in range(10): + l.insert(i) + print(l) + p = l.find(7) + print(p._data) + l.delete(3) + print(l) \ No newline at end of file diff --git a/scala/15_bsearch/BSearch.scala b/scala/15_bsearch/BSearch.scala new file mode 100644 index 0000000..0c8d0b4 --- /dev/null +++ b/scala/15_bsearch/BSearch.scala @@ -0,0 +1,18 @@ +object BSearch { + def search(nums: Array[Int], target: Int): Int = { + var low = 0 + var high = nums.length - 1 + while(low <= high){ + val mid = low + ((high - low) >> 2) + if(nums(mid) > target){ + high = mid - 1 + } else if (nums(mid) < target){ + low = mid + 1 + } else { + return mid + } + } + + return -1 + } +} diff --git a/scala/15_bsearch/BSearchRecursive.scala b/scala/15_bsearch/BSearchRecursive.scala new file mode 100644 index 0000000..b14a706 --- /dev/null +++ b/scala/15_bsearch/BSearchRecursive.scala @@ -0,0 +1,20 @@ +object BSearchRecursive { + def search(nums: Array[Int], target: Int): Int = { + return searchInternal(nums, target, 0, nums.length - 1) + } + + def searchInternal(nums:Array[Int], target: Int, low: Int, high: Int): Int = { + if(low <= high){ + val mid = low + ((high - low) >> 2) + if(nums(mid) > target){ + searchInternal(nums, target, low, mid - 1) + } else if (nums(mid) < target){ + searchInternal(nums, target, mid + 1, high) + } else { + return mid + } + }else{ + return -1 + } + } +} diff --git a/swift/06_linkedlist/SinglyLinkedList.swift b/swift/06_linkedlist/SinglyLinkedList.swift new file mode 100644 index 0000000..a9af410 --- /dev/null +++ b/swift/06_linkedlist/SinglyLinkedList.swift @@ -0,0 +1,128 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright © 2018 Jiandan. All rights reserved. +// + +import Foundation + +class Node { + var value: T? + var next: Node? + + init(){} + + init(value: T) { + self.value = value + } +} + +/// 单链表 +/// 实现插入、删除、查找操作 +class List { + private var dummy = Node() // 哨兵结点,不存储数据 + var size: Int { + var num = 0 + var tmpNode = dummy.next + while tmpNode != nil { + num += 1 + tmpNode = tmpNode!.next + } + return num + } + var isEmpty: Bool { return size > 0 } + + /// find node with value + func node(with value: Element) -> Node? { + var node = dummy.next + while node != nil { + if node!.value == value { + return node + } + node = node!.next + } + + return nil + } + // 约定:链表的 index 从 1 开始 + func node(at index: Int) -> Node? { + var num = 1 + var node = dummy.next + while node != nil { + if num == index { + return node + } + node = node!.next + num += 1 + } + return nil + } + + func insertToHead(value: Element) { + let newNode = Node(value: value) + return insertToHead(node: newNode) + } + + func insertToHead(node: Node) { + node.next = dummy.next + dummy.next = node + } + + func insert(after node: Node, newValue: Element) { + let newNode = Node(value: newValue) + return insert(after: node, newNode: newNode) + } + + func insert(after node: Node, newNode: Node) { + newNode.next = node.next + node.next = newNode + } + + func insert(before node: Node, newValue: Element) { + let newNode = Node(value: newValue) + return insert(before: node, newNode: newNode) + } + + func insert(before node: Node, newNode: Node) { + var lastNode = dummy + var tmpNode = dummy.next + + while tmpNode != nil { + if tmpNode === node { + newNode.next = tmpNode + lastNode.next = newNode + break + } + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } + + func delete(node: Node) { + var lastNode = dummy + var tmpNode = dummy.next + + while tmpNode != nil { + if tmpNode === node { + lastNode.next = tmpNode!.next + break + } + + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } + /// 删除首个 value 符合要求的结点 + func delete(value: Element) { + var lastNode = dummy + var tmpNode = dummy.next + while tmpNode != nil { + if tmpNode!.value == value { + lastNode.next = tmpNode!.next + break + } + + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } +} diff --git a/swift/07_linkedlist/LinkedListAlgo.swift b/swift/07_linkedlist/LinkedListAlgo.swift new file mode 100644 index 0000000..979b8f6 --- /dev/null +++ b/swift/07_linkedlist/LinkedListAlgo.swift @@ -0,0 +1,111 @@ +// +// Created by Jiandan on 2018/10/13. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +/** + * 1) 单链表反转 + * 2) 链表中环的检测 + * 3) 两个有序的链表合并 + * 4) 删除链表倒数第n个结点 + * 5) 求链表的中间结点 + */ +import Foundation +/// 单链表反转 +func reverseSinglyLinkedList(head: Node) -> Node? { + var reverseHead: Node?, currentNode: Node?, prevNode: Node? + currentNode = head + while currentNode != nil { + let nextNode = currentNode!.next + if nextNode == nil { + reverseHead = currentNode + } + currentNode!.next = prevNode + prevNode = currentNode + currentNode = nextNode + } + return reverseHead +} +/// 检测环 +func hasCircle(head: Node) -> Bool { + var fast = head.next + var slow: Node? = head + while fast != nil { + if fast === slow { + return true + } + fast = fast!.next?.next + slow = slow!.next + } + return false +} +/// 两个有序的链表合并 +func mergeSortedLists(headA: Node?, headB: Node?) -> Node? { + guard let headA = headA else { + return headB + } + guard let headB = headB else { + return headA + } + + var head: Node?, tail: Node? + var nodeA: Node? = headA, nodeB: Node? = headB + if nodeA!.value! < nodeB!.value! { + head = nodeA + nodeA = nodeA!.next + } else { + head = nodeB + nodeB = nodeB!.next + } + tail = head + + while nodeA != nil, nodeB != nil { + if nodeA!.value! < nodeB!.value! { + tail!.next = nodeA + nodeA = nodeA!.next + } else { + tail!.next = nodeB + nodeB = nodeB!.next + } + tail = tail!.next + } + + if nodeA != nil { + tail?.next = nodeA + } else { + tail?.next = nodeB + } + + return head +} + +/// 删除倒数第n个结点 +func deleteNode(at lastNum: Int, in head: Node) { + var slow: Node? = head + var fast: Node? = head + var num = 1 + while fast != nil, num < lastNum { + fast = fast!.next + num += 1 + } + + var prevNode: Node? + while fast != nil { + prevNode = slow + fast = fast!.next + slow = slow!.next + } + prevNode?.next = slow?.next +} + +/// 求链表的中间结点 +func halfNode(in head: Node) -> Node? { + var slow: Node? = head + var fast: Node? = head + + while fast?.next != nil, fast?.next?.next != nil { + fast = fast!.next?.next + slow = slow!.next + } + return slow +} diff --git a/swift/08_stack/Browser.swift b/swift/08_stack/Browser.swift new file mode 100644 index 0000000..2b740d7 --- /dev/null +++ b/swift/08_stack/Browser.swift @@ -0,0 +1,27 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct Page { + /// 存储前进 url + private var forwardArray = [String]() + /// 存储后退 url + private var backArray = [String]() + + var currentURL: String? { return forwardArray.last } + + init(url: String) { + forwardArray.append(url) + } + /// 前进 + mutating func goForward(url: String) { + forwardArray.append(url) + } + /// 后退 + mutating func goBack() { + backArray.append(forwardArray.popLast()!) + } +} diff --git a/swift/08_stack/BrowserDemo.swift b/swift/08_stack/BrowserDemo.swift new file mode 100644 index 0000000..3130305 --- /dev/null +++ b/swift/08_stack/BrowserDemo.swift @@ -0,0 +1,27 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct Page { + /// 存储前进 url + private var forwardArray = [String]() + /// 存储后退 url + private var backArray = [String]() + + var currentURL: String { return forwardArray.last! } + + init(url: String) { + forwardArray.append(url) + } + /// 前进 + mutating func goForward(url: String) { + forwardArray.append(url) + } + /// 后退 + mutating func goBack() { + backArray.append(forwardArray.popLast()!) + } +} diff --git a/swift/08_stack/Stack.swift b/swift/08_stack/Stack.swift new file mode 100644 index 0000000..65ddcc9 --- /dev/null +++ b/swift/08_stack/Stack.swift @@ -0,0 +1,21 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +protocol Stack { + /// 持有的数据类型 + associatedtype Element + /// 是否为空 + var isEmpty: Bool { get } + /// 队列大小 + var size: Int { get } + /// 返回队列头部元素 + var peek: Element? { get } + /// 入栈 + mutating func push(newElement: Element) -> Bool + /// 出栈 + mutating func pop() -> Element? +} diff --git a/swift/08_stack/StackBasedOnLinkedList.swift b/swift/08_stack/StackBasedOnLinkedList.swift new file mode 100644 index 0000000..0e11ce7 --- /dev/null +++ b/swift/08_stack/StackBasedOnLinkedList.swift @@ -0,0 +1,37 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct StackBasedOnLinkedList: Stack { + private var head = Node() // 哨兵结点,不存储内容 + + // MARK: Protocol: Stack + + var isEmpty: Bool { return head.next == nil } + + var size: Int { + var count = 0 + while head.next != nil { + count += 1 + } + return count + } + + var peek: Element? { return head.next?.value } + + func push(newElement: Element) -> Bool { + let node = Node(value: newElement) + node.next = head.next + head.next = node + return true + } + + func pop() -> Element? { + let node = head.next + head.next = node?.next + return node?.value + } +} diff --git a/swift/09_queue/ArrayQueue.swift b/swift/09_queue/ArrayQueue.swift index 1dea014..d5c5bca 100644 --- a/swift/09_queue/ArrayQueue.swift +++ b/swift/09_queue/ArrayQueue.swift @@ -6,9 +6,7 @@ import Foundation /// 用数组实现的队列 -struct ArrayQueue: Queue { - typealias Element = T - +struct ArrayQueue: Queue { /// 数组 private var items: [Element] /// 数组最大长度 @@ -78,9 +76,7 @@ struct ArrayQueue: Queue { /// 使用2个数组实现无界队列,用到 Swift 中 Array 较多的方法 /// 来源:《iOS 面试之道》(故胤道长,唐巧) -struct ArrayQueue2: Queue { - typealias Element = T - +struct ArrayQueue2: Queue { /// 输入数组,主要负责入队 var inArray = [Element]() /// 输出数组,主要负责出队 diff --git a/swift/09_queue/CircularQueue.swift b/swift/09_queue/CircularQueue.swift index 6a58f98..c0c1e44 100644 --- a/swift/09_queue/CircularQueue.swift +++ b/swift/09_queue/CircularQueue.swift @@ -6,9 +6,7 @@ import Foundation /// 循环队列 -struct CircularQueue: Queue { - typealias Element = T - +struct CircularQueue: Queue { /// 数组 private var items: [Element] /// 数组最大长度 diff --git a/swift/09_queue/QueueBasedOnLinkedList.swift b/swift/09_queue/QueueBasedOnLinkedList.swift index be73795..be8b66c 100644 --- a/swift/09_queue/QueueBasedOnLinkedList.swift +++ b/swift/09_queue/QueueBasedOnLinkedList.swift @@ -5,18 +5,7 @@ import Foundation -class Node { - var value: T? - var next: Node? - - init(value: T) { - self.value = value - } -} - -struct QueueBasedOnLinkedList: Queue { - typealias Element = T - +struct QueueBasedOnLinkedList: Queue { /// 队首 var head: Node? /// 队尾 @@ -27,10 +16,15 @@ struct QueueBasedOnLinkedList: Queue { var isEmpty: Bool { return head == nil } var size: Int { - var count = 0 + if isEmpty { + return 0 + } + + var count = 1 // head 本身算一个 while head?.next != nil { count += 1 } + return count }