├── .gitignore ├── img ├── heap-sort.png ├── 3-basic-sort.png ├── merge-sort.png ├── news-letter.png ├── quick-sort.png ├── merge-sort-linked-list.png └── quick-sort-linked-list.png ├── README.md ├── algorithms └── sort │ ├── bubble_sort.cc │ ├── selection_sort.cc │ ├── insertion_sort.cc │ ├── quick_sort.cc │ ├── heap_sort.cc │ ├── merge_sort.cc │ ├── linked_list_quick_sort.cc │ ├── linked_list_merge_sort.cc │ └── README.md ├── data_structures ├── blocking_queue.cc ├── lru_cache.cc ├── scheduler.cc ├── hash_table.cc ├── packet.cc ├── heap.cc └── trie.cc └── .clang-format /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | -------------------------------------------------------------------------------- /img/heap-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/heap-sort.png -------------------------------------------------------------------------------- /img/3-basic-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/3-basic-sort.png -------------------------------------------------------------------------------- /img/merge-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/merge-sort.png -------------------------------------------------------------------------------- /img/news-letter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/news-letter.png -------------------------------------------------------------------------------- /img/quick-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/quick-sort.png -------------------------------------------------------------------------------- /img/merge-sort-linked-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/merge-sort-linked-list.png -------------------------------------------------------------------------------- /img/quick-sort-linked-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistSysCorp/infra-interview/HEAD/img/quick-sort-linked-list.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # infra-interview(基础架构面试) 2 | 3 | Interview data structures and algorithms for infra programmers. 4 | 5 | 本仓库主要辑录 infra 程序员常见面试题目:数据结构、算法、语言(主要是 C++)。 6 | 欢迎大家在 issue 里补充你遇到的有趣的题目,然后我增加题解放到仓库里。当然,如果你直接提 PR 更好。 7 | 8 | ![](img/3-basic-sort.png) 9 | 10 | 11 | # video (视频讲解) 12 | 13 | 其中一些题目的背景和讲解上传到了 b 站和 youtube。 14 | 15 | - Bilibili: https://space.bilibili.com/30933812/channel/collectiondetail?sid=1655069 16 | - Youtbue: https://www.youtube.com/playlist?list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv 17 | 18 | # news letter(专栏) 19 | 20 | 其中一些题目的背景和讲解在我的大规模数据系统专栏《[系统日知录](https://xiaobot.net/p/system-thinking)》里。 21 | 22 | ![](img/news-letter.png) 23 | -------------------------------------------------------------------------------- /algorithms/sort/bubble_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // incr order 5 | void bubbleSort(std::vector& arr) { 6 | if (arr.size() <= 1) { 7 | return; 8 | } 9 | 10 | // many passes: n = arr.size() 11 | for (size_t pass = 0; pass < arr.size() - 1; pass++) { 12 | size_t curr_pos = pass; 13 | 14 | for (size_t i = arr.size() - 1; i > curr_pos; --i) { 15 | if (arr[i] < arr[i - 1]) { 16 | // swap 17 | int tmp = arr[i]; 18 | arr[i] = arr[i - 1]; 19 | arr[i - 1] = tmp; 20 | } 21 | } 22 | } 23 | } 24 | 25 | void print(const std::vector& arr) { 26 | for (auto num : arr) { 27 | std::cout << num << " "; 28 | } 29 | std::cout << std::endl; 30 | } 31 | 32 | void test(std::vector& arr) { 33 | std::cout << "before sort: "; 34 | print(arr); 35 | bubbleSort(arr); 36 | std::cout << "after sort: "; 37 | print(arr); 38 | } 39 | 40 | int main() { 41 | { 42 | std::vector arr{4, 10, 2, 1}; 43 | test(arr); 44 | } 45 | 46 | { 47 | std::vector arr{4}; 48 | test(arr); 49 | } 50 | 51 | { 52 | std::vector arr; 53 | test(arr); 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /algorithms/sort/selection_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // incr order 5 | void selectionSort(std::vector &arr) { 6 | if (arr.size() <= 1) { 7 | return; 8 | } 9 | 10 | // many passes: n = arr.size() 11 | for (size_t pass = 0; pass < arr.size() - 1; pass++) { 12 | size_t target_pos = pass; 13 | 14 | // pick a smallest position 15 | size_t smallest_pos = target_pos; 16 | for (size_t i = arr.size() - 1; i > target_pos; --i) { 17 | if (arr[i] < arr[smallest_pos]) { 18 | smallest_pos = i; 19 | } 20 | } 21 | 22 | // swap 23 | int tmp = arr[smallest_pos]; 24 | arr[smallest_pos] = arr[target_pos]; 25 | arr[target_pos] = tmp; 26 | } 27 | } 28 | 29 | void print(const std::vector &arr) { 30 | for (auto num : arr) { 31 | std::cout << num << " "; 32 | } 33 | std::cout << std::endl; 34 | } 35 | 36 | void test(std::vector &arr) { 37 | std::cout << "before sort: "; 38 | print(arr); 39 | selectionSort(arr); 40 | std::cout << "after sort: "; 41 | print(arr); 42 | } 43 | 44 | int main() { 45 | { 46 | std::vector arr{4, 10, 2, 1}; 47 | test(arr); 48 | } 49 | 50 | { 51 | std::vector arr{4}; 52 | test(arr); 53 | } 54 | 55 | { 56 | std::vector arr; 57 | test(arr); 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /algorithms/sort/insertion_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // incr order 5 | void insertionSort(std::vector &arr) { 6 | if (arr.size() <= 1) { 7 | return; 8 | } 9 | 10 | // many passes: n = arr.size() 11 | for (size_t pass = 1; pass < arr.size(); pass++) { 12 | // insertion 13 | for (size_t i = pass; i > 0; --i) { 14 | if (arr[i] < arr[i - 1]) { 15 | int tmp = arr[i]; 16 | arr[i] = arr[i - 1]; 17 | arr[i - 1] = tmp; 18 | } else { 19 | break; 20 | } 21 | } 22 | } 23 | } 24 | 25 | void print(const std::vector &arr) { 26 | for (auto num : arr) { 27 | std::cout << num << " "; 28 | } 29 | std::cout << std::endl; 30 | } 31 | 32 | void test(std::vector &arr) { 33 | std::cout << "before sort: "; 34 | print(arr); 35 | insertionSort(arr); 36 | std::cout << "after sort: "; 37 | print(arr); 38 | } 39 | 40 | int main() { 41 | { 42 | std::vector arr{4, 10, 2, 1}; 43 | test(arr); 44 | } 45 | 46 | { 47 | std::vector arr{1, 2, 3, 4}; 48 | test(arr); 49 | } 50 | 51 | { 52 | std::vector arr{4, 3, 2, 1}; 53 | test(arr); 54 | } 55 | 56 | { 57 | std::vector arr{4, 4, 2, 1}; 58 | test(arr); 59 | } 60 | 61 | { 62 | std::vector arr{4}; 63 | test(arr); 64 | } 65 | 66 | { 67 | std::vector arr; 68 | test(arr); 69 | } 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /algorithms/sort/quick_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // a helper function for recursive 5 | void helperSort(std::vector &arr, int begin, int end) { 6 | if (begin >= end) { 7 | return; 8 | } 9 | 10 | int pivot = begin; 11 | int last = begin; 12 | for (int i = begin + 1; i <= end; ++i) { 13 | if (arr[i] < arr[pivot]) { 14 | ++last; // expand the smaller list 15 | std::swap(arr[i], arr[last]); 16 | } 17 | } 18 | 19 | // put pivot to right place 20 | std::swap(arr[pivot], arr[last]); 21 | 22 | // sort left list and right list recursively 23 | helperSort(arr, begin, last - 1); 24 | helperSort(arr, last + 1, end); 25 | } 26 | 27 | // incr order 28 | void quickSort(std::vector &arr) { 29 | if (arr.size() <= 1) { 30 | return; 31 | } 32 | 33 | helperSort(arr, 0, arr.size() - 1); 34 | } 35 | 36 | void print(const std::vector &arr) { 37 | for (auto num : arr) { 38 | std::cout << num << " "; 39 | } 40 | std::cout << std::endl; 41 | } 42 | 43 | void test(std::vector &arr) { 44 | std::cout << "before sort: "; 45 | print(arr); 46 | quickSort(arr); 47 | std::cout << "after sort: "; 48 | print(arr); 49 | } 50 | 51 | int main() { 52 | { 53 | std::vector arr{4, 10, 2, 1}; 54 | test(arr); 55 | } 56 | 57 | { 58 | std::vector arr{1, 2, 3, 4}; 59 | test(arr); 60 | } 61 | 62 | { 63 | std::vector arr{4, 3, 2, 1}; 64 | test(arr); 65 | } 66 | 67 | { 68 | std::vector arr{4, 4, 2, 1}; 69 | test(arr); 70 | } 71 | 72 | { 73 | std::vector arr{4}; 74 | test(arr); 75 | } 76 | 77 | { 78 | std::vector arr; 79 | test(arr); 80 | } 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /algorithms/sort/heap_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void heapify(std::vector &arr, int begin, int end) { 5 | if (begin >= end) { 6 | return; 7 | } 8 | 9 | int root = begin; 10 | int left = root * 2 + 1; 11 | int right = left + 1; 12 | 13 | int max = root; 14 | if (left <= end && arr[left] > arr[max]) { 15 | max = left; 16 | } 17 | if (right <= end && arr[right] > arr[max]) { 18 | max = right; 19 | } 20 | 21 | if (max == root) { 22 | return; 23 | } 24 | std::swap(arr[root], arr[max]); 25 | heapify(arr, max, end); 26 | } 27 | 28 | // incr order 29 | void heapSort(std::vector &arr) { 30 | if (arr.size() <= 1) { 31 | return; 32 | } 33 | 34 | // step 1: construct heap 35 | for (int root = arr.size(); root >= 0; --root) { 36 | heapify(arr, root, arr.size() - 1); 37 | } 38 | 39 | // step 2: pop and heapify loop 40 | for (int end = arr.size() - 1; end > 0; --end) { 41 | std::swap(arr[0], arr[end]); 42 | heapify(arr, 0, end - 1); 43 | } 44 | } 45 | 46 | void print(const std::vector &arr) { 47 | for (auto num : arr) { 48 | std::cout << num << " "; 49 | } 50 | std::cout << std::endl; 51 | } 52 | 53 | void test(std::vector &arr) { 54 | std::cout << "before sort: "; 55 | print(arr); 56 | heapSort(arr); 57 | std::cout << "after sort: "; 58 | print(arr); 59 | } 60 | 61 | int main() { 62 | { 63 | std::vector arr{4, 10, 2, 1}; 64 | test(arr); 65 | } 66 | 67 | { 68 | std::vector arr{1, 2, 3, 4}; 69 | test(arr); 70 | } 71 | 72 | { 73 | std::vector arr{4, 3, 2, 1}; 74 | test(arr); 75 | } 76 | 77 | { 78 | std::vector arr{4, 4, 2, 1}; 79 | test(arr); 80 | } 81 | 82 | { 83 | std::vector arr{4}; 84 | test(arr); 85 | } 86 | 87 | { 88 | std::vector arr; 89 | test(arr); 90 | } 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /algorithms/sort/merge_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // a helper function for recursive 5 | void helpeSort(std::vector& arr, int begin, int end) { 6 | if (begin >= end) { 7 | return; 8 | } 9 | 10 | // split 11 | // int mid = begin + (end-begin)/2; 12 | int mid = (begin + end) / 2; 13 | 14 | // sort recursively 15 | helpeSort(arr, begin, mid); 16 | helpeSort(arr, mid + 1, end); 17 | 18 | // merge 19 | std::vector tmp_left; 20 | tmp_left.assign(arr.begin() + begin, arr.begin() + mid + 1); 21 | std::vector& tmp_right = arr; 22 | 23 | int l = 0; 24 | int r = mid + 1; 25 | int m = begin; 26 | while (l < tmp_left.size() && r <= end) { 27 | if (tmp_left[l] < tmp_right[r]) { 28 | arr[m++] = tmp_left[l++]; 29 | } else { 30 | arr[m++] = tmp_right[r++]; 31 | } 32 | } 33 | while (l < tmp_left.size()) { 34 | arr[m++] = tmp_left[l++]; 35 | } 36 | while (r <= end) { 37 | arr[m++] = tmp_right[r++]; 38 | } 39 | } 40 | 41 | // incr order 42 | void mergeSort(std::vector& arr) { 43 | if (arr.size() <= 1) { 44 | return; 45 | } 46 | 47 | helpeSort(arr, 0, arr.size() - 1); 48 | } 49 | 50 | void print(const std::vector& arr) { 51 | for (auto num : arr) { 52 | std::cout << num << " "; 53 | } 54 | std::cout << std::endl; 55 | } 56 | 57 | void test(std::vector& arr) { 58 | std::cout << "before sort: "; 59 | print(arr); 60 | mergeSort(arr); 61 | std::cout << "after sort: "; 62 | print(arr); 63 | } 64 | 65 | int main() { 66 | { 67 | std::vector arr{4, 10, 2, 1}; 68 | test(arr); 69 | } 70 | 71 | { 72 | std::vector arr{1, 2, 3, 4}; 73 | test(arr); 74 | } 75 | 76 | { 77 | std::vector arr{4, 3, 2, 1}; 78 | test(arr); 79 | } 80 | 81 | { 82 | std::vector arr{4, 4, 2, 1}; 83 | test(arr); 84 | } 85 | 86 | { 87 | std::vector arr{4}; 88 | test(arr); 89 | } 90 | 91 | { 92 | std::vector arr; 93 | test(arr); 94 | } 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /data_structures/blocking_queue.cc: -------------------------------------------------------------------------------- 1 | // 在 linux 平台上,通过 `g++ BlockingQueue.cc -std=c++17 -lpthread` 编译即可 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | class BlockingQueue { 13 | public: 14 | BlockingQueue(size_t cap) : capacity_(cap) {} 15 | 16 | void push(const T& data) { 17 | std::unique_lock lock(mu_); 18 | while (queue_.size() >= capacity_) { 19 | std::cout << "queue is full, blocking" << std::endl; 20 | fullCond_.wait(lock); 21 | } 22 | 23 | queue_.push(data); 24 | emptyCond_.notify_one(); 25 | } 26 | 27 | size_t size() const { 28 | std::unique_lock lock(mu_); 29 | return queue_.size(); 30 | } 31 | 32 | T pop() { 33 | std::unique_lock lock(mu_); 34 | while (queue_.empty()) { 35 | std::cout << "queue is empty, blocking" << std::endl; 36 | emptyCond_.wait(lock); 37 | } 38 | 39 | auto value = queue_.front(); 40 | queue_.pop(); 41 | fullCond_.notify_one(); 42 | return value; 43 | } 44 | 45 | private: 46 | std::queue queue_; 47 | size_t capacity_; 48 | 49 | std::mutex mu_; 50 | std::condition_variable emptyCond_; 51 | std::condition_variable fullCond_; 52 | }; 53 | 54 | int main() { 55 | std::mutex printMu; 56 | BlockingQueue q(2); 57 | 58 | auto push = [&q, &mu = printMu](std::vector data) { 59 | for (auto num : data) { 60 | { 61 | std::lock_guard lock(mu); 62 | std::cout << std::this_thread::get_id() << ": push " << num << std::endl; 63 | } 64 | q.push(num); 65 | } 66 | }; 67 | 68 | auto pop = [&q, &mu = printMu](size_t count) { 69 | { 70 | std::lock_guard lock(mu); 71 | std::cout << std::this_thread::get_id() << ": wait for 1s, then start pop" << std::endl; 72 | } 73 | std::this_thread::sleep_for(std::chrono::seconds(1)); 74 | while (count--) { 75 | auto num = q.pop(); 76 | { 77 | std::lock_guard lock(mu); 78 | std::cout << std::this_thread::get_id() << ": pop = " << num << std::endl; 79 | } 80 | } 81 | }; 82 | 83 | std::thread t1(std::bind(push, std::vector({1, 2, 3, 4}))); 84 | std::thread t2(std::bind(pop, 8)); 85 | std::this_thread::sleep_for(std::chrono::seconds(2)); 86 | std::thread t3(std::bind(push, std::vector({5, 6, 7, 8}))); 87 | 88 | t1.join(); 89 | t2.join(); 90 | t3.join(); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /algorithms/sort/linked_list_quick_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct LinkNode { 5 | LinkNode* next; 6 | int val; 7 | 8 | LinkNode(int v) : val(v), next(nullptr) {} 9 | }; 10 | 11 | // incr order 12 | LinkNode* quickSort(LinkNode* head) { 13 | // empty list or list with only one node 14 | if (head == nullptr || head->next == nullptr) { 15 | return head; 16 | } 17 | 18 | // select a pivot element 19 | LinkNode* pivot = head; 20 | LinkNode* smaller = nullptr; 21 | LinkNode* larger = nullptr; 22 | 23 | // step 1: split 24 | auto iter = head->next; 25 | while (iter) { 26 | // detach a node 27 | auto curr = iter; 28 | iter = iter->next; 29 | 30 | // attach the node to smaller or larger 31 | if (curr->val < pivot->val) { 32 | curr->next = smaller; 33 | smaller = curr; 34 | } else { 35 | curr->next = larger; 36 | larger = curr; 37 | } 38 | } 39 | 40 | // step 2: sort recursively 41 | smaller = quickSort(smaller); 42 | larger = quickSort(larger); 43 | 44 | // step 3: concat smaller->pivot->larger 45 | pivot->next = larger; 46 | if (smaller == nullptr) { 47 | return pivot; 48 | } 49 | 50 | auto tail = smaller; 51 | while (tail->next) { 52 | tail = tail->next; 53 | } 54 | tail->next = pivot; 55 | return smaller; 56 | } 57 | 58 | void print(const LinkNode* iter) { 59 | for (; iter; iter = iter->next) { 60 | std::cout << iter->val << " "; 61 | } 62 | std::cout << std::endl; 63 | } 64 | 65 | void test(std::vector& arr) { 66 | LinkNode dummy_head(0); 67 | LinkNode* tail = &dummy_head; 68 | for (auto num : arr) { 69 | tail->next = new LinkNode(num); 70 | tail = tail->next; 71 | } 72 | tail->next = nullptr; // seal the tail 73 | 74 | std::cout << "before sort: "; 75 | print(dummy_head.next); 76 | dummy_head.next = quickSort(dummy_head.next); 77 | std::cout << "after sort: "; 78 | print(dummy_head.next); 79 | } 80 | 81 | int main() { 82 | { 83 | std::vector arr{4, 10, 2, 1}; 84 | test(arr); 85 | } 86 | 87 | { 88 | std::vector arr{1, 2, 3, 4}; 89 | test(arr); 90 | } 91 | 92 | { 93 | std::vector arr{4, 3, 2, 1}; 94 | test(arr); 95 | } 96 | 97 | { 98 | std::vector arr{4, 4, 2, 1}; 99 | test(arr); 100 | } 101 | 102 | { 103 | std::vector arr{4}; 104 | test(arr); 105 | } 106 | 107 | { 108 | std::vector arr; 109 | test(arr); 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /algorithms/sort/linked_list_merge_sort.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct LinkNode { 5 | LinkNode* next; 6 | int val; 7 | 8 | LinkNode(int v) : val(v), next(nullptr) {} 9 | }; 10 | 11 | // incr order 12 | LinkNode* mergeSort(LinkNode* head) { 13 | // empty list or list with only one node 14 | if (head == nullptr || head->next == nullptr) { 15 | return head; 16 | } 17 | 18 | // step 1: split 19 | LinkNode dummy_head(0); 20 | dummy_head.next = head; 21 | LinkNode* faster = &dummy_head; 22 | LinkNode* slower = &dummy_head; // tail of left sub-list 23 | 24 | while (faster != nullptr) { 25 | faster = faster->next; 26 | if (faster != nullptr) { 27 | faster = faster->next; 28 | } else { 29 | break; 30 | } 31 | 32 | slower = slower->next; 33 | } 34 | 35 | LinkNode* left = dummy_head.next; 36 | LinkNode* right = slower->next; 37 | slower->next = nullptr; // seal the left list 38 | 39 | // step 2: sort recursively 40 | left = mergeSort(left); 41 | right = mergeSort(right); 42 | 43 | // step 3: merge 44 | LinkNode* tail = &dummy_head; 45 | while (left != nullptr && right != nullptr) { 46 | // detach 47 | LinkNode* curr = nullptr; 48 | if (left->val < right->val) { 49 | curr = left; 50 | left = left->next; 51 | } else { 52 | curr = right; 53 | right = right->next; 54 | } 55 | 56 | // attach 57 | tail->next = curr; 58 | tail = tail->next; 59 | } 60 | 61 | if (left != nullptr) { 62 | tail->next = left; 63 | } 64 | if (right != nullptr) { 65 | tail->next = right; 66 | } 67 | return dummy_head.next; 68 | } 69 | 70 | void print(const LinkNode* iter) { 71 | for (; iter; iter = iter->next) { 72 | std::cout << iter->val << " "; 73 | } 74 | std::cout << std::endl; 75 | } 76 | 77 | void test(std::vector& arr) { 78 | LinkNode dummy_head(0); 79 | LinkNode* tail = &dummy_head; 80 | for (auto num : arr) { 81 | tail->next = new LinkNode(num); 82 | tail = tail->next; 83 | } 84 | tail->next = nullptr; // seal the tail 85 | 86 | std::cout << "before sort: "; 87 | print(dummy_head.next); 88 | dummy_head.next = mergeSort(dummy_head.next); 89 | std::cout << "after sort: "; 90 | print(dummy_head.next); 91 | } 92 | 93 | int main() { 94 | { 95 | std::vector arr{4, 10, 2, 1}; 96 | test(arr); 97 | } 98 | 99 | { 100 | std::vector arr{1, 2, 3, 4}; 101 | test(arr); 102 | } 103 | 104 | { 105 | std::vector arr{4, 3, 2, 1}; 106 | test(arr); 107 | } 108 | 109 | { 110 | std::vector arr{4, 4, 2, 1}; 111 | test(arr); 112 | } 113 | 114 | { 115 | std::vector arr{4}; 116 | test(arr); 117 | } 118 | 119 | { 120 | std::vector arr; 121 | test(arr); 122 | } 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /data_structures/lru_cache.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class Node { 6 | public: 7 | Node(int k, int v) : key(k), val(v) { 8 | prev = next = nullptr; 9 | } 10 | 11 | std::string toString() { 12 | std::stringstream ss; 13 | ss << "{ " << key << ": " << val << "}"; 14 | return ss.str(); 15 | } 16 | 17 | int key; 18 | int val; 19 | Node* prev; 20 | Node* next; 21 | }; 22 | 23 | // could only store 0 and positive numbers 24 | class LRUCache { 25 | public: 26 | LRUCache(int capacity) : cap_(capacity) { 27 | head_ = new Node(-1, -1); 28 | tail_ = new Node(-1, -1); 29 | head_->next = tail_; 30 | tail_->prev = head_; 31 | } 32 | 33 | ~LRUCache() { 34 | release(head_); 35 | } 36 | 37 | int get(int key) { 38 | std::cout << "try to get " << key << " = "; 39 | auto iter = index_.find(key); 40 | if (iter == index_.end()) { 41 | return -1; 42 | } 43 | 44 | auto* node = iter->second; 45 | attach(head_, detach(node)); 46 | return node->val; 47 | } 48 | 49 | void put(int key, int value) { 50 | std::cout << "try to put " << key << " : " << value << std::endl; 51 | auto iter = index_.find(key); 52 | if (iter != index_.end()) { 53 | iter->second->val = value; 54 | attach(head_, detach(iter->second)); 55 | return; 56 | } 57 | 58 | Node* node = new Node(key, value); 59 | index_[key] = attach(head_, node); 60 | 61 | if (index_.size() > cap_) { 62 | auto toDel = detach(tail_->prev); 63 | index_.erase(toDel->key); 64 | std::cout << "evict item: " << toDel->toString() << std::endl; 65 | delete toDel; 66 | } 67 | } 68 | 69 | public: // for test 70 | size_t size() { 71 | return index_.size(); 72 | } 73 | 74 | void echo() { 75 | Node* iter = head_->next; 76 | while (iter != tail_) { 77 | std::cout << iter->toString() << "; "; 78 | iter = iter->next; 79 | } 80 | std::cout << std::endl; 81 | } 82 | 83 | private: 84 | void release(Node* head) { 85 | while (head) { 86 | Node* curr = head; 87 | head = head->next; 88 | std::cout << "release " << curr->toString() << std::endl; 89 | delete curr; 90 | } 91 | } 92 | 93 | Node* detach(Node* node) { 94 | node->next->prev = node->prev; 95 | node->prev->next = node->next; 96 | return node; 97 | } 98 | 99 | Node* attach(Node* head, Node* node) { 100 | node->next = head->next; 101 | node->prev = head; 102 | 103 | head->next->prev = node; 104 | head->next = node; 105 | 106 | return node; 107 | } 108 | 109 | private: 110 | int cap_; 111 | Node* head_; 112 | Node* tail_; 113 | std::unordered_map index_; 114 | }; 115 | 116 | int main() { 117 | LRUCache cache(3); 118 | cache.put(1, 10); 119 | cache.put(2, 20); 120 | cache.put(3, 30); 121 | cache.put(4, 40); 122 | cache.put(5, 50); 123 | cache.echo(); 124 | 125 | cache.put(4, 400); 126 | cache.echo(); 127 | 128 | std::cout << cache.get(1) << std::endl; 129 | cache.echo(); 130 | 131 | std::cout << cache.get(3) << std::endl; 132 | cache.echo(); 133 | 134 | cache.put(2, 200); 135 | cache.echo(); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /data_structures/scheduler.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int64_t getCurrMs() { 13 | auto now = std::chrono::system_clock::now(); 14 | std::chrono::milliseconds nowMs = 15 | std::chrono::duration_cast(now.time_since_epoch()); 16 | return nowMs.count(); 17 | } 18 | 19 | class Scheduler { 20 | public: 21 | Scheduler() { 22 | stopped_ = false; 23 | std::thread loop([this]() { this->scheduleLoop(); }); 24 | loop.detach(); 25 | } 26 | 27 | ~Scheduler() { 28 | std::unique_lock lock(mu_); 29 | stopped_ = true; 30 | } 31 | 32 | int64_t schedule(std::function task, int64_t delayInSec) { 33 | std::unique_lock lock(mu_); 34 | 35 | int64_t taskId = idGen_++; 36 | int64_t targetTs = getCurrMs() + delayInSec * 1000; 37 | 38 | Task newTask(taskId, task, targetTs); 39 | tasks_[taskId] = newTask; 40 | taskBuckets_[targetTs].push_back(taskId); 41 | runCv_.notify_one(); 42 | 43 | return taskId; 44 | } 45 | 46 | void cancel(int64_t taskId) { 47 | std::unique_lock lock(mu_); 48 | tasks_.erase(taskId); 49 | } 50 | 51 | private: 52 | struct Task { 53 | Task() = default; 54 | Task(int64_t id, std::function r, int64_t t) : taskId(id), run(r), targetTs(t) {} 55 | 56 | Task(const Task& other) { 57 | taskId = other.taskId; 58 | run = other.run; 59 | targetTs = other.targetTs; 60 | } 61 | 62 | Task& operator=(const Task& other) { 63 | taskId = other.taskId; 64 | run = other.run; 65 | targetTs = other.targetTs; 66 | return *this; 67 | } 68 | 69 | int64_t taskId; 70 | std::function run; 71 | int64_t targetTs; 72 | }; 73 | 74 | void scheduleLoop() { 75 | std::unique_lock lock(mu_); 76 | while (!stopped_) { 77 | if (taskBuckets_.empty()) { 78 | runCv_.wait_for(lock, std::chrono::milliseconds(1000)); 79 | continue; 80 | } 81 | 82 | // check if we have tasks need to be executed 83 | int64_t nextTs = taskBuckets_.begin()->first; 84 | int64_t delay = nextTs - getCurrMs(); 85 | 86 | if (delay < 0) { 87 | auto& taskIds = taskBuckets_.begin()->second; 88 | for (auto taskId : taskIds) { 89 | // the task maybe canceled 90 | if (tasks_.find(taskId) == tasks_.end()) { 91 | continue; 92 | } 93 | 94 | std::thread t(tasks_[taskId].run); 95 | t.detach(); // run in back ground 96 | tasks_.erase(taskId); 97 | } 98 | taskBuckets_.erase(nextTs); 99 | } else { 100 | runCv_.wait_for(lock, std::chrono::milliseconds(delay)); 101 | } 102 | } 103 | } 104 | 105 | private: 106 | std::mutex mu_; 107 | std::condition_variable runCv_; 108 | bool stopped_; 109 | int64_t idGen_; 110 | std::unordered_map tasks_; 111 | std::map> taskBuckets_; 112 | }; 113 | 114 | int main() { 115 | Scheduler tasks; 116 | 117 | std::cout << "Now time is: " << getCurrMs() << std::endl; 118 | auto task1 = 119 | tasks.schedule([]() { std::cout << "Now time is: " << getCurrMs() << std::endl; }, 2); 120 | 121 | auto task2 = 122 | tasks.schedule([]() { std::cout << "Now time is: " << getCurrMs() << std::endl; }, 1); 123 | 124 | tasks.cancel(task2); 125 | std::this_thread::sleep_for(std::chrono::seconds(10)); 126 | } 127 | -------------------------------------------------------------------------------- /data_structures/hash_table.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | struct LinkNode { 9 | LinkNode() = default; 10 | LinkNode(K k, V v) : key(k), val(v) { 11 | next = nullptr; 12 | } 13 | 14 | K key; 15 | V val; 16 | LinkNode *next; 17 | }; 18 | 19 | template 20 | class HashTable { 21 | public: 22 | HashTable() { 23 | table_.resize(INIT_BUCKET_SIZE); 24 | size_ = 0; 25 | } 26 | 27 | ~HashTable() { 28 | for (auto head : table_) { 29 | for (auto it = head; it != nullptr;) { 30 | auto curr = it; 31 | it = it->next; 32 | 33 | delete curr; 34 | } 35 | } 36 | } 37 | 38 | public: 39 | size_t size() const { 40 | return size_; 41 | } 42 | 43 | void insert(K key, V val) { 44 | std::cout << "insert(" << key << ", " << val << ")" << std::endl; 45 | 46 | auto *iter = find(key); 47 | if (iter != nullptr) { // update 48 | iter->val = val; 49 | return; 50 | } else { // insert in the head 51 | size_t loc = hasher_(key) % table_.size(); 52 | auto newNode = new LinkNode(key, val); 53 | newNode->next = iter; 54 | table_[loc] = newNode; 55 | ++size_; 56 | } 57 | } 58 | 59 | bool erase(K key) { 60 | std::cout << "erase(" << key << ")" << std::endl; 61 | size_t hash = hasher_(key); 62 | size_t loc = hash % table_.size(); 63 | 64 | auto *iter = table_[loc]; 65 | LinkNode *prev = nullptr; 66 | while (iter) { 67 | if (iter->key == key) { 68 | break; 69 | } 70 | prev = iter; 71 | iter = iter->next; 72 | } 73 | 74 | // not found 75 | if (iter == nullptr) { 76 | return false; 77 | } 78 | 79 | // remove 80 | if (prev == nullptr) { // head 81 | table_[loc] = iter->next; 82 | } else { 83 | prev->next = iter->next; 84 | } 85 | --size_; 86 | delete iter; 87 | return true; 88 | } 89 | 90 | V get(K key) const { 91 | auto *iter = find(key); 92 | if (iter == nullptr) { 93 | std::stringstream ss; 94 | ss << key << " is not found"; 95 | throw std::runtime_error(ss.str()); 96 | } 97 | return iter->val; 98 | } 99 | 100 | size_t count(K key) const { 101 | auto iter = find(key); 102 | 103 | // do not find 104 | if (iter == nullptr) { 105 | return 0; 106 | } else { 107 | return 1; 108 | } 109 | } 110 | 111 | void echo() { 112 | std::cout << "==================" << std::endl; 113 | std::cout << "all elements are:" << std::endl; 114 | for (auto *head : table_) { 115 | for (auto *iter = head; iter != nullptr; iter = iter->next) { 116 | std::cout << "(" << iter->key << ":" << iter->val << ")"; 117 | std::cout << std::endl; 118 | } 119 | } 120 | } 121 | 122 | private: 123 | LinkNode *find(K key) const noexcept { 124 | size_t hash = hasher_(key); 125 | size_t loc = hash % table_.size(); 126 | 127 | auto *iter = table_[loc]; 128 | while (iter != nullptr && iter->key != key) { 129 | iter = iter->next; 130 | } 131 | return iter; 132 | } 133 | 134 | private: 135 | const size_t INIT_BUCKET_SIZE = 13; 136 | 137 | std::vector *> table_; 138 | std::hash hasher_; 139 | size_t size_; 140 | }; 141 | 142 | int main() { 143 | HashTable m; 144 | m.insert(1, 1); 145 | m.insert(1, 2); 146 | m.echo(); 147 | std::cout << "m.get(1)=" << m.get(1) << std::endl; 148 | 149 | m.insert(2, 1); 150 | m.echo(); 151 | m.erase(1); 152 | m.echo(); 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /data_structures/packet.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct Packet { 10 | Packet(size_t off, size_t len, uint8_t* data) : offset(off), length(len), data(data) {} 11 | size_t offset; 12 | size_t length; 13 | uint8_t* data; 14 | }; 15 | 16 | class TCP { 17 | public: 18 | TCP() { 19 | nextOffset_ = 0; 20 | finished_ = false; 21 | } 22 | 23 | size_t read(void* buf, size_t count) { 24 | std::unique_lock lock(mu_); 25 | 26 | size_t leftBytes = count; 27 | while (leftBytes > 0) { 28 | if (!packets_.empty()) { 29 | size_t offset = packets_.begin()->first; 30 | auto* p = packets_.begin()->second; 31 | if (offset == nextOffset_) { 32 | // fetch the packet 33 | packets_.erase(offset); 34 | 35 | // copy to the user buf 36 | size_t len = std::min(p->length, leftBytes); 37 | std::memcpy(buf, p->data, len); 38 | leftBytes -= len; 39 | nextOffset_ += len; 40 | 41 | // put back the left data 42 | p->length -= len; 43 | if (p->length > 0) { 44 | p->data += len; 45 | p->offset += len; 46 | packets_[p->offset] = p; 47 | } 48 | 49 | return count - leftBytes; 50 | } 51 | } else if (finished_) { 52 | break; 53 | } 54 | 55 | cv_.wait(lock); 56 | } 57 | 58 | // finished 59 | return 0; 60 | } 61 | 62 | void receive(Packet* p) { 63 | std::unique_lock lock(mu_); 64 | packets_[p->offset] = p; 65 | cv_.notify_one(); 66 | } 67 | 68 | void finish() { 69 | std::unique_lock lock(mu_); 70 | finished_ = true; 71 | cv_.notify_one(); 72 | } 73 | 74 | private: 75 | std::mutex mu_; 76 | std::condition_variable cv_; 77 | size_t nextOffset_; 78 | bool finished_; 79 | std::map packets_; 80 | }; 81 | 82 | TCP tcp; 83 | 84 | void producer(uint8_t* data) { 85 | std::random_device rd; 86 | std::mt19937 gen(rd()); 87 | std::uniform_int_distribution<> dis(50, 99); 88 | 89 | std::cout << "construct data..." << std::endl; 90 | std::vector packets(100); 91 | size_t offset = 0; 92 | for (size_t i = 0; i < 100; ++i) { 93 | size_t randLen = dis(gen); 94 | packets[i] = new Packet(offset, randLen, data); 95 | data += randLen; 96 | offset += randLen; 97 | } 98 | 99 | std::cout << "total " << offset << " bytes" << std::endl; 100 | std::cout << "make the data unordered..." << std::endl; 101 | std::shuffle(packets.begin(), packets.end(), gen); 102 | 103 | std::cout << "give it to tcp..." << std::endl; 104 | for (size_t i = 0; i < 100; ++i) { 105 | tcp.receive(packets[i]); 106 | std::cout << "receive data [" << packets[i]->offset << " ," 107 | << packets[i]->offset + packets[i]->length << "): " << packets[i]->length << " bytes" 108 | << std::endl; 109 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 110 | } 111 | tcp.finish(); 112 | } 113 | 114 | void consumer(uint8_t* data) { 115 | size_t nBytes = 0; 116 | uint8_t buf[100]; 117 | 118 | size_t offset = 0; 119 | while ((nBytes = tcp.read(buf, 50)) > 0) { 120 | auto diff = std::memcmp(data + offset, buf, nBytes); 121 | std::stringstream ss; 122 | ss << "read data [" << offset << " ," << offset + nBytes << "): " << nBytes << " bytes"; 123 | if (!diff) { 124 | ss << "; verify ok"; 125 | } else { 126 | ss << "; verify bad"; 127 | } 128 | std::cout << ss.str() << std::endl; 129 | 130 | offset += nBytes; 131 | } 132 | std::cout << "read finish" << std::endl; 133 | } 134 | 135 | int main() { 136 | uint8_t buffer[10000]; 137 | for (size_t i = 0; i < 10000; ++i) { 138 | buffer[i] = i & 0xff; 139 | } 140 | std::thread c(consumer, buffer); 141 | std::thread p(producer, buffer); 142 | 143 | p.join(); 144 | c.join(); 145 | } 146 | -------------------------------------------------------------------------------- /data_structures/heap.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class FixedMaxHeap { 7 | public: 8 | FixedMaxHeap(size_t capacity) : cap_(capacity) { 9 | data_.reserve(cap_ + 1); 10 | // use an sentinel in the 11 | data_.push_back(INT_MAX); 12 | // if we set a sentinel in position 0, then the binary 13 | // heap looks like: 14 | // 0 15 | // | 16 | // 1 17 | // / \ 18 | // 2 3 19 | // and we will have some really nice properties: 20 | // 1. parent(index) = index/2 21 | // 2. children(index) = index*2, index*2+1(maybe exist or not) 22 | // 3. we need not check the if the index overflow every time 23 | } 24 | 25 | size_t size() const { 26 | return data_.size() - 1; 27 | } 28 | 29 | bool isFull() const { 30 | return size() == cap_; 31 | } 32 | 33 | bool isEmpty() const { 34 | return size() == 0; 35 | } 36 | 37 | // will raise exception when there is no element 38 | int top() const { 39 | if (isEmpty()) { 40 | throw std::invalid_argument("heap is empty"); 41 | } 42 | 43 | return data_[ROOT_IDX]; 44 | } 45 | 46 | void push(int val) { 47 | if (isFull()) { 48 | throw std::invalid_argument("heap is full"); 49 | } 50 | 51 | std::cout << "try to push: " << val << std::endl; 52 | data_.push_back(val); 53 | adjustUp(); 54 | } 55 | 56 | int pop() { 57 | if (isEmpty()) { 58 | throw std::invalid_argument("heap is empty"); 59 | } 60 | std::cout << "try to pop: " << top() << std::endl; 61 | 62 | int val = top(); 63 | data_[ROOT_IDX] = data_.back(); 64 | data_.pop_back(); // shrink 65 | adjustDown(); // make it a heap again 66 | 67 | return val; 68 | } 69 | 70 | private: 71 | // insert from bottom level to the root 72 | // suppose that: only the last value does not 73 | // compose the heap properties 74 | void adjustUp() { 75 | size_t pos = data_.size() - 1; 76 | int val = data_[pos]; 77 | 78 | for (; data_[pos / 2] < data_[pos]; pos /= 2) { 79 | data_[pos] = data_[pos / 2]; 80 | } 81 | 82 | if (pos != data_.size() - 1) { 83 | data_[pos] = val; 84 | } 85 | } 86 | 87 | // adjust from the root to the bottom 88 | // suppose that: only the root value does not 89 | // compose the heap properties 90 | void adjustDown() { 91 | size_t pos = ROOT_IDX; 92 | int val = data_[pos]; 93 | 94 | while (pos * 2 < data_.size()) { 95 | size_t child = pos * 2; 96 | 97 | // find the larger one to be poped 98 | if (child + 1 < data_.size() && data_[child + 1] > data_[child]) { 99 | child++; 100 | } 101 | 102 | if (data_[child] > data_[pos]) { 103 | data_[pos] = data_[child]; 104 | pos = child; 105 | } else { 106 | break; 107 | } 108 | } 109 | 110 | if (pos != ROOT_IDX) { 111 | data_[pos] = val; 112 | } 113 | } 114 | 115 | public: 116 | void echo() { // for debug 117 | for (size_t i = ROOT_IDX; i < data_.size(); ++i) { 118 | std::cout << data_[i] << " "; 119 | } 120 | std::cout << std::endl; 121 | } 122 | 123 | private: 124 | static const size_t ROOT_IDX = 1; 125 | 126 | size_t cap_; 127 | std::vector data_; 128 | }; 129 | 130 | int main() { 131 | std::vector data(100); 132 | for (int i = 0; i < 100; ++i) { 133 | data[i] = i; 134 | } 135 | std::random_device rd; 136 | std::mt19937 rng(rd()); 137 | std::shuffle(data.begin(), data.end(), rng); 138 | 139 | // find top 5 smallest number 140 | FixedMaxHeap q(5); 141 | for (auto num : data) { 142 | if (!q.isFull()) { 143 | q.push(num); 144 | } else if (num < q.top()) { 145 | q.pop(); 146 | q.push(num); 147 | } 148 | 149 | q.echo(); 150 | } 151 | 152 | std::cout << "The smallest 5 number are:" << std::endl; 153 | while (!q.isEmpty()) { 154 | std::cout << q.top() << " "; 155 | q.pop(); 156 | } 157 | 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /algorithms/sort/README.md: -------------------------------------------------------------------------------- 1 | # 排序算法(sort algorighm 2 | 3 | 1. 冒泡排序(bubble sort 4 | 2. 选择排序(selection sort 5 | 3. 插入排序(insertion sort 6 | 4. 归并排序(merge sort 7 | 5. 快速排序(quick sort 8 | 6. 希尔排序(shell sort 9 | 7. 堆排序(heap sort 10 | 8. 桶排序(bucket sort 11 | 12 | ## 冒泡排序 13 | 14 | 视频:[b站](https://www.bilibili.com/video/BV1du41177iu/),[Youtube](https://www.youtube.com/watch?v=NW8SV7gWZzQ&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=2) 15 | 16 | S-order <--bubble-- S-unorder 17 | 18 | 像冒泡一样,每次让最小的数**冒出**剩余数据集(无序)“水面”,抵达有序集。 19 | 20 | 简单总结: 21 | 1. 线性减小规模 22 | 2. 通过交换往前挪 23 | 24 | ## 选择排序 25 | 26 | 视频:[b站](https://www.bilibili.com/video/BV1ru4y1X7cs) [Youtube](https://www.youtube.com/watch?v=B_MZlU68rgU&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=4) 27 | 28 | S-order <--selection-- S-unorder 29 | 30 | 从剩余数据集(无序)中**选择**最值,放到有序集中。 31 | 32 | 简单总结: 33 | 1. 线性减小规模 34 | 2. 找到最值往前放 35 | 36 | ## 插入排序 37 | 38 | 视频:[b站](https://www.bilibili.com/video/BV1Gp4y1E7Dh) [Youtube](https://www.youtube.com/watch?v=ivGRfvUvTww&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=3) 39 | 40 | S-order <--insertion-- S-unorder 41 | 42 | 从剩余数据集(无序)中随意取一个值,然后通过比较“插入”(也可以理解为往前冒泡)到有序集中。 43 | 44 | 简单总结: 45 | 1. 线性增大规模 46 | 2. 选值放合适位置 47 | 48 | ## 基本排序小结 49 | 50 | 51 | ![](../../img/3-basic-sort.png) 52 | 53 | {[order-list], [unorder-list]} 54 | 55 | 冒泡排序、选择排序和插入排序是三种最基本的排序算法。其原理是相通的: 56 | 1. 将数组划分成前后两个子集:前面是有序集,后面是无序集 57 | 2. 三种方法都是线性的一次从无序集中搬一个元素到有序集中,只不过搬法不同: 58 | - 冒泡:从无序集最后逐个比较冒一个到有序集**最后**。 59 | - 选择:遍历无序集,选一个放到有序集**最后**。 60 | - 插入:从边界处选一个,插入到有序集中**合适位置**。 61 | 62 | 三种排序的复杂度都是 O(n^2)。 63 | 64 | ## 快速排序 65 | 66 | 视频:[b站](https://www.bilibili.com/video/BV1mN411i7QT) [Youtube](https://www.youtube.com/watch?v=I5Pb-i5fL44&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=5) 67 | 68 | S-left | pivot number | S-right 69 | 70 | 每次随机找一个值 x(pivot number,枢轴元素),以其为界,在数组原地: 71 | 1. 将比其小的元素拨到左边 72 | 2. 将比其大的元素拨到右边 73 | 则该值 x 一定放到了正确位置。 74 | 75 | 于是剔除该值。如果该值选择得当,便会得到两个较小子集。 76 | 相比前述三种排序算法**线性**速度减小规模,快排通过近似对半拆分,**指数**速度缩小规模。 77 | 78 | 可以看出,枢轴元素的选择会极大影响性能。 79 | 80 | ![](../../img/quick-sort.png) 81 | 82 | 基于链表的快速排序,思想一致,主要复杂度在链表操作上。 83 | 视频:[b站](https://www.bilibili.com/video/BV1jm4y1T7YE) [Youtube](https://www.youtube.com/watch?v=REF8nBlBM7I&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=6) 84 | 85 | 一些保持代码简洁的小技巧: 86 | 1. 使用栈上的空白头结点,避免是否为空的边界判断。 87 | 2. 使用尾插法减少一个 tail 指针变量。 88 | 3. 递归函数将新的链表头结点返回。 89 | 90 | ![](../../img/quick-sort-linked-list.png) 91 | 92 | 一些改进点: 93 | 1. 可以在递归时同时返回第一个节点和最后一个节点指针,就可以避免拼接的时候对 smaller 链表的遍历。 94 | 2. 可以在拼接的时候使用 dummy 头结点,就可以避免对 smaller 空链表的判断。 95 | 96 | ## 归并排序 97 | 98 | 视频:[b站](https://www.bilibili.com/video/BV1194y1s75T) [Youtube](https://www.youtube.com/watch?v=mWlsMiwDfyM&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=7) 99 | 100 | S-half-left | S-half-right 101 | 102 | 归并排序过程: 103 | 1. 将原数组从中间一分为二 104 | 2. 对左右子数组分别递归排序 105 | 3. 合并左右子数组 106 | 107 | 可以看出,思路和快排非常类似,都是分成两部分然后递归。 108 | 109 | 不同的是,不会选择枢轴元素并将所有元素左右分层(相当于分了两个桶)。也正因为如此,最后需要有个合并过程。且,合并的时候需要一个额外的 buffer。如果不用 buffer 进行原地( in-place )合并也不是不可以,只是算法复杂度难以保持 O(nlogn)。 110 | 111 | 但当然,好处就是,由于每次我们都会保证从中间拆分数组,可以保证时间复杂度稳定的为:O(nlogn)。代价就是,由于合并时需要借助 buffer,因此空间复杂度为 O(n),而快排空间复杂度为 O(1)。 112 | 113 | ![](../../img/merge-sort.png) 114 | 115 | 基于链表的归并算法,思想和数组归并类似,主要难点在于拆分的时候边角情况的处理。很容易非法访问和爆栈(stack overflow)。 116 | 117 | 视频:[b站](https://www.bilibili.com/video/BV18z4y157LT) [Youtube](https://www.youtube.com/watch?v=n4vGM1h245k&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=8) 118 | 119 | 可以有两种拆分的方法: 120 | 1. 2 passes:第一遍求长度,利用半长找到分界点。 121 | 2. 1 pass:快慢指针,快指针每次走两步,慢指针每次走一步 122 | 123 | 技巧:只需要考虑 2 node 和 3 node 的链表拆分即可。 124 | 125 | ![](../../img/merge-sort-linked-list.png) 126 | 127 | ## 堆排序 128 | 129 | 视频:[b站](https://www.bilibili.com/video/BV1DV411w7pi) [Youtube](https://www.youtube.com/watch?v=pMvOP9Pq-Sc&list=PLSISRu2b2N55Htp_3tUQoqMPP4EsTLGxv&index=9) 130 | 131 | 关键是实现一个“堆化”(heapify)函数。 132 | 该函数的工作是:在一个除了根元素其他元素都符合堆的性质的情况下,自根向叶的调整,使之完全符合堆的性质。 133 | 由于调整次数最多是一个堆的深度,因此复杂度为 O(nlogn)。 134 | 135 | 则堆排序可以分为两个步骤: 136 | 1. 建堆。自底向上,调用堆化函数调整每个子树,到根子树时,建堆完成。 137 | 2. 排序。对于一个堆,将堆顶元素弹出,从最后拿一个元素到堆顶,重新调用堆函数进行调整。循环往复直到堆变空。 138 | 139 | 注意: 140 | 1. 我们在实现的时候,通常会使用数组模拟满二叉树,进而使用满二叉树表示堆。 141 | 2. 由于每次弹出后会将元素放最后,因此如果是升序排序,需要构建大顶堆。 142 | 143 | ![](../../img/heap-sort.png) 144 | 145 | 可以看出,和三种基本排序相同的是,堆排序也是把数组划分成了两个子集。左边无序子集,右边有序子集,但不同的是,堆排序将无序子集组织成了堆的形式,使得每次移动的复杂度是 O(logn),而不是 O(n)。 146 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 vesoft inc. All rights reserved. 2 | # 3 | # This source code is licensed under Apache 2.0 License. 4 | # 5 | # This is the output of clang-format-10.0.0 --style=google --dump-config, 6 | # except for changes mentioned below. 7 | # We have locked the version of clang-format in order to avoid inconsistencies 8 | # in the format caused by developers using different clang-format versions. 9 | --- 10 | Language: Cpp 11 | # BasedOnStyle: Google 12 | AccessModifierOffset: -1 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveMacros: false 15 | AlignConsecutiveAssignments: false 16 | AlignConsecutiveDeclarations: false 17 | AlignEscapedNewlines: Left 18 | AlignOperands: true 19 | AlignTrailingComments: true 20 | AllowAllArgumentsOnNextLine: true 21 | AllowAllConstructorInitializersOnNextLine: true 22 | AllowAllParametersOfDeclarationOnNextLine: true 23 | AllowShortBlocksOnASingleLine: Never 24 | AllowShortCaseLabelsOnASingleLine: false 25 | AllowShortFunctionsOnASingleLine: Empty 26 | AllowShortLambdasOnASingleLine: All 27 | AllowShortIfStatementsOnASingleLine: WithoutElse 28 | AllowShortLoopsOnASingleLine: true 29 | AlwaysBreakAfterDefinitionReturnType: None 30 | AlwaysBreakAfterReturnType: None 31 | AlwaysBreakBeforeMultilineStrings: true 32 | AlwaysBreakTemplateDeclarations: Yes 33 | BinPackArguments: false 34 | BinPackParameters: false 35 | BraceWrapping: 36 | AfterCaseLabel: false 37 | AfterClass: false 38 | AfterControlStatement: false 39 | AfterEnum: false 40 | AfterFunction: false 41 | AfterNamespace: false 42 | AfterObjCDeclaration: false 43 | AfterStruct: false 44 | AfterUnion: false 45 | AfterExternBlock: false 46 | BeforeCatch: false 47 | BeforeElse: false 48 | IndentBraces: false 49 | SplitEmptyFunction: true 50 | SplitEmptyRecord: true 51 | SplitEmptyNamespace: true 52 | BreakBeforeBinaryOperators: None 53 | BreakBeforeBraces: Attach 54 | BreakBeforeInheritanceComma: false 55 | BreakInheritanceList: BeforeColon 56 | BreakBeforeTernaryOperators: true 57 | BreakConstructorInitializersBeforeComma: false 58 | BreakConstructorInitializers: BeforeColon 59 | BreakAfterJavaFieldAnnotations: false 60 | BreakStringLiterals: true 61 | ColumnLimit: 100 62 | CommentPragmas: '^ IWYU pragma:' 63 | CompactNamespaces: false 64 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 65 | ConstructorInitializerIndentWidth: 4 66 | ContinuationIndentWidth: 4 67 | Cpp11BracedListStyle: true 68 | DeriveLineEnding: true 69 | DerivePointerAlignment: true 70 | DisableFormat: false 71 | ExperimentalAutoDetectBinPacking: false 72 | FixNamespaceComments: true 73 | ForEachMacros: 74 | - foreach 75 | - Q_FOREACH 76 | - BOOST_FOREACH 77 | IncludeBlocks: Regroup 78 | IncludeCategories: 79 | - Regex: '^' 80 | Priority: 2 81 | SortPriority: 0 82 | - Regex: '^<.*\.h>' 83 | Priority: 1 84 | SortPriority: 0 85 | - Regex: '^<.*' 86 | Priority: 2 87 | SortPriority: 0 88 | - Regex: '.*' 89 | Priority: 3 90 | SortPriority: 0 91 | IncludeIsMainRegex: '([-_](test|unittest))?$' 92 | IncludeIsMainSourceRegex: '' 93 | IndentCaseLabels: true 94 | IndentGotoLabels: false 95 | IndentPPDirectives: None 96 | IndentWidth: 2 97 | IndentWrappedFunctionNames: false 98 | JavaScriptQuotes: Leave 99 | JavaScriptWrapImports: true 100 | KeepEmptyLinesAtTheStartOfBlocks: false 101 | MacroBlockBegin: '' 102 | MacroBlockEnd: '' 103 | MaxEmptyLinesToKeep: 1 104 | NamespaceIndentation: None 105 | ObjCBinPackProtocolList: Never 106 | ObjCBlockIndentWidth: 2 107 | ObjCSpaceAfterProperty: false 108 | ObjCSpaceBeforeProtocolList: true 109 | PenaltyBreakAssignment: 2 110 | PenaltyBreakBeforeFirstCallParameter: 1 111 | PenaltyBreakComment: 300 112 | PenaltyBreakFirstLessLess: 120 113 | PenaltyBreakString: 1000 114 | PenaltyBreakTemplateDeclaration: 10 115 | PenaltyExcessCharacter: 1000000 116 | PenaltyReturnTypeOnItsOwnLine: 200 117 | PointerAlignment: Left 118 | RawStringFormats: 119 | - Language: Cpp 120 | Delimiters: 121 | - cc 122 | - CC 123 | - cpp 124 | - Cpp 125 | - CPP 126 | - 'c++' 127 | - 'C++' 128 | CanonicalDelimiter: '' 129 | BasedOnStyle: google 130 | - Language: TextProto 131 | Delimiters: 132 | - pb 133 | - PB 134 | - proto 135 | - PROTO 136 | EnclosingFunctions: 137 | - EqualsProto 138 | - EquivToProto 139 | - PARSE_PARTIAL_TEXT_PROTO 140 | - PARSE_TEST_PROTO 141 | - PARSE_TEXT_PROTO 142 | - ParseTextOrDie 143 | - ParseTextProtoOrDie 144 | CanonicalDelimiter: '' 145 | BasedOnStyle: google 146 | ReflowComments: true 147 | SortIncludes: true 148 | SortUsingDeclarations: true 149 | SpaceAfterCStyleCast: false 150 | SpaceAfterLogicalNot: false 151 | SpaceAfterTemplateKeyword: true 152 | SpaceBeforeAssignmentOperators: true 153 | SpaceBeforeCpp11BracedList: false 154 | SpaceBeforeCtorInitializerColon: true 155 | SpaceBeforeInheritanceColon: true 156 | SpaceBeforeParens: ControlStatements 157 | SpaceBeforeRangeBasedForLoopColon: true 158 | SpaceInEmptyBlock: false 159 | SpaceInEmptyParentheses: false 160 | SpacesBeforeTrailingComments: 2 161 | SpacesInAngles: false 162 | SpacesInConditionalStatement: false 163 | SpacesInContainerLiterals: true 164 | SpacesInCStyleCastParentheses: false 165 | SpacesInParentheses: false 166 | SpacesInSquareBrackets: false 167 | SpaceBeforeSquareBrackets: false 168 | Standard: c++17 169 | StatementMacros: 170 | - Q_UNUSED 171 | - QT_REQUIRE_VERSION 172 | TabWidth: 8 173 | UseCRLF: false 174 | UseTab: Never 175 | ... 176 | -------------------------------------------------------------------------------- /data_structures/trie.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class TrieNode { 8 | public: 9 | TrieNode(float w) : weight(w) { 10 | next.resize(26, nullptr); 11 | } 12 | 13 | float weight; // < 0 means it is not the final node 14 | std::vector next; 15 | }; 16 | 17 | class Trie { 18 | public: 19 | Trie() { 20 | root_ = new TrieNode(-1); 21 | } 22 | 23 | ~Trie() { 24 | release(root_); 25 | } 26 | 27 | void insert(const std::string& word, float weight) { 28 | TrieNode* it = root_; 29 | for (char ch : word) { 30 | size_t pos = ch - 'a'; 31 | if (it->next[pos] == nullptr) { 32 | it->next[pos] = new TrieNode(-1); 33 | } 34 | it = it->next[pos]; 35 | } 36 | it->weight = weight; 37 | } 38 | 39 | bool has(const std::string& word) { 40 | TrieNode* it = root_; 41 | for (auto ch : word) { 42 | size_t pos = ch - 'a'; 43 | if (it->next[pos] == nullptr) { 44 | return false; 45 | } 46 | it = it->next[pos]; 47 | } 48 | return it->weight > 0; 49 | } 50 | 51 | std::vector> list(const std::string& prefix) { 52 | TrieNode* it = root_; 53 | std::vector> words; 54 | for (char ch : prefix) { 55 | size_t pos = ch - 'a'; 56 | if (it->next[pos] == nullptr) { 57 | return words; 58 | } 59 | it = it->next[pos]; 60 | } 61 | 62 | dfs(it, prefix, words); 63 | 64 | return words; 65 | } 66 | 67 | public: 68 | void echo() { 69 | std::vector> allWords; 70 | dfs(root_, "", allWords); 71 | std::cout << "All inserted words are:" << std::endl; 72 | for (auto [word, weight] : allWords) { 73 | std::cout << word << " : " << weight << std::endl; 74 | } 75 | } 76 | 77 | private: 78 | void dfs(TrieNode* root, std::string word, std::vector>& words) { 79 | if (root->weight >= 0) { 80 | words.push_back({word, root->weight}); 81 | } 82 | 83 | for (size_t pos = 0; pos < 26; ++pos) { 84 | auto nxt = root->next[pos]; 85 | if (nxt != nullptr) { 86 | char ch = 'a' + pos; 87 | dfs(nxt, word + ch, words); 88 | } 89 | } 90 | } 91 | 92 | void release(TrieNode* root) { 93 | for (auto it : root->next) { 94 | if (it != nullptr) { 95 | release(it); 96 | } 97 | } 98 | 99 | delete root; 100 | } 101 | 102 | private: 103 | TrieNode* root_; 104 | }; 105 | 106 | class DialPrompt { 107 | public: 108 | DialPrompt(std::vector>& wordTable) { 109 | for (auto& [word, weight] : wordTable) { 110 | dict_.insert(word, weight); 111 | } 112 | dict_.echo(); // for debug 113 | } 114 | 115 | std::vector list(const std::string& numbers) { 116 | std::vector prefixes; 117 | traveral(numbers, 0, "", prefixes); 118 | 119 | std::vector> wordWithWeights; 120 | for (auto& prefix : prefixes) { 121 | auto words = dict_.list(prefix); 122 | for (auto& word : words) { 123 | wordWithWeights.push_back(word); 124 | } 125 | } 126 | 127 | std::sort(wordWithWeights.begin(), 128 | wordWithWeights.end(), 129 | [](const std::pair& a, const std::pair& b) { 130 | return a.second > b.second; 131 | }); 132 | 133 | std::vector result; 134 | for (auto& [word, weight] : wordWithWeights) { 135 | result.push_back(word); 136 | } 137 | 138 | return result; 139 | } 140 | 141 | private: 142 | void traveral(const std::string& numbers, 143 | int pos, 144 | std::string prefix, 145 | std::vector& prefixes) { 146 | if (pos == numbers.size()) { 147 | prefixes.push_back(prefix); 148 | return; 149 | } 150 | 151 | int number = numbers[pos] - '0'; 152 | if (dialBoard.find(number) == dialBoard.end()) { 153 | return; 154 | } 155 | 156 | const std::string chs = dialBoard[number]; 157 | for (auto const ch : chs) { 158 | traveral(numbers, pos + 1, prefix + ch, prefixes); 159 | } 160 | } 161 | 162 | private: 163 | inline static std::unordered_map dialBoard = {{2, "abc"}, 164 | {3, "def"}, 165 | {4, "ghi"}, 166 | {5, "jkl"}, 167 | {6, "mno"}, 168 | {7, "pqrs"}, 169 | {8, "tuv"}, 170 | {9, "wxyz"}}; 171 | Trie dict_; 172 | }; 173 | 174 | int main() { 175 | std::vector> wordTable = {{"qing", 5}, 176 | {"teng", 6}, 177 | {"mu", 7}, 178 | {"niao", 8}, 179 | {"za", 9}, 180 | {"ji", 10}, 181 | {"niu", 11}, 182 | {"qtmuniao", 12}}; 183 | DialPrompt prompt(wordTable); 184 | 185 | std::cout << "Please enter the number:" << std::endl; 186 | std::string numbers; 187 | 188 | while (std::cin >> numbers) { 189 | auto result = prompt.list(numbers); 190 | std::cout << "the all candidates are:" << std::endl; 191 | for (auto word : result) { 192 | std::cout << word << std::endl; 193 | } 194 | std::cout << "Please enter the number:" << std::endl; 195 | } 196 | }; 197 | --------------------------------------------------------------------------------