Repository: wangzheng0822/algo Branch: master Commit: b2c1228ff915 Files: 622 Total size: 1.0 MB Directory structure: gitextract_2921o4fk/ ├── .gitignore ├── DynamicStackBaseArray.java ├── LICENSE ├── README.md ├── StackBaseArray.java ├── c-cpp/ │ ├── .gitignore │ ├── .gitkeep │ ├── 05_array/ │ │ ├── .gitkeep │ │ ├── Array_gp.c │ │ ├── Array_gp.h │ │ └── array.c │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── Dlist/ │ │ │ ├── Dlist.c │ │ │ └── Dlist.h │ │ ├── LRUBasedLinkedList.cpp │ │ ├── list_isPalindrome/ │ │ │ ├── LinkList.cpp │ │ │ └── LinkList.h │ │ ├── palindromeList/ │ │ │ ├── LinkedList.hpp │ │ │ ├── ListNode.hpp │ │ │ └── palindromeList.cpp │ │ ├── single_list.c │ │ └── singlelist_gc/ │ │ ├── singleList.c │ │ └── singleList.h │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ ├── LinkedListAlgo.c │ │ ├── SingleList.cpp │ │ ├── linked_list.h │ │ ├── linked_list_algo.hpp │ │ └── linklist_jinshaohui.c │ ├── 08_stack/ │ │ ├── StackBasedOnArray/ │ │ │ ├── StackBasedOnArray.cpp │ │ │ └── StackBasedOnArray.h │ │ ├── StackBasedOnLinkedList/ │ │ │ ├── StackBasedOnLinkedList.cpp │ │ │ └── StackBasedOnLinkedList.h │ │ ├── arrayStack/ │ │ │ ├── arrayStack.c │ │ │ └── arrayStack.h │ │ ├── linkList/ │ │ │ ├── linklist_stack.c │ │ │ └── linklist_stack.h │ │ ├── linked_list.h │ │ └── stack_based_on_linked_list.hpp │ ├── 09_queue/ │ │ ├── .gitkeep │ │ ├── array_queue/ │ │ │ ├── array_queue.c │ │ │ └── array_queue.h │ │ ├── array_queue.hpp │ │ ├── array_queue_test.cc │ │ ├── block_queue.hpp │ │ ├── circular_queue.hpp │ │ ├── circular_queue_test.cc │ │ ├── concurrency_queue.hpp │ │ ├── dynamic_array_queue.hpp │ │ ├── dynamic_array_queue_test.cc │ │ ├── linked_queue.hpp │ │ ├── linked_queue_test.cc │ │ ├── list_queue/ │ │ │ ├── list_queue.c │ │ │ └── list_queue.h │ │ ├── lock_free_queue.hpp │ │ └── ring_queue.c │ ├── 10_recursive/ │ │ ├── .gitkeep │ │ ├── one_two_step.c │ │ └── one_two_step.cc │ ├── 11_sorts/ │ │ ├── .gitkeep │ │ ├── sorts.c │ │ ├── sorts.cpp │ │ ├── sorts.hpp │ │ ├── sorts_jinshaohui.c │ │ └── sorts_test.cc │ ├── 12_sorts/ │ │ ├── .gitkeep │ │ ├── merge_sort.c │ │ ├── merge_sort.hpp │ │ ├── merge_sort_test.cc │ │ ├── my12_sorts/ │ │ │ ├── merge_sort.c │ │ │ └── quick_sort.c │ │ ├── quick_sort.c │ │ ├── quick_sort.hpp │ │ └── quick_sort_test.cc │ ├── 13_sorts/ │ │ ├── .gitkeep │ │ ├── bucket_sort.hpp │ │ ├── bucket_sort_test.cc │ │ ├── counting_sort.hpp │ │ ├── counting_sort_test.cc │ │ └── sort.c │ ├── 14_sorts/ │ │ ├── .gitkeep │ │ ├── analytics_of_std_sort.md │ │ └── counting_sort.c │ ├── 15_bsearch/ │ │ ├── .gitkeep │ │ ├── binary_search.c │ │ ├── bsearch.hpp │ │ ├── bsearch_c/ │ │ │ ├── bsearch.c │ │ │ └── sqrt.c │ │ └── bsearch_test.cc │ ├── 16_bsearch/ │ │ ├── .gitkeep │ │ ├── bsearch.c │ │ ├── bsearch_variant.c │ │ ├── bsearch_varients.hpp │ │ └── bsearch_varients_test.cc │ ├── 17_skiplist/ │ │ ├── .gitkeep │ │ ├── SkipList.cpp │ │ ├── skiplist.c │ │ ├── skiplist.hpp │ │ ├── skiplist_c/ │ │ │ ├── skiplist.c │ │ │ └── skiplist.h │ │ ├── skiplist_test.cc │ │ ├── skiplist_tr.hpp │ │ └── skiplist_tr_test.cc │ ├── 18_hashtable/ │ │ ├── .gitkeep │ │ ├── hash_map.cc │ │ ├── hashtable.c │ │ └── listhash/ │ │ ├── listhash.c │ │ └── listhash.h │ ├── 19_Dlisthash/ │ │ ├── Dlist.h │ │ ├── LinkedHashMap.c │ │ └── LinkedHashMap.h │ ├── 23_binarytree/ │ │ ├── binarytree.c │ │ └── tree/ │ │ ├── binarytree.c │ │ ├── list_queue.c │ │ └── list_queue.h │ ├── 24_binarysearchtree/ │ │ ├── binary_search_tree.cpp │ │ ├── binarysearchtree.c │ │ ├── binarysearchtree.h │ │ └── bst.c │ ├── 24_tree/ │ │ ├── Trie.c │ │ └── binarysearchtree.c │ ├── 28_heap/ │ │ └── heap.c │ ├── 30_Graph/ │ │ └── graph.c │ └── bst.c ├── csharp/ │ ├── 05-array/ │ │ ├── Array.cs │ │ └── algo05_array.csproj │ ├── 06-linkedlist/ │ │ ├── LRUWithArray.cs │ │ ├── LRUWithLinkedList.cs │ │ ├── LRU缓存实现思路.txt │ │ ├── SingleLinkedList.cs │ │ └── algo06_linked_list.csproj │ ├── 07-linkedlist/ │ │ └── _07_linkedlist/ │ │ ├── SingleLinkedListAlgo.cs │ │ └── algo07_linkedlist.csproj │ ├── 08-stack/ │ │ └── algo08_stack/ │ │ ├── ArrayStack.cs │ │ ├── LinkedStack.cs │ │ ├── LinkedStackBrowser.cs │ │ └── algo08_stack.csproj │ ├── Tests/ │ │ ├── _05_array_tests/ │ │ │ ├── Array.Tests.cs │ │ │ └── algo05_array_tests.csproj │ │ ├── _06_linkedlist_tests/ │ │ │ ├── BaseLinkedListTests.cs │ │ │ ├── LRUWithArray.Tests.cs │ │ │ ├── LRUWithLinkedList.Tests.cs │ │ │ ├── SingleLinkedList.Tests.cs │ │ │ └── algo06_linkedlist_tests.csproj │ │ ├── _07_linkedlist_tests/ │ │ │ ├── SingleLinkedListAlgo.Tests.cs │ │ │ └── algo07_linkedlist_tests.csproj │ │ └── algo08_stack_tests/ │ │ ├── ArrayStack.Tests.cs │ │ ├── LinkedStack.Tests.cs │ │ ├── LinkedStackBrowser.Tests.cs │ │ └── algo08_stack_tests.csproj │ ├── csharp.sln │ └── csharp.sln.DotSettings.user ├── f21 ├── go/ │ ├── .gitkeep │ ├── 05_array/ │ │ ├── .gitkeep │ │ ├── array.go │ │ └── array_test.go │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── palindrome-linked-list.go │ │ ├── palindrome.go │ │ ├── palindrome_test.go │ │ ├── singlelinkedlist.go │ │ └── singlelinkedlist_test.go │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ ├── main.go │ │ └── main_test.go │ ├── 08_stack/ │ │ ├── SimpleBrowser.go │ │ ├── SimpleBrowser_test.go │ │ ├── StackBasedOnArray.go │ │ ├── StackBasedOnArray_test.go │ │ ├── StackBasedOnLinkedList.go │ │ ├── StackBasedOnLinkedList_test.go │ │ └── StatckInterface.go │ ├── 09_queue/ │ │ ├── CircularQueue.go │ │ ├── CircularQueue_test.go │ │ ├── QueueBasedOnArray.go │ │ ├── QueueBasedOnArray_test.go │ │ ├── QueueBasedOnLinkedList.go │ │ └── QueueBasedOnLinkedList_test.go │ ├── 10_recursion/ │ │ ├── Factorial.go │ │ ├── Factorial_test.go │ │ ├── Fibonacci.go │ │ ├── Fibonacci_test.go │ │ ├── RangAll.go │ │ └── RangAll_test.go │ ├── 11_sorts/ │ │ ├── Sort.go │ │ └── Sort_test.go │ ├── 12_sorts/ │ │ ├── MergeSort.go │ │ ├── MergeSort_test.go │ │ ├── QuickSort.go │ │ └── QuickSort_test.go │ ├── 13_sorts/ │ │ ├── BucketSort.go │ │ └── BucketSort_test.go │ ├── 14_sorts/ │ │ ├── CountingSort.go │ │ └── CountingSort_test.go │ ├── 15_binarysearch/ │ │ ├── binarysearch.go │ │ └── binarysearch_test.go │ ├── 17_skiplist/ │ │ ├── skiplist.go │ │ └── skiplist_test.go │ ├── 20_lru/ │ │ ├── lru_cache.go │ │ └── lru_cache_test.go │ ├── 23_binarytree/ │ │ ├── binarytree.go │ │ └── binarytree_test.go │ ├── 24_tree/ │ │ ├── BinarySearchTree.go │ │ ├── BinarySearchTree_test.go │ │ ├── BinaryTree.go │ │ ├── BinaryTree_test.go │ │ ├── StackBasedOnArray.go │ │ └── TreeNode.go │ ├── 28_heap/ │ │ ├── heap.go │ │ └── heap_sort.go │ ├── 29_priority_queue/ │ │ ├── heap.go │ │ ├── heap_test.go │ │ ├── priority_queue.go │ │ ├── priority_queue_test.go │ │ └── readme.md │ ├── 31_graph/ │ │ └── graph_search.go │ ├── 32_string/ │ │ ├── string_bf.go │ │ └── string_bm.go │ ├── 34_kmp/ │ │ └── kmp.go │ ├── 41_dynamic_programming/ │ │ ├── backtracking/ │ │ │ ├── leastcoins.go │ │ │ └── leastcoins_test.go │ │ └── dp/ │ │ ├── leastcoins.go │ │ └── leastcoins_test.go │ ├── 42_dynamic_programming/ │ │ └── longest_common_substring.go │ ├── 45_bitmap/ │ │ ├── bitmap.go │ │ └── bitmap_test.go │ └── binarysearch2.go ├── java/ │ ├── 05_array/ │ │ ├── .gitkeep │ │ ├── Array.java │ │ └── GenericArray.java │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── LRUBaseLinkedList.java │ │ ├── LRUBasedArray.java │ │ └── SinglyLinkedList.java │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ └── LinkedListAlgo.java │ ├── 08_stack/ │ │ ├── SampleBrowser.java │ │ └── StackBasedOnLinkedList.java │ ├── 09_queue/ │ │ ├── ArrayQueue.java │ │ ├── CircularQueue.java │ │ ├── DynamicArrayQueue.java │ │ └── QueueBasedOnLinkedList.java │ ├── 11_sorts/ │ │ ├── InsertionSortAdd.java │ │ ├── Sorts.java │ │ └── SortsAddOn.java │ ├── 12_sorts/ │ │ ├── KthSmallest.java │ │ ├── MergeSort.java │ │ ├── QuickSort.java │ │ └── Sorts.java │ ├── 13_sorts/ │ │ ├── BucketSort.java │ │ ├── CountingSort.java │ │ └── RadixSort.java │ ├── 17_skiplist/ │ │ ├── SkipList.java │ │ └── SkipList2.java │ ├── 18_hashtable/ │ │ └── HashTable.java │ ├── 20_hashtable/ │ │ └── LRUBaseHashTable.java │ ├── 24_tree/ │ │ └── BinarySearchTree.java │ ├── 28_sorts/ │ │ └── HeapSort.java │ ├── 30_graph/ │ │ └── Graph.java │ ├── 32_BFRK │ └── 36_ac_automata/ │ └── ACAutoMata.java ├── javascript/ │ ├── .gitkeep │ ├── 05_array/ │ │ ├── .gitkeep │ │ └── Array.md │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ └── SinglyLinkedList.js │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ └── LinkedListAlgo.js │ ├── 08_stack/ │ │ ├── SampleBrowser.js │ │ └── StackBasedOnLinkedList.js │ ├── 09_queue/ │ │ ├── CircularQueueBasedOnLinkedList.js │ │ └── QueueBasedOnLinkedList.js │ ├── 11_sorts/ │ │ └── sort.js │ ├── 12_sorts/ │ │ ├── KthNum.js │ │ ├── MergeSort.js │ │ └── QuickSort.js │ ├── 13_sorts/ │ │ ├── bucketSort.js │ │ └── countingSort.js │ ├── 15_binary/ │ │ └── binaryFind.js │ ├── 16_binary/ │ │ └── binary-find.js │ ├── 17_skiplist/ │ │ └── SkipList.js │ ├── 18_hashmap/ │ │ └── HashTable.html │ ├── 19_hashTable/ │ │ └── hashtable.js │ ├── 23_tree/ │ │ └── binary_tree.js │ ├── 28_heapsort/ │ │ ├── heap-sort.js │ │ └── heap.js │ ├── 35_trie/ │ │ └── trie.js │ ├── 36_ac_automata/ │ │ ├── ac_automata.js │ │ └── ac_automata_unicode.js │ ├── 42_dynamic_programming/ │ │ └── levenshtein_distance.js │ ├── 43_topological_sorting/ │ │ └── dsf.js │ └── 45_bitmap/ │ └── bitmap.js ├── kotlin/ │ ├── 05_array/ │ │ ├── ArrayKt.kt │ │ └── DynamicArray.kt │ ├── 06_linkedlist/ │ │ └── SinglyLinkedList.kt │ ├── 07_linkedlist/ │ │ └── LinkedListAlgo.kt │ └── 08_stack/ │ └── StackBasedOnLinkedList.kt ├── notes/ │ ├── .gitkeep │ ├── 10_recursion/ │ │ └── readme.md │ ├── 11_sorts/ │ │ └── readme.md │ ├── 12_sorts/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 13_sorts/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 14_sorts/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 15_bsearch/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 16_bsearch/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 17_skiplist/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 18_hashtable/ │ │ ├── .gitkeep │ │ └── readme.md │ ├── 19_hashtable/ │ │ ├── .gitkeep │ │ └── readme.md │ └── 20_hashtable/ │ ├── .gitkeep │ └── readme.md ├── object-c/ │ ├── .gitkeep │ ├── 05_array/ │ │ ├── .gitkeep │ │ ├── MyArray.h │ │ └── MyArray.m │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── ListNode.h │ │ ├── ListNode.m │ │ ├── SinglyLinkedList.h │ │ ├── SinglyLinkedList.m │ │ └── SinglyLinkedListTests.m │ ├── 07_linkedlist/ │ │ └── .gitkeep │ ├── 08_stack/ │ │ ├── LinkedStack.h │ │ ├── LinkedStack.m │ │ ├── LinkedStackTests.m │ │ ├── ListNode.h │ │ ├── ListNode.m │ │ └── stack_practice/ │ │ ├── ArrayStack.h │ │ ├── ArrayStack.m │ │ ├── BalancedParentheses.h │ │ ├── BalancedParentheses.m │ │ ├── FourOperation.h │ │ ├── FourOperation.m │ │ └── main.m │ ├── 11_Sort/ │ │ ├── Sort.h │ │ └── Sort.m │ └── 33_bm_match/ │ ├── BM.h │ ├── BM.m │ └── main.m ├── php/ │ ├── .gitignore │ ├── 05_array/ │ │ ├── .gitkeep │ │ ├── array.php │ │ └── array_test.php │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── SingleLinkedList.php │ │ ├── SingleLinkedListNode.php │ │ └── main.php │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ └── main.php │ ├── 08_stack/ │ │ ├── .gitkeep │ │ ├── Compute.php │ │ ├── StackOnLinkedList.php │ │ └── main.php │ ├── 09_queue/ │ │ ├── QueueOnLinkedList.php │ │ ├── Sequential.php │ │ └── main.php │ ├── 10_heap/ │ │ ├── Heap.php │ │ ├── findmiddle.php │ │ ├── main.php │ │ └── topn.php │ ├── 11_sort/ │ │ └── Sort.php │ ├── 12_sort/ │ │ ├── mergeSort.php │ │ └── quicksort.php │ ├── 13_sort/ │ │ ├── bucketSort.php │ │ ├── countingSort.php │ │ └── radixSort.php │ ├── 15_binary/ │ │ └── binary.php │ ├── 16_binary/ │ │ └── binary.php │ ├── 17_skiplist/ │ │ └── skipList.php │ ├── 24_tree/ │ │ ├── Tree.php │ │ ├── TreeNode.php │ │ ├── levelOrder.php │ │ └── main.php │ ├── 38_divide_and_conquer/ │ │ └── matrix_production.php │ ├── 39_backtracking/ │ │ └── queens.php │ ├── README.md │ ├── Stack/ │ │ └── Compute.php │ ├── buildAutoLoad.sh │ └── composer.json ├── python/ │ ├── .gitkeep │ ├── 05_array/ │ │ ├── .gitkeep │ │ └── myarray.py │ ├── 06_linkedlist/ │ │ ├── .gitkeep │ │ ├── LRUCache.py │ │ ├── palindrome.py │ │ ├── singlyLinkedList.py │ │ └── singly_linked_list.py │ ├── 07_linkedlist/ │ │ ├── .gitkeep │ │ └── linked_list_algo.py │ ├── 08_stack/ │ │ ├── linked_stack.py │ │ └── simple_browser.py │ ├── 09_queue/ │ │ ├── array_queue.py │ │ ├── circular_queue.py │ │ ├── dynamic_array_queue.py │ │ └── linked_queue.py │ ├── 11_sorts/ │ │ └── sorts.py │ ├── 12_sorts/ │ │ ├── merge_sort.py │ │ ├── quick_sort.py │ │ └── quicksort_twoway.py │ ├── 14_sorts/ │ │ └── counting_sort.py │ ├── 15_bsearch/ │ │ ├── bsearch.py │ │ └── bsearch_recursion.py │ ├── 16_bsearch/ │ │ └── bsearch_variants.py │ ├── 17_skiplist/ │ │ ├── skip_list.py │ │ └── skip_list_comments.py │ ├── 23_binarytree/ │ │ ├── binary_search_tree.py │ │ └── binary_tree.py │ ├── 24_tree/ │ │ └── binary_search_tree.py │ ├── 26_red_black_tree/ │ │ └── red_black_tree.py │ ├── 28_binary_heap/ │ │ ├── binary_heap.py │ │ ├── binary_heap_sort.py │ │ ├── heap.py │ │ ├── priority_queue.py │ │ └── top_k.py │ ├── 28_heap/ │ │ ├── heap.py │ │ └── min_heap.py │ ├── 31_bfs_dfs/ │ │ ├── bfs_dfs.py │ │ ├── graph.py │ │ └── graph_application.py │ ├── 32_bf_rk/ │ │ └── bf_rk.py │ ├── 33_bm/ │ │ ├── bm.py │ │ └── bm_.py │ ├── 34_kmp/ │ │ ├── kmp.py │ │ └── kmp_.py │ ├── 35_trie/ │ │ ├── trie.py │ │ └── trie_.py │ ├── 36_ac_automata/ │ │ ├── ac_automata.py │ │ └── ac_automata_.py │ ├── 38_divide_and_conquer/ │ │ └── merge_sort_counting.py │ ├── 39_back_track/ │ │ ├── 01_bag.py │ │ ├── eight_queens.py │ │ ├── permutations.py │ │ └── regex.py │ ├── 39_backtracking/ │ │ └── backtracking.py │ ├── 40_dynamic_programming/ │ │ ├── 01_bag.py │ │ ├── knapsack.py │ │ └── yh_triangle.py │ ├── 41_dynamic_programming/ │ │ ├── coins_problem.py │ │ └── min_dist.py │ ├── 42_dynamic_programming/ │ │ ├── longest_increasing_subsequence.py │ │ └── min_edit_dist.py │ ├── 43_topological_sorting/ │ │ └── topological_sorting.py │ ├── 44_shortest_path/ │ │ ├── dijkstra.py │ │ └── shortest_path.py │ ├── 45_bitmap/ │ │ └── bitmap.py │ └── array.py ├── rust/ │ ├── 05_array/ │ │ └── main.rs │ ├── 07_linkedlist/ │ │ ├── linked_list_cycle.rs │ │ ├── merge_two_sorted_lists.rs │ │ ├── middle_of_the_linked_list.rs │ │ ├── remove_nth_node_from_end_of_list.rs │ │ ├── reverse_linked_list.rs │ │ └── util/ │ │ └── linked_list.rs │ ├── 08_stack/ │ │ ├── simple_browser.rs │ │ ├── stack_based_on_array.rs │ │ └── stack_based_on_linked_list.rs │ ├── 09_queue/ │ │ ├── array_queue.rs │ │ ├── circle_queue.rs │ │ └── linked_list_queue.rs │ ├── 11_sorts/ │ │ ├── bubble_sort.rs │ │ ├── insertion_sort.rs │ │ └── selection_sort.rs │ ├── 12_sorts/ │ │ ├── kth_largest.rs │ │ ├── merge_sort.rs │ │ └── quick_sort.rs │ ├── 13_sorts/ │ │ ├── bucket_sort.rs │ │ ├── counting_sort.rs │ │ ├── radix_sort.rs │ │ └── sort_string.rs │ ├── 15_binary_search/ │ │ ├── binary_search.rs │ │ └── sqrtx.rs │ ├── 16_binary_search/ │ │ ├── binary_search.rs │ │ └── search_in_rotated_sorted_array.rs │ ├── 19_hash_table/ │ │ └── hash_table.rs │ ├── 23_binary_tree/ │ │ ├── inorder_traversal.rs │ │ ├── level_order_traversal.rs │ │ ├── postorder_traversal.rs │ │ ├── preorder_traversal.rs │ │ └── util/ │ │ └── tree.rs │ ├── 24_binary_tree/ │ │ ├── insert_in_binary_tree.rs │ │ ├── max_depth_in_binary_tree.rs │ │ ├── search_in_binary_tree.rs │ │ └── util/ │ │ └── tree.rs │ ├── 28_heap/ │ │ ├── build_heap.rs │ │ ├── heap.rs │ │ └── sort_heap.rs │ ├── 29_heap/ │ │ ├── get_median.rs │ │ ├── get_top_k.rs │ │ └── merge_sorted_array.rs │ ├── 31_graph/ │ │ └── graph_search.rs │ ├── 32_string/ │ │ └── bf_rk.rs │ ├── 33_string/ │ │ └── bm.rs │ ├── 34_string/ │ │ └── kmp.rs │ ├── 35_trie/ │ │ └── trie.rs │ ├── 38_divide_and_conquer/ │ │ └── merge_sort_count.rs │ ├── 39_back_tracking/ │ │ ├── bag.rs │ │ ├── bag_exec.rs │ │ ├── n_queens.rs │ │ └── regex.rs │ ├── 40_dynamic_programming/ │ │ ├── bag.rs │ │ ├── knapsack.rs │ │ └── triangle.rs │ ├── 41_dynamic_programming/ │ │ ├── coin_change.rs │ │ └── min_dis_path.rs │ └── 42_dynamic_programming/ │ ├── edit_distance.rs │ └── longest_increasing_subsequence.rs ├── scala/ │ ├── .gitignore │ ├── build.sbt │ ├── project/ │ │ └── build.properties │ └── src/ │ ├── main/ │ │ └── scala/ │ │ ├── ch05_array/ │ │ │ └── ArrayDemo.scala │ │ ├── ch06_linkedlist/ │ │ │ └── SinglyLinkedList.scala │ │ ├── ch07_linkedlist/ │ │ │ └── LinkedListAlgo.scala │ │ ├── ch08_stack/ │ │ │ ├── BrowserDemo.scala │ │ │ └── StackDemo.scala │ │ ├── ch09_queue/ │ │ │ ├── ArrayQueue.scala │ │ │ ├── CircularQueue.scala │ │ │ ├── DemoQueue.scala │ │ │ ├── DynamicArrayQueue.scala │ │ │ └── LinkedListQueue.scala │ │ ├── ch10_recursive/ │ │ │ └── RecursiveDemo.scala │ │ ├── ch11_sorts/ │ │ │ └── Sorts.scala │ │ ├── ch12_sorts/ │ │ │ ├── MergeSort.scala │ │ │ └── QuickSort.scala │ │ ├── ch15_bsearch/ │ │ │ ├── BSearch.scala │ │ │ └── BSearchRecursive.scala │ │ ├── ch16_bsearch/ │ │ │ └── BSearch.scala │ │ ├── ch17_skip_list/ │ │ │ └── SkipList.scala │ │ ├── ch20_linked_hash_map/ │ │ │ └── LRUCache.scala │ │ ├── ch23_binary_tree/ │ │ │ └── BinaryTree.scala │ │ ├── ch24_binary_search_tree/ │ │ │ └── BinarySearchTree.scala │ │ ├── ch28_heap/ │ │ │ └── Heap.scala │ │ ├── ch29_heap_solutions/ │ │ │ ├── FileMerger.scala │ │ │ ├── MiddleNumberKeeper.scala │ │ │ └── TopKItemsKeeper.scala │ │ ├── ch31_graph/ │ │ │ └── Graph.scala │ │ ├── ch32_matching/ │ │ │ ├── BruteForce.scala │ │ │ └── RabinKarp.scala │ │ ├── ch35_tire_tree/ │ │ │ └── TrieTree.scala │ │ ├── ch39_back_tracking/ │ │ │ ├── BagWeight.scala │ │ │ ├── EightQueens.scala │ │ │ ├── NQueens.scala │ │ │ └── Sudoku.scala │ │ └── ch43_topology_sort/ │ │ └── GraphTopology.scala │ └── test/ │ └── scala/ │ ├── ch05_array/ │ │ └── ArrayDemoSpec.scala │ ├── ch06_linkedlist/ │ │ ├── NodeTest.scala │ │ └── SinglyLinkedListTest.scala │ ├── ch07_linkedlist/ │ │ └── LinkedListAlgoTest.scala │ ├── ch08_stack/ │ │ ├── BrowserDemoTest.scala │ │ └── StackDemoTest.scala │ ├── ch09_queue/ │ │ ├── ArrayQueueTest.scala │ │ ├── CircularQueueTest.scala │ │ ├── DemoQueueTest.scala │ │ ├── DynamicArrayQueueTest.scala │ │ └── LinkedListQueueTest.scala │ ├── ch10_recursive/ │ │ └── RecursiveDemoTest.scala │ ├── ch11_sorts/ │ │ └── SortsTest.scala │ ├── ch12_sorts/ │ │ ├── MergeSortTest.scala │ │ └── QuickSortTest.scala │ ├── ch15_bsearch/ │ │ ├── BSearchRecursiveTest.scala │ │ └── BSearchTest.scala │ ├── ch16_bsearch/ │ │ └── BSearchTest.scala │ ├── ch17_skip_list/ │ │ └── SkipListTest.scala │ ├── ch20_linked_hash_map/ │ │ └── LRUCacheTest.scala │ ├── ch23_binary_tree/ │ │ └── BinaryTreeTest.scala │ ├── ch24_binary_search_tree/ │ │ └── BinarySearchTreeTest.scala │ ├── ch28_heap/ │ │ └── HeapTest.scala │ ├── ch29_heap_solutions/ │ │ ├── FileMergerTest.scala │ │ ├── MiddleNumberKeeperTest.scala │ │ └── TopKItemsKeeperTest.scala │ ├── ch31_graph/ │ │ └── GraphTest.scala │ ├── ch32_matching/ │ │ ├── BruteForceTest.scala │ │ └── RabinKarpTest.scala │ ├── ch35_tire_tree/ │ │ └── TrieTreeTest.scala │ ├── ch39_back_tracking/ │ │ ├── BagWeightTest.scala │ │ ├── EightQueensTest.scala │ │ ├── NQueensTest.scala │ │ └── SudokuTest.scala │ └── ch43_topology_sort/ │ └── GraphTopologyTest.scala ├── swift/ │ ├── 05_array/ │ │ └── MyArray.swift │ ├── 06_linkedlist/ │ │ └── SinglyLinkedList.swift │ ├── 07_linkedlist/ │ │ └── LinkedListAlgo.swift │ ├── 08_stack/ │ │ ├── Browser.swift │ │ ├── BrowserDemo.swift │ │ ├── Stack.swift │ │ └── StackBasedOnLinkedList.swift │ ├── 09_queue/ │ │ ├── ArrayQueue.swift │ │ ├── CircularQueue.swift │ │ ├── Queue.swift │ │ └── QueueBasedOnLinkedList.swift │ ├── 11_sorts/ │ │ └── Sorts.swift │ ├── 12_sorts/ │ │ ├── QuickSort.swift │ │ ├── SortsTests.swift │ │ └── mergeSort.swift │ └── 14_sorts/ │ └── CountingSort.swift └── typescript/ ├── 06_linkedlist/ │ ├── LRUCache.ts │ ├── LinkedList.ts │ ├── List.ts │ └── SingleLinkedList.ts ├── 07_linkedlist/ │ └── LinkedListAlog.ts ├── 08_stack/ │ └── StackAndBrowser.ts ├── 09_queue/ │ ├── CircularQueue.ts │ ├── README.md │ └── SimpleQueue.ts ├── 10_recursive/ │ └── climbStairs.ts ├── 11_sorts/ │ └── simpleSort.ts ├── 12_sorts/ │ ├── KthNum.ts │ ├── MergeSort.ts │ └── quickSort.ts ├── 13_sorts/ │ ├── BucketSort.ts │ └── CountingSort.ts ├── 14_binarysearch/ │ └── BinarySearch.ts ├── 15_binarysearch/ │ └── BinaryFind.ts ├── 17_skiplist/ │ └── SkipList.ts └── 24_treesearch/ └── TreeSearch.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar *.DS_Store *.exe # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # editor files .vscode .*.swp # WebStorm .idea/ # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ **/*.idea **/*.iml **/*out ================================================ FILE: DynamicStackBaseArray.java ================================================ package Stack; import java.util.Iterator; /** * 顺序栈的动态扩容 * Author: PeiJiaNi * @param 顺序栈元素类型 */ public class DynamicStackBaseArray implements Iterable { private T[] items; // 数组 private int count; // 栈中的元素个数 private int length; // 栈空间大小 /** * 初始化栈 * * @param length 栈空间大小 */ public DynamicStackBaseArray(int length) { this.items = (T[]) new Object[length]; this.count = 0; this.length = length; } /** * 入栈操作 平均时间复杂度O(1) * * @param item 入栈元素 */ public void push(T item) { // 栈空间已满,则扩容 if (count == length) { resize(2 * items.length); } items[count++] = item; } /** * 出栈操作 平均时间复杂度O(1) * * @return 如果栈内不为空,则返回栈顶元素,否则返回-1 */ public T pop() { if (count == 0) { System.out.println("当前栈已空,无法进行出栈操作"); return null; } T item = items[--count]; items[count] = null; if (count > 0 && (count == items.length / 4)) { resize(items.length / 2); } // 返回下标为 count-1 的数组元素,并且栈中元素个数count-1 return item; } /** * 栈空间动态增加或减小 * * @param size */ private void resize(int size) { T[] newItems = (T[]) new Object[size]; for (int i = 0; i < count; i++) { newItems[i] = this.items[i]; } this.items = newItems; } //返回栈中最近添加的元素而不删除它 public T peek() { return items[count - 1]; } /** * 判断当前栈是否为空 * * @return 栈为空,则返回true,否则返回-1 */ public boolean isEmpty() { return count == 0; } /** * 返回栈中元素个数 * * @return */ public int size() { return count; } @Override public Iterator iterator() { return new ArrayIterator(); } // 内部类 class ArrayIterator implements Iterator { int numOfItems = count; @Override public boolean hasNext() { return numOfItems > 0; } @Override public T next() { return items[--numOfItems]; } } public static void main(String[] args) { DynamicStackBaseArray stack = new DynamicStackBaseArray(6); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); // System.out.println(stack.peek()); Iterator iterator = stack.iterator(); // System.out.println(iterator.hasNext()); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # 数据结构和算法必知必会的50个代码实现 ### 微信搜索我的公众号“小争哥”,或者微信扫描下面二维码关注 ### 关注微信公众号,回复”PDF“获取独家算法资料。 ### 前Google工程师,10万人跟着学的《数据结构和算法之美》《设计模式之美》专栏作者 ![t2](https://github.com/wangzheng0822/markdownphotos/blob/master/pics/qrcode_for_gh_9b0e7afdff20_258.jpg) ## 数组 * 实现一个支持动态扩容的数组 * 实现一个大小固定的有序数组,支持动态增删改操作 * 实现两个有序数组合并为一个有序数组 ## 链表 * 实现单链表、循环链表、双向链表,支持增删操作 * 实现单链表反转 * 实现两个有序的链表合并为一个有序链表 * 实现求链表的中间结点 ## 栈 * 用数组实现一个顺序栈 * 用链表实现一个链式栈 * 编程模拟实现一个浏览器的前进、后退功能 ## 队列 * 用数组实现一个顺序队列 * 用链表实现一个链式队列 * 实现一个循环队列 ## 递归 * 编程实现斐波那契数列求值f(n)=f(n-1)+f(n-2) * 编程实现求阶乘n! * 编程实现一组数据集合的全排列 ## 排序 * 实现归并排序、快速排序、插入排序、冒泡排序、选择排序 * 编程实现O(n)时间复杂度内找到一组数据的第K大元素 ## 二分查找 * 实现一个有序数组的二分查找算法 * 实现模糊二分查找算法(比如大于等于给定值的第一个元素) ## 散列表 * 实现一个基于链表法解决冲突问题的散列表 * 实现一个LRU缓存淘汰算法 ## 字符串 * 实现一个字符集,只包含a~z这26个英文字母的Trie树 * 实现朴素的字符串匹配算法 ## 二叉树 * 实现一个二叉查找树,并且支持插入、删除、查找操作 * 实现查找二叉查找树中某个节点的后继、前驱节点 * 实现二叉树前、中、后序以及按层遍历 ## 堆 * 实现一个小顶堆、大顶堆、优先级队列 * 实现堆排序 * 利用优先级队列合并K个有序数组 * 求一组动态数据集合的最大Top K ## 图 * 实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法 * 实现图的深度优先搜索、广度优先搜索 * 实现Dijkstra算法、A*算法 * 实现拓扑排序的Kahn算法、DFS算法 ## 回溯 * 利用回溯算法求解八皇后问题 * 利用回溯算法求解0-1背包问题 ## 分治 * 利用分治算法求一组数据的逆序对个数 ## 动态规划 * 0-1背包问题 * 最小路径和 * 编程实现莱文斯坦最短编辑距离 * 编程实现查找两个字符串的最长公共子序列 * 编程实现一个数据序列的最长递增子序列 ================================================ FILE: StackBaseArray.java ================================================ package Stack; /** * 顺序栈(基于数组实现) * Author: PeiJiaNi */ public class StackBaseArray { private int[] items; // 数组 private int count; // 栈中元素个数 private int length; // 栈空间大小 public StackBaseArray(int capactiy) { this.items = new int[capactiy]; this.count = 0; this.length = capactiy; } /** * 入栈操作 时间复杂度O(1) * @param item 要入栈的元素 * @return 入栈成功则返回true,否则返回false */ public boolean push(int item) { if(count == length) { System.out.println("当前栈已满,无法进行入栈操作"); return false; } items[count] = item; ++count; return true; } /** * 出栈操作 时间复杂度O(1) * @return 如果栈内不为空,则返回栈顶元素,否则返回-1 */ public int pop(){ if(count == 0) { System.out.println("当前栈已空,无法进行出栈操作"); return -1; } // 返回下标为 count-1 的数组元素,并且栈中元素个数count-1 return items[--count]; } public static void main(String[] args){ StackBaseArray stack = new StackBaseArray(6); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); } } ================================================ FILE: c-cpp/.gitignore ================================================ # main files main.* # executives a.out # objective files *.o *.obj ================================================ FILE: c-cpp/.gitkeep ================================================ ================================================ FILE: c-cpp/05_array/.gitkeep ================================================ ================================================ FILE: c-cpp/05_array/Array_gp.c ================================================ #include "Array.h" #include #include Array* arrayCreate() { struct Array *array = NULL; array = malloc(sizeof(*array)); if (NULL == array) { return NULL; } array->p = NULL; array->size = 0; array->typeSize = 0; array->len = 0; array->dup = NULL; array->free = NULL; array->match = NULL; return array; } void arrayInit(Array *array, int size, int typeSize) { if (NULL == array || typeSize <= 0 || size < 0) { return; } void *p = calloc(1, size* typeSize); if (NULL == p) { return; } array->p = p; array->len = 0; array->size = size; array->typeSize = typeSize; } int arrayInsert(Array *array, size_t pos, void *const value) { if (NULL == array) { return -1; } if (array->len >= array->size) { return -2; } if (pos > array->size || pos <= 0) { return -3; } char *pBegin = array->p; for (size_t i = array->len; i > pos - 1; --i) { void *pNew = pBegin + i * array->typeSize; void *pOld = pBegin + (i - 1) *array->typeSize; if (NULL != array->dup) { array->dup(pNew, pOld); } else { memcpy(pNew, pOld, array->typeSize); } } void *pCopy = (void*)(pBegin + ((pos - 1) * array->typeSize)); if (NULL != array->dup) { array->dup(pCopy, value); } else { memcpy(pCopy, value, array->typeSize); } ++array->len; return 0; } size_t arraySearchValue(Array *array, void* const value) { if (NULL == array) { return -1; } char *pBegin = array->p; size_t i = 0; for (; i < array->len; ++i) { int nCmp = 0; if (NULL != array->match) { nCmp = array->match(pBegin + i * array->typeSize, value); } else { nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize); } if (nCmp == 0) { break; } } return i; } void* arrayIndex(Array *array, size_t index) { if (NULL == array) { return NULL; } if (index > array->len || index <= 0) { return NULL; } char *pBegin = array->p; return pBegin + array->typeSize * (index - 1); } int arrayModify(Array *array, size_t pos, void *const value) { if (NULL == array) { return -1; } if (pos > array->len || pos <= 0) { return -2; } char *pBegin = array->p; void *pOld = pBegin + (pos - 1) * array->typeSize; if (NULL != array->dup) { array->dup(pOld, value); } else { memcpy(pOld, value, array->typeSize); } return 0; } size_t arrayLen(Array *array) { if (NULL == array) { return 0; } return array->len; } size_t arraySize(Array *array) { if (NULL == array) { return 0; } return array->size; } void arrayEmpty(Array *array) { if (NULL == array) { return; } free(array->p); array->p = NULL; free(array); array = NULL; } void arrayDelValue(Array *array, void *value) { if (NULL == array) { return; } char* pBegin = array->p; bool bCopy = false; for (size_t i = 0; i < array->len; ++i) { if (!bCopy) { int nCmp = 0; if (NULL != array->match) { nCmp = array->match(pBegin + i * array->typeSize, value); } else { nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize); } if (0 == nCmp) { bCopy = true; continue; } } else { void *pOld = pBegin + (i + 1) * array->typeSize; void *pNew = pBegin + i * array->typeSize; if (NULL != array->dup) { array->dup(pNew, pOld); } else { memcpy(pNew, pOld, array->typeSize); } } } if (bCopy) { --array->len; } } void arrayDelIndex(Array *array, size_t pos) { if (NULL == array) { return; } if (pos > array->len || pos <= 0) { return; } char* pBegin = array->p; for (size_t i = pos - 1; i < array->len - 1; ++i) { void *pOld = pBegin + (i + 1) * array->typeSize; void *pNew = pBegin + i * array->typeSize; if (NULL != array->dup) { array->dup(pNew, pOld); } else { memcpy(pNew, pOld, array->typeSize); } } --array->len; } ================================================ FILE: c-cpp/05_array/Array_gp.h ================================================ #ifndef __ARRAY_H__ #define __ARRAY_H__ #include #include typedef struct Array { // pָĿռС size_t size; // pָѾʹõĿռС size_t len; // ͵ĴС size_t typeSize; // ֵƺ void(*dup)(void *ptr, void *key); // ֵͷź void(*free)(void *ptr); // ֵȽϺ int(*match)(void *ptr, void *key); // ݵָ void *p; }Array; #define arraySetDupMethod(a, m) ((a)->dup = (m)) #define arraySetFreeMethod(a, m) ((a)->free = (m)) #define arraySetMatchMethod(a, m) ((a)->match = (m)) #define arrayGetDupMethod(a) ((a)->dup) #define arrayGetFree(a) ((a)->free) #define arrayGetMatchMethod(a) ((a)->match) Array* arrayCreate(); void arrayInit(Array *array, int size, int typeSize); int arrayInsert(Array *array, size_t pos, void *const value); size_t arraySearchValue(Array *array, void* const value); void* arrayIndex(Array *array, size_t index); int arrayModify(Array *array, size_t pos, void *const value); size_t arrayLen(Array *array); size_t arraySize(Array *array); void arrayEmpty(Array *array); void arrayDelValue(Array *array, void *value); void arrayDelIndex(Array *array, size_t pos); #endif // !__ARRAY_H__ ================================================ FILE: c-cpp/05_array/array.c ================================================ #include #include #include struct array { int size; int used; int *arr; }; void dump(struct array *array) { int idx; for (idx = 0; idx < array->used; idx++) printf("[%02d]: %08d\n", idx, array->arr[idx]); } void alloc(struct array *array) { array->arr = (int *)malloc(array->size * sizeof(int)); } int insert(struct array *array, int elem) { int idx; if (array->used >= array->size) return -1; for (idx = 0; idx < array->used; idx++) { if (array->arr[idx] > elem) break; } if (idx < array->used) memmove(&array->arr[idx+1], &array->arr[idx], (array->used - idx) * sizeof(int)); array->arr[idx] = elem; array->used++; return idx; } int delete(struct array *array, int idx) { if (idx < 0 || idx >= array->used) return -1; memmove(&array->arr[idx], &array->arr[idx+1], (array->used - idx - 1) * sizeof(int)); array->used--; return 0; } int search(struct array *array, int elem) { int idx; for (idx = 0; idx < array->used; idx++) { if (array->arr[idx] == elem) return idx; if (array->arr[idx] > elem) return -1; } return -1; } int main() { int idx; struct array ten_int = {10, 0, NULL}; alloc(&ten_int); if (!ten_int.arr) return -1; insert(&ten_int, 1); insert(&ten_int, 3); insert(&ten_int, 2); printf("=== insert 1, 3, 2\n"); dump(&ten_int); idx = search(&ten_int, 2); printf("2 is at position %d\n", idx); idx = search(&ten_int, 9); printf("9 is at position %d\n", idx); printf("=== delete [6] element \n"); delete(&ten_int, 6); dump(&ten_int); printf("=== delete [0] element \n"); delete(&ten_int, 0); dump(&ten_int); return 0; } ================================================ FILE: c-cpp/06_linkedlist/.gitkeep ================================================ ================================================ FILE: c-cpp/06_linkedlist/Dlist/Dlist.c ================================================ /************************************************************************* > File Name: Dlist.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-07 > Desc: ************************************************************************/ #include #include #include "./Dlist.h" void dlist_init(stDlistHead *dlist) //链表初始化 { dlist->size = 0; dlist->head = NULL; dlist->tail = NULL; return; } void dlist_destory(stDlistHead *dlist) //删除链表 { stDlistNode *pNode = NULL; while(dlist->size > 0) { pNode = dlist->head; dlist->head = dlist->head->next; free(pNode); dlist->size--; } memset(dlist,0,sizeof(stDlistHead)); return; } int dlist_insert_head(stDlistHead *dlist,stDlistNode *pNode,int data) //插入头结点,操作的链表,操作的节点,数据 { if(pNode == NULL) //当只传递一个数据时 { pNode = (stDlistNode *)malloc(sizeof(stDlistNode)); //新建节点,为节点分配空间(malloc()可能需要#include) if (pNode == NULL) { return -1; } } pNode->data = data; pNode->prev = NULL; pNode->next = NULL; if (dlist->size == 0) //如果链表长度为0,即链表当前无节点, { dlist->head = pNode; dlist->tail = pNode; } else //如果链表已有节点,则令新插入节点为头节点 { pNode->next = dlist->head; dlist->head->prev = pNode; dlist->head = pNode; } dlist->size++; //每成功调用一次,链表长度+1 return 0; } stDlistNode * dlist_remove_tail(stDlistHead *dlist) //删除尾部节点,并返回删除节点 { stDlistNode *pNode = NULL; if(dlist->size == 0) { return NULL; } pNode = dlist->tail; if(dlist->size > 1) { dlist->tail = dlist->tail->prev; dlist->tail->next = NULL; } else { dlist->head = NULL; dlist->tail = NULL; } dlist->size--; return pNode; } void dlist_remove_node(stDlistHead * dlist,stDlistNode *pNode) //删除指定节点 { if ((dlist == NULL)||(pNode == NULL)) { return; } if (dlist->head == pNode) { dlist->head = dlist->head->next; } else if (dlist->tail == pNode) { dlist->tail = pNode->prev; dlist->tail->next = NULL; } else { pNode->prev->next = pNode->next; pNode->next->prev = pNode->prev; } dlist->size--; pNode->prev = NULL; pNode->next = NULL; if (dlist->size == 0) { memset(dlist,0,sizeof(stDlistHead)); //将dlist占用内存块的所有值置为0,也就是清空head,tail指针内容 } return; } stDlistNode * dlist_search(stDlistHead * dlist,int data) //根据值搜索节点,并返回 { stDlistNode *pNode = dlist->head; while(pNode != NULL) { if (pNode->data == data) { return pNode; } pNode = pNode->next; } return NULL; } void dlist_dump(stDlistHead *dlist) //显示链表中的数据 { int no = 0; stDlistNode *pNode = dlist->head; while(pNode != NULL) { printf("\r\n [%d] = %d",no++,pNode->data); pNode = pNode->next; //将pNode的下一个节点赋值给pNode,推进循环 } return; } void Lru_dlist(stDlistHead *dlist,int data) //LRU(最近最少使用)缓存淘汰算法 { stDlistNode *pNode = NULL; pNode = dlist_search(dlist,data); if (pNode != NULL) //如果在链表中找到这个值,则删除储存这个值的节点,之后吧这个节点放在头部 { dlist_remove_node(dlist,pNode); } else if(dlist->size >= 4) //没在链表中找到,且链表长度大于4,则从链表中删除尾部节点,将新数据放在头部 { pNode = dlist_remove_tail(dlist); } dlist_insert_head(dlist ,pNode,data); return; } int main() { stDlistHead dlist = {0}; stDlistNode * pNode = NULL; dlist_init(&dlist); printf("\r\n inset 1,2,3"); dlist_insert_head(&dlist,NULL,1); dlist_insert_head(&dlist,NULL,2); dlist_insert_head(&dlist,NULL,3); dlist_dump(&dlist); pNode = dlist_remove_tail(&dlist); if(pNode != NULL) { printf("\r\n remove %d",pNode->data); } dlist_insert_head(&dlist,pNode,4); dlist_dump(&dlist); Lru_dlist(&dlist,5); dlist_dump(&dlist); Lru_dlist(&dlist,6); dlist_dump(&dlist); Lru_dlist(&dlist,7); dlist_dump(&dlist); Lru_dlist(&dlist,5); dlist_dump(&dlist); while(dlist.size > 0) { pNode = dlist_remove_tail(&dlist); if(pNode != NULL) { printf("\r\n remove %d",pNode->data); free (pNode); } } return 0; } ================================================ FILE: c-cpp/06_linkedlist/Dlist/Dlist.h ================================================ /************************************************************************* > File Name: Dlist.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-07 > Desc: ************************************************************************/ #include typedef struct DlistNode //双向链表中每一个节点 { struct DlistNode *prev; //节点前项指针 struct DlistNode *next; //节点后项指针 int data; //数据 }stDlistNode; typedef struct Dlisthead //定义链表总体 { int size; //链表长度 stDlistNode *head; //头指针 stDlistNode *tail; //尾部指针 }stDlistHead; ================================================ FILE: c-cpp/06_linkedlist/LRUBasedLinkedList.cpp ================================================ typedef int DataType; //定义 class SNode { public: DataType data; SNode * next; }; class SList { public: SList(); SList(int MaxSize); ~SList(); void intsertElemAtBegin(DataType x); //头部插入节点 bool findElem(DataType x); //查找x,存在则返回1,不存在则返回0 void deleteElemAtEnd(); //删除尾节点 bool deleteElem(DataType x); //删除指定节点,如果存在则删除,返回1,如果不存在,则删除失败返回0 bool isEmpty(); // 查看链表是否为空,1表示不为空,0表示为空 bool isFull(); // 查看链表是否满,1表示不满,0表示满 void printAll(); void * findElemOptim(DataType x); //针对此应用的优化,查找,返回指定元素的前一个节点的指针 void deleteElemOptim(void * snode); //针对此应用的优化,删除 private: int MaxSize; // 链表可以存放最大的数据 int length; // 链表的长度 SNode * head; // 指向头节点 }; /** * 1)单链表的插入,删除,查找操作; * 2)链表中存储的是 int 类型 * * Author:caozx */ #include using namespace std; // 初始化单链表 SList::SList(){ head = new SNode; //申请头节点 head -> next = NULL; this -> MaxSize = 10; this -> length = 0; } SList::SList(int MaxSize){ head = new SNode; //申请头节点 head -> next = NULL; this -> MaxSize = MaxSize; this -> length = 0; } // 销毁单链表,要把开辟的空间都释放,然后再销毁。 SList::~SList(){ SNode * ptr, * temp; ptr = head; while(ptr -> next != NULL){ temp = ptr -> next; ptr -> next = ptr -> next -> next; delete temp; } delete head ; //删除头节点 this -> head = NULL; this -> length = 0; } //链表头部插入节点 void SList::intsertElemAtBegin(DataType x){ SNode * ptr = new SNode; ptr -> data = x; ptr -> next = head ->next; head -> next = ptr; this -> length ++; } //查找x,存在则返回1,不存在则返回0 bool SList::findElem(DataType x) { SNode * ptr; ptr = head; while(ptr -> next != NULL){ if(ptr -> next ->data == x){ return 1; } ptr = ptr -> next; } return 0; } // 删除尾结点 void SList::deleteElemAtEnd(){ SNode * ptr , * temp; ptr = head; while(ptr -> next != NULL && ptr -> next -> next != NULL){ //倒数第二个节点 ptr = ptr -> next; } temp = ptr -> next; ptr -> next = temp -> next; this -> length --; delete temp; } //删除指定节点, //如果存在则删除,返回1,表示存在且删除成功; //如果不存在则不删除,返回0,表示不存在该元素,不需要删除,也即删除失败 bool SList::deleteElem(DataType x) { SNode * ptr, * temp; ptr = head; while(ptr -> next != NULL){ if(ptr -> next ->data == x){ temp = ptr -> next; ptr -> next = temp -> next; delete temp; this -> length --; return 1; } ptr = ptr -> next; } return 0; } // 查看链表是否为空,1表示不为空,0表示为空 bool SList::isEmpty() { if(this -> length == 0){ //空 return 0; } else{ return 1; } } // 查看链表是否满,1表示不满,0表示满 bool SList::isFull() { if(this -> length == this -> MaxSize){ //满 return 0; } else{ return 1; } } // 打印 void SList::printAll() { SNode * ptr; ptr = head; while(ptr -> next != NULL){ ptr = ptr -> next; cout << ptr-> data <<" "; } cout << endl; } //针对此应用的优化,查找, //若存在则返回指定元素的前一个节点的指针 //若不存在,则返回NULL void * SList::findElemOptim(DataType x) { SNode * ptr; ptr = head; while(ptr -> next != NULL){ if(ptr -> next ->data == x){ return (void *)ptr; } ptr = ptr -> next; } return NULL; } //针对此应用的优化,删除 void SList::deleteElemOptim(void * snode) { SNode * ptr, * temp; ptr = (SNode *)snode; temp = ptr -> next; ptr -> next = temp -> next; this -> length --; delete temp; } int main(int argc, char const *argv[]) { cout << "test "<< endl; SList slist(10); //缓存最大10个。 int num = 0; while(1) { cout << "please enter a number,99999== exit" << endl; cin >> num; if(num == 99999) break; /* 未优化 if(slist.findElem(num)){ //存在 slist.deleteElem(num); //把原来的位置删除 slist.intsertElemAtBegin(num); //在链表头插入 } */ //优化 SNode * prePtr = (SNode *)slist.findElemOptim(num); if(prePtr != NULL){ //存在 slist.deleteElemOptim(prePtr); //把原来的位置删除 slist.intsertElemAtBegin(num); //在链表头插入 } else{ //不存在 if(slist.isFull()){ //不满 slist.intsertElemAtBegin(num); } else{ //满 slist.deleteElemAtEnd(); slist.intsertElemAtBegin(num); } } slist.printAll(); } return 0; system("pause"); } ================================================ FILE: c-cpp/06_linkedlist/list_isPalindrome/LinkList.cpp ================================================ #include "LinkList.h" void CreateListHead(LinkList *&L,ElemType a[],int n) { int i; LinkList *s; L = (LinkList *)malloc(sizeof(LinkList)); L->next = NULL; for(i = 0;i < n;i++) { s=(LinkList*)malloc(sizeof(LinkList)); s->data = a[i]; s->next = L->next; L->next = s; } } void CreateListTail(LinkList *&L,ElemType a[],int n) { int i; LinkList * s,* r; L = (LinkList *)malloc(sizeof(LinkList)); r = L; for(i = 0;i < n;i++) { s = (LinkList *)malloc(sizeof(LinkList)); s->data = a[i]; r->next = s; r = s; } r->next = NULL; } void InitList(LinkList *&L) { L=(LinkList *)malloc(sizeof(LinkList)); L->next = NULL; } void DestroyList(LinkList *&L) { LinkList * pre = L,*p = L->next; while(p!=NULL) { free(pre); pre = p; p = L->next; } free(pre); } bool ListEmpty(LinkList *L) { return(L->next==NULL); } int ListLength(LinkList *L) { int n = 0; LinkList * p = L; while(p->next!=NULL) { n++; p=p->next; } return(n); } void ShowList(LinkList *L) { LinkList * p = L->next;//ָʼڵ while(p!=NULL) { printf(" %c ",p->data); p = p->next; } printf("\n"); } bool GetListElem(LinkList *L,int i,ElemType &e) { int j = 0; LinkList *p = L; while(jnext; } if(p==NULL) return false; else { e=p->data; return true; } } int LocateElem(LinkList*L,ElemType e) { int i=1; LinkList *p = L->next; while(p!=NULL&&p->data!=e){ p=p->next; i++; } if(p==NULL) { return(0); } else return(i); } bool ListInsert(LinkList *&L,int i,ElemType e) { int j=0; LinkList *p =L,*s; while(jnext; } if(p==NULL) { return false; } else { s= (LinkList*)malloc(sizeof(LinkList)); s->data = e; s->next = p->next; p->next = s; return true; } } bool ListDelete(LinkList *&L,int i,ElemType &e) { int j=0; LinkList * p =L,*q; while(jnext; } if(p==NULL) return false; else { q=p->next; if(q==NULL) return false; e=q->data; p->next=q->next; free(q); return true; } } ================================================ FILE: c-cpp/06_linkedlist/list_isPalindrome/LinkList.h ================================================ #ifndef LINKLIST_H #define LINKLIST_H #include #include typedef char ElemType; typedef struct LNode { ElemType data; struct LNode*next; }LinkList; void CreateListHead(LinkList *&L,ElemType a[],int n); void CreateListTail(LinkList *&L,ElemType a[],int n); void InitList(LinkList *&L); void DestroyList(LinkList *&L); bool ListEmpty(LinkList *L); int ListLength(LinkList *L); void ShowList(LinkList *L); bool GetListElem(LinkList *L,int i,ElemType &e); int LocateElem(LinkList*L,ElemType e); bool ListInsert(LinkList *&L,int i,ElemType e); bool ListDelete(LinkList *&L,int i,ElemType &e); #endif ================================================ FILE: c-cpp/06_linkedlist/palindromeList/LinkedList.hpp ================================================ /** * Author: TripleZ * Date: 2018-10-10 * Brief: Linked list class. */ #ifndef _LINKEDLIST_HPP_ #define _LINKEDLIST_HPP_ #include #include "ListNode.hpp" class LinkedList { public: int size; int length; ListNode *head; LinkedList(); LinkedList(int size); ~LinkedList(); ListNode* FindElem(int elemVal); bool DeleteElem(ListNode *elem); bool DeleteLastElem(); bool InsertElemAtFront(int elemVal); bool InsertElemAtBack(int elemVal); void PrintList(); }; LinkedList::LinkedList() { this -> head = new ListNode(); this -> head->next = nullptr; this -> head->val = -1; this -> size = 10; // default this -> length = 0; } LinkedList::LinkedList(int size) { this -> head = new ListNode(); this -> head->next = nullptr; this -> head->val = -1; this -> size = size; this -> length = 0; } LinkedList::~LinkedList() { ListNode *p, *q; p = this -> head; while(p -> next != nullptr) { q = p -> next; p -> next = p -> next -> next; delete q; } delete head; this -> head = nullptr; this -> length = 0; } ListNode* LinkedList::FindElem(int elemVal) { ListNode *p; for (p = this -> head -> next; p != nullptr; p = p -> next) { if (p -> val == elemVal) { return p; } } return nullptr; } bool LinkedList::DeleteElem(ListNode *elem) { ListNode *prev, *next; for (prev = this -> head; prev -> next != elem; prev = prev -> next); next = elem -> next; prev -> next = next; delete elem; this -> length --; return true; } bool LinkedList::DeleteLastElem() { ListNode *prev, *elem; for (prev = this -> head; prev -> next -> next != nullptr; prev = prev -> next) ; elem = prev -> next; prev -> next = nullptr; delete elem; this -> length --; return true; } bool LinkedList::InsertElemAtFront(int elemVal) { ListNode *newNode = new ListNode(); newNode -> val = elemVal; newNode -> next = this -> head -> next; this -> head -> next = newNode; this -> length ++; return true; } bool LinkedList::InsertElemAtBack(int elemVal) { ListNode *newNode = new ListNode(); newNode -> val = elemVal; ListNode *end; for (end = this -> head; end -> next != nullptr; end = end -> next); end -> next = newNode; newNode -> next = nullptr; this -> length ++; return true; } void LinkedList::PrintList() { ListNode *elem; printf("List: "); for (elem = this -> head -> next; elem -> next != nullptr; elem = elem -> next) { printf("%d - ", elem -> val); } printf("%d\n", elem -> val); } #endif ================================================ FILE: c-cpp/06_linkedlist/palindromeList/ListNode.hpp ================================================ /** * Author: TripleZ * Date: 2018-10-10 * Brief: ListNode class. */ #ifndef _LISTNODE_HPP_ #define _LISTNODE_HPP_ class ListNode { public: int val; ListNode *next; }; #endif ================================================ FILE: c-cpp/06_linkedlist/palindromeList/palindromeList.cpp ================================================ /** * Author: TripleZ * Date: 2018-10-10 * Brief: Check a list whether is palindrome. */ #include #include "LinkedList.hpp" bool CheckPalindromeList(LinkedList *list) { // 使用快慢指针找链表中点 ListNode *slow, *fast, *mid2; slow = list -> head; fast = list -> head; while (fast -> next != nullptr) { slow = slow -> next; fast = fast -> next; if (fast -> next != nullptr) { fast = fast -> next; mid2 = slow -> next; } else { mid2 = nullptr; } } // 从中点向后逆转链表(区分奇偶情况) ListNode *mid = slow; ListNode *elem, *prev, *save; if (mid2 == nullptr) { // odd elem = mid; prev = mid -> next; } else { // even elem = mid2; prev = mid2 -> next; mid2 -> next = nullptr; } save = prev -> next; mid -> next = nullptr; while (save != nullptr) { prev -> next = elem; elem = prev; prev = save; save = save -> next; } prev -> next = elem; ListNode *end = prev; ListNode *front = list -> head -> next; // 从头尾同时遍历比较,检测链表是否为回文 bool palindrome = true; while (front != end) { // printf("%d, %d\n", front -> val, end -> val); if (front -> val != end -> val) { palindrome = false; break; } front = front -> next; end = end -> next; } palindrome ? printf("The list is palindrome~\n") : printf("The list is not palindrome!\n"); return palindrome; } int main(int argc, char const *argv[]) { int init[] = {1, 2, 3, 2, 1}; LinkedList *list = new LinkedList(5); for (int i = 0; i < 5; i++) { list -> InsertElemAtBack(init[i]); } list -> PrintList(); CheckPalindromeList(list); // true list -> InsertElemAtFront(5); CheckPalindromeList(list); // false int init2[] = {1, 2, 3, 3, 2, 1}; LinkedList *list2 = new LinkedList(10); for (int i = 0; i < 6; i++) list2 -> InsertElemAtBack(init2[i]); list2 -> PrintList(); CheckPalindromeList(list2); list2 -> InsertElemAtBack(4); CheckPalindromeList(list2); return 0; } ================================================ FILE: c-cpp/06_linkedlist/single_list.c ================================================ #include #include struct single_list { struct single_list *next; int val; }; struct single_list_head { struct single_list *head; }; bool is_empty(struct single_list_head *head) { return head->head == NULL; } void dump(struct single_list_head *head) { struct single_list *tmp = head->head; int idx = 0; while (tmp) { printf("[%02d]: %08d\n", idx++, tmp->val); tmp = tmp->next; } } void insert(struct single_list **prev, struct single_list *elem) { if (!prev) return; elem->next = *prev; *prev = elem; } void insert_head(struct single_list_head *head, struct single_list *elem) { insert(&head->head, elem); } struct single_list* del(struct single_list **prev) { struct single_list *tmp; if (!prev) return NULL; if (*prev == NULL) return NULL; tmp = *prev; *prev = (*prev)->next; tmp->next = NULL; return tmp; }; struct single_list* delete_head(struct single_list_head* head) { return del(&head->head); }; struct single_list** search(struct single_list_head* head, int target) { struct single_list **prev, *tmp; for (prev = &head->head, tmp = *prev; tmp && (tmp->val < target); prev = &tmp->next, tmp = *prev) ; return prev; }; void reverse(struct single_list_head* head) { struct single_list_head tmp = {NULL}; struct single_list *elem; while (!is_empty(head)) { elem = delete_head(head); insert_head(&tmp, elem); } head->head = tmp.head; } bool is_cyclic(struct single_list_head* head) { struct single_list *s1, *s2; s1 = s2 = head->head; while(s1 && s2) { s1 = s1->next; s2 = s2->next ? s2->next->next:s2->next; if (s1 == s2) return true; } return false; } struct single_list* middle(struct single_list_head* head) { struct single_list *s1, *s2; struct single_list pseudo_head; pseudo_head.next = head->head; s1 = s2 = &pseudo_head; while (true) { if (!s2 || !s2->next) return s1; s1 = s1->next; s2 = s2->next->next; } return NULL; }; int main() { struct single_list_head head = {NULL}; struct single_list lists[10]; struct single_list **prev; int idx; for (idx = 0; idx < 10; idx++) { lists[idx].val = idx; lists[idx].next = NULL; } insert_head(&head, &lists[6]); insert_head(&head, &lists[5]); insert_head(&head, &lists[4]); insert_head(&head, &lists[1]); insert_head(&head, &lists[0]); printf("=== insert 0, 1, 4, 5, 6\n"); dump(&head); prev = search(&head, 2); insert(prev, &lists[2]); printf("=== insert 2\n"); dump(&head); printf("middle elem is %d\n", middle(&head)->val); prev = search(&head, 2); if ((*prev) && ((*prev)->val == 2)) printf("The list contains 2\n"); else printf("The list not contains 2\n"); del(prev); prev = search(&head, 2); printf("After remove 2\n"); if ((*prev) && ((*prev)->val == 2)) printf("The list contains 2\n"); else printf("The list not contains 2\n"); dump(&head); printf("After reverse \n"); reverse(&head); dump(&head); printf("middle elem is %d\n", middle(&head)->val); lists[0].next = &lists[6]; printf("list is%s cyclic\n", is_cyclic(&head)?"":" not"); return 0; } ================================================ FILE: c-cpp/06_linkedlist/singlelist_gc/singleList.c ================================================ #include "singleList.h" #include linkedList * listCreate() { linkedList *list = NULL; list = malloc(sizeof(*list)); if (NULL == list) { return NULL; } list->dup = NULL; list->free = NULL; list->match = NULL; list->head = NULL; list->len = 0; return list; } // ͷ void listRelease(linkedList *list) { if (NULL == list) { return; } listEmpty(list); free(list); list = NULL; } void listEmpty(linkedList *list) { if (NULL == list) { return; } while (NULL != list->head) { listNode *pNode = list->head; list->head = pNode->next; if (NULL != list->free) { list->free(pNode->value); } else { free(pNode->value); } pNode->next = NULL; free(pNode); pNode = NULL; } } linkedList * listAddNodeHead(linkedList *list, void * value) { if (NULL == list || NULL == value) { return list; } listNode *node = NULL; node = malloc(sizeof(*node)); if (NULL == node) { return list; } node->value = value; node->next = list->head; list->head = node; ++list->len; return list; } linkedList * listAddNodeTail(linkedList *list, void *value) { if (NULL == list || NULL == value) { return list; } listNode *node = NULL; node = malloc(sizeof(*node)); if (NULL == node) { return list; } node->value = value; node->next = NULL; if (NULL == list->head && list->len == 0) { list->head = node; } else { listNode *tail = list->head; listNode *pre = list->head; while (NULL != tail) { pre = tail; tail = tail->next; } pre->next = node; } ++list->len; return list; } linkedList * listInsertNode(linkedList *list, listNode *old_node, void *value, bool after) { if (NULL == list || NULL == old_node) { return list; } listNode *pNode = NULL; pNode = malloc(sizeof(*pNode)); if (NULL == pNode) { return list; } pNode->value = value; if (after) { pNode->next = old_node->next; old_node->next = pNode; } else { listNode *pre = list->head; while (pre->next != old_node) { pre = pre->next; } if (NULL != pre) { pre->next = pNode; pNode->next = old_node; } } ++list->len; return list; } // ûͷźʱͷŴ void listDelNode(linkedList *list, listNode *node) { if (NULL == list || NULL == node) { return; } listNode *pre = list->head; listNode *cur = list->head; while (NULL != cur && cur != node) { pre = cur; cur = cur->next; } // ڸ if (NULL == pre) { return; } pre->next = node->next; node->next = NULL; --list->len; if (NULL != list->free) { list->free(node->value); free(node); node = NULL; } } listNode * listSearchKey(linkedList *list, void *key) { if (NULL == list) { return NULL; } listNode *node = list->head; while (NULL != node) { if (NULL != list->match) { if (list->match(key, node->value) == 0) { return node; } } else { if (key == node->value) { return node; } } node = node->next; } return NULL; } listNode * listIndex(linkedList *list, long index) { if (NULL == list) { return NULL; } if (index <= 0 || index > list->len) { return NULL; } listNode *pNode = list->head; for (long i = 0; i < index; ++i) { pNode = pNode->next; } return pNode; } linkedList* listRewind(linkedList *list) { if (NULL == list) { return NULL; } listNode *head = list->head; listNode *pre = NULL; listNode *next = NULL; while (NULL != head) { next = head->next; head->next = pre; pre = head; head = next; } list->head = pre; return list; } size_t listLength(linkedList *list) { if (NULL == list) { return 0; } return list->len; } ================================================ FILE: c-cpp/06_linkedlist/singlelist_gc/singleList.h ================================================ #ifndef __SINGLELIST_H__ #define __SINGLELIST_H__ #include #include typedef struct listNode { struct listNode *next; void *value; }listNode; typedef struct linkedList { listNode *head; size_t len; size_t typesize; void(*dup)(void*, void*); int(*match)(void*, void*); void(*free)(void*); }linkedList; #define listSetDupMethod(l,m) ((l)->dup = (m)) #define listSetFreeMethod(l,m) ((l)->free = (m)) #define listSetMatchMethod(l,m) ((l)->match = (m)) #define listGetDupMethod(l) ((l)->dup) #define listGetFree(l) ((l)->free) #define listGetMatchMethod(l) ((l)->match) linkedList *listCreate(); void listRelease(linkedList *list); void listEmpty(linkedList *list); linkedList *listAddNodeHead(linkedList *list, void *value); linkedList *listAddNodeTail(linkedList *list, void *value); linkedList *listInsertNode(linkedList *list, listNode *old_node, void *value, bool after); void listDelNode(linkedList *list, listNode *node); listNode *listSearchKey(linkedList *list, void *key); listNode *listIndex(linkedList *list, long index); linkedList* listRewind(linkedList *list); size_t listLength(linkedList *list); #endif // !__SINGLELIST_H__ ================================================ FILE: c-cpp/07_linkedlist/.gitkeep ================================================ ================================================ FILE: c-cpp/07_linkedlist/LinkedListAlgo.c ================================================ #include #include /** * 1) 单链表反转 * 2) 链表中环的检测 * 3) 两个有序的链表合并 * 4) 删除链表倒数第 n 个结点 * 5) 求链表的中间结点 * * Author: Smallfly */ typedef struct SinglyLinkedNode { int data; struct SinglyLinkedNode* next; } SinglyLinkedNode; void insertNode(SinglyLinkedNode** head_ref, int data); void printLinkedList(SinglyLinkedNode* head); /** 反转单链表 */ void reverse(SinglyLinkedNode** head_ref) { if (*head_ref == NULL) return; SinglyLinkedNode *prev = NULL; SinglyLinkedNode *current = *head_ref; while (current) { SinglyLinkedNode *next = current->next; if (!next) { // 到达尾结点时,将地址存入 head_ref *head_ref = current; } current->next = prev; prev = current; current = next; } } void test_reverse() { SinglyLinkedNode* head = NULL; insertNode(&head, 3); insertNode(&head, 2); insertNode(&head, 1); reverse(&head); printLinkedList(head); } /** 检测单链表是否有环 */ // 这里使用一级指针也可以 int checkCircle(SinglyLinkedNode** head_ref) { if (*head_ref == NULL) return 0; SinglyLinkedNode *slow = *head_ref, *fast = *head_ref; while (fast != NULL && fast->next != NULL) { fast = fast->next->next; slow = slow->next; if (slow == fast) return 1; } return 0; } void test_checkCircle() { SinglyLinkedNode* head = NULL; insertNode(&head, 3); insertNode(&head, 2); insertNode(&head, 1); int result1 = checkCircle(&head); printf("has circle: %d\n", result1); // make circle linklist SinglyLinkedNode* current = malloc(sizeof(SinglyLinkedNode)); current->data = 0; SinglyLinkedNode* h = current; for (int i = 1; i < 4; ++i) { SinglyLinkedNode* node = malloc(sizeof(SinglyLinkedNode)); node->data = i; current->next = node; //reset current node current = node; } current->next = h; int result2 = checkCircle(&h); printf("has circle: %d\n", result2); } /** 有序链表合并 */ void moveNode(SinglyLinkedNode** dest_ref, SinglyLinkedNode** src_ref); SinglyLinkedNode* mergeSortedLinkedList(SinglyLinkedNode* la, SinglyLinkedNode* lb) { // 辅助结点,next 指针持有合并后的有序链表 SinglyLinkedNode dummy; // 有序链表的尾结点 SinglyLinkedNode* tail = &dummy; while (1) { // 如果有一个链表为空,直接与另一个链表接起来 if (!la) { tail->next = lb; break; } else if (!lb) { tail->next = la; break; } // 将头结点较小的优先添加到 tail if (la->data <= lb->data) { moveNode(&(tail->next), &la); } else { moveNode(&(tail->next), &lb); } tail = tail->next; } return dummy.next; } // 将 src_ref 的头结点,添加到 dest_ref 的头部。 void moveNode(SinglyLinkedNode** dest_ref, SinglyLinkedNode** src_ref) { if (*src_ref == NULL) return; SinglyLinkedNode* new_node = *src_ref; *src_ref = new_node->next; new_node->next = *dest_ref; *dest_ref = new_node; } void test_mergeSortedLinkedList() { SinglyLinkedNode* a = NULL; insertNode(&a, 10); insertNode(&a, 5); insertNode(&a, 0); SinglyLinkedNode* b = NULL; insertNode(&b, 8); insertNode(&b, 6); insertNode(&b, 3); SinglyLinkedNode* result = mergeSortedLinkedList(a, b); printLinkedList(result); SinglyLinkedNode* result2 = mergeSortedLinkedList(a, NULL); printLinkedList(result2); } /** 删除倒数第 K 个结点 */ void deleteLastKth(SinglyLinkedNode** head_ref, int k) { if (*head_ref == NULL || k == 0) return; // 快指针向前移动 k-1 SinglyLinkedNode* fast = *head_ref; int i = 1; while (i < k && fast != NULL) { fast = fast->next; ++i; } // 如果快指针为空,说明结点个数小于 k if (fast == NULL) return; SinglyLinkedNode* slow = *head_ref; SinglyLinkedNode* prev = NULL; while (fast->next != NULL) { fast = fast->next; prev = slow; slow = slow->next; } // 如果 prev 为空,头结点刚好是第 k 个结点 if (!prev) { (*head_ref) = (*head_ref)->next; } else { prev->next = slow->next; } free(slow); } void test_deleteLastKth() { SinglyLinkedNode* head = NULL; insertNode(&head, 1); insertNode(&head, 2); insertNode(&head, 3); insertNode(&head, 4); insertNode(&head, 5); // 1. 删除头结点 deleteLastKth(&head, 5); printLinkedList(head); // 2. 删除中间结点 deleteLastKth(&head, 2); printLinkedList(head); } /** 求中间结点 */ SinglyLinkedNode* findMiddleNode(SinglyLinkedNode* head) { if (!head) return NULL; SinglyLinkedNode* slow = head; SinglyLinkedNode* fast = head; // 1. 慢指针走一步,快指针两步 while (fast->next != NULL && fast->next->next != NULL) { slow = slow->next; fast = fast->next->next; } return slow; } void test_findMiddleNode() { SinglyLinkedNode* head = NULL; insertNode(&head, 1); insertNode(&head, 2); insertNode(&head, 3); insertNode(&head, 4); insertNode(&head, 5); SinglyLinkedNode* middleNode = findMiddleNode(head); printf("%d\n", middleNode->data); printLinkedList(head); } /** 工具方法 */ // 插入新结点到链表头部 void insertNode(SinglyLinkedNode** head_ref, int data) { SinglyLinkedNode* new_node = malloc(sizeof(SinglyLinkedNode)); new_node->data = data; new_node->next = *head_ref; *head_ref = new_node; } // 打印链表 void printLinkedList(SinglyLinkedNode* node) { printf("--- start ---\n"); while (node) { printf("data: %d\n", node->data); node = node->next; } printf("--- end ---\n"); } // 跑测试 void test() { test_reverse(); // test_checkCircle(); // test_mergeSortedLinkedList(); // test_deleteLastKth(); // test_findMiddleNode(); } ================================================ FILE: c-cpp/07_linkedlist/SingleList.cpp ================================================ #include #include #include using namespace std; class CElement; /*** * @brief */ class CSingleList { public: CSingleList(); ~CSingleList(); /** * @brief ..ĩβ * @return ɹطǿָ,ʧ */ CElement* Insert(void* lpData, int iDataSize); /** * @brief ..ָλò * @return ɹطǿָ,ʧ */ CElement* Insert(CElement* lpElement, void* lpData, int iDataSize); /** * @brief ɾ */ void Delete(CElement*); /** * @brief */ CElement* Begin(); /** * @brief һԪ */ CElement* Next(); /*** * @brief β */ CElement* End(); /** * @brief Ƿǿ * @return շTRUE򷵻FALSE */ bool Empty(); /** * @brief ת */ void Reverse(); /** * @brief ⻷ * @return TRUEʱʾڻ,򲻴ڻ. */ bool CheckCircle(); /** * @brief ϲ2 */ void Merge(CSingleList& lst, std::function); /** * @brief ɾK */ void DeleteLastKth(int k); /** * @brief мڵ */ CElement* Center(); private: void Insert(CElement* lpNewElement, CElement* lpCurElement, bool bBack = true); void Insert(CElement* lpNewElement); CElement* Tail(); CSingleList(CSingleList const & rhs); CSingleList& operator= (CSingleList const& rhs); private: /**ͷ*/ CElement* m_lpHead; /**ڱ*/ CElement* m_lpSentinel; /**ս㣬End() */ CElement* m_lpNull; /**ǰ. öʱʹ. */ CElement* m_lpCur; }; /*** * @brief Ԫ. */ class CElement { friend class CSingleList; protected: CElement(); ~CElement(); public: /*** * @brief ȡָ */ void* GetDataPtr(); protected: /**һ*/ CElement* m_lpNext; void* m_lpData; }; void CreateList(CSingleList& lst) { //ѭԪصβ for(int i=1; i<10;i++) { int* p = new int(); *p = i; lst.Insert(p, 4); } } void PrintList(CSingleList& lst) { CElement* lpElement = lst.Begin(); while(lpElement != lst.End()) { std::cout<<*((int*)lpElement->GetDataPtr())<GetDataPtr()) == 5) { int* p = new int(); *p = 55; lst.Insert(lpElement,p, 4); break; }else{ lpElement = lst.Next(); } } std::cout<<"öǰԪ"<GetDataPtr()) == 7) { lst.Delete(lpElement); break; }else{ lpElement = lst.Next(); } } std::cout<<"öǰԪ"< int{ if(*((int*)lpT1) < *((int*)lpT2)){ return -1; }else if(*((int*)lpT1) == *((int*)lpT2)){ return 0; }else if(*((int*)lpT1) > *((int*)lpT2)){ return 1; } return 0; }); std::cout<<"ϲ֮󣬴ӡǰ."<GetDataPtr())<GetDataPtr())<GetDataPtr())<m_lpNext = m_lpSentinel; } CSingleList::~CSingleList() { if(NULL != m_lpSentinel) { delete m_lpSentinel; m_lpSentinel = NULL; } if(NULL != m_lpNull) { delete m_lpNull; m_lpNull = NULL; } if(NULL != m_lpHead) { delete m_lpHead; m_lpHead = NULL; } } CElement* CSingleList::Insert(void* lpData, int iDataSize) { CElement* lpNewElement = new CElement(); if(NULL == lpNewElement) { return NULL; } lpNewElement->m_lpData = lpData; Insert(lpNewElement, Tail()); return lpNewElement; } CElement* CSingleList::Insert(CElement* lpElement, void* lpData, int iDataSize) { if((NULL == lpElement) || (End() == lpElement)) { return NULL; } CElement* lpNewElement = new CElement(); if(NULL == lpNewElement) { return NULL; } lpNewElement->m_lpData = lpData; Insert(lpNewElement, lpElement); return lpNewElement; } void CSingleList::Insert(CElement* lpNewElement, CElement* lpCurElement, bool bBack /*= true*/) { if(bBack){//뵽ָԪصĺ lpNewElement->m_lpNext = lpCurElement->m_lpNext; lpCurElement->m_lpNext = lpNewElement; }else{//뵽ָԪصǰ CElement* lpIter = m_lpSentinel; while(NULL != lpIter) { if(lpIter->m_lpNext == lpCurElement) { lpNewElement->m_lpNext = lpIter->m_lpNext; lpIter->m_lpNext = lpNewElement; break; }else{ lpIter = lpIter->m_lpNext; } } } } void CSingleList::Delete(CElement* lpElement) { if((NULL == lpElement) || (End() == lpElement)) { return; } CElement* lpCurElement = m_lpHead->m_lpNext; while(NULL != lpCurElement->m_lpNext) { if(lpCurElement->m_lpNext == lpElement) { lpCurElement->m_lpNext = lpCurElement->m_lpNext->m_lpNext; break; }else{ lpCurElement = lpCurElement->m_lpNext; } } } CElement* CSingleList::Tail() { CElement* lpCurElement = m_lpHead->m_lpNext; while(NULL != lpCurElement->m_lpNext) { lpCurElement = lpCurElement->m_lpNext; } return lpCurElement; } CElement* CSingleList::Begin() { m_lpCur = NULL; if(NULL == m_lpHead->m_lpNext->m_lpNext) { m_lpCur = End(); }else{ m_lpCur = m_lpHead->m_lpNext->m_lpNext; } return m_lpCur; } CElement* CSingleList::Next() { if((NULL == m_lpCur) || (End() == m_lpCur)) { return m_lpCur; } m_lpCur = m_lpCur->m_lpNext; if(NULL == m_lpCur) { m_lpCur = End(); } return m_lpCur; } CElement* CSingleList::End() { return m_lpNull; } bool CSingleList::Empty() { return Begin() == End(); } void CSingleList::Reverse() { if(Empty()) { return; } CElement* lpPre = NULL; CElement* lpTmp = NULL; CElement* lpCurElement = m_lpSentinel->m_lpNext; while(1) { lpTmp = lpCurElement->m_lpNext; lpCurElement->m_lpNext = lpPre; if(NULL == lpTmp) { break; } lpPre = lpCurElement; lpCurElement = lpTmp; } m_lpSentinel->m_lpNext = lpCurElement; } bool CSingleList::CheckCircle() { if(Empty()) { return false; } CElement* lpFast = m_lpSentinel->m_lpNext; CElement* lpSlow = m_lpSentinel->m_lpNext; while ((NULL != lpFast) && (NULL != lpFast->m_lpNext)) { lpFast = lpFast->m_lpNext->m_lpNext; lpSlow = lpSlow->m_lpNext; if (lpFast == lpSlow) { return true; } } return false; } /** * ϲ2 */ void CSingleList::Merge(CSingleList& lst, std::function fnCompare) { CElement* lpL1 = Begin(); CElement* lpL2 = lst.Begin(); if(!fnCompare) { return; } int iRet = 0; while((lpL2 != lst.End())) { 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; } CElement* lpNewElement = new CElement(); if(NULL != lpNewElement) { lpNewElement->m_lpData = lpL2->GetDataPtr(); if(lpL1 != End()) { Insert(lpNewElement,lpL1, iRet < 0); }else{ CElement* lpTail = Tail(); Insert(lpNewElement,lpTail); } } lpL2 = lst.Next(); } } void CSingleList::DeleteLastKth(int k) { int i = 1; if(k <= 0) { return; } CElement* lpFast = Begin(); while((lpFast != End()) && (i < k)) { lpFast = Next(); ++i; } if (lpFast == End()) { return; } CElement* lpSlow = Begin(); CElement* lpPrev = NULL; while (NULL != lpFast->m_lpNext) { lpFast = lpFast->m_lpNext; lpPrev = lpSlow; lpSlow = Next(); } if(NULL != lpPrev) { lpPrev->m_lpNext = lpPrev->m_lpNext->m_lpNext; } } CElement* CSingleList::Center() { CElement* lpFast = Begin(); CElement* lpSlow = lpFast; while((NULL != lpFast->m_lpNext) && (NULL != lpFast->m_lpNext->m_lpNext)) { lpFast = lpFast->m_lpNext->m_lpNext; lpSlow = lpSlow->m_lpNext; } return lpSlow; } CElement::CElement() { m_lpNext = NULL; m_lpData = NULL; } CElement::~CElement() { } void* CElement::GetDataPtr() { return m_lpData; } ================================================ FILE: c-cpp/07_linkedlist/linked_list.h ================================================ /** * 单链表 * * Author: Liam Huang (Liam0205) */ #ifndef LINKEDLIST_LINKED_LIST_H_ #define LINKEDLIST_LINKED_LIST_H_ #include template struct Node { using ptr_t = std::shared_ptr>; T data; ptr_t next; Node(T data_) : data(data_), next(nullptr) {} Node() : next(nullptr) {} }; #endif // LINKEDLIST_LINKED_LIST_H_ ================================================ FILE: c-cpp/07_linkedlist/linked_list_algo.hpp ================================================ /** * 0) 遍历单链表 * 1) 单链表反转 * 2) 链表中环的检测 * 3) 两个有序的链表合并 * 4) 删除链表倒数第n个结点 * 5) 求链表的中间结点 * * Author: Liam Huang (Liam0205) */ #ifndef LINKEDLIST_LINKED_LIST_ALGO_HPP_ #define LINKEDLIST_LINKED_LIST_ALGO_HPP_ #include #include "linked_list.h" template void traverse(typename Node::ptr_t head, UnaryFunc do_traverse) { auto sentry = std::make_shared>(); sentry->next = head; decltype(sentry) work = sentry; while (work = work->next) { do_traverse(work); } } template typename Node::ptr_t reverse(typename Node::ptr_t head) { if (nullptr == head or nullptr == head->next) { return head; } decltype(head) prev = nullptr, curr = head, next = head->next; while (nullptr != next) { curr->next = prev; prev = curr; curr = next; next = curr->next; } curr->next = prev; return curr; } template bool check_circle(typename Node::ptr_t head) { if (nullptr == head or nullptr == head->next) { return false; } decltype(head) slow = head, fast = head; while (nullptr != fast and nullptr != fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { return true; } } return false; } template typename Node::ptr_t merge_two_sorted_lists(typename Node::ptr_t lhs, typename Node::ptr_t rhs) { if (nullptr == lhs) { return rhs; } if (nullptr == rhs) { return lhs; } decltype(lhs) l = lhs, r = rhs, head = nullptr, work = nullptr; if (l->data < r->data) { head = l; l = l->next; } else { head = r; r = r->next; } work = head; while (nullptr != l and nullptr != r) { if (l->data < r->data) { work->next = l; l = l->next; } else { work->next = r; r = r->next; } work = work->next; } if (nullptr != l) { work->next = l; } else { work->next = r; } return head; } template typename Node::ptr_t deleth_last_Kth(typename Node::ptr_t head, size_t n) { decltype(head) sentry = std::make_shared>(); sentry->next = head; decltype(head) prev = sentry, curr = sentry->next, fast = sentry->next; for (size_t i = 0; i != n; ++i) { if (nullptr != fast) { fast = fast->next; } else { return sentry->next; } } while (nullptr != fast) { prev = curr; curr = curr->next; fast = fast->next; } prev->next = curr->next; return sentry->next; } template typename Node::ptr_t find_middle_node(typename Node::ptr_t head) { if (nullptr == head or nullptr == head->next) { return head; } decltype(head) slow = head, fast = head; while (nullptr != fast and nullptr != fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { return nullptr; } } return slow; } #endif // LINKEDLIST_LINKED_LIST_ALGO_HPP_ ================================================ FILE: c-cpp/07_linkedlist/linklist_jinshaohui.c ================================================ /************************************************************************* > File Name: lisklist.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-07 > Desc: ************************************************************************/ #include struct stlistNode { int val; struct listNode *next; }listNode; /*反转链表*/ listNode reverseList(listNode *head) { listNode *prev = NULL; listNode *next = NULL; while(head != NULL) { next = head->next; head->next = prev; prev = head; head = next; } return prev; } /*判断链表是否有环*/ int hasCycle(listNode *head) { listNode * fast = head; listNode * low = head; while(fast != NULL && fast->next != NULL) { low = low->next; fast = fast->next->next; if (low == fast) { return 1; } } return 0; } /*合并有序链表*/ listNode *mergeTwoLists(listNode *l1,listNode *l2) { listNode head = {0}; listNode *pRes = &head; while(1) { if(l1 == NULL) { pRes->next = l2; } if (l2 == NULL) { pRes->next = l1; } if(l1->val < l2->val) { pRes->next = l1; l1 = l1->next; } else { pRes->next = l2; l2 = l2->next; } pRes = pRes->next; } return head; } /* *删除链表倒数第n个节点,并返回链表头节点 */ listNode * removeNthFromEnd(listNode*headi,int n) { listNode *fast = head; listNode *prev = NULL; listNpde *next = head; int k = n; /*快指针往后移动k-1*/ while((k > 1) && (fast != NULL)) { fast = fast->next; k--; } /*说明链表数目不足n个*/ if (fast == NULL) { return head; } while (fast->next != NULL) { fast = fast->next; prev = next; next = next->next; } if(prev == NULL) { head = head->next; } else { prev->next = prev->next->next; } return head; } /*求链表的中间节点*/ listNode *middleNode(listNode *head) { listNode * fast = head; listNode * low = head; while(fast != NULL && fast->next != NULL) { low = low->next; fast = fast->next->next; } return low; } ================================================ FILE: c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.cpp ================================================ /** * 1)顺序栈的操作:入栈和出栈; * 2)采用模板的方法实现存储任意类型的数据 * 3)采用数组的栈,支持动态扩容,每次扩容1.5 倍,初始栈的大小是 10 。 * * Author:caozx * time ;2018年10月11日 */ #include #include "StackBasedOnArray.h" using namespace std; //构造函数,创建栈 //类模板成员函数的写法 template 返回值类型 类名::成员函数名(参数列表){} template ArrayStack::ArrayStack() { this -> count = 10; this -> flag = 0; this -> array = new T[this -> count]; if (! this -> array){ cout << "array malloc memory failure" << endl; } } //有参构造函数,创建栈 template ArrayStack::ArrayStack(int count) { this -> count = count; this -> flag = 0; this -> array = new T[this -> count]; if (! this -> array){ cout << "array malloc memory failure" << endl; } } //析构函数,销毁栈 template ArrayStack::~ArrayStack(){ this -> count = 0; this -> flag = 0; if(this -> array){ delete [] this -> array; this -> array = NULL; } } // 入栈 template void ArrayStack::push(T data){ if(this -> flag == this -> count){ cout << "The stack is full , so need to enlarge 1.5x! "<< endl; this -> count = int (1.5 * this -> count); T * temp = new T [this -> count]; for(int i = 0; i < this -> flag ; i++){ temp[i] = this -> array[i]; //cout << temp[i] < array; //释放原来的空间 temp[this -> flag] = data; this -> flag ++; this -> array = temp; } else{ this -> array [this -> flag] = data; this -> flag ++ ; } } //出栈,并删除栈顶元素 template T ArrayStack::pop(){ this -> flag --; T temp = this -> array[this -> flag]; return temp; } //出栈,不删除栈顶元素 template T ArrayStack::peek(){ T temp = this -> array[this -> flag - 1]; return temp; } template int ArrayStack::stackSize(){ return this -> flag; } template int ArrayStack::stackMaxSize(){ return this -> count; } int main(int argc, char const *argv[]) { cout << " === test begin ===" << endl; ArrayStack arrstack(12); arrstack.push(10); arrstack.push(20); arrstack.push(30); arrstack.push(40); arrstack.push(50); arrstack.push(60); arrstack.push(70); arrstack.push(80); arrstack.push(90); arrstack.push(100); arrstack.push(110); arrstack.push(120); arrstack.push(130); arrstack.push(140); arrstack.push(150); cout << "peek , not delete " << arrstack.peek() << endl; cout << "pop , delete " << arrstack.pop()< class 类名{} template class ArrayStack { public: ArrayStack(); ArrayStack(int count); ~ArrayStack(); void push(T data); //入栈 T pop(); //出栈,并删除栈顶元素 T peek(); //返回栈顶元素,不删除栈顶元素,栈顶指针不变 int stackSize(); int stackMaxSize(); private: int flag; //栈顶标签,指向栈顶 int count ; //栈的容量 T *array; //指针 }; ================================================ FILE: c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.cpp ================================================ /** * 1)链式栈的操作:入栈,出栈以及返回栈的大小; * 2)采用模板的方法实现存储任意类型的数据 * 3)采用单链表实现栈 * 4)pop和peek 出栈的返回值稍微有点问题,当栈为空的时候,返回null,cpp默认返回的是0。 * * 改进方法就是不用函数的返回值返回栈顶元素,而是采用参数列表的形式返回,这样稍微有点麻烦 * * 或者就是在使用的时候先调用size函数判断以下 * Author:caozx * time ;2018年10月11日 */ #include #include "StackBasedOnLinkedList.h" using namespace std; template LinkedListStack::LinkedListStack() { this -> count = 0; this -> head = new LinkedNode; this -> head -> next = NULL; } template LinkedListStack::~LinkedListStack() { LinkedNode * ptr, * temp; ptr = head; while(ptr -> next != NULL){ temp = ptr -> next; ptr -> next = temp -> next; delete temp; } delete head ; //删除头节点 this -> head = NULL; this -> count = 0; } // 入栈 template void LinkedListStack::push(const T & data) { LinkedNode * insertPtr = new LinkedNode; insertPtr -> data = data; insertPtr -> next = this -> head -> next; head -> next = insertPtr; this -> count ++; cout << "push data : " << this -> head -> next -> data << endl; } //返回栈顶元素,即出栈,不删除栈顶元素 template T LinkedListStack::peek() { if(this -> count == 0 || this -> head -> next == NULL){ cout << " stack is empty, peek fail"<< endl; return NULL; } else{ return this -> head -> next -> data; } } //出栈,删除栈顶元素 template T LinkedListStack::pop() { if(this -> count == 0 || this -> head -> next == NULL){ cout << " stack is empty, pop fail"<< endl; return NULL; } else{ LinkedNode * temp = this -> head -> next; this -> head -> next = temp -> next; T data = temp -> data; delete temp; this -> count --; return data; } } //返回栈的大小 template int LinkedListStack::size() const { return this -> count; } int main(int argc, char const *argv[]) { cout << " === StackBasedOnLinkedList test begin ===" << endl; LinkedListStack stack; cout << "size==="< class LinkedListStack { public: LinkedListStack(); ~LinkedListStack(); void push(const T & data); //入栈 T peek(); //返回栈顶元素,即出栈,不删除栈顶元素 T pop(); //出栈,删除栈顶元素 int size() const; //返回栈的大小 private: int count; //存放栈的大小,因为是单链表所以这里不规定栈的最大可承载量 struct LinkedNode { T data; LinkedNode * next; }; LinkedNode * head; // 单链表的头指针,不带头节点 }; ================================================ FILE: c-cpp/08_stack/arrayStack/arrayStack.c ================================================ /************************************************************************* > File Name: arrayStack.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: 数组实现顺序栈 ************************************************************************/ #include #include #include #include"./arrayStack.h" /*创建并初始化顺序栈*/ stArrayStack * arrayStack_create(int size) { stArrayStack *parrStack = NULL; parrStack = (stArrayStack *)malloc(sizeof(stArrayStack)); if (parrStack == NULL) { return NULL; } parrStack->size = size; parrStack->pos = -1; parrStack->array = (int *)malloc(sizeof(int)*size); if(parrStack->array == NULL) { free(parrStack); return NULL; } return parrStack; } /*销毁顺序栈*/ void arrayStack_destory(stArrayStack * parrStack) { if(parrStack == NULL) { return; } if (parrStack->array != NULL) { free(parrStack->array); } free(parrStack); return; } /*出栈*/ int arrayStack_pop(stArrayStack *parrStack) { int data = 0; if(arrayStack_is_empty(parrStack)) { return -1; } data = parrStack->array[parrStack->pos]; parrStack->pos--; return data; } /*入栈*/ int arrayStack_push(stArrayStack *parrStack,int data) { if(arrayStack_is_full(parrStack)) { return -1; } parrStack->pos++; parrStack->array[parrStack->pos] = data; return 0; } int arrayStack_push_new(stArrayStack*parrStack,int data) { int *ptmp = NULL; /*如果栈不满,直接插入*/ if(!arrayStack_is_full(parrStack)) { return arrayStack_push(parrStack,data); } /*如果栈已经满,申请内存*/ ptmp = (int *)malloc(2*parrStack->size*sizeof(int)); if (ptmp == NULL) { return -1; } memcpy(ptmp,parrStack->array,parrStack->size*sizeof(int)); free(parrStack->array); parrStack->array = ptmp; parrStack->size = 2*parrStack->size; parrStack->pos++; parrStack->array[parrStack->pos] = data; return ; } void arrayStack_dump(stArrayStack *parrStack) { int i = 0; if (arrayStack_is_empty(parrStack)) { printf("\r\n arrayStack is empty."); return; } printf("\r\narrayStack size = %d,pos= %d,", parrStack->size,parrStack->pos); for(i = 0; i <= parrStack->pos; i++) { printf("\r\narry[%d] = %d",i,parrStack->array[i]); } } int main() { int i = 0; int ret = 0; stArrayStack * parrStack = NULL; printf("\r\n create size = 4 arrayStack."); parrStack = arrayStack_create(4); if (parrStack == NULL) { printf("\r\n create size = 4 arrayStack faided."); return 0; } for (i = 0; i < 5; i++) { ret = arrayStack_push(parrStack,i); if(ret != 0) { printf("\r\n push size = %d arrayStack faided.",i); } } arrayStack_dump(parrStack); ret = arrayStack_push_new(parrStack,4); if(ret != 0) { printf("\r\n push size = %d arrayStack faided.",4); } arrayStack_dump(parrStack); arrayStack_destory(parrStack); return; } ================================================ FILE: c-cpp/08_stack/arrayStack/arrayStack.h ================================================ /************************************************************************* > File Name: arrayStack.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: ************************************************************************/ #ifndef ARRAY_STACJ_H #define ARRAY_STACJ_H typedef struct _array_stack { int size;/*栈的大小*/ int pos;/*当前存储元素的个数,即栈顶元素下表*/ int *array;/*数据存储区*/ }stArrayStack; #define arrayStack_size(arrayStack) (arrayStack->size) #define arrayStack_is_empty(arrayStack) (arrayStack->pos == -1) #define arrayStack_is_full(arrayStack) (arrayStack->pos == (arrayStack->size-1)) #endif ================================================ FILE: c-cpp/08_stack/linkList/linklist_stack.c ================================================ /************************************************************************* > File Name: linklist_stack.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: ************************************************************************/ #include #include #include #include "./linklist_stack.h" linklist_stack * stack_create() { linklist_stack * stack = NULL; stack = (linklist_stack *)malloc(sizeof(linklist_stack)); if (stack == NULL) { return NULL; } stack->next = NULL; return stack; } void stack_destory(linklist_stack* stack) { linklist_stack * ptmp = NULL; while(!stack_is_empty(stack)) { ptmp = stack->next; stack->next = stack->next->next; free(ptmp); } free(stack); return; } int stack_push(linklist_stack *stack,int data) { linklist_stack * ptmp = NULL; ptmp = (linklist_stack *)malloc(sizeof(linklist_stack)); if (ptmp == NULL) { return -1; } ptmp->data = data; ptmp->next = stack->next; stack->next = ptmp; return 0; } int stack_pop(linklist_stack*stack,int *data) { linklist_stack *ptmp = NULL; if (data == NULL) { return -1; } if(stack_is_empty(stack)) { return -1; } *data = stack->next->data; ptmp = stack->next; stack->next = ptmp->next; free(ptmp); return 0; } void stack_dump(linklist_stack *stack) { linklist_stack * ptmp = stack->next; while(ptmp != NULL) { printf("\r\n data = %d",ptmp->data); ptmp = ptmp->next; } return; } int main() { int i = 0; int ret = 0; int data = 0; linklist_stack * stack = NULL; stack = stack_create(); if (stack == NULL) { printf("\r\n stack create falied."); return 0; } for (i = 0; i < 4; i++) { ret = stack_push(stack,i); if(ret != 0) { printf("\r\n stack push %d falied.",i); } } stack_dump(stack); for (i = 0; i < 5; i++) { ret = stack_pop(stack,&data); if(ret != 0) { printf("\r\n stack pop%d falied.", i); } else { printf("\r\n data = %d,",data); } } stack_destory(stack); return 0; } ================================================ FILE: c-cpp/08_stack/linkList/linklist_stack.h ================================================ /************************************************************************* > File Name: linklist_stack.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: ************************************************************************/ #ifndef STACK_LINK_LIST_H #define STACK_LINK_LIST_H typedef struct _linkliststack { int data; struct _linkliststack *next; }linklist_stack; #define stack_is_empty(liststack) (liststack->next == NULL) #endif ================================================ FILE: c-cpp/08_stack/linked_list.h ================================================ /** * C++ 版本单链表结点 * * Author: Liam Huang (Liam0205) */ #ifndef STACK_LINKED_LIST_H_ #define STACK_LINKED_LIST_H_ #include template struct Node { using ptr_t = std::shared_ptr>; T data; ptr_t next; Node(T data_) : data(data_), next(nullptr) {} Node() : next(nullptr) {} }; #endif ================================================ FILE: c-cpp/08_stack/stack_based_on_linked_list.hpp ================================================ /** * 基于链表实现的栈。 * * Author: Liam Huang (Liam0205) */ #ifndef STACK_STACK_BASED_ON_LINKED_LIST_HPP_ #define STACK_STACK_BASED_ON_LINKED_LIST_HPP_ #include #include "linked_list.h" template class Stack { public: using value_type = T; using node_type = typename Node::ptr_t; private: node_type top_ = nullptr; public: bool empty(void) const { return nullptr == top_; } void push(const value_type& value) { auto node = std::make_shared(value); if (this->empty()) { top_ = node; } else { node->next = top_; top_ = node; } } value_type top(void) const { if (not this->empty()) { return top_->data; } else { throw "Fetch data from empty stack!"; } } void pop(void) { if (not this->empty()) { top_ = top_->next; return; } else { throw "Pop from empty stack!"; } } }; #endif ================================================ FILE: c-cpp/09_queue/.gitkeep ================================================ ================================================ FILE: c-cpp/09_queue/array_queue/array_queue.c ================================================ /************************************************************************* > File Name: array_queue.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: ************************************************************************/ #include #include #include #include"./array_queue.h" array_queue * array_queue_create(int size) { array_queue * queue = NULL; queue = (array_queue*)malloc(sizeof(array_queue)); if (queue == NULL) { return NULL; } queue->array = (int *)malloc(sizeof(int)*size); if (queue->array == NULL) { free(queue); return NULL; } queue->size = size; queue->num = 0; queue->head = 0; queue->tail = 0; return queue; } void array_queue_destory(array_queue *queue) { if (queue == NULL) { return; } if (queue->array != NULL) { free(queue->array); } free(queue); return; } /*入队列 */ int array_queue_enqueue(array_queue *queue,int data) { /*队列为空,或者队列满时,返回-1*/ if ((queue == NULL) || (array_queue_is_full(queue))) { return -1; } queue->num++; queue->array[queue->tail] = data; queue->tail = (queue->tail + 1) % queue->size; return 0; } /*出队列*/ int array_queue_dequeue(array_queue * queue,int *data) { /*队列为空,数据存储为空,队列为空时返回-1*/ if ((queue == NULL) || (data == NULL) || (array_queue_is_empty(queue))) { return -1; } *data = queue->array[queue->head]; queue->num--; queue->head = (queue->head + 1) % queue->size; return 0; } void array_queue_dump(array_queue *queue) { int i = 0; int pos = 0; if ((queue == NULL) || (array_queue_is_empty(queue))) { printf("\r\n queue is empty"); return; } printf("\r\n size:%d,num:%d,head:%d,tali:%d", queue->size,queue->num,queue->head,queue->tail); for (i = 0; i < queue->num; i ++) { pos = (queue->head + i) %queue->size; printf("\r\n array[%d] = %d",pos,queue->array[pos]); } return; } int main() { int i = 0; int ret = 0; int data = 0; array_queue * queue = NULL; queue = array_queue_create(4); if (queue == NULL) { printf("\r\n queue is create failed."); return 0; } /*队列时空时,出队返回错误*/ ret = array_queue_dequeue(queue, &data); if (ret != 0) { printf("\r\n queue %d dequeue failed.",ret); } /*队列大小是4,入队5个,最后一个报错*/ for (i = 0; i < 5; i++) { ret = array_queue_enqueue(queue,i); if (ret != 0) { printf("\r\n queue %d enqueue failed.",i); } } array_queue_dump(queue); ret = array_queue_dequeue(queue, &data); if (ret != 0) { printf("\r\n queue %d dequeue failed.",i); } printf("\r\n queue %d dequue.",data); array_queue_dump(queue); data = 5; printf("\r\n queue %d enqueue.",data); ret = array_queue_enqueue(queue,data); if (ret != 0) { printf("\r\n queue %d enqueue failed.",data); } array_queue_dump(queue); array_queue_destory(queue); return 0; } ================================================ FILE: c-cpp/09_queue/array_queue/array_queue.h ================================================ /************************************************************************* > File Name: array_queue.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-12 > Desc: ************************************************************************/ #ifndef ARRAY_QUEUE_H #define ARRAY_QUEUE_H typedef struct _array_queue { int size;/*队列的大小*/ int num; /*当前存储数据的大小*/ int head;/*队列的头*/ int tail;/*队列的尾*/ int *array;/*数据存储区*/ }array_queue; #define array_queue_is_empty(array_queue) (array_queue->num == 0) #define array_queue_is_full(array_queue) ((array_queue->num) == (array_queue->size)) #endif ================================================ FILE: c-cpp/09_queue/array_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/10. */ #ifndef QUEUE_ARRAY_QUEUE_HPP_ #define QUEUE_ARRAY_QUEUE_HPP_ template class ArrayQueue { private: T* items_ = nullptr; size_t capacity_ = 0; size_t head_ = 0; size_t tail_ = 0; public: ArrayQueue() = delete; ArrayQueue(const size_t capacity) : capacity_(capacity) { items_ = new T[capacity_]; } ~ArrayQueue() { if (nullptr != items_) { delete[] items_; items_ = nullptr; } } ArrayQueue(const ArrayQueue& other) : capacity_(other.capacity_) { items_ = new T[capacity_]; for (size_t i = other.head_; i != other.tail_; ++i) { enqueue(other.items_[i]); } } ArrayQueue& operator=(const ArrayQueue& rhs) { delete[] items_; head_ = 0; tail_ = 0; capacity_ = rhs.capacity_; items_ = new T[capacity_]; for (size_t i = rhs.head_; i != rhs.tail_; ++i) { enqueue(rhs.items_[i]); } return *this; } ArrayQueue(ArrayQueue&& other) : items_(other.items_), capacity_(other.capacity_), head_(other.head_), tail_(other.tail_) { other.items_ = nullptr; other.capacity_ = 0; other.head_ = 0; other.tail_ = 0; } ArrayQueue& operator=(ArrayQueue&& rhs) { delete[] items_; items_ = rhs.items_; capacity_ = rhs.capacity_; head_ = rhs.head_; tail_ = rhs.tail_; rhs.items_ = nullptr; rhs.capacity_ = 0; rhs.head_ = 0; rhs.tail_ = 0; return *this; } public: void enqueue(T item) { if (capacity_ == tail_) { throw "Push data into a full queue!"; } items_[tail_++] = item; } T head() const { if (head_ != tail_) { return items_[head_]; } else { throw "Fetch data from an empty queue!"; } } void dequeue() { if (head_ != tail_) { ++head_; } else { throw "Pop data from an empty queue!"; } } public: template void traverse(UnaryFunc do_traverse) { for (size_t i = head_; i != tail_; ++i) { do_traverse(items_[i]); } } }; #endif // QUEUE_ARRAY_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/array_queue_test.cc ================================================ #include #include "array_queue.hpp" int main() { auto do_traverse = [&](auto item){ std::cout << item << ' '; }; ArrayQueue array_queue_1(3); array_queue_1.enqueue(1); array_queue_1.enqueue(2); array_queue_1.enqueue(3); // array_queue_1.enqueue(4); // throw array_queue_1.traverse(do_traverse); std::cout << std::endl; ArrayQueue array_queue_2(array_queue_1); // copy constructor array_queue_2.traverse(do_traverse); std::cout << std::endl; ArrayQueue array_queue_3(std::move(array_queue_2)); // move constructor array_queue_3.traverse(do_traverse); std::cout << std::endl; array_queue_2.traverse(do_traverse); std::cout << std::endl; std::cout << array_queue_3.head() << std::endl; array_queue_3.dequeue(); std::cout << array_queue_3.head() << std::endl; array_queue_3.dequeue(); std::cout << array_queue_3.head() << std::endl; array_queue_3.dequeue(); // std::cout << array_queue_3.head() << std::endl; // throw // array_queue_3.dequeue(); // throw ArrayQueue array_queue_4(1); array_queue_4 = array_queue_1; // copy assignment array_queue_4.traverse(do_traverse); std::cout << std::endl; ArrayQueue array_queue_5(100); array_queue_5 = std::move(array_queue_4); // move assignment array_queue_5.traverse(do_traverse); std::cout << std::endl; array_queue_4.traverse(do_traverse); std::cout << std::endl; std::cout << array_queue_5.head() << std::endl; array_queue_5.dequeue(); std::cout << array_queue_5.head() << std::endl; array_queue_5.dequeue(); std::cout << array_queue_5.head() << std::endl; array_queue_5.dequeue(); // std::cout << array_queue_5.head() << std::endl; // throw // array_queue_5.dequeue(); // throw return 0; } ================================================ FILE: c-cpp/09_queue/block_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/11. */ #ifndef QUEUE_BLOCK_QUEUE_HPP_ #define QUEUE_BLOCK_QUEUE_HPP_ #include #include #include template class BlockQueue { public: using value_type = T; using container_type = std::queue; using size_type = typename container_type::size_type; private: size_type capacity_ = 0; container_type container_; mutable std::mutex mutex_; mutable std::condition_variable not_empty_; mutable std::condition_variable not_full_; public: BlockQueue() = delete; BlockQueue(const size_type capacity) : capacity_(capacity) {} BlockQueue(const BlockQueue&) = default; BlockQueue(BlockQueue&&) = default; BlockQueue& operator=(const BlockQueue&) = default; BlockQueue& operator=(BlockQueue&&) = default; private: bool empty() const { return container_.empty(); } bool full() const { return not(container_.size() < capacity_); } public: void put(const value_type& item) { std::unqiue_lock lock(mutex_); while (full()) { not_full_.wait(lock); } container_.push(item); not_empty_.notify_one(); } void take(value_type& out) { std::unique_lock lock(mutex_); while (empty()) { not_empty_.wait(lock); } out = container_.front(); container_.pop(); not_full_.notify_one(); } template bool put_for(const value_type& item, const Duration& d) { std::unqiue_lock lock(mutex_); if (not_full_.wait_for(lock, d, [&](){ return not full(); })) { container_.push(item); not_empty_.notify_one(); return true; } else { return false; } } template bool take_for(const Duration& d, value_type& out) { std::unique_lock lock(mutex_); if (not_empty_.wait_for(lock, d, [&](){ return not empty(); })) { out = container_.front(); container_.pop(); not_full_.notify_one(); return true; } else { return false; } } }; #endif // QUEUE_BLOCK_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/circular_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/10. */ #ifndef QUEUE_CIRCULAR_QUEUE_HPP_ #define QUEUE_CIRCULAR_QUEUE_HPP_ template class CircularQueue { private: T* items_ = nullptr; size_t capacity_ = 0; size_t head_ = 0; size_t tail_ = 0; public: CircularQueue() = delete; CircularQueue(const size_t capacity) : capacity_(capacity) { items_ = new T[capacity_]; } ~CircularQueue() { if (nullptr != items_) { delete[] items_; items_ = nullptr; } } CircularQueue(const CircularQueue& other) : capacity_(other.capacity_) { items_ = new T[capacity_]; for (size_t i = other.head_; i != other.tail_; ++i) { enqueue(other.items_[i]); } } CircularQueue& operator=(const CircularQueue& rhs) { delete[] items_; head_ = 0; tail_ = 0; capacity_ = rhs.capacity_; items_ = new T[capacity_]; for (size_t i = rhs.head_; i != rhs.tail_; ++i) { enqueue(rhs.items_[i]); } return *this; } CircularQueue(CircularQueue&& other) : items_(other.items_), capacity_(other.capacity_), head_(other.head_), tail_(other.tail_) { other.items_ = nullptr; other.capacity_ = 0; other.head_ = 0; other.tail_ = 0; } CircularQueue& operator=(CircularQueue&& rhs) { delete[] items_; items_ = rhs.items_; capacity_ = rhs.capacity_; head_ = rhs.head_; tail_ = rhs.tail_; rhs.items_ = nullptr; rhs.capacity_ = 0; rhs.head_ = 0; rhs.tail_ = 0; return *this; } public: void enqueue(T item) { if ((tail_ + 1) % capacity_ == head_) { throw "Push data into a full queue!"; } items_[tail_] = item; tail_ = (tail_ + 1) % capacity_; } T head() const { if (head_ != tail_) { return items_[head_]; } else { throw "Fetch data from an empty queue!"; } } void dequeue() { if (head_ != tail_) { head_ = (head_ + 1) % capacity_; } else { throw "Pop data from an empty queue!"; } } public: template void traverse(UnaryFunc do_traverse) { if (0 == capacity_) return; for (size_t i = head_; i % capacity_ != tail_; ++i) { do_traverse(items_[i % capacity_]); } } }; #endif // QUEUE_CIRCULAR_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/circular_queue_test.cc ================================================ #include #include "circular_queue.hpp" int main() { auto do_traverse = [&](auto item){ std::cout << item << ' '; }; CircularQueue circular_queue_1(4); circular_queue_1.enqueue(1); circular_queue_1.enqueue(2); circular_queue_1.enqueue(3); // circular_queue_1.enqueue(4); // throw circular_queue_1.traverse(do_traverse); std::cout << std::endl; CircularQueue circular_queue_2(circular_queue_1); // copy constructor circular_queue_2.traverse(do_traverse); std::cout << std::endl; CircularQueue circular_queue_3(std::move(circular_queue_2)); // move constructor circular_queue_3.traverse(do_traverse); std::cout << std::endl; circular_queue_2.traverse(do_traverse); std::cout << std::endl; std::cout << circular_queue_3.head() << std::endl; circular_queue_3.dequeue(); std::cout << circular_queue_3.head() << std::endl; circular_queue_3.dequeue(); std::cout << circular_queue_3.head() << std::endl; circular_queue_3.dequeue(); // std::cout << circular_queue_3.head() << std::endl; // throw // circular_queue_3.dequeue(); // throw CircularQueue circular_queue_4(1); circular_queue_4 = circular_queue_1; // copy assignment circular_queue_4.traverse(do_traverse); std::cout << std::endl; CircularQueue circular_queue_5(100); circular_queue_5 = std::move(circular_queue_4); // move assignment circular_queue_5.traverse(do_traverse); std::cout << std::endl; circular_queue_4.traverse(do_traverse); std::cout << std::endl; std::cout << circular_queue_5.head() << std::endl; circular_queue_5.dequeue(); std::cout << circular_queue_5.head() << std::endl; circular_queue_5.dequeue(); std::cout << circular_queue_5.head() << std::endl; circular_queue_5.dequeue(); // std::cout << circular_queue_5.head() << std::endl; // throw // circular_queue_5.dequeue(); // throw for (size_t i = 0; i != 4; ++i) { circular_queue_1.dequeue(); circular_queue_1.enqueue(i + 4); circular_queue_1.traverse(do_traverse); std::cout << std::endl; } return 0; } ================================================ FILE: c-cpp/09_queue/concurrency_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/11. */ #ifndef QUEUE_CONCURRENCY_QUEUE_HPP_ #define QUEUE_CONCURRENCY_QUEUE_HPP_ #include #include #include #include template class ConcurrencyQueue { public: using value_type = T; using container_type = std::queue; using size_type = typename container_type::size_type; private: container_type container_; mutable std::mutex mutex_; std::condition_variable container_cond_; public: ConcurrencyQueue() = default; ConcurrencyQueue(const ConcurrencyQueue&) = default; ConcurrencyQueue(ConcurrencyQueue&&) = default; ConcurrencyQueue& operator=(const ConcurrencyQueue&) = default; ConcurrencyQueue& operator=(ConcurrencyQueue&&) = default; private: bool empty_() const { return container_.empty(); } public: bool empty() const { std::lock_guard lg(mutex_); return container_.empty(); } void push(value_type item) { std::lock_guard lg(mutex_); container_.push(std::move(item)); container_cond_.notify_one(); } void wait_and_pop(value_type& out) { std::unique_lock lk(mutex_); while (empty_()) { container_cond_.wait(lk) } out = std::move(container_.front()); container_.pop(); } std::shared_ptr wait_and_pop() { std::unique_lock lk(mutex_); while (empty_()) { container_cond_.wait(lk) } auto res = std::make_shared(std::move(container_.front())); container_.pop(); return res; } bool try_pop(value_type& out) { std::lock_guard lg(mutex_); if (empty_()) { return false; } else { out = std::move(container_.front()); container_.pop(); return true; } } std::shared_ptr try_pop() { std::lock_guard lg(mutex_); if (empty_()) { return nullptr; } else { auto res = std::make_shared(std::move(container_.front())); container_.pop(); return res; } } }; #endif // QUEUE_CONCURRENCY_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/dynamic_array_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/10. */ #ifndef QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ #define QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ template class DynamicArrayQueue { private: T* items_ = nullptr; size_t capacity_ = 0; size_t head_ = 0; size_t tail_ = 0; public: DynamicArrayQueue() = delete; DynamicArrayQueue(const size_t capacity) : capacity_(capacity) { items_ = new T[capacity_]; } ~DynamicArrayQueue() { if (nullptr != items_) { delete[] items_; items_ = nullptr; } } DynamicArrayQueue(const DynamicArrayQueue& other) : capacity_(other.capacity_) { items_ = new T[capacity_]; for (size_t i = other.head_; i != other.tail_; ++i) { enqueue(other.items_[i]); } } DynamicArrayQueue& operator=(const DynamicArrayQueue& rhs) { delete[] items_; head_ = 0; tail_ = 0; capacity_ = rhs.capacity_; items_ = new T[capacity_]; for (size_t i = rhs.head_; i != rhs.tail_; ++i) { enqueue(rhs.items_[i]); } return *this; } DynamicArrayQueue(DynamicArrayQueue&& other) : items_(other.items_), capacity_(other.capacity_), head_(other.head_), tail_(other.tail_) { other.items_ = nullptr; other.capacity_ = 0; other.head_ = 0; other.tail_ = 0; } DynamicArrayQueue& operator=(DynamicArrayQueue&& rhs) { delete[] items_; items_ = rhs.items_; capacity_ = rhs.capacity_; head_ = rhs.head_; tail_ = rhs.tail_; rhs.items_ = nullptr; rhs.capacity_ = 0; rhs.head_ = 0; rhs.tail_ = 0; return *this; } public: void enqueue(T item) { if (capacity_ == tail_ - head_) { throw "Push data into a full queue!"; } if (capacity_ == tail_) { // item transport for (size_t i = head_; i != tail_; ++i) { items_[i - head_] = items_[i]; } tail_ = tail_ - head_; head_ = 0; } items_[tail_++] = item; } T head() const { if (head_ != tail_) { return items_[head_]; } else { throw "Fetch data from an empty queue!"; } } void dequeue() { if (head_ != tail_) { ++head_; } else { throw "Pop data from an empty queue!"; } } public: template void traverse(UnaryFunc do_traverse) { for (size_t i = head_; i != tail_; ++i) { do_traverse(items_[i]); } } }; #endif // QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/dynamic_array_queue_test.cc ================================================ #include #include "dynamic_array_queue.hpp" int main() { auto do_traverse = [&](auto item){ std::cout << item << ' '; }; DynamicArrayQueue dynamic_array_queue_1(3); dynamic_array_queue_1.enqueue(1); dynamic_array_queue_1.enqueue(2); dynamic_array_queue_1.enqueue(3); // dynamic_array_queue_1.enqueue(4); // throw dynamic_array_queue_1.traverse(do_traverse); std::cout << std::endl; DynamicArrayQueue dynamic_array_queue_2(dynamic_array_queue_1); // copy constructor dynamic_array_queue_2.traverse(do_traverse); std::cout << std::endl; DynamicArrayQueue dynamic_array_queue_3(std::move(dynamic_array_queue_2)); // move constructor dynamic_array_queue_3.traverse(do_traverse); std::cout << std::endl; dynamic_array_queue_2.traverse(do_traverse); std::cout << std::endl; std::cout << dynamic_array_queue_3.head() << std::endl; dynamic_array_queue_3.dequeue(); std::cout << dynamic_array_queue_3.head() << std::endl; dynamic_array_queue_3.dequeue(); std::cout << dynamic_array_queue_3.head() << std::endl; dynamic_array_queue_3.dequeue(); // std::cout << dynamic_array_queue_3.head() << std::endl; // throw // dynamic_array_queue_3.dequeue(); // throw DynamicArrayQueue dynamic_array_queue_4(1); dynamic_array_queue_4 = dynamic_array_queue_1; // copy assignment dynamic_array_queue_4.traverse(do_traverse); std::cout << std::endl; DynamicArrayQueue dynamic_array_queue_5(100); dynamic_array_queue_5 = std::move(dynamic_array_queue_4); // move assignment dynamic_array_queue_5.traverse(do_traverse); std::cout << std::endl; dynamic_array_queue_4.traverse(do_traverse); std::cout << std::endl; std::cout << dynamic_array_queue_5.head() << std::endl; dynamic_array_queue_5.dequeue(); std::cout << dynamic_array_queue_5.head() << std::endl; dynamic_array_queue_5.dequeue(); std::cout << dynamic_array_queue_5.head() << std::endl; dynamic_array_queue_5.dequeue(); // std::cout << dynamic_array_queue_5.head() << std::endl; // throw // dynamic_array_queue_5.dequeue(); // throw for (size_t i = 0; i != 3; ++i) { dynamic_array_queue_1.dequeue(); dynamic_array_queue_1.enqueue(i + 4); dynamic_array_queue_1.traverse(do_traverse); std::cout << std::endl; } return 0; } ================================================ FILE: c-cpp/09_queue/linked_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/10. */ #ifndef QUEUE_LINKED_QUEUE_HPP_ #define QUEUE_LINKED_QUEUE_HPP_ #include template struct Node { using ptr_t = std::shared_ptr>; T data; ptr_t next; Node(T data_) : data(data_), next(nullptr) {} Node() : next(nullptr) {} }; template class LinkedQueue { public: using node_type = Node; using node_ptr_t = typename node_type::ptr_t; private: node_ptr_t head_ = nullptr; node_ptr_t before_tail_ = nullptr; public: LinkedQueue() = default; ~LinkedQueue() = default; LinkedQueue(const LinkedQueue& other) = default; LinkedQueue& operator=(const LinkedQueue& rhs) = default; LinkedQueue(LinkedQueue&& other) = default; LinkedQueue& operator=(LinkedQueue&& rhs) = default; public: void enqueue(T item) { if (nullptr == head_) { head_ = std::make_shared(item); before_tail_ = head_; } else { before_tail_->next = std::make_shared(item); before_tail_ = before_tail_->next; } } T head() const { if (nullptr != head_) { return head_->data; } else { throw "Fetch data from an empty queue!"; } } void dequeue() { if (nullptr != head_) { head_ = head_->next; if (nullptr == head_) { before_tail_ = nullptr; } } else { throw "Pop data from an empty queue!"; } } public: template void traverse(UnaryFunc do_traverse) { for (node_ptr_t work = head_; nullptr != work; work = work->next) { do_traverse(work->data); } } }; #endif // QUEUE_LINKED_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/linked_queue_test.cc ================================================ #include #include "linked_queue.hpp" int main() { auto do_traverse = [&](auto item){ std::cout << item << ' '; }; LinkedQueue linked_queue_1; linked_queue_1.enqueue(1); linked_queue_1.enqueue(2); linked_queue_1.enqueue(3); linked_queue_1.traverse(do_traverse); std::cout << std::endl; LinkedQueue linked_queue_2(linked_queue_1); // copy constructor linked_queue_2.traverse(do_traverse); std::cout << std::endl; LinkedQueue linked_queue_3(std::move(linked_queue_2)); // move constructor linked_queue_3.traverse(do_traverse); std::cout << std::endl; linked_queue_2.traverse(do_traverse); std::cout << std::endl; std::cout << linked_queue_3.head() << std::endl; linked_queue_3.dequeue(); std::cout << linked_queue_3.head() << std::endl; linked_queue_3.dequeue(); std::cout << linked_queue_3.head() << std::endl; linked_queue_3.dequeue(); // std::cout << linked_queue_3.head() << std::endl; // throw // linked_queue_3.dequeue(); // throw LinkedQueue linked_queue_4; linked_queue_4 = linked_queue_1; // copy assignment linked_queue_4.traverse(do_traverse); std::cout << std::endl; LinkedQueue linked_queue_5; linked_queue_5 = std::move(linked_queue_4); // move assignment linked_queue_5.traverse(do_traverse); std::cout << std::endl; linked_queue_4.traverse(do_traverse); std::cout << std::endl; std::cout << linked_queue_5.head() << std::endl; linked_queue_5.dequeue(); std::cout << linked_queue_5.head() << std::endl; linked_queue_5.dequeue(); std::cout << linked_queue_5.head() << std::endl; linked_queue_5.dequeue(); // std::cout << linked_queue_5.head() << std::endl; // throw // linked_queue_5.dequeue(); // throw return 0; } ================================================ FILE: c-cpp/09_queue/list_queue/list_queue.c ================================================ /************************************************************************* > File Name: list_queue.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-13 > Desc: ************************************************************************/ #include #include #include #include"./list_queue.h" /*创建队列头*/ list_queue *list_queue_create() { list_queue * queue = NULL; queue = (list_queue *)malloc(sizeof(list_queue)); if(queue == NULL) { return NULL; } queue->num = 0; queue->head = NULL; queue->tail = NULL; return queue; } void list_queue_destroy(list_queue*queue) { int i = 0; int data = 0; if ((queue == NULL) || (list_queue_is_empty(queue))) { return ; } while(!list_queue_is_empty(queue)) { (void)list_queue_dequeue(queue,&data); } free(queue); return; } int list_queue_enqueue(list_queue *queue,int data) { queue_node *ptmp = NULL; if(queue == NULL) { return -1; } ptmp = (queue_node *)malloc(sizeof(queue_node)); if (ptmp == NULL) { return -1; } ptmp->data = data; ptmp->next = NULL; if (queue->head == NULL) { queue->head = ptmp; } else { queue->tail->next = ptmp; } queue->tail = ptmp; queue->num++; return 0; } /*出队*/ int list_queue_dequeue(list_queue *queue,int *data) { queue_node * ptmp = NULL; if ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue)) { return -1; } *data = queue->head->data; ptmp = queue->head; queue->head = queue->head->next; queue->num--; if (queue->head == NULL) { queue->tail = NULL; } free(ptmp); return 0; } void list_queue_dump(list_queue*queue) { int i = 0; queue_node *ptmp = NULL; if ((queue == NULL) || (list_queue_is_empty(queue))) { return; } ptmp = queue->head; printf("\r\n----dump queue num = %d--------",queue->num); while(ptmp != NULL) { printf("\r\nnode[%d] = %d",i,ptmp->data); i++; ptmp = ptmp->next; } printf("\r\n---------------------------------\r\n"); return; } int main() { int i = 0; int data = 0; int ret = 0; list_queue * queue; queue = list_queue_create(); if (queue == NULL) { printf("\r\nlist queue create falied.."); return 0; } for (i = 0; i < 5; i++) { (void)list_queue_enqueue(queue,i); } list_queue_dump(queue); ret = list_queue_dequeue(queue,&data); if(ret != 0) { printf("\r\nlist queue dequeue %d falied.",data); } printf("\r\nlist queue dequeue %d",data); list_queue_dump(queue); ret = list_queue_dequeue(queue,&data); if(ret != 0) { printf("\r\nlist queue dequeue %d failed.",data); } printf("\r\nlist queue dequeue %d",data); list_queue_dump(queue); printf("\r\nlist queue enqueue %d",data); (void)list_queue_enqueue(queue,data); list_queue_dump(queue); list_queue_destroy(queue); return 0; } ================================================ FILE: c-cpp/09_queue/list_queue/list_queue.h ================================================ /************************************************************************* > File Name: list_queue.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-13 > Desc: ************************************************************************/ #ifndef LINK_LIST_QUEUE_H #define LINK_LIST_QUEUE_H typedef struct _list_queue_node { int data; struct _list_queue_node *next; }queue_node; typedef struct _list_queue { int num; queue_node *head; queue_node *tail; }list_queue; #define list_queue_is_empty(queue) ((queue->num) == 0) #endif ================================================ FILE: c-cpp/09_queue/lock_free_queue.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/11. */ #ifndef QUEUE_LOCK_FREE_QUEUE_HPP_ #define QUEUE_LOCK_FREE_QUEUE_HPP_ #include #include template class LockFreeQueue { public: using value_type = T; private: struct node { std::shared data = nullptr; node* next = nullptr; }; std::atomic head = nullptr; std::atomic tail = nullptr; public: LockFreeQueue() head(new node), tail(head.load()) {} LockFreeQueue(const LockFreeQueue&) = delete; LockFreeQueue(LockFreeQueue&& other) : head(other.head.load()), tail(other.tail.load()) { other.head.store(nullptr); other.tail.store(nullptr); } LockFreeQueue& operator=(const LockFreeQueue&) = delete; LockFreeQueue& operator=(LockFreeQueue&& rhs) { while (node* const old_head = head.load()) { head.store(old_head->next); delete old_head; } head.store(rhs.head.load()); tail.store(rhs.tail.load()); rhs.head.store(nullptr); rhs.tail.store(nullptr); } ~LockFreeQueue() { while (node* const old_head = head.load()) { head.store(old_head->next); delete old_head; } } private: node* pop_head() { node* const res = head.load(); if (res == tail.load()) { return nullptr; } head.store(res->next); return res; } public: bool empty() const { return head.load() == tail.load(); } std::shared_ptr pop() { node* old_head = pop_head(); if (nullptr == old_head) { return nullptr; } else { auto res = old_head->data; delete old_head; return res; } } void push(value_type new_value) { auto new_data = std::make_shared(new_value); node* p = new node; node* old_tail = tail.load(); old_tail->data.swap(new_data); old_tail->next = p; tail_.store(p); } }; #endif // QUEUE_LOCK_FREE_QUEUE_HPP_ ================================================ FILE: c-cpp/09_queue/ring_queue.c ================================================ #include #include #include #include struct ring_queue { int cap; int head, tail; int *_q; }; int alloc_queue(struct ring_queue* queue, int cap) { if (!queue || cap < 0) return -1; if (queue->_q) return -1; queue->_q = (int *)malloc(cap * sizeof(int)); if (!queue->_q) return -1; queue->head = queue->tail = 0; queue->cap = cap; return 0; } void free_queue(struct ring_queue *queue) { queue->cap = 0; queue->head = queue->tail = 0; free(queue->_q); } int _valid_index(int curr, int step, int cap) { return (curr + step) % cap; } int _next(int curr, int cap) { return _valid_index(curr, 1, cap); } bool is_empty(struct ring_queue *queue) { return (queue->head == queue->tail); } bool is_full(struct ring_queue *queue) { int next_tail = _next(queue->tail, queue->cap); return (next_tail == queue->head); } int enqueue(struct ring_queue* queue, int elem) { if (is_full(queue)) return -1; queue->_q[queue->tail] = elem; queue->tail = _next(queue->tail, queue->cap); return 0; } int dequeue(struct ring_queue* queue, int *elem) { if (is_empty(queue)) return -1; if (elem) *elem = queue->_q[queue->head]; queue->head = _next(queue->head, queue->cap); return 0; } int size(struct ring_queue* queue) { int size = queue->tail - queue->head; if (size < 0) size += queue->cap; return size; } void dump(struct ring_queue* queue) { int i, idx; printf("Queue has %d elements with %d capacity\n", size(queue), queue->cap); for (i = 0; i < size(queue); i++) { idx = _valid_index(queue->head, i, queue->cap); printf("[%02d]: %08d\n", idx, queue->_q[idx]); } } int main() { struct ring_queue queue = {0, 0, 0, NULL}; int i; if (alloc_queue(&queue, 8)) { printf("Failed to allocate a queue\n"); return -1; } printf("A new queue is %s\n", is_empty(&queue)?"empty":"not empty"); enqueue(&queue, 1); printf("After enqueue 1 element, queue is %s\n", is_empty(&queue)?"empty":"not empty"); dequeue(&queue, NULL); printf("After dequeue 1 element, queue is %s\n", is_empty(&queue)?"empty":"not empty"); for (i = 0; i < 7; i++) enqueue(&queue, i); printf("After enqueue 7 element, queue is %s\n", is_full(&queue)?"full":"not full"); for (i = 0; i < 4; i++) { dequeue(&queue, NULL); enqueue(&queue, i); } printf("After enqueue/dequeue 4 element, queue is %s\n", is_full(&queue)?"full":"not full"); printf("Head is %d, Tail is %d\n", queue.head, queue.tail); dump(&queue); free_queue(&queue); return 0; } ================================================ FILE: c-cpp/10_recursive/.gitkeep ================================================ ================================================ FILE: c-cpp/10_recursive/one_two_step.c ================================================ /************************************************************************* > File Name: one_two_step.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-19 > Desc: ************************************************************************/ #include #include #include /*爬楼梯的问题,解决重复计算,采用数据保存方法*/ int helper(int n ,int *vlaue) { if(vlaue[n] != 0) { return vlaue[n]; } vlaue[n] = helper(n - 1,vlaue) + helper(n - 2,vlaue); return vlaue[n]; } int climbStaris(int n) { int *vlaue = NULL; int res = 0; vlaue = (int *)malloc(sizeof(int)*(n+1)); if(vlaue == NULL) { return -1; } memset(vlaue,0,sizeof(int)*(n + 1)); vlaue[0] = 0; vlaue[1] = 1; vlaue[2] = 2; res = helper(n,vlaue); free(vlaue); return res; } int main() { printf("\r\nnum%d ,%d",5,climbStaris(5)); printf("\r\nnum%d ,%d",6,climbStaris(6)); printf("\r\nnum%d ,%d",7,climbStaris(7)); return 0; } ================================================ FILE: c-cpp/10_recursive/one_two_step.cc ================================================ #include #include class SolutionOFOneTwoStep { private: static std::unordered_map result_; public: enum class POLICY { RECURSIVE, NONRECURSIVE }; private: size_t recursive(size_t steps) { auto iter = result_.find(steps); if (result_.end() != iter) { // found. return iter->second; } else { size_t res = operator()(steps - 1) + operator()(steps - 2); result_.insert({steps, res}); return res; } } size_t nonrecursive(size_t steps) { auto iter = result_.find(steps); if (result_.end() != iter) { // found. return iter->second; } else { size_t start; for (start = steps; start != 2 and result_.end() == result_.find(start); --start) {} for (size_t i = start; i != steps; ++i) { result_.insert({i + 1, result_[i - 1] + result_[i]}); } return result_[steps]; } } public: size_t operator()(size_t steps, const POLICY policy = POLICY::RECURSIVE) { if (policy == POLICY::RECURSIVE) { return recursive(steps); } else if (policy == POLICY::NONRECURSIVE) { return nonrecursive(steps); } } static void debug() { for (auto kv : result_) { std::cout << kv.first << ' ' << kv.second << std::endl; } std::cout << std::endl; } }; std::unordered_map SolutionOFOneTwoStep::result_ = {{1, 1}, {2, 2}}; int main() { SolutionOFOneTwoStep::debug(); std::cout << SolutionOFOneTwoStep()(5, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl; SolutionOFOneTwoStep::debug(); std::cout << SolutionOFOneTwoStep()(10, SolutionOFOneTwoStep::POLICY::NONRECURSIVE) << std::endl; SolutionOFOneTwoStep::debug(); std::cout << SolutionOFOneTwoStep()(20, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl; SolutionOFOneTwoStep::debug(); return 0; } ================================================ FILE: c-cpp/11_sorts/.gitkeep ================================================ ================================================ FILE: c-cpp/11_sorts/sorts.c ================================================ #include #include #include struct array { int size; int used; int *arr; }; void dump(struct array *array) { int idx; for (idx = 0; idx < array->used; idx++) printf("[%02d]: %08d\n", idx, array->arr[idx]); } void alloc(struct array *array) { array->arr = (int *)malloc(array->size * sizeof(int)); } void bubble_sort(struct array *array) { int i, j; if (array->used <= 1) return; for (i = 0; i < array->used; i++) { bool has_swap = false; for (j = 0; j < array->used - i - 1; j++) { if (array->arr[j] > array->arr[j+1]) { int tmp; tmp = array->arr[j]; array->arr[j] = array->arr[j+1]; array->arr[j+1] = tmp; has_swap = true; } } if (!has_swap) break; } } void bubble_sort_test() { int idx; struct array ten_int = {10, 0, NULL}; alloc(&ten_int); for (idx = 0; idx < 10; idx++) ten_int.arr[idx] = 30 - idx; ten_int.used = 10; dump(&ten_int); bubble_sort(&ten_int); dump(&ten_int); } void insertion_sort(struct array *array) { int i, j; if (array->used <= 1) return; for (i = 1; i < array->used; i++) { int val = array->arr[i]; for (j = i - 1; j >= 0; j--) { if (val < array->arr[j]) array->arr[j+1] = array->arr[j]; else break; } array->arr[j+1] = val; } } void insertion_sort_test() { int idx; struct array ten_int = {10, 0, NULL}; alloc(&ten_int); for (idx = 0; idx < 10; idx++) ten_int.arr[idx] = 30 - idx; ten_int.used = 10; dump(&ten_int); insertion_sort(&ten_int); dump(&ten_int); } void selection_sort(struct array *array) { int i, j; if (array->used <= 1) return; for (i = 0; i < array->used - 1; i++) { int tmp, idx = i; for (j = i + 1; j < array->used; j++) if (array->arr[j] < array->arr[idx]) idx = j; if (idx == i) continue; tmp = array->arr[i]; array->arr[i] = array->arr[idx]; array->arr[idx] = tmp; } } void selection_sort_test() { int idx; struct array ten_int = {10, 0, NULL}; alloc(&ten_int); for (idx = 0; idx < 10; idx++) ten_int.arr[idx] = 30 - idx; ten_int.used = 10; dump(&ten_int); selection_sort(&ten_int); dump(&ten_int); } int main() { //bubble_sort_test(); //selection_sort_test(); insertion_sort_test(); return 0; } ================================================ FILE: c-cpp/11_sorts/sorts.cpp ================================================ // C program for implementation of selection sort #include void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } void selectionSort(int arr[], int n) { int i, j, min_idx; // One by one move boundary of unsorted subarray for (i = 0; i < n-1; i++) { // Find the minimum element in unsorted array min_idx = i; for (j = i+1; j < n; j++) if (arr[j] < arr[min_idx]) min_idx = j; // Swap the found minimum element with the first element swap(&arr[min_idx], &arr[i]); } } /* Function to print an array */ void printArray(int arr[], int size) { int i; for (i=0; i < size; i++) printf("%d ", arr[i]); printf("\n"); } // Driver program to test above functions int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr)/sizeof(arr[0]); selectionSort(arr, n); printf("Sorted array: \n"); printArray(arr, n); return 0; } ================================================ FILE: c-cpp/11_sorts/sorts.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/16. */ #ifndef SORTS_SORTS_HPP_ #define SORTS_SORTS_HPP_ #include #include template ::value_type>> void bubble_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { if (std::distance(first, last) <= 1) { return; } bool flag = true; for (auto it = first; flag and it != last; ++it) { flag = false; for (auto itt = first; itt != last - std::distance(first, it) - 1; ++itt) { if (comp(*(itt + 1), *itt)) { std::swap(*itt, *(itt + 1)); flag = true; } } } } template ::value_type>> void insertion_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { if (std::distance(first, last) <= 1) { return; } for (auto it = first + 1; it != last; ++it) { const auto target = *it; auto itt = it; for (; std::distance(first, itt) > 0 and comp(target, *(itt - 1)); --itt) { *itt = *(itt - 1); } *itt = target; } } template ::value_type>> void selection_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { if (std::distance(first, last) <= 1) { return; } for (auto it = first; it != last - 1; ++it) { auto tag = it; for (auto itt = it + 1; itt != last; ++itt) { if (comp(*itt, *tag)) { tag = itt; } } if (tag != it) { std::swap(*it, *tag); } } } template ::value_type>> void bubble_down_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) { if (std::distance(first, last) <= 1) { return; } for (auto it = first; it != last; ++it) { for (auto itt = it + 1; itt != last; ++itt) { if (comp(*itt, *it)) { std::swap(*it, *itt); } } } } template ::value_type>> void shell_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { const size_t len = std::distance(first, last); if (len <= 1) { return; } for (size_t step = len / 2; step >= 1; step /= 2) { for (auto it = first + step; it != last; ++it) { auto target = *it; auto itt = it - step; for (; std::distance(first, itt) >= 0 and comp(target, *itt); itt -= step) { *(itt + step) = *itt; } *(itt + step) = target; } } } #endif // SORTS_SORTS_HPP_ ================================================ FILE: c-cpp/11_sorts/sorts_jinshaohui.c ================================================ /************************************************************************* > File Name: sorts_jinshaohui.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-19 > Desc: ************************************************************************/ #include #include #include #define SWAP(a,b) \ do{\ (a) ^= (b);\ (b) ^= (a);\ (a) ^= (b);\ }while(0) /*冒泡排序*/ void bubble_sort(int a[],int size) { int i = 0; int j = 0; int swap_flg = 0; if (size < 1) { return; } for (i = size - 1; i > 0; i--)/*排序的趟数*/ { swap_flg = 0;/*每次设置交换标识为0*/ for (j = 0; j < i; j++)/*本趟排序的遍历元素个数*/ { if (a[j] > a[j + 1]) { SWAP(a[j],a[j+1]); swap_flg = 1; } } /*本趟数,无数据交换的话,说明已经有序,直接退出*/ if (swap_flg == 0) { break; } } return; } /*插入排序*/ void insert_sort(int a[],int size) { int i = 0; int j = 0; int key = 0; for (i = 1; i < size; i ++)/*需要插入的元素个数*/ { key = a[i];/*保存插入的元素数据*/ j = i - 1; /* i 之前的元素都是有序的,找到比key小的插入到他后面, * 比key大的,需要往后挪一个位置*/ while((j >= 0) && (a[j] > key)) { a[j + 1] = a[j]; j--; } a[j + 1] = key; } return; } /*选择排序*/ void select_sort(int a[],int size) { int i = 0; int j = 0; int min = 0; for (i = 0; i < size - 1; i++) { min = i; for (j = i + 1; j < size; j++) { if (a[j] < a[min]) { min = j; } } if (min != i) { SWAP(a[i],a[min]); } } return; } void dump(int a[],int size) { int i = 0; printf("\r\n"); for (i = 0; i < size; i++ ) { printf("%d ",a[i]); } printf("\r\n"); return; } int main() { int a[10] = {9,11,4,15,16,3,20,44,5,10}; //bubble_sort(a,sizeof(a)/sizeof(int)); //insert_sort(a,sizeof(a)/sizeof(int)); select_sort(a,sizeof(a)/sizeof(int)); dump(a,sizeof(a)/sizeof(int)); return 0; } ================================================ FILE: c-cpp/11_sorts/sorts_test.cc ================================================ #include #include #include "sorts.hpp" int main() { const std::vector test_data{1, 2, 3, 0}; std::vector a(test_data.begin(), test_data.end()); bubble_sort(a.begin(), a.end()); for (auto i : a) { std::cout << i << ' '; } std::cout << '\n'; std::vector b(test_data.begin(), test_data.end()); insertion_sort(b.begin(), b.end()); for (auto i : b) { std::cout << i << ' '; } std::cout << '\n'; std::vector c(test_data.begin(), test_data.end()); selection_sort(c.begin(), c.end()); for (auto i : c) { std::cout << i << ' '; } std::cout << '\n'; std::vector d(test_data.begin(), test_data.end()); bubble_down_sort(d.begin(), d.end()); for (auto i : d) { std::cout << i << ' '; } std::cout << '\n'; std::vector e(test_data.begin(), test_data.end()); shell_sort(e.begin(), e.end()); for (auto i : e) { std::cout << i << ' '; } std::cout << '\n'; return 0; } ================================================ FILE: c-cpp/12_sorts/.gitkeep ================================================ ================================================ FILE: c-cpp/12_sorts/merge_sort.c ================================================ #include #include #include #include void dump(int *arr, int size) { int idx; for (idx = 0; idx < size; idx++) printf("%08d\n", arr[idx]); } void __merge(int *arr, int p, int q, int r) { int *tmp; int i, j, k; tmp = (int*)malloc((r - p + 1) * sizeof(int)); if (!tmp) abort(); for (i = p, j = q + 1, k = 0; i <= q && j <= r;) { if (arr[i] <= arr[j]) tmp[k++] = arr[i++]; else tmp[k++] = arr[j++]; } if (i == q + 1) { for (; j <= r;) tmp[k++] = arr[j++]; } else { for (; i <= q;) tmp[k++] = arr[i++]; } memcpy(arr + p, tmp, (r - p + 1) * sizeof(int)); free(tmp); } void __merge_sort(int *arr, int p, int r) { int q; if (p >= r) return; q = (p + r) / 2; __merge_sort(arr, p, q); __merge_sort(arr, q + 1, r); __merge(arr, p, q, r); } void merge_sort(int *arr, int size) { __merge_sort(arr, 0, size - 1); } void merge_verify() { int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; __merge(test, 0, 4, 9); dump(test, 10); } void merge_sort_test() { int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; merge_sort(test, 10); dump(test, 10); } int main() { //merge_verify(); merge_sort_test(); return 0; } ================================================ FILE: c-cpp/12_sorts/merge_sort.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/17. */ #ifndef SORTS_MERGE_SORT_HPP_ #define SORTS_MERGE_SORT_HPP_ #include #include #include #include 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 template ::value_type>> 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::value_type> tmp; tmp.reserve(len); detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp); std::copy(tmp.begin(), tmp.end(), first); } template ::value_type>> void inplace_merge_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { const auto len = std::distance(first, last); if (len <= 1) { return; } auto cut = first + len / 2; inplace_merge_sort(first, cut, comp); inplace_merge_sort(cut, last, comp); std::inplace_merge(first, cut, last, comp); } #endif // SORTS_MERGE_SORT_HPP_ ================================================ FILE: c-cpp/12_sorts/merge_sort_test.cc ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/17. */ #include #include #include "merge_sort.hpp" int main() { const std::vector test_data{0, -1, 3, 190, -500}; std::vector a{test_data}; merge_sort(a.begin(), a.end()); for (auto i : a) { std::cout << i << ' '; } std::cout << std::endl; std::vector b{test_data}; inplace_merge_sort(b.begin(), b.end()); for (auto i : b) { std::cout << i << ' '; } std::cout << std::endl; return 0; } ================================================ FILE: c-cpp/12_sorts/my12_sorts/merge_sort.c ================================================ /************************************************************************* > File Name: merge_sort.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-19 > Desc: ************************************************************************/ #include #include #include #include #define SORT_MAX (1000000) void dump(int a[],int size); void merge_sentry(int a[],int middle,int left,int right) { int *pleft = NULL; int *pright = NULL; int i = 0; int j = 0; int k = 0; int left_size = middle - left + 1; int right_size = right - middle; pleft = (int *)malloc(sizeof(int)*(left_size + 1)); assert(pleft != NULL); pright = (int *)malloc(sizeof(int)*(right_size + 1)); assert(pright != NULL); for(i = 0; i < left_size; i ++) { pleft[i] = a[left + i]; } pleft[left_size] = SORT_MAX; for(i = 0; i < right_size; i ++) { pright[i] = a[middle + 1 + i]; } pright[right_size] = SORT_MAX; for (k = left,i = 0,j = 0; k <= right; k++) { if (pleft[i] <= pright[j]) { a[k] = pleft[i++]; } else { a[k] = pright[j++]; } } free(pleft); free(pright); return; } /*两个有序数组合并*/ void merge(int a[],int middle,int left,int right) { int *tmp = NULL; int i = 0; int j = 0; int k = 0; tmp = (int*)malloc((right - left + 1)*sizeof(int)); assert(tmp != NULL); i = left; j = middle + 1; while(1) { if((i > middle) || (j > right)) { break; } if (a[i] > a[j]) { tmp[k++] = a[j++]; } else { tmp[k++] = a[i++]; } } if (i > middle) { while(j <= right) { tmp[k++] = a[j++]; } } else { while(i <= middle) { tmp[k++] = a[i++]; } } memcpy((a + left),tmp,(right - left + 1)*sizeof(int)); free(tmp); return ; } void merge_sort(int a[],int left,int right) { int middle = 0; if(left >= right) { return; } middle = (left + right)/2; merge_sort(a,left,middle); merge_sort(a,middle + 1,right); merge_sentry(a,middle,left,right); return; } void dump(int a[],int size) { int i = 0; if(size == 0) { return; } printf("\r\n"); for (i = 0; i < size; i++ ) { printf("%d ",a[i]); } printf("\r\n"); return; } int main() { int a[10] = {30,20,10,15,4,8,40,80,20,9}; merge_sort(a,0,9); dump(a,10); return 0; } ================================================ FILE: c-cpp/12_sorts/my12_sorts/quick_sort.c ================================================ /************************************************************************* > File Name: quick_sort.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-19 > Desc: ************************************************************************/ #include #include #include #include /* SWAP 使用必须主要,不能是同一个数据进行交换*/ #define SWAP(a,b) \ do{\ (a) ^= (b);\ (b) ^= (a);\ (a) ^= (b);\ }while(0) int partition2(int a[],int left,int right) { int i = left; int j = left; for(; j < right;j++) { if (a[j] < a[right]) { if(i != j) { SWAP(a[i],a[j]); } i++; } } if(i != right) { SWAP(a[i],a[right]); } return i; } int partition(int a[],int left,int right) { int i = left; int j = right; int key = a[left]; while(i < j) { while((i < j)&& (a[j] >= key)) { j--; } if (i < j) { a[i] = a[j]; } while((i < j) && a[i] <= key) { i++; } if (i= right) { return; } q = partition2(a,left,right); quick_sort(a,left,(q - 1)); quick_sort(a,(q + 1),right); return; } void dump(int a[],int size) { int i = 0; printf("\r\n"); for (i = 0; i < size; i++ ) { printf("%d ",a[i]); } printf("\r\n"); return; } int helper(int a[],int left,int right,int k) { int q = 0; q = partition(a,left,right); if (q > (k - 1)) { return helper(a,left,q-1,k); } else if (q < (k - 1)) { return helper(a,q+1,right,k); } return a[q]; } /*求无序数组中从小到大第K个元素的数值*/ int findKthlagest(int a[],int size,int k) { return helper(a,0,size-1,k); } int main() { int a[10] = {30,20,10,15,4,8,40,80,20,9}; int k; scanf("%d",&k); printf("\r\n从小到大排序,第%d个元素数值是%d",k,findKthlagest(a,10,k)); quick_sort(a,0,9); dump(a,10); return 0; } ================================================ FILE: c-cpp/12_sorts/quick_sort.c ================================================ #include #include #include #include void dump(int *arr, int size) { int idx; for (idx = 0; idx < size; idx++) printf("%08d\n", arr[idx]); } void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int partition(int *arr, int p, int r) { //int pivot = arr[r]; int i, j; i = j = p; for (; j < r; j++) { if (arr[j] < arr[r]) { if(i != j) { swap(arr + i, arr + j); } i++; } } swap(arr + i, arr + r); return i; } void __quick_sort(int *arr, int p, int r) { int q; if (p >= r) return; q = partition(arr, p, r); __quick_sort(arr, p, q-1); __quick_sort(arr, q+1, r); } void quick_sort(int *arr, int size) { __quick_sort(arr, 0, size - 1); } void quick_sort_test() { int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; quick_sort(test, 10); dump(test, 10); } int main() { quick_sort_test(); return 0; } ================================================ FILE: c-cpp/12_sorts/quick_sort.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/17. */ #ifndef SORTS_QUICK_SORT_HPP_ #define SORTS_QUICK_SORT_HPP_ #include #include #include #include namespace detail { template > const T& median(const T& a, const T& b, const T& c, Compare comp = Compare()) { if (comp(a, b) and comp(b, c) or comp(c, b) and comp(b, a)) { return b; } else if (comp(b, c) and comp(c, a) or comp(a, c) and comp(c, b)) { return c; } else { return a; } } template ::value_type, typename Compare = std::less> const T& iter_median(Iter a, Iter b, Iter c, Compare comp = Compare()) { return median(*a, *b, *c, comp); } 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}; } } // namespace detail template ::value_type, typename Compare = std::less> void quick_sort(BidirIt first, BidirIt last, Compare comp = Compare()) { for (auto size = std::distance(first, last); size > 1; size = std::distance(first, last)) { const T pivot = detail::iter_median(first, last - 1, first + size / 2, comp); const auto eq = detail::inplace_partition(first, last, pivot, comp); quick_sort(first, eq.first, comp); first = eq.second; // Liam Huang: economize half of recursive calling. } } #endif // SORTS_QUICK_SORT_HPP_ ================================================ FILE: c-cpp/12_sorts/quick_sort_test.cc ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/17. */ #include #include #include #include "quick_sort.hpp" void test_quick_sort(std::vector test_data) { quick_sort(test_data.begin(), test_data.end()); std::transform(test_data.begin(), test_data.end(), std::ostream_iterator(std::cout, " "), [](int i){ return i; }); std::cout << '\n'; } int main() { test_quick_sort({-3, -1, 1, -2, -3, 0, -3, 100, 1, 1, -100}); test_quick_sort({1, 1, 1}); test_quick_sort({1, 0, -1}); test_quick_sort({1}); return 0; } ================================================ FILE: c-cpp/13_sorts/.gitkeep ================================================ ================================================ FILE: c-cpp/13_sorts/bucket_sort.hpp ================================================ /** * 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_ ================================================ FILE: c-cpp/13_sorts/bucket_sort_test.cc ================================================ /** * 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; } ================================================ FILE: c-cpp/13_sorts/counting_sort.hpp ================================================ /** * 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_ ================================================ FILE: c-cpp/13_sorts/counting_sort_test.cc ================================================ /** * 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; } ================================================ FILE: c-cpp/13_sorts/sort.c ================================================ /************************************************************************* > File Name: sort.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-20 > Desc: ************************************************************************/ #include #include #include #include #include void dump(int a[],int size) { int i = 0; printf("\r\n"); for(i = 0; i max) { max = a[i]; } } count = (int *)malloc(sizeof(int)*(max + 1)); assert(count != NULL); memset(count,0,sizeof(int)*(max + 1)); /*计数*/ for (i = 0; i < size;i++) { count[a[i]]++; } /*依次累加*/ for(i = 1 ;i <= max; i ++) { count[i] += count[i-1]; } res = (int *)malloc(sizeof(int)*(size)); assert(res != NULL); /*核心代码,count[a[i] - 1]就是排序好的下标*/ for (i = size-1;i >= 0; i--) { res[count[a[i]] -1] = a[i]; count[a[i]]--; } memcpy(a,res,size*(sizeof(int))); free(res); free(count); return; } int count_sort_test() { int a [10]={1,5,6,8,10,9,3,1,2,1}; printf("\r\n conunt sort test ...."); count_sort(a,10); dump(a,10); return 0; } #define NUM_OF_POS(a,pval) ((a)/pval)%10 void radix_sort(int a[],int size,int num_count) { int count[10] = {0}; /*计数*/ int *pres = NULL; int i = 0; int j = 0; int pval = 10; int index = 0; int break_flg = 0; pres = (int *)malloc(sizeof(int)*size); assert(pres != NULL); for (i = 0; i < num_count; i ++) { memset(count,0,sizeof(int)*10); /*求当前的基数*/ pval = pow(10,i); /*计数*/ for (j = 0; j < size; j++) { index = NUM_OF_POS(a[j],pval); count[index]++; } /*小的优化,可能位数最大的就1,其他的位数差很多*/ if(count[0] == 9) { break_flg++; } if(break_flg >=2) { printf("\r\n %i",i); break; } /*累加*/ for(j = 1; j < 10; j ++) { count[j] += count[j-1]; } /*排序必须从后往前,否则不是稳定排序*/ for(j = size -1; j >= 0; j--) { index = NUM_OF_POS(a[j],pval); pres[count[index] - 1] = a[j]; count[index]--; } /*本轮排序好的,拷贝到a中*/ memcpy(a,pres,sizeof(int)*size); } return; } void radix_sort_test() { int a[10] = {123,12341,1232134,124,236,128,1112313129,98,9,8989}; printf("\r\n radix sort test....."); radix_sort(a,10,10); dump(a,10); return; } struct barrel { int node[10]; int count;/* the num of node */ }; int partition(int a[],int left,int right) { int i = left; int j = right; int key = a[left]; while(i < j) { while((i < j)&& (a[j] >= key)) { j--; } if (i < j) { a[i] = a[j]; } while((i < j) && a[i] <= key) { i++; } if (i= right) { return; } q = partition(a,left,right); quick_sort(a,left,(q - 1)); quick_sort(a,(q + 1),right); return; } void bucket_sort(int data[], int size) { int max, min, num, pos; int i, j, k; struct barrel *pBarrel; max = min = data[0]; for (i = 1; i < size; i++) { if (data[i] > max) { max = data[i]; } else if (data[i] < min) { min = data[i]; } } num = (max - min + 1) / 10 + 1; pBarrel = (struct barrel*)malloc(sizeof(struct barrel) * num); memset(pBarrel, 0, sizeof(struct barrel) * num); /* put data[i] into barrel which it belong to */ for (i = 0; i < size; i++) { k = (data[i] - min + 1) / 10;/* calculate the index of data[i] in barrel */ (pBarrel + k)->node[(pBarrel + k)->count] = data[i]; (pBarrel + k)->count++; } pos = 0; for (i = 0; i < num; i++) { if ((pBarrel + i)->count != 0) { quick_sort((pBarrel+i)->node, 0, ((pBarrel+i)->count)-1);/* sort node in every barrel */ for (j = 0; j < (pBarrel+i)->count; j++) { data[pos++] = (pBarrel+i)->node[j]; } } } free(pBarrel); } void bucket_sort_test() { int a[] = {78, 17, 39, 26, 72, 94, 21, 12, 23, 91}; int size = sizeof(a) / sizeof(int); printf("\r\n bucket sort test ..."); bucket_sort(a, size); dump(a,size); } int main() { count_sort_test(); radix_sort_test(); bucket_sort_test(); return 0; } ================================================ FILE: c-cpp/14_sorts/.gitkeep ================================================ ================================================ FILE: c-cpp/14_sorts/analytics_of_std_sort.md ================================================ # C++ STL 中的 std::sort 分析 参见 [Liam Huang 的博客](https://liam.page/)中的 3 篇文章: * [谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/) * [谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/) * [谈谈 STL 中的 std::sort](https://liam.page/2018/09/18/std-sort-in-STL/) ================================================ FILE: c-cpp/14_sorts/counting_sort.c ================================================ #include #include #include #include void dump(int *arr, int size) { int i; for (i = 0; i < size; i++) printf("%08d\n", arr[i]); } // content in arr must be positive integer void counting_sort(int *arr, int size) { int max, i; int *count, *tmp; if (size <= 1) return; max = 0; // find the biggest integer for (i = 0; i < size; i++) { if (max < arr[i]) max = arr[i]; } // init count to 0 count = (int*)malloc((max+1) * sizeof(int)); tmp = (int*)malloc(size * sizeof(int)); if (!count || !tmp) return; memset(count, 0, (max + 1) * sizeof(int)); // counting for (i = 0; i < size; i++) count[arr[i]]++; for (i = 1; i < max + 1; i++) count[i] = count[i-1] + count[i]; // iterate arr and put it to the correct index in tmp for (i = 0; i < size; i++){ int index = count[arr[i]] - 1; tmp[index] = arr[i]; count[arr[i]]--; } // move back to arr memcpy(arr, tmp, size * sizeof(int)); } void counting_sort_test() { int test_data[10] = {3, 23, 98, 1, 27, 36, 52, 89, 76, 44}; counting_sort(test_data, 10); dump(test_data, 10); } int main() { counting_sort_test(); return 0; } ================================================ FILE: c-cpp/15_bsearch/.gitkeep ================================================ ================================================ FILE: c-cpp/15_bsearch/binary_search.c ================================================ #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; } ================================================ FILE: c-cpp/15_bsearch/bsearch.hpp ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/24. */ #ifndef BSEARCH_BSEARCH_HPP_ #define BSEARCH_BSEARCH_HPP_ #include #include // 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 = std::less> IterT bsearch(IterT first, IterT last, ValueT target, Compare comp = Compare()) { IterT result = last; while (std::distance(first, last) > 0) { IterT mid = first + std::distance(first, last) / 2; if (comp(*mid, target)) { first = mid + 1; } else if (comp(target, *mid)) { last = mid; } else { // equal result = mid; break; } } return result; } #endif // BSEARCH_BSEARCH_HPP_ ================================================ FILE: c-cpp/15_bsearch/bsearch_c/bsearch.c ================================================ /************************************************************************* > File Name: bsearch.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-21 > Desc: ************************************************************************/ #include #include #include int mybsearch(int a[],int size,int value) { int mid = 0; int left = 0; int right = size - 1; while(left <= right) { /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] == value) { return mid; } else if (a[mid] < value) { left = mid + 1; } else { right = mid - 1; } } return -1; } int helper(int a[], int left,int right,int value) { int mid = 0; if (left > right) { return -1; } /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] == value) { return mid; } else if (a[mid] < value) { return helper(a,mid + 1,right,value); } else { return helper(a,left,mid - 1,value); } return -1; } /*递归实现*/ int mybsearch_2(int a[],int size,int value) { return helper(a,0,size-1,value); } int main() { int a[10] = {5,6,8,9,10,11,23,42,53,123}; int data = 0; int res = 0; printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch(a,10,data); printf("data[%d] %s 在数据中,下标是%d",data,(res != -1)?"":"不",res); printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch_2(a,10,data); printf("data[%d] %s 在数据中,下标是%d",data,(res != -1)?"":"不",res); return; } ================================================ FILE: c-cpp/15_bsearch/bsearch_c/sqrt.c ================================================ /************************************************************************* > File Name: sqrt.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-31 > Desc: ************************************************************************/ #include #include #include #include /*求解精度设置*/ #define E 0.000001 double mybsearch(double num) { double start = 1.0; double end = num; double mid = 0.0; while(1) { mid = (start + end)/2; if(((mid*mid - num) <= E) && ((mid*mid - num) >= -E)) { return mid; } if ((mid*mid - num) > E) { end = mid; } else { start = mid; } } return 0; } int main() { double num = 0.0; /*这里需要注意:double的输入方式*/ scanf("%lf",&num); printf("\r\n num %lf的平方根是%lf",num,mybsearch(num)); return 0; } ================================================ FILE: c-cpp/15_bsearch/bsearch_test.cc ================================================ /** * Created by Liam Huang (Liam0205) on 2018/10/24. */ #include #include #include "bsearch.hpp" template 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; } int main() { std::vector test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 7}; // std::less() test_bsearch(test, 8); // 14 test_bsearch(test, -1); // 14 test_bsearch(test, 0); // 0, 1 test_bsearch(test, 4); // 5, 6 test_bsearch(test, 5); // 7, 8, 9, 10, 11 test_bsearch(test, 7); // 13 return 0; } ================================================ FILE: c-cpp/16_bsearch/.gitkeep ================================================ ================================================ FILE: c-cpp/16_bsearch/bsearch.c ================================================ /************************************************************************* > File Name: bsearch.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-21 > Desc: ************************************************************************/ #include #include #include /*二分查找算法的变形问题 *1、查找第一个等于给定数值的元素 *2、查找最后一个等于给定数值的元素 *3、查找第一个大于等于给定数值的元素 *4、查找第一个小于等于给定数值的元素 * */ /*1、查找第一个等于给定数值的元素*/ int mybsearch_1(int a[],int size,int value) { int mid = 0; int left = 0; int right = size - 1; while(left <= right) { /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] < value) { left = mid + 1; } else if (a[mid] > value) { right = mid - 1; } else { if ((mid == 0) || (a[mid - 1] != value)) { return mid; } else { right = mid - 1; } } } return -1; } /*2、查找最后一个等于给定数值的元素*/ int mybsearch_2(int a[],int size,int value) { int mid = 0; int left = 0; int right = size - 1; while(left <= right) { /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] < value) { left = mid + 1; } else if (a[mid] > value) { right = mid - 1; } else { if ((mid == (size - 1)) || (a[mid + 1] != value)) { return mid; } else { left = mid + 1; } } } return -1; } /*3、查找第一个大于等于给定数值的元素*/ int mybsearch_3(int a[],int size,int value) { int mid = 0; int left = 0; int right = size - 1; while(left <= right) { /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] < value) { left = mid + 1; } else { /*a[mid] >= value 当mid==0 或者a[mid-1] > value 说明是第一个大于等于value*/ if ((mid == 0) || (a[mid - 1] < value)) { return mid; } else { right = mid - 1; } } } return -1; } /*4、查找第一个小于等于给定数值的元素*/ int mybsearch_4(int a[],int size,int value) { int mid = 0; int left = 0; int right = size - 1; while(left <= right) { /*防止size数量太大是,(left + right)数据翻转,导致问题*/ mid = left + ((right - left)>>1); if (a[mid] > value) { right = mid - 1; } else { /*a[mid] <= value 时,当前mid == size -1 数组中最大的数值; * 或者a[mid + 1] 大于vlaue,就是mid就第一个小于等于value*/ if ((mid == (size - 1)) || (a[mid + 1] > value)) { return mid; } else { left = mid + 1; } } } return -1; } int main() { int a[10] = {5,6,6,9,10,11,11,22,33,33}; int data = 0; int i = 0; int res =0; printf("\r\n"); for(i = 0; i < 10 ; i++) { printf("%d ",a[i]); } printf("\r\n"); printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch_1(a,10,data); printf("第一个等于data[%d],下标是%d",data,res); printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch_2(a,10,data); printf("最后一个等于data[%d],下标是%d",data,res); printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch_2(a,10,data); printf("第一个大于等于data[%d],下标是%d",data,res); printf("\r\n输入一个整数"); scanf("%d",&data); res = mybsearch_2(a,10,data); printf("第一个小等于data[%d],下标是%d",data,res); return; } ================================================ FILE: c-cpp/16_bsearch/bsearch_variant.c ================================================ #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; } ================================================ FILE: c-cpp/16_bsearch/bsearch_varients.hpp ================================================ /** * 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_ ================================================ FILE: c-cpp/16_bsearch/bsearch_varients_test.cc ================================================ /** * 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; } ================================================ FILE: c-cpp/17_skiplist/.gitkeep ================================================ ================================================ FILE: c-cpp/17_skiplist/SkipList.cpp ================================================ #include #include #include #include #include #include #include using namespace std; /** * һʵַ * д洢Ҵ洢Dzظġ * * C++汾. * JAVA汾 ԭ AuthorZHENG * * Authorpuhuaqiang * * ṹ: * * K 1 9 * K-1 1 5 9 * K-2 1 3 5 7 9 * ... .... * 0(ԭʼ) 1 2 3 4 5 6 7 8 9 */ const int MAX_LEVEL = 16; /** * @brief ڵ */ class CNode { public: CNode(); ~CNode(); std::string toString(); /** * @brief ȡ */ CNode** GetIdxList(); /** * @brief */ void SetData(int v); /** * @brief ȡ */ int GetData(); /** * @brief */ void SetLevel(int l); private: /**ǰڵֵ*/ int m_data; /** * ǰڵÿȼһڵ. * 2 N1 N2 * 1 N1 N2 * N1DZڵ, m_lpForwards[x] N2 * * [0] ԭʼ. */ CNode* m_lpForwards[MAX_LEVEL]; /**ǰڵڵ*/ int m_iMaxLevel; }; /** * @brief */ class CSkipList { public: CSkipList(); ~CSkipList(); /** * @brief ֵָĽڵ * @param v */ CNode* Find(int v); /** * @brief ֵָ * @param v */ void Insert(int v); /** * @brief ɾֵָĽڵ * @param v */ int Delete(int v); void PrintAll(); /** * @brief ӡṹ * @param l -1ʱӡмĽṹ >=0ʱӡָĽṹ */ void PrintAll(int l); /** * @brief ڵʱ,õK * @return K */ int RandomLevel(); private: int levelCount; /** * * ͷ/(ڵ) */ CNode* m_lpHead; }; int main() { CSkipList skipList; /// ԭʼֵ for(int i=1; i< 50; i++){ if((i%3) == 0){ skipList.Insert(i); } } for(int i=1; i< 50; i++){ if((i%3) == 1){ skipList.Insert(i); } } skipList.PrintAll(); std::cout<GetData()< k-1 -> k-2 ...->0 */ for(int i=levelCount-1; i>=0; --i){ /** * СvĽڵ(lpNode). */ while((NULL != lpNode->GetIdxList()[i]) && (lpNode->GetIdxList()[i]->GetData() < v)){ lpNode = lpNode->GetIdxList()[i]; } } /** * lpNode СvĽڵ, lpNodeһڵ͵ڻvĽڵ */ if((NULL != lpNode->GetIdxList()[0]) && (lpNode->GetIdxList()[0]->GetData() == v)){ return lpNode->GetIdxList()[0]; } return NULL; } void CSkipList::Insert(int v) { /// ½ڵ CNode* lpNewNode = new CNode(); if(NULL == lpNewNode){ return; } /** * ½ڵֲڵ * 3, µĽڵ123ϵ */ int level = RandomLevel(); lpNewNode->SetData(v); lpNewNode->SetLevel(level); /** * ʱ * ҪǵõµĽڵÿϵλ */ CNode *lpUpdateNode[level]; for(int i=0; i= 0; --i){ /** * λ * eg. 1 1 7 10 * 6 * lpFind->GetIdxList()[i]->GetData() : ʾڵlpFindڵ1һڵ * "lpFind->GetIdxList()[i]->GetData() < v"ʱ, * ½ڵҪ뵽 lpFindڵĺ, lpFind->GetIdxList()[i] ڵǰ * lpFind1 lpFind->GetIdxList()[i] 7 */ while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){ lpFind = lpFind->GetIdxList()[i]; } /// lpFind ½ڵ iĺһڵ lpUpdateNode[i] = lpFind; } for(int i=0; iGetIdxList()[i]ڵ7 * * 2 6 17֮ */ lpNewNode->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i]; lpUpdateNode[i]->GetIdxList()[i] = lpNewNode; } if(levelCount < level){ levelCount = level; } } int CSkipList::Delete(int v) { int ret = -1; CNode *lpUpdateNode[levelCount]; CNode *lpFind = m_lpHead; for(int i=levelCount-1; i>= 0; --i){ /** * СvĽڵ(lpFind). */ while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){ lpFind = lpFind->GetIdxList()[i]; } lpUpdateNode[i] = lpFind; } /** * lpFind СvĽڵ, lpFindһڵ͵ڻvĽڵ */ if((NULL != lpFind->GetIdxList()[0]) && (lpFind->GetIdxList()[0]->GetData() == v)){ for(int i=levelCount-1; i>=0; --i){ if((NULL != lpUpdateNode[i]->GetIdxList()[i]) && (v == lpUpdateNode[i]->GetIdxList()[i]->GetData())){ lpUpdateNode[i]->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i]->GetIdxList()[i]; ret = 0; } } } return ret; } void CSkipList::PrintAll() { CNode* lpNode = m_lpHead; while(NULL != lpNode->GetIdxList()[0]){ std::cout<GetIdxList()[0]->toString().data()<GetIdxList()[0]; } } void CSkipList::PrintAll(int l) { for(int i=MAX_LEVEL-1; i>=0;--i){ CNode* lpNode = m_lpHead; std::cout<<""<= 0) && (l == i))){ while(NULL != lpNode->GetIdxList()[i]){ std::cout<GetIdxList()[i]->GetData()<<" "; lpNode = lpNode->GetIdxList()[i]; } std::cout<= 0){ break; } } } } int GetRandom() { static int _count = 1; std::default_random_engine generator(time(0) + _count); std::uniform_int_distribution distribution(1,99999/*0x7FFFFFFF*/); int dice_roll = distribution(generator); _count += 100; return dice_roll; } int CSkipList::RandomLevel() { int level = 1; for(int i=1; i #include #include #include #include // https://www.youtube.com/watch?v=2g9OSRKJuzM&t=17s #define MAX_LEVEL 15 struct node { int val; int max_level; struct node *forward[MAX_LEVEL]; }; struct skip_list { struct node head; int max_level; int max_level_nodes; }; void node_init(struct node* node) { memset(node, 0, sizeof(struct node)); } void skip_list_init(struct skip_list* sl) { node_init(&sl->head); sl->max_level = 0; sl->max_level_nodes = 0; } void random_init() { static bool done = false; if (done) return; srandom(time(NULL)); done = true; } int random_level(void) { int i, level = 1; random_init(); for (i = 1; i < MAX_LEVEL; i++) if (random() % 2 == 1) level++; return level; } void random_level_test() { printf("random level %d\n", random_level()); printf("random level %d\n", random_level()); printf("random level %d\n", random_level()); printf("random level %d\n", random_level()); printf("random level %d\n", random_level()); } void insert(struct skip_list *sl, int val) { int level = random_level(); struct node *update[MAX_LEVEL]; struct node *new, *p; int i; new = (struct node*)malloc(sizeof(struct node)); if (!new) return; new->max_level = level; new->val = val; for (int i = 0; i < MAX_LEVEL; i++) update[i] = &sl->head; p = &sl->head; for (i = level - 1; i >= 0; i--) { while(p->forward[i] && p->forward[i]->val < val) p = p->forward[i]; update[i] = p; } for (i = 0; i < level; i++) { new->forward[i] = update[i]->forward[i]; update[i]->forward[i] = new; } if (sl->max_level < level) { sl->max_level = level; sl->max_level_nodes = 1; } else if (sl->max_level == level) sl->max_level_nodes++; } struct node *find(struct skip_list* sl, int val) { struct node *node = &sl->head; int i; for (i = sl->max_level - 1; i >= 0; i--) { while (node->forward[i] && node->forward[i]->val < val) node = node->forward[i]; } if (node->forward[0] && node->forward[0]->val == val) { return node->forward[0]; } else return NULL; } void delete(struct skip_list* sl, int val) { struct node *update[MAX_LEVEL]; struct node *p; int i; p = &sl->head; for (i = sl->max_level; i >= 0; i--) { while (p->forward[i] && p->forward[i]->val < val) p = p->forward[i]; update[i] = p; } if (p->forward[0] == NULL || p->forward[0]->val != val) return; if (p->forward[0]->max_level == sl->max_level) sl->max_level_nodes--; for (i = sl->max_level-1; i >= 0; i--) { if (update[i]->forward[i] && update[i]->forward[i]->val == val) update[i]->forward[i] = update[i]->forward[i]->forward[i]; } // fixup max_level and max_level_nodes if (sl->max_level_nodes == 0) { //sl->max_level--; p = &sl->head; // skip (max_level - 1), direct test (max_level - 2) // since no nodes on (max_level - 1) for (i = sl->max_level - 2; i >= 0; i--) { while (p->forward[i]) { sl->max_level_nodes++; p = p->forward[i]; } if (sl->max_level_nodes) { sl->max_level = i + 1; break; } else sl->max_level = i; } } } void print_sl(struct skip_list* sl) { struct node *node; int level; printf("%d level skip list with %d nodes on top\n", sl->max_level, sl->max_level_nodes); for (level = sl->max_level - 1; level >= 0; level--) { node = &sl->head; printf("Level[%02d]:", level); while (node->forward[level]) { printf("%4d", node->forward[level]->val); node = node->forward[level]; } printf("\n"); } } int main() { struct skip_list sl; struct node *node = NULL; int i; skip_list_init(&sl); print_sl(&sl); for (i = 0; i < 10; i++) insert(&sl, i); print_sl(&sl); node = find(&sl, 8); if (node) printf("find 8 in sl %d\n", node->val); else printf("8 not in sl\n"); for (i = 0; i < 10; i++) { delete(&sl, i); print_sl(&sl); } return 0; } ================================================ FILE: c-cpp/17_skiplist/skiplist.hpp ================================================ /** * 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_ ================================================ FILE: c-cpp/17_skiplist/skiplist_c/skiplist.c ================================================ /************************************************************************* > File Name: skiplist.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-31 > Desc: ************************************************************************/ #include #include #include #include #include"./skiplist.h" /*创建node节点*/ node* skip_list_create_node(int level,int key,int value) { node * tmp = NULL; tmp =(node *)malloc(sizeof(node) + level*sizeof(node *)); assert(tmp != NULL); memset(tmp,0,sizeof(node) + level*sizeof(node*)); tmp->key = key; tmp->value = value; tmp->max_level = level; return tmp; } /*创建跳表的表头,max_level层数*/ skiplist * skip_list_create(int max_level) { int i = 0; skiplist * list = NULL; list = (skiplist *)malloc (sizeof(skiplist)); assert(list != NULL); list->level = 1; list->count = 0; list->head = skip_list_create_node(max_level,0,0); if(list->head == NULL) { free(list); return NULL; } return list; } /*skiplist 销毁*/ void skip_list_destory(skiplist * list) { int i = 0; node * tmp = NULL; if((list == NULL) || (list->head == NULL)) { return; } while(list->head->next[0] != NULL) { tmp = list->head->next[0]; list->head->next[0] = tmp->next[0]; free(tmp); } free(list->head); free(list); return; } /*插入元素获得层数,是随机产生的*/ int skip_list_level(skiplist * list) { int i = 0; int level = 1; for (i = 1; i < list->head->max_level; i++) { if ((rand()%2) == 1) { level++; } } return level; } int skip_list_insert(skiplist *list,int key,int value) { int i = 0; int level = 0; node **update = NULL;/*用来更新每层的指针*/ node *tmp = NULL; node *prev = NULL; if (list == NULL) { return 1; } /*申请update空间用于保存每层的指针*/ update = (node **)malloc(sizeof(node *)*list->head->max_level); if (update == NULL) { return 2; } /*逐层查询节点的*/ prev = list->head; for (i = (list->level -1); i >= 0; i--) { /*初始化每level层的头指针*/ while(((tmp = prev->next[i]) != NULL) && (tmp->key < key)) { prev = tmp; } update[i] = prev; } /*当前key已经存在,返回错误*/ if ((tmp!= NULL) && (tmp->key == key)) { return 3; } /*获取插入元素的随机层数,并更新跳表的最大层数*/ level = skip_list_level(list); /*创建当前数据节点*/ tmp = skip_list_create_node(level,key,value); if (tmp == NULL) { return 4; } /*更新最大层数*/ if (level > list->level) { for (i = list->level;i < level; i ++) { update[i] = list->head; } list->level = level; } /*逐层更新节点的指针*/ for(i = 0; i < level; i++) { tmp->next[i] = update[i]->next[i]; update[i]->next[i] = tmp; } list->count++; return 0; } int skip_list_delete(skiplist * list, int key ,int *value) { int i = 0; node **update = NULL;/*用来更新每层的指针*/ node *tmp = NULL; node *prev = NULL; if ((list == NULL) && (value == NULL)&& (list->count == 0)) { return 1; } /*申请update空间用于保存每层的指针*/ update = (node **)malloc(sizeof(node *)*list->level); if (update == NULL) { return 2; } /*逐层查询节点的*/ prev = list->head; for (i = (list->level -1); i >= 0; i--) { /*初始化每level层的头指针*/ while(((tmp = prev->next[i]) != NULL) && (tmp->key < key)) { prev = tmp; } update[i] = prev; } if ((tmp != NULL) && (tmp->key == key)) { *value = tmp->value; /*逐层删除*/ for(i = 0; i < list->level; i++) { if(update[i]->next[i] == tmp) { update[i]->next[i] = tmp->next[i]; } } free(tmp); tmp = NULL; /*更新level的层数*/ for (i = list->level - 1; i >= 0; i++) { if (list->head->next[i] == NULL ) { list->level--; } else { break; } } list->count--; } else { return 3;/*未找到节点*/ } return 0 ; } /*查询当前key是否在跳表中,如果存在返回查询的value数值,不存在返回-1*/ int skip_list_search(skiplist *list,int key,int *value) { int i = 0; node *prev = NULL; node *tmp = NULL; if((list == NULL) || (list->count == 0) || (value == NULL)) { return 1; } prev = list->head; for(i = list->level - 1; i >= 0; i--) { while(((tmp = prev->next[i]) != NULL) && (tmp->key <= key)) { if (tmp->key == key) { *value = tmp->value; return 0; } prev = tmp; } } return -1; } void skip_list_dump(skiplist *list) { int i = 0; node *ptmp = NULL; printf("\r\n----------------------------------------------"); printf("\r\n skip list level[%d],count[%d]",list->level,list->count); for(i = list->level - 1; i >= 0; i --) { ptmp = list->head->next[i]; printf("\r\n level[%d]:",i); while(ptmp != NULL) { printf("%d-%d ",ptmp->key,ptmp->value); ptmp = ptmp->next[i]; } } printf("\r\n----------------------------------------------"); return; } int main() { int res = 0; int key = 0; int value = 0; skiplist *list = NULL; list = skip_list_create(5); assert(list != NULL); while(1) { printf("\r\n 请输入key 和 value,当key = 1000时,退出输入:"); scanf("%d%d",&key,&value); if (key == 1000) { break; } res = skip_list_insert(list,key,value); if (res != 0) { printf("\r\n skip list insert %d,failed,res=%d.",key,res); } } skip_list_dump(list); while(1) { printf("\r\n 通过key 查询value的数值,当key = 1000时,退出查询"); scanf("%d",&key); if(key == 1000) { break; } res = skip_list_search(list,key,&value); if (res != 0) { printf("\r\n skip list search %d,failed,res=%d.",key,res); } else { printf("\r\n skip list search %d,sucessful,value=%d.",key,value); } } skip_list_dump(list); while(1) { printf("\r\n 通过key 删除节点,当key = 1000时,退出删除"); scanf("%d",&key); if(key == 1000) { break; } res = skip_list_delete(list,key,&value); if (res != 0) { printf("\r\n skip list search %d,failed,res=%d.",key,res); } else { printf("\r\n skip list search %d,sucessful,value=%d.",key,value); } } skip_list_dump(list); skip_list_destory(list); return 0; } ================================================ FILE: c-cpp/17_skiplist/skiplist_c/skiplist.h ================================================ /************************************************************************* > File Name: skiplist.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-31 > Desc: ************************************************************************/ #ifndef __SKIP_LIST_H__ #define __SKIP_LIST_H__ typedef struct _node { int key; /*key是唯一的*/ int value; /*存储的内容*/ int max_level; /*当前节点最大层数*/ struct _node *next[0];/*level层链表结构*/ }node; typedef struct _skiplist { int level; int count; node *head; }skiplist; /*根据当前结构体元素的地址,获取到结构体首地址*/ #define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container(ptr,type,member) ({\ const typeof( ((type *)0)->member) *__mptr = (ptr);\ (type *) ( (char *)__mptr - offsetof(type,member));}) #endif ================================================ FILE: c-cpp/17_skiplist/skiplist_test.cc ================================================ /** * 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 } ================================================ FILE: c-cpp/17_skiplist/skiplist_tr.hpp ================================================ /** * 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_, cont_.end()); iterator tail_iter = 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_, tail_iter); cont_.insert(cont_.begin(), std::move(head)); #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_ ================================================ FILE: c-cpp/17_skiplist/skiplist_tr_test.cc ================================================ /** * 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() } ================================================ FILE: c-cpp/18_hashtable/.gitkeep ================================================ ================================================ FILE: c-cpp/18_hashtable/hash_map.cc ================================================ /** * Created by Liam Huang (Liam0205) on 2018/08/14. * This is an old test file for hash_map, created by Liam. * Just for showing the inner logic for hash_map class template. * Original posted on: * https://github.com/Liam0205/leetcode/tree/master/met/hash_map.cc */ #include #include #include #include #include template > class hash_map { public: using key_type = Key; using mapped_type = T; using value_type = std::pair; using size_type = size_t; using hasher = std::hash; private: // helper using wrapper = std::shared_ptr; public: // constructor hash_map() { container_.resize(primes_[size_level_]); } public: // capacity bool empty() const { return empty_; } size_type size() const { return size_; } size_type max_size() const { return primes_[size_level_]; } public: // find and modify mapped_type& operator[](const key_type& key) { auto hashed = find_hash(key); if (not(container_[hashed]) and construct_new_on_position(hashed, key) and load_factor() > max_load_factor()) { expand(); } return container_[hashed]->second; } public: // hash policy double load_factor() const { return static_cast(size()) / max_size(); } double max_load_factor() const { return max_load_factor_; } void expand() const { ++size_level_; std::vector temp; temp.resize(primes_[size_level_]); for (auto w : container_) { if (nullptr != w) { auto hashed = find_hash(w->first); temp[hashed] = w; } } container_ = std::move(temp); } private: // helper functions size_type find_hash(const key_type& key) const { const size_t csz = container_.size(); size_t count = 0; size_t hashed = hasher_(key) % csz; while (nullptr != container_[hashed] and container_[hashed]->first != key) { hashed = (hashed + ++count) % csz; } return hashed; } bool construct_new_on_position(const size_type pos, const key_type& key) { empty_ = false; ++size_; container_[pos] = std::make_shared(std::make_pair(key, mapped_type())); return true; } private: const hasher hasher_ = hasher(); mutable size_t size_level_ = 0; mutable std::vector container_; static const size_t primes_[]; bool empty_ = true; size_type size_ = 0; double max_load_factor_ = 0.75; }; template const size_t hash_map::primes_[] = {7, 17, 29, 53, 101, 211, 401, 809, 1601, 3203}; // ... int main() { hash_map test; test[1]; test[2] = 2; std::cout << test[1] << ' ' << test[2] << std::endl; return 0; } ================================================ FILE: c-cpp/18_hashtable/hashtable.c ================================================ #include #include #include #include #include /* One implementation of hash table with linear probing. */ #define HASH_SHIFT 4 #define HASH_SIZE (1 << HASH_SHIFT) #define HASH_MASK (HASH_SIZE - 1) struct hash_table { unsigned int used; unsigned long entry[HASH_SIZE]; }; void hash_table_reset(struct hash_table *table) { int i; table->used = 0; for (i = 0; i < HASH_SIZE; i++) table->entry[i] = ~0; } unsigned int hash_function(unsigned long value) { return value & HASH_MASK; } void dump_hash_table(struct hash_table *table) { int i; for (i = 0; i < HASH_SIZE; i++) { if (table->entry[i] == ~0) printf("%2u: nil \n", i); else printf("%2u:%10lu -> %2u\n", i, table->entry[i], hash_function(table->entry[i])); } } void hash_function_test() { int i; srandom(time(NULL)); for (i = 0; i < 10; i++) { unsigned long val = random(); printf("%10lu -> %2u\n", val, hash_function(val));; } } unsigned int next_probe(unsigned int prev_key) { return (prev_key + 1) & HASH_MASK; } void next_probe_test() { int i; unsigned int key1, key2; key1 = 0; for (i = 0; i < HASH_SIZE; i++) { key2 = next_probe(key1); printf("%2u -> %2u\n", key1, key2); key1 = key2; } } void hash_table_add(struct hash_table *table, unsigned long value) { unsigned int key = hash_function(value); if (table->used >= HASH_SIZE) return; while (table->entry[key] != ~0) key = next_probe(key); table->entry[key] = value; table->used++; } unsigned int hash_table_slot(struct hash_table *table, unsigned long value) { int i; unsigned int key = hash_function(value); for (i = 0; i < HASH_SIZE; i++) { if (table->entry[key] == value || table->entry[key] == ~0) break; key = next_probe(key); } return key; } bool hash_table_find(struct hash_table *table, unsigned long value) { return table->entry[hash_table_slot(table, value)] == value; } void hash_table_del(struct hash_table *table, unsigned long value) { unsigned int i, j, k; if (!hash_table_find(table, value)) return; i = j = hash_table_slot(table, value); while (true) { table->entry[i] = ~0; do { j = next_probe(j); if (table->entry[j] == ~0) return; k = hash_function(table->entry[j]); } while ((i <= j) ? (i < k && k <= j) : (i < k || k <= j)); table->entry[i] = table->entry[j]; i = j; } table->used++; } void hash_table_add_test() { struct hash_table table; hash_table_reset(&table); hash_table_add(&table, 87645); printf("Table has%s 87645\n", hash_table_find(&table, 87645) ? "":"n't"); printf("Table has%s 87647\n", hash_table_find(&table, 87647) ? "":"n't"); } void hash_table_del_test1() { struct hash_table table; hash_table_reset(&table); hash_table_add(&table, 0x1ff0); hash_table_add(&table, 0x2ff0); hash_table_add(&table, 0x3ff0); dump_hash_table(&table); printf("=== Remove 0x1ff0\n"); hash_table_del(&table, 0x1ff0); dump_hash_table(&table); } void hash_table_del_test2() { struct hash_table table; hash_table_reset(&table); hash_table_add(&table, 0x1ff0); hash_table_add(&table, 0x1ff1); hash_table_add(&table, 0x1ff2); hash_table_add(&table, 0x2ff0); dump_hash_table(&table); printf("=== Remove 0x1ff0\n"); hash_table_del(&table, 0x1ff0); dump_hash_table(&table); } int main() { //hash_function_test(); //next_probe_test(); //hash_table_add_test(); hash_table_del_test2(); return 0; } ================================================ FILE: c-cpp/18_hashtable/listhash/listhash.c ================================================ /************************************************************************* > File Name: listhash.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-07 > Desc: ************************************************************************/ #include #include #include #include #include"listhash.h" #ifdef MEMORY_TEST #include #endif hashtab * hashtab_create(int size,hash_key_func hash_value, keycmp_func keycmp,hash_node_free_func hash_node_free) { hashtab * h = NULL; int i = 0; if ((size < 0) || (hash_value == NULL) || (keycmp == NULL)) { return NULL; } h = (hashtab *)malloc(sizeof(hashtab)); if (h == NULL) { return NULL; } h->htables = (hashtab_node **)malloc(size * sizeof(hashtab_node*)); if (h->htables == NULL) { return NULL; } h->size = size; h->nel = 0; h->hash_value = hash_value; h->keycmp = keycmp; h->hash_node_free = hash_node_free; for (i = 0; i < size; i++) { h->htables[i] = NULL; } return h; } void hashtab_destory(hashtab *h) { int i = 0; hashtab_node * cur = NULL; hashtab_node * tmp = NULL; if (h == NULL) { return; } for (i = 0; i size; i++) { cur = h->htables[i]; while (cur != NULL) { tmp = cur; cur = cur->next; h->hash_node_free(tmp); } h->htables[i] = NULL; } free(h->htables); free(h); return; } int hashtab_insert(hashtab * h,void *key,void *data) { unsigned int hvalue = 0; int i = 0; hashtab_node *cur = NULL; hashtab_node *prev = NULL; hashtab_node *newnode = NULL; if ((h == NULL) || (key == NULL) || (data == NULL)) { return 1; } /*获取hash 数值*/ hvalue = h->hash_value(h,key); cur = h->htables[hvalue]; /*hash桶中元素是从小到大排列的,找到要插入的位置*/ while((cur != NULL) && (h->keycmp(h,key,cur->key) > 0)) { prev = cur; cur = cur->next; } /*如果key和当前key比对一致,直接返回,数据已经存在*/ if ((cur != NULL) && (h->keycmp(h,key,cur->key) == 0)) { return 2; } newnode = (hashtab_node *)malloc(sizeof(hashtab_node)); if (newnode == NULL) { return 3; } newnode->key = key; newnode->data = data; if (prev == NULL) { newnode->next = h->htables[hvalue]; h->htables[hvalue] = newnode; } else { newnode->next = prev->next; prev->next = newnode; } h->nel++; return 0; } hashtab_node *hashtab_delete(hashtab *h, void *key) { int hvalue = 0; int i = 0; hashtab_node *cur = NULL; hashtab_node *prev = NULL; if ((h == NULL) || (key == NULL)) { return NULL; } /*获取hash 数值*/ hvalue = h->hash_value(h,key); cur = h->htables[hvalue]; /*hash桶中元素是从小到大排列的,找到要插入的位置*/ while((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0)) { if (h->keycmp(h,key,cur->key) == 0) { if (prev == NULL) { h->htables[hvalue] = cur->next; } else { prev->next = cur->next; } return cur; } prev = cur; cur = cur->next; } return NULL; } void *hashtab_search(hashtab*h,void *key) { int hvalue = 0; int i = 0; hashtab_node *cur = NULL; if ((h == NULL) || (key == NULL)) { return NULL; } /*获取hash 数值*/ hvalue = h->hash_value(h,key); cur = h->htables[hvalue]; /*hash桶中元素是从小到大排列的,找到要插入的位置*/ while((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0)) { if (h->keycmp(h,key,cur->key) == 0) { return cur->data; } cur = cur->next; } return NULL; } void hashtab_dump(hashtab *h) { int i = 0; hashtab_node * cur = NULL; if (h == NULL) { return ; } printf("\r\n----开始--size[%d],nel[%d]------------",h->size,h->nel); for( i = 0; i < h->size; i ++) { printf("\r\n htables[%d]:",i); cur = h->htables[i]; while((cur != NULL)) { printf("key[%s],data[%s] ",cur->key,cur->data); cur = cur->next; } } printf("\r\n----结束--size[%d],nel[%d]------------",h->size,h->nel); } struct test_node { char key[80]; char data[80]; }; unsigned int siample_hash(const char *str) { register unsigned int hash = 0; register unsigned int seed = 131; while(*str) { hash = hash*seed + *str++; } return hash & (0x7FFFFFFF); } int hashtab_hvalue(hashtab *h,const void *key) { return (siample_hash(key) % h->size); } int hashtab_keycmp(hashtab *h,const void *key1,const void *key2) { return strcmp(key1,key2); } void hashtab_node_free(hashtab_node*node) { struct test_node * ptmp = NULL; ptmp = container(node->key,struct test_node,key); free(ptmp); free(node); } int main () { int i = 0; int res = 0; char *pres = NULL; hashtab_node * node = NULL; struct test_node *p = NULL; hashtab *h = NULL; #ifdef MEMORY_TEST setenv("MALLOC_TRACE","1.txt",1); mtrace(); #endif h = hashtab_create(5,hashtab_hvalue,hashtab_keycmp,hashtab_node_free); assert(h!= NULL); while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 和value,当可以等于\"quit\"时退出"); scanf("%s",p->key); scanf("%s",p->data); if(strcmp(p->key,"quit") == 0) { free(p); break; } res = hashtab_insert(h,p->key,p->data); if (res != 0) { free(p); printf("\r\n key[%s],data[%s] insert failed %d",p->key,p->data,res); } else { printf("\r\n key[%s],data[%s] insert success %d",p->key,p->data,res); } } hashtab_dump(h); while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 查询value的数值,当可以等于\"quit\"时退出"); scanf("%s",p->key); if(strcmp(p->key,"quit") == 0) { free(p); break; } pres = hashtab_search(h,p->key); if (pres == NULL) { printf("\r\n key[%s] search data failed",p->key); } else { printf("\r\n key[%s],search data[%s] success",p->key,pres); } free(p); } hashtab_dump(h); while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 删除节点的数值,当可以等于\"quit\"时退出"); scanf("%s",p->key); if(strcmp(p->key,"quit") == 0) { free(p); break; } node = hashtab_delete(h,p->key); if (node == NULL) { printf("\r\n key[%s] delete node failed ",p->key); } else { printf("\r\n key[%s],delete data[%s] success",node->key,node->data); h->hash_node_free(node); } free(p); hashtab_dump(h); } hashtab_destory(h); #ifdef MEMORY_TEST muntrace(); #endif return 0; } ================================================ FILE: c-cpp/18_hashtable/listhash/listhash.h ================================================ /************************************************************************* > File Name: listhash.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-06 > Desc: 根据linux内核模块hashtab编写用户层hashtab接口 linux-4.19.1\security\selinux\ss\hashtab.c linux-4.19.1\security\selinux\ss\hashtab.h ************************************************************************/ #ifndef __HASHTAB_H__ #define __HASHTAB_H__ typedef struct _hashtab_node { void * key; void * data; struct _hashtab_node *next; }hashtab_node; typedef struct _hashtab { hashtab_node **htables; /*哈希桶*/ int size; /*哈希桶的最大数量*/ int nel; /*哈希桶中元素的个数*/ int (*hash_value)(struct _hashtab *h,const void *key); /*哈希函数*/ int (*keycmp)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ void (*hash_node_free)(hashtab_node *node); }hashtab; #define HASHTAB_MAX_NODES (0xffffffff) typedef int (*hash_key_func)(struct _hashtab *h,const void *key); /*哈希函数*/ typedef int (*keycmp_func)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ typedef void (*hash_node_free_func)(hashtab_node *node); /*根据当前结构体元素的地址,获取到结构体首地址*/ #define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container(ptr,type,member) ({\ const typeof( ((type *)0)->member) *__mptr = (ptr);\ (type *) ( (char *)__mptr - offsetof(type,member));}) hashtab * hashtab_create(int size,hash_key_func hash_value, keycmp_func keycmp,hash_node_free_func hash_node_free); void hashtab_destory(hashtab *h); int hashtab_insert(hashtab * h,void *key,void *data); hashtab_node *hashtab_delete(hashtab *h, void *key); void *hashtab_search(hashtab*h,void *key); #endif ================================================ FILE: c-cpp/19_Dlisthash/Dlist.h ================================================ /************************************************************************* > File Name: Dlist.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-08 > Desc: linux内核源码双向链表实现include/linux/list.h ************************************************************************/ #ifndef _LIST_HEAD_H #define _LIST_HEAD_H // 双向链表节点 struct list_head { struct list_head *next, *prev; }; // 初始化节点:设置name节点的前继节点和后继节点都是指向name本身。 #define LIST_HEAD_INIT(name) { &(name), &(name) } // 定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。 #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) // 初始化节点:将list节点的前继节点和后继节点都是指向list本身。 static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } // 添加节点:将new插入到prev和next之间。 static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } // 添加new节点:将new添加到head之后,是new称为head的后继节点。 static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } // 添加new节点:将new添加到head之前,即将new添加到双链表的末尾。 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } // 从双链表中删除entry节点。 static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } // 从双链表中删除entry节点。 static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } // 从双链表中删除entry节点。 static inline void __list_del_entry(struct list_head *entry) { __list_del(entry->prev, entry->next); } // 从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。 static inline void list_del_init(struct list_head *entry) { __list_del_entry(entry); INIT_LIST_HEAD(entry); } // 用new节点取代old节点 static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } // 双链表是否为空 static inline int list_empty(const struct list_head *head) { return head->next == head; } // 获取"MEMBER成员"在"结构体TYPE"中的位置偏移 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) // 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) // 遍历双向链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #endif ================================================ FILE: c-cpp/19_Dlisthash/LinkedHashMap.c ================================================ /************************************************************************* > File Name: LinkedHashMap.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-08 > Desc: ************************************************************************/ #include #include #include #include #include "Dlist.h" #include "LinkedHashMap.h" LinkedHashMap *LinkedHashMap_Create(int size,int nel_max, hash_value_func hash_value,keycmp_func keycmp, hash_node_free_func hash_node_free) { int i = 0; LinkedHashMap *h = NULL; if ((size <= 0) || (hash_value == NULL) || (keycmp == NULL)) { return NULL; } h = (LinkedHashMap *)malloc(sizeof(LinkedHashMap)); if (h == NULL) { return NULL; } h->hTabs = (LiskedHashMapNode**)malloc(sizeof(LiskedHashMapNode*) *size); if (h->hTabs == NULL) { return NULL; } h->size = size; h->nel = 0; h->nel_max = nel_max; h->hash_value = hash_value; h->keycmp = keycmp; h->hash_node_free = hash_node_free; for (i = 0; i < size; i++) { h->hTabs[i] = NULL; } INIT_LIST_HEAD(&(h->header)); return h; } void LinkedHashMap_destory(LinkedHashMap *h) { struct list_head * pos = NULL; struct list_head * next = NULL; LiskedHashMapNode * ptmp = NULL; if (h == NULL) { return; } list_for_each_safe(pos,next,&h->header) { ptmp = container_of(pos,LiskedHashMapNode,Dlist_node); /*从双向链表中删除*/ list_del_init(pos); if (h->hash_node_free != NULL) { h->hash_node_free(ptmp,1); } } free(h->hTabs); free(h); return; } int LinkedHashMap_insert(LinkedHashMap *h,void *key,void *data) { int i = 0; int hPos = 0; struct list_head *pos = NULL; LiskedHashMapNode *cur = NULL; LiskedHashMapNode *prev = NULL; hPos = h->hash_value(h,key); cur = h->hTabs[hPos]; while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) { prev = cur; cur = cur->next; } if(cur == NULL) { /*链表节点满时,取表头节点,从当前哈希表和双向链表中都删除*/ if(h->nel_max == h->nel) { cur = LinkedHashMap_delete(h,list_entry(h->header.next,LiskedHashMapNode,Dlist_node)->key); assert(cur != NULL); /*释放节点key 和data的内容*/ h->hash_node_free(cur,0); } else/*链表不满时,创建新的节点*/ { cur = (LiskedHashMapNode *)malloc(sizeof(LiskedHashMapNode)); if (cur == NULL) { return 1; } } /*插入到hash桶中*/ if(prev == NULL) { cur->next = h->hTabs[hPos]; h->hTabs[hPos] = cur; } else { cur->next = prev->next; prev->next= cur; } h->nel++; } else { /*从双向链表中删除*/ list_del_init(&(cur->Dlist_node)); /*只删除key 和data的内存*/ h->hash_node_free(cur,0); } /*赋值*/ cur->key = key; cur->data = data; /*加的双向链表尾部*/ list_add_tail(&(cur->Dlist_node),&(h->header)); return 0; } LiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key) { int hPos = 0; struct list_head *pos = NULL; LiskedHashMapNode *cur = NULL; LiskedHashMapNode *prev = NULL; /*查找当前节点*/ hPos = h->hash_value(h,key); cur = h->hTabs[hPos]; while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) { prev = cur; cur = cur->next; } if (cur == NULL) { return NULL; } /*从哈希桶中删除*/ if(prev == NULL) { h->hTabs[hPos] = cur->next; } else { prev->next = cur->next; } /*从双向链表中删除*/ list_del_init(&(cur->Dlist_node)); h->nel--; return cur; } void *LinkedHashMap_search(LinkedHashMap *h,void *key) { int hPos = 0; LiskedHashMapNode *cur = NULL; /*查找当前节点*/ hPos = h->hash_value(h,key); cur = h->hTabs[hPos]; while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) { cur = cur->next; } if (cur == NULL) { return NULL; } /*从双向链表中删除节点,加入尾部*/ if (h->header.prev != &(cur->Dlist_node)) { list_del_init(&(cur->Dlist_node)); list_add_tail(&(cur->Dlist_node),&(h->header)); } return cur->data; } void LinkedHashMap__dump(LinkedHashMap *h) { int i = 0; LiskedHashMapNode * cur = NULL; struct list_head *pos = NULL; if (h == NULL) { return ; } printf("\r\n----开始--size[%d],nel[%d]------------",h->size,h->nel); for( i = 0; i < h->size; i ++) { printf("\r\n htables[%d]:",i); cur = h->hTabs[i]; while((cur != NULL)) { printf("key[%s],data[%s] ",cur->key,cur->data); cur = cur->next; } } printf("\r\n--------------------------------------------------------\r\n"); list_for_each(pos,&(h->header)) { cur = list_entry(pos,LiskedHashMapNode,Dlist_node); printf("key[%s] ",cur->key); } printf("\r\n----结束--size[%d],nel[%d]------------",h->size,h->nel); } struct test_node { char key[80]; char data[80]; }; unsigned int siample_hash(const char *str) { register unsigned int hash = 0; register unsigned int seed = 131; while(*str) { hash = hash*seed + *str++; } return hash & (0x7FFFFFFF); } int hashtab_hvalue(LinkedHashMap *h,const void *key) { return (siample_hash(key) % h->size); } int hashtab_keycmp(LinkedHashMap *h,const void *key1,const void *key2) { return strcmp(key1,key2); } void hashtab_node_free(LiskedHashMapNode *node,int flg) { struct test_node * ptmp = NULL; ptmp = list_entry(node->key,struct test_node,key); free(ptmp); if (flg) { free(node); } } int main () { int i = 0; int res = 0; char *pres = NULL; LiskedHashMapNode * node = NULL; struct test_node *p = NULL; LinkedHashMap *h = NULL; setenv("MALLOC_TRACE","1.txt",1); mtrace(); h = LinkedHashMap_Create(3,6,hashtab_hvalue,hashtab_keycmp,hashtab_node_free); assert(h!= NULL); while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 和value,当可以等于\"quit\"时退出"); scanf("%s",p->key); scanf("%s",p->data); if(strcmp(p->key,"quit") == 0) { free(p); break; } res = LinkedHashMap_insert(h,p->key,p->data); if (res != 0) { free(p); printf("\r\n key[%s],data[%s] insert failed %d",p->key,p->data,res); } else { printf("\r\n key[%s],data[%s] insert success %d",p->key,p->data,res); } LinkedHashMap__dump(h); } while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 查询value的数值,当可以等于\"quit\"时退出"); scanf("%s",p->key); if(strcmp(p->key,"quit") == 0) { free(p); break; } pres = LinkedHashMap_search(h,p->key); if (pres == NULL) { printf("\r\n key[%s] search data failed",p->key); } else { printf("\r\n key[%s],search data[%s] success",p->key,pres); } free(p); LinkedHashMap__dump(h); } while(1) { p = (struct test_node*)malloc(sizeof(struct test_node)); assert(p != NULL); printf("\r\n 请输入key 删除节点的数值,当可以等于\"quit\"时退出"); scanf("%s",p->key); if(strcmp(p->key,"quit") == 0) { free(p); break; } node = LinkedHashMap_delete(h,p->key); if (node == NULL) { printf("\r\n key[%s] delete node failed ",p->key); } else { printf("\r\n key[%s],delete data[%s] success",node->key,node->data); h->hash_node_free(node,1); } free(p); LinkedHashMap__dump(h); } LinkedHashMap_destory(h); muntrace(); return 0; } ================================================ FILE: c-cpp/19_Dlisthash/LinkedHashMap.h ================================================ /************************************************************************* > File Name: LinkedHashMap.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-08 > Desc: ************************************************************************/ #ifndef __LINKED_HASH_MAP__ #define __LINKED_HASH_MAP__ /*数据存放节点*/ typedef struct _lisked_hash_map_node { void *key; /*键*/ void *data; /*数据*/ZZ struct _lisked_hash_map_node *next; /*哈希冲突时,用来挂接后续节点*/ struct list_head Dlist_node;/*用来挂接双向链表*/ }LiskedHashMapNode; typedef struct _lisked_hash_map { LiskedHashMapNode **hTabs;/*哈希桶*/ struct list_head header;/*双向循环链表头*/ int size; /**/ int nel_max; /*支持最大节点数*/ int nel; /*当前节点数*/ int (*hash_value)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/ int (*keycmp)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ void (*hash_node_free)(LiskedHashMapNode *node,int flg);/*用来释放节点内存*/ }LinkedHashMap; typedef int (*hash_value_func)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/ typedef int (*keycmp_func)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ typedef void (*hash_node_free_func)(LiskedHashMapNode *node,int flg); LiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key); #endif ================================================ FILE: c-cpp/23_binarytree/binarytree.c ================================================ #include #include #include #include #include /* Implement binary tree in array */ #define MAX_TREE_NODES (1 << 8) struct node { int data; }; struct binary_tree { union { unsigned long nodes; struct node *n[MAX_TREE_NODES]; }; }; void init_binary_tree(struct binary_tree *tree) { int i; for(i = 0; i < MAX_TREE_NODES; i++) { tree->n[i] = NULL; } } struct node* create_node(int data) { struct node* n; n = malloc(sizeof(struct node)); if (n) n->data = data; return n; } void fake_a_tree(struct binary_tree* tree) { /* data is in ordered */ int i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5}; init_binary_tree(tree); /* root start at 1 */ for (i = 0; i < 10; i++) tree->n[i+1] = create_node(data[i]); tree->nodes = 10; } void _in_order(struct binary_tree* tree, int index) { if (!tree->n[index]) return; /* left child at (index << 1) */ _in_order(tree, index << 1); printf("[%2d]: %4d\n", index, tree->n[index]->data); /* right child at (index << 1) + 1 */ _in_order(tree, (index << 1) + 1); } void in_order(struct binary_tree* tree) { _in_order(tree, 1); } int main() { struct binary_tree tree; fake_a_tree(&tree); in_order(&tree); return 0; } ================================================ FILE: c-cpp/23_binarytree/tree/binarytree.c ================================================ /************************************************************************* > File Name: binarytree.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-12 > Desc: ************************************************************************/ #include #include #include #include #include"list_queue.h" typedef struct _treenode { int data; struct _treenode *lchild; struct _treenode *rchild; }Tnode,Tree; void binarytree_create(Tree **Root) { int a = 0; printf("\r\n输入节点数值((当输入为100时,当前节点创建完成))):"); scanf("%d",&a); if (a == 100) { *Root = NULL; } else { *Root = (Tnode *)malloc(sizeof(Tnode)); if (*Root == NULL) { return; } (*Root)->data = a; printf("\r\n create %d 的左孩子:",a); binarytree_create(&((*Root)->lchild)); printf("\r\n create %d 的右孩子:",a); binarytree_create(&((*Root)->rchild)); } return ; } void binarytree_destory(Tree *root) { if (root == NULL) { return; } binarytree_destory(root->lchild); binarytree_destory(root->rchild); free(root); } /*先序遍历:根结点--》左子树---》右子树*/ void binarytree_preorder(Tree *root) { if (root == NULL) { return; } printf(" %d ",root->data); binarytree_preorder(root->lchild); binarytree_preorder(root->rchild); return; } /*中序遍历:左子树--》跟节点---》右子树*/ void binarytree_inorder(Tree *root) { if (root == NULL) { return; } binarytree_inorder(root->lchild); printf(" %d ",root->data); binarytree_inorder(root->rchild); return; } /*后序遍历:左子树---》右子树-》根节点*/ void binarytree_postorder(Tree *root) { if (root == NULL) { return; } binarytree_postorder(root->lchild); binarytree_postorder(root->rchild); printf(" %d ",root->data); return; } void binarytree_levelorder(Tree * root) { list_queue *queue = NULL; Tnode * node = NULL; if(root == NULL) { return; } queue = list_queue_create(); /*根节点先入队*/ list_queue_enqueue(queue,(void *)root); while(!list_queue_is_empty(queue)) { list_queue_dequeue(queue,(void *)&node); printf(" %d ",node->data); if(node->lchild != NULL) { list_queue_enqueue(queue,(void *)node->lchild); } if(node->rchild != NULL) { list_queue_enqueue(queue,(void *)node->rchild); } } free(queue); } /*打印叶子节点*/ void binarytree_printfleaf(Tree *root) { if (root == NULL) { return; } if ((root->lchild == NULL) && (root->rchild == NULL)) { printf(" %d ",root->data); } else { binarytree_printfleaf(root->lchild); binarytree_printfleaf(root->rchild); } } /*打印叶子的个数*/ int binarytree_getleafnum(Tree*root) { if (root == NULL) { return 0; } if ((root->lchild == NULL) && (root->rchild == NULL)) { return 1; } return binarytree_getleafnum(root->lchild) + binarytree_getleafnum(root->rchild); } /*打印数的高度*/ int binarytree_gethigh(Tree *root) { int lhigh = 0; int rhigh = 0; if (root == NULL) { return 0; } lhigh = binarytree_gethigh(root->lchild); rhigh = binarytree_gethigh(root->rchild); return ((lhigh > rhigh)?(lhigh + 1):(rhigh + 1)); } int main() { Tree *root = NULL; setenv("MALLOC_TRACE","1.txt",1); mtrace(); printf("\r\n创建二叉树:"); binarytree_create(&root); printf("\r\n先序遍历二叉树:"); binarytree_preorder(root); printf("\r\n中序遍历二叉树:"); binarytree_inorder(root); printf("\r\n后序遍历二叉树:"); binarytree_postorder(root); printf("\r\n层次遍历二叉树:"); binarytree_levelorder(root); printf("\r\n打印二叉树叶子节点:"); binarytree_printfleaf(root); printf("\r\n打印二叉树叶子节点个数:%d",binarytree_getleafnum(root)); printf("\r\n打印二叉树高度:%d",binarytree_gethigh(root)); binarytree_destory(root); muntrace(); return 0; } ================================================ FILE: c-cpp/23_binarytree/tree/list_queue.c ================================================ /************************************************************************* > File Name: list_queue.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-13 > Desc: ************************************************************************/ #include #include #include #include"./list_queue.h" /*创建队列头*/ list_queue *list_queue_create() { list_queue * queue = NULL; queue = (list_queue *)malloc(sizeof(list_queue)); if(queue == NULL) { return NULL; } queue->num = 0; queue->head = NULL; queue->tail = NULL; return queue; } int list_queue_enqueue(list_queue *queue,void *data) { queue_node *ptmp = NULL; if(queue == NULL) { return -1; } ptmp = (queue_node *)malloc(sizeof(queue_node)); if (ptmp == NULL) { return -1; } ptmp->data = data; ptmp->next = NULL; if (queue->head == NULL) { queue->head = ptmp; } else { queue->tail->next = ptmp; } queue->tail = ptmp; queue->num++; return 0; } /*出队*/ int list_queue_dequeue(list_queue *queue,void **data) { queue_node * ptmp = NULL; if ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue)) { return -1; } *data = queue->head->data; ptmp = queue->head; queue->head = queue->head->next; queue->num--; if (queue->head == NULL) { queue->tail = NULL; } free(ptmp); return 0; } ================================================ FILE: c-cpp/23_binarytree/tree/list_queue.h ================================================ /************************************************************************* > File Name: list_queue.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-10-13 > Desc: ************************************************************************/ #ifndef LINK_LIST_QUEUE_H #define LINK_LIST_QUEUE_H typedef struct _list_queue_node { void *data; struct _list_queue_node *next; }queue_node; typedef struct _list_queue { int num; queue_node *head; queue_node *tail; }list_queue; #define list_queue_is_empty(queue) ((queue->num) == 0) list_queue *list_queue_create(); int list_queue_enqueue(list_queue *queue,void *data); int list_queue_dequeue(list_queue *queue,void **data); #endif ================================================ FILE: c-cpp/24_binarysearchtree/binary_search_tree.cpp ================================================ /* * Filename: /home/zwk/code/data_structrue/c++/tree/binary_search_tree/main.cpp * Path: /home/zwk/code/data_structrue/c++/tree/binary_search_tree * Created Date: Wednesday, May 8th 2019, 11:04:48 pm * Author: zwk * * refer to https://time.geekbang.org/column/article/68334 */ #include using namespace std; typedef int DataType; struct treeNode { DataType data; treeNode *left = nullptr; treeNode *right = nullptr; }; class binarySearchTree { private: treeNode *root; int num; // tree node numbers public: binarySearchTree() : num(0) { root = new treeNode; root->left = nullptr; root->right = nullptr; } bool find(DataType it, treeNode *root) { if (nullptr == root) return false; if (it == root->data) { return true; } else if (it > root->data) { return find(it, root->right); } else { return find(it, root->left); } } bool find_data(DataType it) { return find(it, root); /* treeNode* p = root; while (p != nullptr) { if (it < p->data)p = p->left; else if (it>p->data)p = p->right; else return true; } return false; */ } DataType get_max() { if (nullptr == root) return NULL; treeNode *tmp = root; while (tmp->right != nullptr) { tmp = tmp->right; } return tmp->data; } DataType get_min() { if (nullptr == root) return NULL; treeNode *tmp = root; while (tmp->left != nullptr) { tmp = tmp->left; } return tmp->data; } void insert_data(DataType it) // 利用二分查找的思想,借助树的结构使用递归 { if (0 == num) { root->data = it; num++; return; } treeNode *p = root; while (p != nullptr) { if (it < p->data) { if (nullptr == p->left) { p->left = new treeNode; p->left->data = it; num++; return; } p = p->left; } else { if (nullptr == p->right) { p->right = new treeNode; p->right->data = it; num++; return; } p = p->right; } } } DataType get_prenode(DataType it) { if (nullptr == root) return NULL; if (it == root->data) return NULL; treeNode *p = root; treeNode *pp = nullptr; while (p != nullptr) { if (p->data < it) { pp = p; // label parent root p = p->right; } else if (p->data > it) { pp = p; // label parent root p = p->left; } else { break; } } return ((nullptr == p) ? NULL : pp->data); } DataType get_postnode(DataType it) { if (nullptr == root) return -1; treeNode *p = root; while (p != nullptr) { if (p->data < it) { p = p->right; } else if (p->data > it) { p = p->left; } else { break; } } if (nullptr == p) { return -1; } else if (p->left != nullptr) { return p->left->data; } else if (p->right != nullptr) { return p->right->data; } else { return NULL; } } void mid_order(treeNode *rt) { if (nullptr == rt) return; mid_order(rt->left); cout << rt->data << '\t'; mid_order(rt->right); } void order() { if (nullptr == root) return; return mid_order(root); } int get_high(treeNode *rt) { int lhigh = 0; int rhigh = 0; if (nullptr == rt) return 0; lhigh = get_high(rt->left); rhigh = get_high(rt->right); return ((lhigh > rhigh) ? (lhigh + 1) : (rhigh + 1)); } int high() { if (nullptr == root) return 1; return get_high(root); } void delet(DataType it) { if (NULL == root) return; treeNode *p = root; treeNode *pp = NULL; //pp记录的是p的父节点 while (p != NULL && p->data != it) { pp = p; if (it > p->data) p = p->right; else p = p->left; } if (p == NULL) return; //没有找到 //删除的节点有两个子节点 if (p->left != NULL && p->right != NULL) { treeNode *minP = p->right; treeNode *minPP = p; //记录P的父节点 while (minP->left != NULL) //寻找右子树最小节点 { minPP = minP; minP = minP->left; } // 注意这里,非常巧妙的办法。只是换值。“换汤不换药” // 用后继节点替换到要删除节点的位置。 然后就变成删除后继节点的问题了。为了逻辑统一 代码书写简洁。我们把后继节点赋给了p p->data = minP->data; //将minP的值替换到p中 //将p换到叶节点上,使用叶节点方法进行删除, 而且最小节点肯定没有左节点,叶节点删除方法参见后面的代码。 p = minP; pp = minPP; } //删除节点是叶节点或者是仅有一个节点 treeNode *child; if (p->left != NULL) child = p->left; else if (p->right != NULL) child = p->right; else child = NULL; if (NULL == pp) root = child; //删除的是根节点 else if (p == pp->left) pp->left = child; else pp->right = child; } }; int main() { binarySearchTree my_tree; // must input in the order of layers my_tree.insert_data(33); my_tree.insert_data(16); my_tree.insert_data(50); my_tree.insert_data(13); my_tree.insert_data(18); my_tree.insert_data(34); my_tree.insert_data(58); my_tree.insert_data(15); my_tree.insert_data(17); my_tree.insert_data(25); my_tree.insert_data(51); my_tree.insert_data(66); my_tree.insert_data(19); my_tree.insert_data(27); my_tree.insert_data(55); if (my_tree.find_data(25)) { cout << "找到了数字25" << endl; } else { cout << "没有找到数字25" << endl; } my_tree.delet(13); my_tree.delet(18); my_tree.delet(55); cout << "Max: " << my_tree.get_max() << endl; cout << "Min: " << my_tree.get_min() << endl; cout << "pre node of 17 is " << my_tree.get_prenode(17) << endl; cout << "pre node of 51 is " << my_tree.get_prenode(51) << endl; cout << "pre node of 33 is " << my_tree.get_prenode(33) << endl; cout << "post node of 19 is " << my_tree.get_postnode(19) << endl; cout << "post node of 25 is " << my_tree.get_postnode(25) << endl; cout << "post node of 58 is " << my_tree.get_postnode(58) << endl; cout << "post node of 58 is " << my_tree.get_postnode(51) << endl; my_tree.order(); cout << "high of tree is " << my_tree.high() << endl; return 0; } ================================================ FILE: c-cpp/24_binarysearchtree/binarysearchtree.c ================================================ /************************************************************************* > File Name: binarysearchtree.c > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-12 > Desc: ************************************************************************/ #include #include #include #include #include"binarysearchtree.h" bstree *bstree_create(compare_fuc compare,destory_fuc destory) { bstree *tree = NULL; tree = (bstree*)malloc(sizeof(bstree)); if (tree == NULL) { return NULL; } tree->size = 0; tree->compare = compare; tree->destory = destory; tree->root = NULL; return tree; } bstree_node *bstree_search(bstree *tree,mytype data) { bstree_node *node = NULL; int res = 0; if ((tree == NULL) || (bstree_is_empty(tree))) { return NULL; } node = tree->root; while(node != NULL) { res = tree->compare(data,node->data); if(res == 0) { return node; } else if (res > 0) { node = node->rchild; } else { node = node->lchild; } } return NULL; } int bstree_insert(bstree * tree, mytype data) { bstree_node *node = NULL; bstree_node *tmp = NULL; int res = 0; if (tree == NULL) { return -1; } node = (bstree_node *)malloc(sizeof(bstree_node)); if (node == NULL) { return -2; } node->data = data; node->lchild = NULL; node->rchild = NULL; /*如果二叉树为空,直接挂到根节点*/ if (bstree_is_empty(tree)) { tree->root = node; tree->size++; return 0; } tmp = tree->root; while(tmp != NULL) { res = tree->compare(data,tmp->data); if (res > 0) /*去右孩子查找*/ { if (tmp->rchild == NULL) { tmp->rchild = node; tree->size++; return 0; } tmp = tmp->rchild; } else /*去左孩子查找*/ { if(tmp->lchild == NULL) { tmp->lchild = node; tree->size++; return 0; } tmp = tmp->lchild; } } return -3; } int bstree_delete(bstree *tree,mytype data) { bstree_node *node = NULL;/*要删除的节点*/ bstree_node *pnode = NULL;/*要删除节点的父节点*/ bstree_node *minnode = NULL;/*要删除节点的父节点*/ bstree_node *pminnode = NULL;/*要删除节点的父节点*/ mytype tmp = 0; int res = 0; if ((tree == NULL) || (bstree_is_empty(tree))) { return -1; } node = tree->root; while ((node != NULL) && ((res = tree->compare(data,node->data)) != 0)) { pnode = node; if(res > 0) { node = node->rchild; } else { node = node->lchild; } } /*说明要删除的节点不存在*/ if (node == NULL) { return -2; } /*1、如果要删除node有2个子节点,需要找到右子树的最小节点minnode, * 更新minnode和node节点数据,这样minnode节点就是要删除的节点 * 再更新node和pnode节点指向要删除的节点*/ if ((node->lchild != NULL) && (node->rchild != NULL)) { minnode = node->rchild; pminnode = node; while(minnode->lchild != NULL) { pminnode = minnode; minnode = minnode->lchild; } /*node 节点和minnode节点数据互换*/ tmp = node->data; node->data = minnode->data; minnode->data = tmp; /*更新要删除的节点和其父节点*/ node = minnode; pnode = pminnode; } /*2、当前要删除的节点只有左孩子或者右孩子时,直接父节点的直向删除的节点*/ if (node->lchild != NULL) { minnode = node->lchild; } else if (node->rchild != NULL) { minnode = node->rchild; } else { minnode = NULL; } if (pnode == NULL)/*当要删除的时根节点时,*/ { tree->root = minnode; } else if (pnode->lchild == node) { pnode->lchild = minnode; } else { pnode->rchild = minnode; } tree->size--; free (node); return 0; } /*采用递归方式删除节点*/ void bstree_destory_node(bstree *tree,bstree_node *root) { if (root == NULL) { return; } bstree_destory_node(tree,root->lchild); bstree_destory_node(tree,root->rchild); free(root); } /*二叉搜索树销毁*/ void bstree_destory(bstree *tree) { bstree_destory_node(tree,tree->root); free(tree); return; } /*中序遍历打印树节点*/ void bstree_inorder_node(bstree_node *root) { bstree_node *node = NULL; if (root == NULL) { return; } bstree_inorder_node(root->lchild); printf(" %d ",root->data); bstree_inorder_node(root->rchild); return; } void bstree_dump(bstree *tree) { bstree_node *node = NULL; if ((tree == NULL) || (bstree_is_empty(tree))) { printf("\r\n 当前树是空树"); } printf("\r\nSTART-----------------%d------------\r\n",tree->size); bstree_inorder_node(tree->root); printf("\r\nEND---------------------------------",tree->size); } int bstree_compare(mytype key1,mytype key2) { if (key1 == key2) { return 0; } else if (key1 > key2) { return 1; } else { return -1; } } int main() { bstree *tree = NULL; bstree_node *node = NULL; mytype data = 0; int res = 0; setenv("MALLOC_TRACE","1.txt",1); mtrace(); tree = bstree_create(bstree_compare,NULL); assert(tree != NULL); while(1) { printf("\r\n插入一个数字,输入100时退出:"); scanf("%d",&data); if(data == 100)break; res = bstree_insert(tree,data); printf("\r\n %d 插入%s成功",data,(res != 0)?("不"):(" ")); } bstree_dump(tree); while(1) { printf("\r\n查询一个数字,输入100时退出:"); scanf("%d",&data); if(data == 100)break; node = bstree_search(tree,data); printf("\r\n %d %s存在树中",data,(node == NULL)?("不"):(" ")); } bstree_dump(tree); while(1) { printf("\r\n删除一个数字,输入100时退出:"); scanf("%d",&data); if(data == 100)break; res = bstree_delete(tree,data); printf("\r\n %d 删除%s成功",data,(res != 0)?("不"):(" ")); bstree_dump(tree); } bstree_destory(tree); muntrace(); return 0; } ================================================ FILE: c-cpp/24_binarysearchtree/binarysearchtree.h ================================================ /************************************************************************* > File Name: binarysearchtree.h > Author: jinshaohui > Mail: jinshaohui789@163.com > Time: 18-11-12 > Desc: ************************************************************************/ #ifndef __BINARY_SEARCH_TREE__ #define __BINARY_SEARCH_TREE__ typedef int mytype; typedef struct _bstree_node { mytype data; struct _bstree_node *lchild; struct _bstree_node *rchild; }bstree_node; typedef struct _bstree { int size; int (*compare)(mytype key1,mytype key2); int (*destory)(mytype data); bstree_node *root; }bstree; typedef int (*compare_fuc)(mytype key1,mytype key2); typedef int (*destory_fuc)(mytype data); #define bstree_is_empty(tree) (tree->size == 0) bstree *bstree_create(compare_fuc compare,destory_fuc destory); #endif ================================================ FILE: c-cpp/24_binarysearchtree/bst.c ================================================ #include #include #include #include #include enum child_dir { left_child, right_child, root, }; struct node { unsigned long data; struct node *left; struct node *right; }; struct root { struct node *r; }; void dump(struct node *node, int level, enum child_dir dir) { if (!node) return; dump(node->right, level + 1, right_child); if (dir == left_child) printf("%*s\n", level*3, "|"); printf("%*s - %05lu\n", level*3, " ", node->data); if (dir == right_child) printf("%*s\n", level*3, "|"); dump(node->left, level + 1, left_child); } struct node* find(struct root *root, unsigned long data) { struct node* n = root->r; while (n) { if (n->data == data) return n; if (data < n->data) n = n->left; else n = n->right; } return NULL; } struct node* new_node(unsigned long data) { struct node *n; n = malloc(sizeof(struct node)); n->data = data; n->left = n->right = NULL; return n; } void insert(struct root *root, struct node *new) { struct node *parent; if (!root->r) { root->r = new; return; } parent = root->r; while (true) { /* Don't support duplicate data */ if (new->data == parent->data) break; if (new->data < parent->data) { if (!parent->left) { parent->left = new; break; } parent = parent->left; } else { if (!parent->right) { parent->right = new; break; } parent = parent->right; } } } struct node* delete(struct root *root, unsigned long data) { struct node *n = root->r, **p = &root->r; struct node *child; while (n && n->data != data) { if (data < n->data) { p = &n->left; n = n->left; } else { p = &n->right; n = n->right; } } if (!n) return NULL; if (n->left && n->right) { struct node *rn = n->right, **rp = &n->right; while (rn->left) { rp = &rn->left; rn = rn->left; } n->data = rn->data; n = rn; p = rp; } child = n->left ? n->left : n->right; *p = child; return NULL; } void insert_test() { struct root tree; struct node* n; tree.r = NULL; insert(&tree, new_node(9)); insert(&tree, new_node(5)); insert(&tree, new_node(2)); insert(&tree, new_node(8)); insert(&tree, new_node(18)); insert(&tree, new_node(13)); insert(&tree, new_node(21)); insert(&tree, new_node(20)); dump(tree.r, 0, root); n = find(&tree, 18); if (n && n->data == 18) printf("Get 18\n"); } void delete_test() { struct root tree; struct node* n; tree.r = NULL; insert(&tree, new_node(9)); insert(&tree, new_node(5)); insert(&tree, new_node(2)); insert(&tree, new_node(8)); insert(&tree, new_node(18)); insert(&tree, new_node(13)); insert(&tree, new_node(21)); insert(&tree, new_node(20)); dump(tree.r, 0, root); delete(&tree, 20); printf("Delete 20\n"); dump(tree.r, 0, root); delete(&tree, 9); printf("Delete 9\n"); dump(tree.r, 0, root); } int main() { //insert_test(); delete_test(); return 0; } ================================================ FILE: c-cpp/24_tree/Trie.c ================================================ /************************************************************************* > Author: Liu Zhang > Mail: lz-850610@163.com > Time: 2019-05-10 > Desc: 字典树实现 ************************************************************************/ #include #include #include #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status; typedef struct Node { char data; struct Node *children[26]; Status end; } Trie, *TriePtr; void Init(TriePtr *T) { (*T) = (TriePtr)malloc(sizeof(Trie)); (*T)->data = '/'; (*T)->end = FALSE; } void Insert(TriePtr T, char *str) { int index; char c; while(c = *str++) { index = c - 'a'; if (T->children[index] == NULL) { TriePtr Node; Node = (TriePtr)malloc(sizeof(Trie)); Node->data = c; Node->end = FALSE; T->children[index] = Node; } T = T->children[index]; } T->end = TRUE; } Status Search(TriePtr T, char *str) { int index; char c; while(c = *str++) { index = c - 'a'; if (T->children[index] == NULL) { return FALSE; } T = T->children[index]; } if (T->end) { return TRUE; } else { return FALSE; } } int main(int argc, char const *argv[]) { TriePtr T; Init(&T); char *str = "hello"; char *str2 = "hi"; Insert(T, str); printf("str is search %d\n", Search(T, str)); printf("str2 is search %d\n", Search(T, str2)); return 0; } ================================================ FILE: c-cpp/24_tree/binarysearchtree.c ================================================ /************************************************************************* > Author: Liu Zhang > Mail: lz-850610@163.com > Time: 2019-05-10 > Desc: 二叉搜索树实现 ************************************************************************/ #include #include #include #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status; typedef char ElemType; typedef struct node { ElemType data; struct node *lchild, *rchild; } BTree, *BTreePtr; /**************** 插入 **********************/ Status Insert(BTreePtr *T, ElemType e) { BTreePtr p; if (*T == NULL) { *T = (BTreePtr)malloc(sizeof(BTree)); (*T)->data = e; return TRUE; } else { p = *T; while ( p != NULL) { if (e > p->data) { if (p->rchild == NULL) { p->rchild = (BTreePtr) malloc (sizeof(BTree)); p->rchild->data = e; return TRUE; } p = p->rchild; } else { if (p->lchild == NULL) { p->lchild = (BTreePtr) malloc (sizeof(BTree)); p->lchild->data = e; return TRUE; } p = p->lchild; } } } return FALSE; } /**************** 删除 **********************/ Status Delete(BTreePtr T, ElemType e) { BTreePtr p, pp, minP, minPP, child; child = NULL; p = T; pp = NULL; while ( (p != NULL) && (p->data != e) ) { pp = p; if (e > p->data) { p = p->rchild; } else { p = p->lchild; } } if (p == NULL) return FALSE; //双节点 if ((p->lchild != NULL) && (p->rchild != NULL)) { minPP = p; minP = p->rchild; while (minP->lchild != NULL) { minPP = minP; minP = minP->lchild; } p->data = minP->data; minPP->lchild = minP->rchild; free(minP); return TRUE; } //有一个节点 if ((p->lchild != NULL) || (p->rchild != NULL)) { //应该将原有的pp同child连接在一起 if (p->lchild) { child = p->lchild; } else { child = p->rchild; } if(pp->data>p->data) { pp->lchild=child; } else { pp->rchild=child; } free(p); return TRUE; } //没有节点 if (pp->lchild == p) {//这里面临pp除p以外的节点为null的情况 pp->lchild = child; } else { pp->rchild = child; } return TRUE; } /**************** 查找 **********************/ Status Find(BTreePtr T, ElemType e) { if (T == NULL) return FALSE; while ((T != NULL) && (T->data != e)) { if (e > T->data) { T = T->rchild; } else { T = T->lchild; } } if (T) { return TRUE; } else { return FALSE; } } /**************** 最大值 **********************/ ElemType FindMax(BTreePtr T) { ElemType max; while(T != NULL) { max = T->data; T = T->rchild; } return max; } /**************** 最小值 **********************/ ElemType FindMin(BTreePtr T) { ElemType min; while(T != NULL) { min = T->data; T = T->lchild; } return min; } void PreOrderTraverse(BTreePtr T)//前序遍历二叉树 { if (T == NULL) return; if(T) { printf("%d ",T->data); PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } } void DestroyTree(BTreePtr T) { if (T) { if (T->lchild) { DestroyTree(T->lchild); } if(T->rchild) { DestroyTree(T->rchild); } free(T); T = NULL; } } /***************** 执行测试 *************************/ int main(int argc, char const *argv[]) { BTreePtr T; T = NULL; int a[] = {33, 16, 50, 13, 18, 34, 58, 15, 17, 25, 51, 66, 19, 27, 55}; int i; for (i = 0; i < 15; i++) { Insert(&T, a[i]); } printf("Max is %d\n", FindMax(T)); printf("Min is %d\n", FindMin(T)); Delete(T, 18); Delete(T, 13); PreOrderTraverse(T); DestroyTree(T); return 0; } ================================================ FILE: c-cpp/28_heap/heap.c ================================================ #include #include #include #include #include /* Implement heap */ #define MAX_HEAP_SIZE (1 << 8) struct element { int data; }; struct heap { union { unsigned long elements; struct element *elem[MAX_HEAP_SIZE]; }; }; void init_heap(struct heap *heap) { int i; for(i = 0; i < MAX_HEAP_SIZE; i++) { heap->elem[i] = NULL; } } void dump_heap(struct heap *heap, int index) { struct element *elem; int level; if (index > heap->elements) return; elem = heap->elem[index]; level = fls(index); dump_heap(heap, index * 2 + 1); if (!(index % 2) && index != 1) printf("%*s\n", level*3, "|"); printf("%*s - %05d\n", level*3, " ", elem->data); if (index % 2 && index != 1) printf("%*s\n", level*3, "|"); dump_heap(heap, index * 2); } void dump(struct heap *heap, int elements) { int i; for (i = 1; i <= elements; i++) printf("[%02d]: %4d\n", i, heap->elem[i]->data); } struct element* create_element(int data) { struct element *elem; elem = malloc(sizeof(struct element)); if (elem) elem->data = data; return elem; } void fake_a_heap(struct heap *heap) { /* data is in ordered */ int i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5}; init_heap(heap); /* root start at 1 */ for (i = 0; i < 10; i++) heap->elem[i+1] = create_element(data[i]); heap->elements = 10; } void swap(struct heap *heap, int i, int j) { struct element *tmp; tmp = heap->elem[j]; heap->elem[j] = heap->elem[i]; heap->elem[i] = tmp; } void heapify(struct heap *heap, int parent) { struct element **elem = heap->elem; int elements = heap->elements; int left, right, max; while (true) { left = parent * 2; right = left + 1; max = parent; if (left <= elements && elem[max]->data < elem[left]->data) max = left; if (right <= elements && elem[max]->data < elem[right]->data) max = right; if (max == parent) break; swap(heap, max, parent); parent = max; } } void build_heap(struct heap *heap) { int i; for (i = heap->elements / 2; i >= 1; i--) heapify(heap, i); } int heap_sort(struct heap *heap) { int elements = heap->elements; while (heap->elements) { swap(heap, 1, heap->elements); heap->elements--; heapify(heap, 1); } return elements; } int main() { struct heap heap; int elements; fake_a_heap(&heap); dump_heap(&heap, 1); printf("After Heapify:\n"); build_heap(&heap); dump_heap(&heap, 1); printf("After Heap sort:\n"); elements = heap_sort(&heap); dump(&heap, elements); return 0; } ================================================ FILE: c-cpp/30_Graph/graph.c ================================================ #include #include #include #include #include struct vertex; struct vertex_adjs { struct vertex *v; struct vertex_adjs *next; }; struct vertex { int data; struct vertex_adjs *adj; }; #define MAX_GRAPH_VERTEX (1 << 8) struct graph { struct vertex *vxs[MAX_GRAPH_VERTEX]; }; void init_graph(struct graph *graph) { int i; for (i = 0; i < MAX_GRAPH_VERTEX; i++) graph->vxs[i] = NULL; } struct vertex *create_vertex(int data) { struct vertex *v; v = malloc(sizeof(struct vertex)); if (v) { v->data = data; v->adj = NULL; } return v; } struct vertex_adjs *create_vertex_adj(struct vertex *v) { struct vertex_adjs *v_adj; v_adj = malloc(sizeof(struct vertex_adjs)); if (!v_adj) return NULL; v_adj->v = v; v_adj->next = NULL; return v_adj; } void insert_adj(struct vertex *v, struct vertex *adj) { struct vertex_adjs **v_adj; v_adj = &v->adj; while (*v_adj) v_adj = &(*v_adj)->next; *v_adj = create_vertex_adj(adj); } void dump_raw(struct graph *graph) { int i; for (i = 0; i < MAX_GRAPH_VERTEX; i++) { struct vertex *v = graph->vxs[i]; struct vertex_adjs *adj; if (v == NULL) continue; printf("Vertex[%02d]: %8d ->", i, v->data); adj = v->adj; while (adj) { printf(" %8d,", adj->v->data); adj = adj->next; } printf("\n"); } } /* 1 ----- 2 ----- 3 | / | / | / | / | / | / | / | / | / | / 4 ----- 5 */ void fake_a_graph(struct graph *graph) { int i; init_graph(graph); for (i = 0; i < 5; i++) graph->vxs[i] = create_vertex(i+1); /* connect 1 -> 2, 1 -> 4 */ insert_adj(graph->vxs[0], graph->vxs[1]); insert_adj(graph->vxs[0], graph->vxs[3]); /* connect 2 -> 1, 2 -> 3, 2 -> 5, 2 -> 4 */ insert_adj(graph->vxs[1], graph->vxs[0]); insert_adj(graph->vxs[1], graph->vxs[2]); insert_adj(graph->vxs[1], graph->vxs[4]); insert_adj(graph->vxs[1], graph->vxs[3]); /* connect 3 -> 2, 3 -> 5 */ insert_adj(graph->vxs[2], graph->vxs[1]); insert_adj(graph->vxs[2], graph->vxs[4]); /* connect 4 -> 1, 4 -> 2, 4 -> 5 */ insert_adj(graph->vxs[3], graph->vxs[0]); insert_adj(graph->vxs[3], graph->vxs[1]); insert_adj(graph->vxs[3], graph->vxs[4]); /* connect 5 -> 4, 5 -> 2, 5 -> 3 */ insert_adj(graph->vxs[4], graph->vxs[3]); insert_adj(graph->vxs[4], graph->vxs[1]); insert_adj(graph->vxs[4], graph->vxs[3]); } int main() { struct graph g; fake_a_graph(&g); dump_raw(&g); return 0; } ================================================ FILE: c-cpp/bst.c ================================================ #include #include #include #include #include enum child_dir { left_child, right_child, root, }; struct node { unsigned long data; struct node *left; struct node *right; }; struct root { struct node *r; }; void dump(struct node *node, int level, enum child_dir dir) { if (!node) return; dump(node->right, level + 1, right_child); if (dir == left_child) printf("%*s\n", level*3, "|"); printf("%*s - %05lu\n", level*3, " ", node->data); if (dir == right_child) printf("%*s\n", level*3, "|"); dump(node->left, level + 1, left_child); } struct node* find(struct root *root, unsigned long data) { struct node* n = root->r; while (n) { if (n->data == data) return n; if (data < n->data) n = n->left; else n = n->right; } return NULL; } struct node* new_node(unsigned long data) { struct node *n; n = malloc(sizeof(struct node)); n->data = data; n->left = n->right = NULL; return n; } void insert(struct root *root, struct node *new) { struct node *parent; if (!root->r) { root->r = new; return; } parent = root->r; while (true) { /* Don't support duplicate data */ if (new->data == parent->data) break; if (new->data < parent->data) { if (!parent->left) { parent->left = new; break; } parent = parent->left; } else { if (!parent->right) { parent->right = new; break; } parent = parent->right; } } } struct node* delete(struct root *root, unsigned long data) { struct node *n = root->r, **p = &root->r; struct node *child; while (n && n->data != data) { if (data < n->data) { p = &n->left; n = n->left; } else { p = &n->right; n = n->right; } } if (!n) return NULL; if (n->left && n->right) { struct node *rn = n->right, **rp = &n->right; while (rn->left) { rp = &rn->left; rn = rn->left; } n->data = rn->data; n = rn; p = rp; } child = n->left ? n->left : n->right; *p = child; return NULL; } void insert_test() { struct root tree; struct node* n; tree.r = NULL; insert(&tree, new_node(9)); insert(&tree, new_node(5)); insert(&tree, new_node(2)); insert(&tree, new_node(8)); insert(&tree, new_node(18)); insert(&tree, new_node(13)); insert(&tree, new_node(21)); insert(&tree, new_node(20)); dump(tree.r, 0, root); n = find(&tree, 18); if (n && n->data == 18) printf("Get 18\n"); } void delete_test() { struct root tree; struct node* n; tree.r = NULL; insert(&tree, new_node(9)); insert(&tree, new_node(5)); insert(&tree, new_node(2)); insert(&tree, new_node(8)); insert(&tree, new_node(18)); insert(&tree, new_node(13)); insert(&tree, new_node(21)); insert(&tree, new_node(20)); dump(tree.r, 0, root); delete(&tree, 20); printf("Delete 20\n"); dump(tree.r, 0, root); delete(&tree, 9); printf("Delete 9\n"); dump(tree.r, 0, root); } int main() { //insert_test(); delete_test(); return 0; } ================================================ FILE: csharp/05-array/Array.cs ================================================ using System; namespace algo05_array { public sealed class Array where T : IComparable { private T[] _data; private readonly int _capacity; private int _length; public Array(int capacity) { _data = new T[capacity]; _capacity = capacity; _length = 0; } // length of list public int Length => _length; // insert a new element at specified index (index start from 0) public void Insert(int index, T newElem) { if (_length == _capacity) { throw new OutOfMemoryException("List has no more space"); } if (index < 0 || index > _length) { throw new IndexOutOfRangeException("Index was outside the bounds of the list"); } // to loop array from end until finding the target index for (int k = _length; k > index; k--) { _data[k] = _data[k - 1]; } _data[index] = newElem; _length++; } // get an element base on index public T Find(int index) { if (index < 0 || index > _length - 1) { throw new IndexOutOfRangeException("Index was outside the bounds of the list"); } return _data[index]; } // search the node which matches specified value and return its index (index start from 0) public int IndexOf(T val) { if (_length == 0) return -1; if (_data[0].Equals(val)) return 0; if (_data[_length - 1].CompareTo(val) == 0) return _length - 1; int start = 1; while (start < _length - 1) { if (_data[start].CompareTo(val) == 0) return start; start++; } return -1; } // delete an node which is on the specified index public bool Delete(int index) { if (index < 0 || index > _length - 1) { throw new IndexOutOfRangeException("Index must be in the bound of list"); } T deletedElem = _data[index]; if (index < _length - 1) { for (int k = index; k < _length; k++) { _data[k] = _data[k + 1]; } } _length--; return true; } // delete an node public bool Delete(T val) { int index; for (index = 0; index < Length; index++) { if (_data[index].CompareTo(val) == 0) break; } if (index >= Length) return false; return Delete(index); } // clear list public void Clear() { _data = new T[_capacity]; _length = 0; } } } ================================================ FILE: csharp/05-array/algo05_array.csproj ================================================  netcoreapp2.2 false ================================================ FILE: csharp/06-linkedlist/LRUWithArray.cs ================================================ using algo05_array; namespace algo06_linked_list { /// /// 使用数组实现LRU缓存淘汰算法 /// public class LRUWithArray { private readonly int _capacity; public LRUWithArray(int capacity) { _capacity = capacity; CachedList = new Array(capacity); } public Array CachedList { get; } public void Set(int val) { // 找出该值在缓存中的索引位置 int idx = CachedList.IndexOf(val); // 存在该缓存值 if (idx != -1) { CachedList.Delete(idx); CachedList.Insert(0, val); return; } // 不存在该缓存值 if (CachedList.Length == _capacity) { // 缓存已满,删除最后一个元素 CachedList.Delete(CachedList.Length - 1); } // 将新缓存插入到表头 CachedList.Insert(0, val); } } } ================================================ FILE: csharp/06-linkedlist/LRUWithLinkedList.cs ================================================ namespace algo06_linked_list { /// /// 使用单链表实现LRU缓存淘汰算法 /// public class LRUWithLinkedList { private readonly int _capacity; /// /// 构造函数 /// /// 缓存容量 public LRUWithLinkedList(int capacity = 10) { _capacity = capacity; } public SingleLinkedList CachedList { get; } = new SingleLinkedList(); /// /// 存储缓存数据 /// /// public void Set(int val) { // 尝试删除匹配到和给定值相等的结点,并返回 var deletedNode = CachedList.Delete(value: val); // 数据在缓存中存在,从原位置删除,然后插入到表头 if (deletedNode != null) { CachedList.Insert(1, val); return; } // 数据不存在 if (CachedList.Length == _capacity) { // 链表已满,删除尾结点,将新数据插入到头部 CachedList.Delete(CachedList.Length); } // 将新缓存值插入到表头 CachedList.Insert(1, val); } } } ================================================ FILE: csharp/06-linkedlist/LRU缓存实现思路.txt ================================================ 实现LRU缓存淘汰算法思路: 维护一个有序单链表,越靠近链尾的数据是最早访问的。 当有一个新的数据被访问时, 1. 如果数据在缓存中,则将其从原位置删除,然后插入到表头; 2. 如果数据不在缓存中,有两种情况: 1) 链表未满,则将数据插入到表头; 2) 链表已满,则删除尾结点,将新数据插入到表头。 ================================================ FILE: csharp/06-linkedlist/SingleLinkedList.cs ================================================ using System; namespace algo06_linked_list { /// /// 单链表的插入、删除、清空、查找 /// /// public class SingleLinkedList where T : IComparable { public SingleLinkedList() { Head = new ListNode(default(T)); } public SingleLinkedList(params T[] list) { Head = new ListNode(default(T)); if (list == null) return; var p = Head; foreach (var item in list) { var q = new ListNode(item); p.Next = q; p = q; } Length = list.Length; } // Head node public ListNode First => Head.Next; public ListNode Head { get; } public int Length { get; private set; } public ListNode Insert(int position, T newElem) { if (position < 1 || position > Length + 1) { throw new IndexOutOfRangeException("Position must be in bound of list"); } var p = Head; int j = 1; while (p != null && j < position) { p = p.Next; ++j; } var newNode = new ListNode(newElem); newNode.Next = p.Next; p.Next = newNode; Length++; return newNode; } public ListNode Find(int position) { ListNode p = First; int j = 1; while (p != null && j < position) { p = p.Next; j++; } if (p == null || j > position) { return null; } return p; } public ListNode Find(T elem) { ListNode p = Head.Next; while (p != null) { if (p.Value.CompareTo(elem) == 0) return p; p = p.Next; } return null; } public ListNode Delete(T value) { ListNode cur = Head; while (cur.Next != null && cur.Next.Value.CompareTo(value) != 0) { cur = cur.Next; } if (cur.Next == null) return null; var q = cur.Next; cur.Next = q.Next; Length--; return q; } public ListNode Delete(int position) { if (position < 1 || position > Length) { return null; } var p = First; int j = 1; while (p != null && j < position - 1) { p = p.Next; ++j; } var q = p.Next; p.Next = q.Next; Length--; return q; } public void Clear() { var cur = Head; while (cur.Next != null) { var q = cur.Next; cur.Next = null; cur = q; } Length = 0; } } public class ListNode { public ListNode(T value) { Value = value; } public T Value { get; } public ListNode Next { get; set; } } } ================================================ FILE: csharp/06-linkedlist/algo06_linked_list.csproj ================================================  netcoreapp2.2 false ================================================ FILE: csharp/07-linkedlist/_07_linkedlist/SingleLinkedListAlgo.cs ================================================ using System; using algo06_linked_list; namespace algo07_linkedlist { /// /// 单链表常用算法操作 /// 1. 链表反转 /// 2. 环的检测 /// 3. 两个有序链表的合并 /// 4. 删除链表倒数第n个结点 /// 5. 求链表的中间结点 /// /// public class SingleLinkedListAlgo : SingleLinkedList where T : IComparable { public SingleLinkedListAlgo(params T[] list) : base(list) { } /// /// 链表反转 /// public void Reverse() { if (Length <= 1) return; ListNode p = First; ListNode q = First.Next; ListNode r = null; p.Next = null; while (q != null) { r = q.Next; q.Next = p; p = q; q = r; } Head.Next = p; } /// /// 环的检测 /// /// /// 用快慢两个指针,快指针每次移动2个结点,慢指针每次移动1个结点,当两个指针相遇时,说明存在环。 /// LeetCode 编号: 141 /// public bool HasCycle() { if (Length == 0) return false; var slow = Head.Next; var fast = Head.Next.Next; while (fast != null && slow != null && fast != slow) { fast = fast.Next?.Next; slow = slow.Next; } bool ret = fast == slow; return ret; } /// /// 合并两个有序链表(从小到大) /// /// LeetCode 编号: 21 /// /// public SingleLinkedListAlgo Merge(SingleLinkedListAlgo listAlgo) { if (listAlgo == null) return null; var root = new SingleLinkedListAlgo(); ListNode pointer = root.Head; // 总是向新链表的尾结点 var head1 = listAlgo.First; var head2 = this.First; while (head1 != null && head2 != null) { if (head1.Value.CompareTo(head2.Value) < 0) { pointer.Next = head1; head1 = head1.Next; } else { pointer.Next = head2; head2 = head2.Next; } pointer = pointer.Next; // 指向尾结点 } if (head1 != null) { pointer.Next = head1; } if (head2 != null) { pointer.Next = head2; } return root; } /// /// 删除倒数第n个结点 /// /// /// 用快慢两个指针,快指针比慢指针早n个结点,然后再同步移动两个指针,当快指针指向尾结点时,慢指针就是将要删除的结点 /// LeetCode 编号: 19 /// /// public void RemoveNthNodeFromEnd(int n) { if (n < 1 || n > Length) return; ListNode preNode = Head; ListNode curNode = Head; for (int i = 0; i < n; i++) { curNode = curNode.Next; } if (curNode == null) return; while (curNode.Next != null) { preNode = preNode.Next; curNode = curNode.Next; } preNode.Next = preNode.Next.Next; } /// /// 链表的中间结点 /// /// /// 思路: 利用快慢指针,快指针步长2,慢指针步长1,当快指针到达尾结点时,慢指针正好到达中间结点 /// LeetCode 编号: 876 /// /// public ListNode FindMiddleNode() { if (First?.Next == null) return null; ListNode slowPointer = First; ListNode fastPointer = First.Next; while (fastPointer.Next?.Next != null) { fastPointer = fastPointer.Next.Next; slowPointer = slowPointer.Next; } slowPointer = slowPointer.Next; return slowPointer; } } } ================================================ FILE: csharp/07-linkedlist/_07_linkedlist/algo07_linkedlist.csproj ================================================  netcoreapp2.2 ================================================ FILE: csharp/08-stack/algo08_stack/ArrayStack.cs ================================================ using System; namespace algo08_stack { public class ArrayStack { private readonly int _capacity; private readonly T[] _data; private int _top = -1; // 指向栈顶元素,当为-1时表示栈为空 public ArrayStack(int capacity) { _capacity = capacity; _data = new T[capacity]; } public int Count => _top + 1; public void Push(T val) { if (Count == _capacity) throw new InvalidOperationException("Stack full."); _top++; _data[_top] = val; } public T Pop() { if (_top == -1) throw new InvalidOperationException("Stack empty."); T val = _data[_top]; _top--; return val; } } } ================================================ FILE: csharp/08-stack/algo08_stack/LinkedStack.cs ================================================ using System; namespace algo08_stack { public class LinkedStack { private StackListNode _top; public int Count { get; private set; } public void Push(T val) { var newNode = new StackListNode(val); newNode.Next = _top; _top = newNode; Count++; } public T Pop() { if (_top == null) throw new InvalidOperationException("Stack empty"); T val = _top.Value; _top = _top.Next; Count--; return val; } public void Clear() { while (Count > 0) { Pop(); } } } public class StackListNode { public StackListNode(T nodeValue) { Value = nodeValue; } public T Value { get; set; } public StackListNode Next { get; set; } } } ================================================ FILE: csharp/08-stack/algo08_stack/LinkedStackBrowser.cs ================================================ namespace algo08_stack { /// /// 利用链栈实现浏览器怎么进后退 /// public class LinkedStackBrowser { private readonly LinkedStack _backStack = new LinkedStack(); private readonly LinkedStack _forwardStack = new LinkedStack(); public void Open(string url) { _backStack.Push(url); _forwardStack.Clear(); } public string Backward() { if (_backStack.Count == 0) return string.Empty; string url = _backStack.Pop(); _forwardStack.Push(url); return url; } public string Forward() { if (_forwardStack.Count == 0) return string.Empty; string url = _forwardStack.Pop(); _backStack.Push(url); return url; } } } ================================================ FILE: csharp/08-stack/algo08_stack/algo08_stack.csproj ================================================  netcoreapp2.2 ================================================ FILE: csharp/Tests/_05_array_tests/Array.Tests.cs ================================================ using System; using algo05_array; using Xunit; using Xunit.Abstractions; namespace _05_array_tests { public class ArrayTests { private readonly Array _sqList; private readonly ITestOutputHelper _output; public ArrayTests(ITestOutputHelper output) { _sqList = new Array(10); _output = output; } private void PrintList() { for (int idx = 0; idx < _sqList.Length; idx++) { var elem = _sqList.Find(idx); _output.WriteLine(elem.ToString()); } } [Fact] public void Length_Equal_1_After_InsertOneElement() { _sqList.Insert(0, 1); Assert.True(_sqList.Length == 1); } [Fact] public void Insert_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length() { _sqList.Insert(0, 1); Exception ex = Assert.Throws(() => _sqList.Insert(3, 2)); Assert.IsType(ex); } [Fact] public void Insert_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero() { Exception ex = Assert.Throws(() => _sqList.Insert(-1, 1)); Assert.IsType(ex); } [Fact] public void Insert_ThrowIndexOutOfRangeException_When_List_Is_Full() { _sqList.Insert(0, 11); _sqList.Insert(1, 10); _sqList.Insert(2, 9); _sqList.Insert(3, 8); _sqList.Insert(4, 7); _sqList.Insert(5, 6); _sqList.Insert(6, 5); _sqList.Insert(7, 4); _sqList.Insert(8, 3); _sqList.Insert(9, 2); PrintList(); Exception ex = Assert.Throws(() => _sqList.Insert(10, 101)); Assert.IsType(ex); } [Fact] public void Delete_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero() { Exception ex = Assert.Throws(() => _sqList.Delete(-1)); Assert.IsType(ex); } [Fact] public void Delete_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length() { _sqList.Insert(0, 11); _sqList.Insert(1, 22); Exception ex = Assert.Throws(() => _sqList.Delete(3)); Assert.IsType(ex); } [Fact] public void Delete_First_Element_Success() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); bool ret = _sqList.Delete(1); Assert.True(ret); } [Fact] public void Delete_Last_Element_Success() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); bool ret = _sqList.Delete(3); Assert.True(ret); } [Fact] public void Delete_Middle_Element() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); bool ret = _sqList.Delete(2); Assert.True(ret); } [Fact] public void Find_ThrowsIndexOutOfRangeException_When_Index_LessThan_Zero() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); Exception ex = Assert.Throws(() => _sqList.Find(-1)); Assert.IsType(ex); } [Fact] public void Find_ThrowsIndexOutOfRangeException_When_Index_GreaterThan_Length() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); Exception ex = Assert.Throws(() => _sqList.Find(4)); Assert.IsType(ex); } [Fact] public void Find_Last_Position_Return_33() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); var elem = _sqList.Find(3); Assert.Equal(33, elem); } [Fact] public void Find_First_Element_Return_11() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); var elem = _sqList.Find(1); Assert.Equal(11, elem); } [Fact] public void IndexOf_Return_Negative_1_When_Element_Not_Exist() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); var elem = _sqList.IndexOf(55); Assert.Equal(-1, elem); } [Fact] public void IndexOf_Return_First_Index() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); var elem = _sqList.IndexOf(100); Assert.Equal(0, elem); } [Fact] public void IndexOf_Return_Last_Index() { _sqList.Insert(0, 100); _sqList.Insert(1, 11); _sqList.Insert(2, 22); _sqList.Insert(3, 33); var elem = _sqList.IndexOf(33); Assert.Equal(3, elem); } [Fact] public void Clear_Then_Length_Equal_Zero() { _sqList.Insert(0, 100); _sqList.Insert(1, 10); _sqList.Insert(2, 9); _sqList.Insert(3, 8); _sqList.Insert(4, 7); _sqList.Insert(5, 6); _sqList.Insert(6, 5); _sqList.Insert(7, 4); _sqList.Insert(8, 3); _sqList.Insert(9, 2); Assert.Equal(10, _sqList.Length); _sqList.Clear(); Assert.Equal(0, _sqList.Length); } } } ================================================ FILE: csharp/Tests/_05_array_tests/algo05_array_tests.csproj ================================================  netcoreapp2.2 false 05_array_tests ================================================ FILE: csharp/Tests/_06_linkedlist_tests/BaseLinkedListTests.cs ================================================ using System; using algo06_linked_list; namespace algo06_linkedlist_tests { public class BaseLinkedListTests { protected void PrintLinkedList (SingleLinkedList list) where T : IComparable { if (list == null) return; var p = list.First; while (p != null) { System.Console.WriteLine (p.Value); p = p.Next; } } } } ================================================ FILE: csharp/Tests/_06_linkedlist_tests/LRUWithArray.Tests.cs ================================================ using algo05_array; using algo06_linked_list; using Xunit; using Xunit.Abstractions; namespace algo06_linkedlist_tests { public class LRUWithArrayTests { private ITestOutputHelper _output; public LRUWithArrayTests(ITestOutputHelper output) { _output = output; } private void PrintList(Array list) { if (list == null) return; for (int idx = 0; idx < list.Length; idx++) { _output.WriteLine(list.Find(idx).ToString()); } } [Fact] public void LRU_Set_Value_When_Not_Existed() { var lru = new LRUWithArray(5); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(9); var list = lru.CachedList; PrintList(list); Assert.Equal(9, list.Find(0)); } [Fact] public void LRU_Set_Value_When_Existed() { var lru = new LRUWithArray(5); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(3); var list = lru.CachedList; PrintList(list); Assert.Equal(3, list.Find(0)); } [Fact] public void LRU_Set_Value_When_Full() { var lru = new LRUWithArray(5); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(9); lru.Set(8); var list = lru.CachedList; PrintList(list); Assert.Equal(8, list.Find(0)); } } } ================================================ FILE: csharp/Tests/_06_linkedlist_tests/LRUWithLinkedList.Tests.cs ================================================ using Xunit; using Xunit.Abstractions; using algo06_linked_list; namespace algo06_linkedlist_tests { public class LRUWithLinkedListTests : BaseLinkedListTests { [Fact] public void LRU_Set_Value_When_Not_Existed() { var lru = new LRUWithLinkedList(); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(9); var list = lru.CachedList; PrintLinkedList(list); Assert.Equal(9, list.First.Value); } [Fact] public void LRU_Set_Value_When_Existed() { var lru = new LRUWithLinkedList(); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(3); var list = lru.CachedList; PrintLinkedList(list); Assert.Equal(3, list.First.Value); } [Fact] public void LRU_Set_Value_When_Full() { var lru = new LRUWithLinkedList(5); lru.Set(1); lru.Set(3); lru.Set(5); lru.Set(7); lru.Set(9); lru.Set(8); var list = lru.CachedList; PrintLinkedList(list); Assert.Equal(8, list.First.Value); } } } ================================================ FILE: csharp/Tests/_06_linkedlist_tests/SingleLinkedList.Tests.cs ================================================ using System; using Xunit; using Xunit.Abstractions; using algo06_linked_list; namespace algo06_linkedlist_tests { public class SingleLinkedListTests : BaseLinkedListTests { [Fact] public void Insert_3_Elements_Return_Length_3() { var list = new SingleLinkedList("The", "Quick", "Brown"); PrintLinkedList(list); Assert.Equal(3, list.Length); } [Fact] public void Insert_Some_Elements_Then_Verify_First() { var list = new SingleLinkedList("The", "Quick", "Brown"); Assert.Equal("The", list.First.Value); } [Fact] public void Insert_Some_Elements_Then_Verify_Last() { var list = new SingleLinkedList("The", "Quick", "Brown"); Assert.Equal("Brown", list.First.Next.Next.Value); } [Fact] public void Find_Return_Null_When_Position_LessThan_1() { var list = new SingleLinkedList("The", "Quick", "Brown"); var node = list.Find(0); Assert.Null(node); } [Fact] public void Find_Return_Null_When_Position_GreaterThan_Length() { var list = new SingleLinkedList("The", "Quick", "Brown"); var node = list.Find(4); Assert.Null(node); } [Fact] public void Find_Return_Correct_When_Position_Valid() { var list = new SingleLinkedList("The", "Quick", "Brown"); var node = list.Find(2); Assert.Equal("Quick", node.Value); } [Fact] public void Delete_Return_Null_When_Position_LessThan_1() { var list = new SingleLinkedList("The", "Quick", "Brown"); var node = list.Delete(0); Assert.Null(node); } [Fact] public void Delete_Return_Null_When_Position_GreaterThan_Length() { var list = new SingleLinkedList("The", "Quick", "Brown"); var node = list.Delete(4); Assert.Null(node); } [Fact] public void Delete_By_Value_Success_When_Element_Exist() { var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); var deletedNode = list.Delete("over"); PrintLinkedList(list); Assert.Equal("over", deletedNode.Value); Assert.Equal(8, list.Length); } [Fact] public void Delete_By_Value_Success_When_Element_Not_Exist() { var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); var deletedNode = list.Delete("hello"); PrintLinkedList(list); Assert.Null(deletedNode); Assert.Equal(9, list.Length); } [Fact] public void Delete_By_Value_Success_When_Delete_First() { var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); var deletedNode = list.Delete("The"); PrintLinkedList(list); Assert.Equal("The", deletedNode.Value); Assert.Equal(8, list.Length); } [Fact] public void Delete_By_Value_Success_When_Delete_Last() { var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); var deletedNode = list.Delete("dog"); PrintLinkedList(list); Assert.Equal("dog", deletedNode.Value); Assert.Equal(8, list.Length); } [Fact] public void Delete_Success_When_Position_Valid() { var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); var node = list.Delete(3); PrintLinkedList(list); Assert.Equal("Brown", node.Value); Assert.Equal(8, list.Length); } [Fact] public void Clear_Length_Equal_0() { var list = new SingleLinkedList("The", "Quick", "Brown"); list.Clear(); Assert.Equal(0, list.Length); } [Fact] public void Clear_First_Is_Null() { var list = new SingleLinkedList("The", "Quick", "Brown"); list.Clear(); Assert.Null(list.First); } } } ================================================ FILE: csharp/Tests/_06_linkedlist_tests/algo06_linkedlist_tests.csproj ================================================  netcoreapp2.2 false ================================================ FILE: csharp/Tests/_07_linkedlist_tests/SingleLinkedListAlgo.Tests.cs ================================================ using System; using Xunit; using algo06_linkedlist_tests; using algo06_linked_list; using algo07_linkedlist; namespace algo07_linkedlist_tests { public class SingleLinkedListAlgoTests : BaseLinkedListTests { [Fact] public void Reverse_When_List_Is_Empty() { var list = new SingleLinkedListAlgo(); list.Reverse(); PrintLinkedList(list); Assert.Null(list.First); } [Fact] public void Reverse_When_List_Has_Many_Elements() { var list = new SingleLinkedListAlgo("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); list.Reverse(); PrintLinkedList(list); Assert.True(list.First.Value == "dog"); } [Fact] public void HasCycle_List_Empty() { var list = new SingleLinkedListAlgo("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", "dog"); bool hasCycle = list.HasCycle(); Assert.False(hasCycle); } [Fact] public void HasCycle_False_When_List_Length_1() { var list = new SingleLinkedListAlgo("The"); bool hasCycle = list.HasCycle(); Assert.False(hasCycle); } [Fact] public void HasCycle_False_When_List_Length_2() { var list = new SingleLinkedListAlgo("The", "Quick"); bool hasCycle = list.HasCycle(); Assert.False(hasCycle); } [Fact] public void HasCycle_True_When_List_Length_2() { var list = new SingleLinkedListAlgo(); var firstNode = list.Insert(1, "The"); var secondNode = list.Insert(2, "Quick"); secondNode.Next = firstNode; bool hasCycle = list.HasCycle(); Assert.True(hasCycle); } [Fact] public void HasCycle_False() { var linkList = new SingleLinkedListAlgo("The", "Quick", "Brown", "fox", "jumps", "over", "the", "lazy", "dog"); bool hasCycle = linkList.HasCycle(); Assert.False(hasCycle); } [Fact] public void HasCycle_True() { var list = new SingleLinkedListAlgo(); // 初始化一个具有环的链表 list.Insert(1, "The"); list.Insert(2, "Quick"); list.Insert(3, "Brown"); var fourthNode = list.Insert(4, "fox"); list.Insert(5, "jumps"); list.Insert(6, "over"); list.Insert(7, "the"); list.Insert(8, "lazy"); var last = list.Insert(9, "dog"); last.Next = fourthNode; bool hasCycle = list.HasCycle(); Assert.True(hasCycle); } [Fact] public void Merge() { var list1 = new SingleLinkedListAlgo(1, 2, 4); var list2 = new SingleLinkedListAlgo(1, 3, 4); var list3 = list1.Merge(list2); PrintLinkedList(list3); Assert.True(list1.First.Next.Next.Value == 2); } [Fact] public void Remove_2th_Node_From_End() { var list = new SingleLinkedListAlgo(1, 2, 3, 4, 5); list.RemoveNthNodeFromEnd(2); PrintLinkedList(list); Assert.True(list.First.Next.Next.Next.Value == 5); } [Fact] public void FindMiddleNode() { var list = new SingleLinkedListAlgo(1, 2, 3, 4, 5); ListNode middleNode = list.FindMiddleNode(); Assert.True(middleNode.Value == 3); } } } ================================================ FILE: csharp/Tests/_07_linkedlist_tests/algo07_linkedlist_tests.csproj ================================================  netcoreapp2.2 false ================================================ FILE: csharp/Tests/algo08_stack_tests/ArrayStack.Tests.cs ================================================ using System; using algo08_stack; using Xunit; using Xunit.Abstractions; namespace algo08_stack_tests { public class ArrayStackTests { private readonly ITestOutputHelper _output; public ArrayStackTests(ITestOutputHelper output) { _output = output; } private void PrintStackArray(ArrayStack list) { if (list.Count == 0) return; while (list.Count > 0) { T item = list.Pop(); _output.WriteLine(item.ToString()); } } [Fact] public void Push_3_Elements_Then_Length_Equal_3() { var stack = new ArrayStack(5); stack.Push(2); stack.Push(4); stack.Push(6); Assert.Equal(3, stack.Count); PrintStackArray(stack); } [Fact] public void Push_Throw_InvalidOperationException_When_Stack_Full() { var stack = new ArrayStack(5); stack.Push(2); stack.Push(4); stack.Push(6); stack.Push(8); stack.Push(10); Exception ex = Assert.Throws(() => stack.Push(11)); Assert.IsType(ex); PrintStackArray(stack); } [Fact] public void Pop_Throw_InvalidOperationException_When_Stack_Empty() { var stack = new ArrayStack(5); Exception ex = Assert.Throws(() => stack.Pop()); Assert.IsType(ex); PrintStackArray(stack); } [Fact] public void Pop_Valid_When_Stack_Not_Empty() { var stack = new ArrayStack(5); stack.Push(2); stack.Push(4); int val = stack.Pop(); Assert.Equal(4, val); PrintStackArray(stack); } } } ================================================ FILE: csharp/Tests/algo08_stack_tests/LinkedStack.Tests.cs ================================================ using System; using algo08_stack; using Xunit; using Xunit.Abstractions; namespace algo08_stack_tests { public class LinkedStackTests { private readonly ITestOutputHelper _output; public LinkedStackTests(ITestOutputHelper output) { _output = output; } private void PrintStackLinkedList(LinkedStack list) { if (list.Count == 0) return; while (list.Count > 0) { var val = list.Pop(); _output.WriteLine(val.ToString()); } } [Fact] public void Push_3_Elements_Then_Length_Equal_3() { var stack = new LinkedStack(); stack.Push(2); stack.Push(4); stack.Push(6); Assert.Equal(3, stack.Count); PrintStackLinkedList(stack); } [Fact] public void Pop_Throw_InvalidOperationException_When_Stack_Empty() { var stack = new LinkedStack(); Exception ex = Assert.Throws(() => stack.Pop()); Assert.IsType(ex); PrintStackLinkedList(stack); } [Fact] public void Pop_Valid_When_Stack_Not_Empty() { var stack = new LinkedStack(); stack.Push(2); stack.Push(4); var nodeVal = stack.Pop(); Assert.Equal(4, nodeVal); PrintStackLinkedList(stack); } } } ================================================ FILE: csharp/Tests/algo08_stack_tests/LinkedStackBrowser.Tests.cs ================================================ using algo08_stack; using Xunit; using Xunit.Abstractions; namespace algo08_stack_tests { public class LinkedStackBrowserTests { private readonly ITestOutputHelper _output; public LinkedStackBrowserTests(ITestOutputHelper output) { _output = output; } [Fact] public void Browser_Open_4_Links_Back_2_Return_Right_Link() { var browser = new LinkedStackBrowser(); browser.Open("www.google.com"); browser.Open("www.baidu.com"); browser.Open("www.qq.com"); browser.Open("www.dadu.com"); string url = browser.Backward(); url = browser.Backward(); Assert.Equal("www.qq.com", url); } [Fact] public void Browser_Open_4_Links_Back_4_Return_Empty() { var browser = new LinkedStackBrowser(); browser.Open("www.google.com"); browser.Open("www.baidu.com"); browser.Open("www.qq.com"); browser.Open("www.dadu.com"); browser.Backward(); browser.Backward(); browser.Backward(); browser.Backward(); string url = browser.Backward(); Assert.Equal(string.Empty, url); } [Fact] public void Browser_Forward_Before_End() { var browser = new LinkedStackBrowser(); browser.Open("www.google.com"); browser.Open("www.baidu.com"); browser.Open("www.qq.com"); browser.Open("www.dadu.com"); browser.Backward(); browser.Backward(); browser.Backward(); browser.Forward(); string url = browser.Forward(); Assert.Equal("www.qq.com", url); } [Fact] public void Browser_Forward_Until_End() { var browser = new LinkedStackBrowser(); browser.Open("www.google.com"); browser.Open("www.baidu.com"); browser.Open("www.qq.com"); browser.Open("www.dadu.com"); browser.Backward(); browser.Backward(); browser.Backward(); browser.Forward(); browser.Forward(); browser.Forward(); string url = browser.Forward(); Assert.Equal(string.Empty, url); } [Fact] public void Browser_Backward_And_Open_New_Then_Cannot_Forward() { var browser = new LinkedStackBrowser(); browser.Open("www.google.com"); browser.Open("www.baidu.com"); browser.Open("www.qq.com"); browser.Backward(); browser.Backward(); browser.Open("www.dadu.com"); string url = browser.Forward(); Assert.Equal(string.Empty, url); } } } ================================================ FILE: csharp/Tests/algo08_stack_tests/algo08_stack_tests.csproj ================================================  netcoreapp2.2 false ================================================ FILE: csharp/csharp.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo05_array", "05-array\algo05_array.csproj", "{B88033F6-FF08-434A-AED7-91F5CDB73402}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo06_linked_list", "06-linkedlist\algo06_linked_list.csproj", "{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo05_array_tests", "Tests\_05_array_tests\algo05_array_tests.csproj", "{14982212-49E4-4409-8BFD-2D8A2945BD83}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo06_linkedlist_tests", "Tests\_06_linkedlist_tests\algo06_linkedlist_tests.csproj", "{1B93D9C6-D6C1-4619-BFB9-D84C29099223}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo07_linkedlist", "07-linkedlist\_07_linkedlist\algo07_linkedlist.csproj", "{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo07_linkedlist_tests", "Tests\_07_linkedlist_tests\algo07_linkedlist_tests.csproj", "{66C3BC00-C135-4279-A666-A330A86F20D5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo08_stack", "08-stack\algo08_stack\algo08_stack.csproj", "{E080D481-C98E-43F3-B1D1-51DCF4CF7146}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo08_stack_tests", "Tests\algo08_stack_tests\algo08_stack_tests.csproj", "{6A249475-54EA-4039-9B0C-DC0E0A884C07}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.Build.0 = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.ActiveCfg = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.Build.0 = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.ActiveCfg = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.Build.0 = Debug|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.ActiveCfg = Release|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.Build.0 = Release|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.ActiveCfg = Release|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.Build.0 = Release|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.ActiveCfg = Release|Any CPU {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.Build.0 = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.ActiveCfg = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.Build.0 = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.ActiveCfg = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.Build.0 = Debug|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.Build.0 = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.ActiveCfg = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.Build.0 = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.ActiveCfg = Release|Any CPU {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.Build.0 = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.Build.0 = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.ActiveCfg = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.Build.0 = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.ActiveCfg = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.Build.0 = Debug|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.ActiveCfg = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.Build.0 = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.ActiveCfg = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.Build.0 = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.ActiveCfg = Release|Any CPU {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.Build.0 = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.ActiveCfg = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.Build.0 = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.ActiveCfg = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.Build.0 = Debug|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.Build.0 = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.ActiveCfg = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.Build.0 = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.ActiveCfg = Release|Any CPU {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.Build.0 = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.ActiveCfg = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.Build.0 = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.ActiveCfg = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.Build.0 = Debug|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.Build.0 = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.ActiveCfg = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.Build.0 = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.ActiveCfg = Release|Any CPU {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.Build.0 = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.ActiveCfg = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.Build.0 = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.ActiveCfg = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.Build.0 = Debug|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.Build.0 = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.ActiveCfg = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.Build.0 = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.ActiveCfg = Release|Any CPU {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.Build.0 = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.Build.0 = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.ActiveCfg = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.Build.0 = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.ActiveCfg = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.Build.0 = Debug|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.ActiveCfg = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.Build.0 = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.ActiveCfg = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.Build.0 = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.ActiveCfg = Release|Any CPU {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.Build.0 = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.ActiveCfg = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.Build.0 = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.ActiveCfg = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.Build.0 = Debug|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.Build.0 = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.ActiveCfg = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.Build.0 = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.ActiveCfg = Release|Any CPU {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {14982212-49E4-4409-8BFD-2D8A2945BD83} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} {1B93D9C6-D6C1-4619-BFB9-D84C29099223} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} {66C3BC00-C135-4279-A666-A330A86F20D5} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} {6A249475-54EA-4039-9B0C-DC0E0A884C07} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} EndGlobalSection EndGlobal ================================================ FILE: csharp/csharp.sln.DotSettings.user ================================================  True <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> True True ================================================ FILE: f21 ================================================ // 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; } ================================================ FILE: go/.gitkeep ================================================ ================================================ FILE: go/05_array/.gitkeep ================================================ ================================================ FILE: go/05_array/array.go ================================================ package _5_array import ( "errors" "fmt" ) /** * 1) 数组的插入、删除、按照下标随机访问操作; * 2)数组中的数据是int类型的; * * Author: leo */ type Array struct { data []int length uint } //为数组初始化内存 func NewArray(capacity uint) *Array { if capacity == 0 { return nil } return &Array{ data: make([]int, capacity, capacity), length: 0, } } func (this *Array) Len() uint { return this.length } //判断索引是否越界 func (this *Array) isIndexOutOfRange(index uint) bool { if index >= uint(cap(this.data)) { return true } return false } //通过索引查找数组,索引范围[0,n-1] func (this *Array) Find(index uint) (int, error) { if this.isIndexOutOfRange(index) { return 0, errors.New("out of index range") } return this.data[index], nil } //插入数值到索引index上 func (this *Array) Insert(index uint, v int) error { if this.Len() == uint(cap(this.data)) { return errors.New("full array") } if index != this.length && this.isIndexOutOfRange(index) { return errors.New("out of index range") } for i := this.length; i > index; i-- { this.data[i] = this.data[i-1] } this.data[index] = v this.length++ return nil } func (this *Array) InsertToTail(v int) error { return this.Insert(this.Len(), v) } //删除索引index上的值 func (this *Array) Delete(index uint) (int, error) { if this.isIndexOutOfRange(index) { return 0, errors.New("out of index range") } v := this.data[index] for i := index; i < this.Len()-1; i++ { this.data[i] = this.data[i+1] } this.length-- return v, nil } //打印数列 func (this *Array) Print() { var format string for i := uint(0); i < this.Len(); i++ { format += fmt.Sprintf("|%+v", this.data[i]) } fmt.Println(format) } ================================================ FILE: go/05_array/array_test.go ================================================ package _5_array import ( "testing" ) func TestInsert(t *testing.T) { capacity := 10 arr := NewArray(uint(capacity)) for i := 0; i < capacity-2; i++ { err := arr.Insert(uint(i), i+1) if nil != err { t.Fatal(err.Error()) } } arr.Print() arr.Insert(uint(6), 999) arr.Print() arr.InsertToTail(666) arr.Print() } func TestDelete(t *testing.T) { capacity := 10 arr := NewArray(uint(capacity)) for i := 0; i < capacity; i++ { err := arr.Insert(uint(i), i+1) if nil != err { t.Fatal(err.Error()) } } arr.Print() for i := 9; i >= 0; i-- { _, err := arr.Delete(uint(i)) if nil != err { t.Fatal(err) } arr.Print() } } func TestFind(t *testing.T) { capacity := 10 arr := NewArray(uint(capacity)) for i := 0; i < capacity; i++ { err := arr.Insert(uint(i), i+1) if nil != err { t.Fatal(err.Error()) } } arr.Print() t.Log(arr.Find(0)) t.Log(arr.Find(9)) t.Log(arr.Find(11)) } ================================================ FILE: go/06_linkedlist/.gitkeep ================================================ ================================================ FILE: go/06_linkedlist/palindrome-linked-list.go ================================================ /** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func isPalindrome(head *ListNode) bool { var slow *ListNode = head var fast *ListNode = head var prev *ListNode = nil var temp *ListNode = nil if (head == nil || head.Next == nil) { return true } for (fast != nil && fast.Next !=nil){ fast = fast.Next.Next temp = slow.Next slow.Next = prev prev = slow slow = temp } // 快的先跑完,同时反转了一半链表,剪短 if fast != nil { slow = slow.Next // 处理余数,跨过中位数 // prev 增加中 2->1->nil } var l1 *ListNode = prev var l2 *ListNode = slow for (l1 != nil && l2 !=nil && l1.Val == l2.Val){ l1 = l1.Next l2 = l2.Next } return (l1 == nil && l2 == nil) } ================================================ FILE: go/06_linkedlist/palindrome.go ================================================ package _6_linkedlist /* 思路1:开一个栈存放链表前半段 时间复杂度:O(N) 空间复杂度:O(N) */ func isPalindrome1(l *LinkedList) bool { lLen := l.length if lLen == 0 { return false } if lLen == 1 { return true } s := make([]string, 0, lLen/2) cur := l.head for i := uint(1); i <= lLen; i++ { cur = cur.next if lLen%2 != 0 && i == (lLen/2+1) { //如果链表有奇数个节点,中间的直接忽略 continue } if i <= lLen/2 { //前一半入栈 s = append(s, cur.GetValue().(string)) } else { //后一半与前一半进行对比 if s[lLen-i] != cur.GetValue().(string) { return false } } } return true } /* 思路2 找到链表中间节点,将前半部分转置,再从中间向左右遍历对比 时间复杂度:O(N) */ func isPalindrome2(l *LinkedList) bool { lLen := l.length if lLen == 0 { return false } if lLen == 1 { return true } var isPalindrome = true step := lLen / 2 var pre *ListNode = nil cur := l.head.next next := l.head.next.next for i := uint(1); i <= step; i++ { tmp := cur.GetNext() cur.next = pre pre = cur cur = tmp next = cur.GetNext() } mid := cur var left, right *ListNode = pre, nil if lLen%2 != 0 { // right = mid.GetNext() } else { right = mid } for nil != left && nil != right { if left.GetValue().(string) != right.GetValue().(string) { isPalindrome = false break } left = left.GetNext() right = right.GetNext() } //复原链表 cur = pre pre = mid for nil != cur { next = cur.GetNext() cur.next = pre pre = cur cur = next } l.head.next = pre return isPalindrome } ================================================ FILE: go/06_linkedlist/palindrome_test.go ================================================ package _6_linkedlist import "testing" func TestPalindrome1(t *testing.T) { strs := []string{"heooeh", "hello", "heoeh", "a", ""} for _, str1 := range strs { l := NewLinkedList() for _, c := range str1 { l.InsertToTail(string(c)) } l.Print() t.Log(isPalindrome1(l)) } } func TestPalindrome2(t *testing.T) { strs := []string{"heooeh", "hello", "heoeh", "a", ""} for _, str1 := range strs { l := NewLinkedList() for _, c := range str1 { l.InsertToTail(string(c)) } l.Print() t.Log(isPalindrome2(l)) l.Print() } } ================================================ FILE: go/06_linkedlist/singlelinkedlist.go ================================================ package _6_linkedlist import "fmt" /* 单链表基本操作 author:leo */ type ListNode struct { next *ListNode value interface{} } type LinkedList struct { head *ListNode length uint } func NewListNode(v interface{}) *ListNode { return &ListNode{nil, v} } func (this *ListNode) GetNext() *ListNode { return this.next } func (this *ListNode) GetValue() interface{} { return this.value } func NewLinkedList() *LinkedList { return &LinkedList{NewListNode(0), 0} } //在某个节点后面插入节点 func (this *LinkedList) InsertAfter(p *ListNode, v interface{}) bool { if nil == p { return false } newNode := NewListNode(v) oldNext := p.next p.next = newNode newNode.next = oldNext this.length++ return true } //在某个节点前面插入节点 func (this *LinkedList) InsertBefore(p *ListNode, v interface{}) bool { if nil == p || p == this.head { return false } cur := this.head.next pre := this.head for nil != cur { if cur == p { break } pre = cur cur = cur.next } if nil == cur { return false } newNode := NewListNode(v) pre.next = newNode newNode.next = cur this.length++ return true } //在链表头部插入节点 func (this *LinkedList) InsertToHead(v interface{}) bool { return this.InsertAfter(this.head, v) } //在链表尾部插入节点 func (this *LinkedList) InsertToTail(v interface{}) bool { cur := this.head for nil != cur.next { cur = cur.next } return this.InsertAfter(cur, v) } //通过索引查找节点 func (this *LinkedList) FindByIndex(index uint) *ListNode { if index >= this.length { return nil } cur := this.head.next var i uint = 0 for ; i < index; i++ { cur = cur.next } return cur } //删除传入的节点 func (this *LinkedList) DeleteNode(p *ListNode) bool { if nil == p { return false } cur := this.head.next pre := this.head for nil != cur { if cur == p { break } pre = cur cur = cur.next } if nil == cur { return false } pre.next = p.next p = nil this.length-- return true } //打印链表 func (this *LinkedList) Print() { cur := this.head.next format := "" for nil != cur { format += fmt.Sprintf("%+v", cur.GetValue()) cur = cur.next if nil != cur { format += "->" } } fmt.Println(format) } ================================================ FILE: go/06_linkedlist/singlelinkedlist_test.go ================================================ package _6_linkedlist import "testing" func TestInsertToHead(t *testing.T) { l := NewLinkedList() for i := 0; i < 10; i++ { l.InsertToHead(i + 1) } l.Print() } func TestInsertToTail(t *testing.T) { l := NewLinkedList() for i := 0; i < 10; i++ { l.InsertToTail(i + 1) } l.Print() } func TestFindByIndex(t *testing.T) { l := NewLinkedList() for i := 0; i < 10; i++ { l.InsertToTail(i + 1) } t.Log(l.FindByIndex(0)) t.Log(l.FindByIndex(9)) t.Log(l.FindByIndex(5)) t.Log(l.FindByIndex(11)) } func TestDeleteNode(t *testing.T) { l := NewLinkedList() for i := 0; i < 3; i++ { l.InsertToTail(i + 1) } l.Print() t.Log(l.DeleteNode(l.head.next)) l.Print() t.Log(l.DeleteNode(l.head.next.next)) l.Print() } ================================================ FILE: go/07_linkedlist/.gitkeep ================================================ ================================================ FILE: go/07_linkedlist/main.go ================================================ package _7_linkedlist import "fmt" //单链表节点 type ListNode struct { next *ListNode value interface{} } //单链表 type LinkedList struct { head *ListNode } //打印链表 func (this *LinkedList) Print() { cur := this.head.next format := "" for nil != cur { format += fmt.Sprintf("%+v", cur.value) cur = cur.next if nil != cur { format += "->" } } fmt.Println(format) } /* 单链表反转 时间复杂度:O(N) */ func (this *LinkedList) Reverse() { if nil == this.head || nil == this.head.next || nil == this.head.next.next { return } var pre *ListNode = nil cur := this.head.next for nil != cur { tmp := cur.next cur.next = pre pre = cur cur = tmp } this.head.next = pre } /* 判断单链表是否有环 */ func (this *LinkedList) HasCycle() bool { if nil != this.head { slow := this.head fast := this.head for nil != fast && nil != fast.next { slow = slow.next fast = fast.next.next if slow == fast { return true } } } return false } /* 两个有序单链表合并 */ func MergeSortedList(l1, l2 *LinkedList) *LinkedList { if nil == l1 || nil == l1.head || nil == l1.head.next { return l2 } if nil == l2 || nil == l2.head || nil == l2.head.next { return l1 } l := &LinkedList{head: &ListNode{}} cur := l.head curl1 := l1.head.next curl2 := l2.head.next for nil != curl1 && nil != curl2 { if curl1.value.(int) > curl2.value.(int) { cur.next = curl2 curl2 = curl2.next } else { cur.next = curl1 curl1 = curl1.next } cur = cur.next } if nil != curl1 { cur.next = curl1 } else if nil != curl2 { cur.next = curl2 } return l } /* 删除倒数第N个节点 */ func (this *LinkedList) DeleteBottomN(n int) { if n <= 0 || nil == this.head || nil == this.head.next { return } fast := this.head for i := 1; i <= n && fast != nil; i++ { fast = fast.next } if nil == fast { return } slow := this.head for nil != fast.next { slow = slow.next fast = fast.next } slow.next = slow.next.next } /* 获取中间节点 */ func (this *LinkedList) FindMiddleNode() *ListNode { if nil == this.head || nil == this.head.next { return nil } if nil == this.head.next.next { return this.head.next } slow, fast := this.head, this.head for nil != fast && nil != fast.next { slow = slow.next fast = fast.next.next } return slow } ================================================ FILE: go/07_linkedlist/main_test.go ================================================ package _7_linkedlist import "testing" var l *LinkedList func init() { n5 := &ListNode{value: 5} n4 := &ListNode{value: 4, next: n5} n3 := &ListNode{value: 3, next: n4} n2 := &ListNode{value: 2, next: n3} n1 := &ListNode{value: 1, next: n2} l = &LinkedList{head: &ListNode{next: n1}} } func TestReverse(t *testing.T) { l.Print() l.Reverse() l.Print() } func TestHasCycle(t *testing.T) { t.Log(l.HasCycle()) l.head.next.next.next.next.next.next = l.head.next.next.next t.Log(l.HasCycle()) } func TestMergeSortedList(t *testing.T) { n5 := &ListNode{value: 9} n4 := &ListNode{value: 7, next: n5} n3 := &ListNode{value: 5, next: n4} n2 := &ListNode{value: 3, next: n3} n1 := &ListNode{value: 1, next: n2} l1 := &LinkedList{head: &ListNode{next: n1}} n10 := &ListNode{value: 10} n9 := &ListNode{value: 8, next: n10} n8 := &ListNode{value: 6, next: n9} n7 := &ListNode{value: 4, next: n8} n6 := &ListNode{value: 2, next: n7} l2 := &LinkedList{head: &ListNode{next: n6}} MergeSortedList(l1, l2).Print() } func TestDeleteBottomN(t *testing.T) { l.Print() l.DeleteBottomN(3) l.Print() } func TestFindMiddleNode(t *testing.T) { l.DeleteBottomN(1) l.DeleteBottomN(1) l.DeleteBottomN(1) l.DeleteBottomN(1) l.Print() t.Log(l.FindMiddleNode()) } ================================================ FILE: go/08_stack/SimpleBrowser.go ================================================ package _8_stack import "fmt" type Browser struct { forwardStack Stack backStack Stack } func NewBrowser() *Browser { return &Browser{ forwardStack: NewArrayStack(), backStack: NewLinkedListStack(), } } func (this *Browser) CanForward() bool { if this.forwardStack.IsEmpty() { return false } return true } func (this *Browser) CanBack() bool { if this.backStack.IsEmpty() { return false } return true } func (this *Browser) Open(addr string) { fmt.Printf("Open new addr %+v\n", addr) this.forwardStack.Flush() } func (this *Browser) PushBack(addr string) { this.backStack.Push(addr) } func (this *Browser) Forward() { if this.forwardStack.IsEmpty() { return } top := this.forwardStack.Pop() this.backStack.Push(top) fmt.Printf("forward to %+v\n", top) } func (this *Browser) Back() { if this.backStack.IsEmpty() { return } top := this.backStack.Pop() this.forwardStack.Push(top) fmt.Printf("back to %+v\n", top) } ================================================ FILE: go/08_stack/SimpleBrowser_test.go ================================================ package _8_stack import "testing" func TestBrowser(t *testing.T) { b := NewBrowser() b.PushBack("www.qq.com") b.PushBack("www.baidu.com") b.PushBack("www.sina.com") if b.CanBack() { b.Back() } if b.CanForward() { b.Forward() } if b.CanBack() { b.Back() } if b.CanBack() { b.Back() } if b.CanBack() { b.Back() } b.Open("www.taobao.com") if b.CanForward() { b.Forward() } } ================================================ FILE: go/08_stack/StackBasedOnArray.go ================================================ package _8_stack import "fmt" /* 基于数组实现的栈 */ type ArrayStack struct { //数据 data []interface{} //栈顶指针 top int } func NewArrayStack() *ArrayStack { return &ArrayStack{ data: make([]interface{}, 0, 32), top: -1, } } func (this *ArrayStack) IsEmpty() bool { if this.top < 0 { return true } return false } func (this *ArrayStack) Push(v interface{}) { if this.top < 0 { this.top = 0 } else { this.top += 1 } if this.top > len(this.data)-1 { this.data = append(this.data, v) } else { this.data[this.top] = v } } func (this *ArrayStack) Pop() interface{} { if this.IsEmpty() { return nil } v := this.data[this.top] this.top -= 1 return v } func (this *ArrayStack) Top() interface{} { if this.IsEmpty() { return nil } return this.data[this.top] } func (this *ArrayStack) Flush() { this.top = -1 } func (this *ArrayStack) Print() { if this.IsEmpty() { fmt.Println("empty statck") } else { for i := this.top; i >= 0; i-- { fmt.Println(this.data[i]) } } } ================================================ FILE: go/08_stack/StackBasedOnArray_test.go ================================================ package _8_stack import "testing" func TestArrayStack_Push(t *testing.T) { s := NewArrayStack() s.Push(1) s.Push(2) t.Log(s.Pop()) s.Push(3) t.Log(s.Pop()) t.Log(s.Pop()) s.Push(4) t.Log(s.Pop()) s.Print() } func TestArrayStack_Pop(t *testing.T) { s := NewArrayStack() s.Push(1) s.Push(2) s.Push(3) s.Print() t.Log(s.Pop()) t.Log(s.Pop()) t.Log(s.Pop()) t.Log(s.Pop()) s.Print() } func TestArrayStack_Top(t *testing.T) { s := NewArrayStack() s.Push(1) s.Push(2) s.Push(3) t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() } ================================================ FILE: go/08_stack/StackBasedOnLinkedList.go ================================================ package _8_stack import "fmt" /* 基于链表实现的栈 */ type node struct { next *node val interface{} } type LinkedListStack struct { //栈顶节点 topNode *node } func NewLinkedListStack() *LinkedListStack { return &LinkedListStack{nil} } func (this *LinkedListStack) IsEmpty() bool { return this.topNode == nil } func (this *LinkedListStack) Push(v interface{}) { this.topNode = &node{next: this.topNode, val: v} } func (this *LinkedListStack) Pop() interface{} { if this.IsEmpty() { return nil } v := this.topNode.val this.topNode = this.topNode.next return v } func (this *LinkedListStack) Top() interface{} { if this.IsEmpty() { return nil } return this.topNode.val } func (this *LinkedListStack) Flush() { this.topNode = nil } func (this *LinkedListStack) Print() { if this.IsEmpty() { fmt.Println("empty stack") } else { cur := this.topNode for nil != cur { fmt.Println(cur.val) cur = cur.next } } } ================================================ FILE: go/08_stack/StackBasedOnLinkedList_test.go ================================================ package _8_stack import "testing" func TestLinkedListStack_Push(t *testing.T) { s := NewLinkedListStack() s.Push(1) s.Push(2) s.Push(3) s.Print() } func TestLinkedListStack_Pop(t *testing.T) { s := NewLinkedListStack() s.Push(1) s.Push(2) s.Push(3) s.Print() t.Log(s.Pop()) t.Log(s.Pop()) t.Log(s.Pop()) t.Log(s.Pop()) s.Print() } func TestLinkedListStack_Top(t *testing.T) { s := NewLinkedListStack() s.Push(1) s.Push(2) s.Push(3) t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() t.Log(s.Top()) s.Pop() } ================================================ FILE: go/08_stack/StatckInterface.go ================================================ package _8_stack type Stack interface { Push(v interface{}) Pop() interface{} IsEmpty() bool Top() interface{} Flush() } ================================================ FILE: go/09_queue/CircularQueue.go ================================================ package _9_queue import "fmt" type CircularQueue struct { q []interface{} capacity int head int tail int } func NewCircularQueue(n int) *CircularQueue { if n == 0 { return nil } return &CircularQueue{make([]interface{}, n), n, 0, 0} } /* 栈空条件:head==tail为true */ func (this *CircularQueue) IsEmpty() bool { if this.head == this.tail { return true } return false } /* 栈满条件:(tail+1)%capacity==head为true */ func (this *CircularQueue) IsFull() bool { if this.head == (this.tail+1)%this.capacity { return true } return false } func (this *CircularQueue) EnQueue(v interface{}) bool { if this.IsFull() { return false } this.q[this.tail] = v this.tail = (this.tail + 1) % this.capacity return true } func (this *CircularQueue) DeQueue() interface{} { if this.IsEmpty() { return nil } v := this.q[this.head] this.head = (this.head + 1) % this.capacity return v } func (this *CircularQueue) String() string { if this.IsEmpty() { return "empty queue" } result := "head" var i = this.head for true { result += fmt.Sprintf("<-%+v", this.q[i]) i = (i + 1) % this.capacity if i == this.tail { break } } result += "<-tail" return result } ================================================ FILE: go/09_queue/CircularQueue_test.go ================================================ package _9_queue import "testing" func TestCircularQueue_EnQueue(t *testing.T) { q := NewCircularQueue(5) q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) } func TestCircularQueue_DeQueue(t *testing.T) { q := NewCircularQueue(5) q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) t.Log(q.DeQueue()) t.Log(q) q.EnQueue(5) t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) } ================================================ FILE: go/09_queue/QueueBasedOnArray.go ================================================ package _9_queue import "fmt" type ArrayQueue struct { q []interface{} capacity int head int tail int } func NewArrayQueue(n int) *ArrayQueue { return &ArrayQueue{make([]interface{}, n), n, 0, 0} } func (this *ArrayQueue) EnQueue(v interface{}) bool { if this.tail == this.capacity { return false } this.q[this.tail] = v this.tail++ return true } func (this *ArrayQueue) DeQueue() interface{} { if this.head == this.tail { return nil } v := this.q[this.head] this.head++ return v } func (this *ArrayQueue) String() string { if this.head == this.tail { return "empty queue" } result := "head" for i := this.head; i <= this.tail-1; i++ { result += fmt.Sprintf("<-%+v", this.q[i]) } result += "<-tail" return result } ================================================ FILE: go/09_queue/QueueBasedOnArray_test.go ================================================ package _9_queue import "testing" func TestArrayQueue_EnQueue(t *testing.T) { q := NewArrayQueue(5) q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) } func TestArrayQueue_DeQueue(t *testing.T) { q := NewArrayQueue(5) q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) } ================================================ FILE: go/09_queue/QueueBasedOnLinkedList.go ================================================ package _9_queue import "fmt" type ListNode struct { val interface{} next *ListNode } type LinkedListQueue struct { head *ListNode tail *ListNode length int } func NewLinkedListQueue() *LinkedListQueue { return &LinkedListQueue{nil, nil, 0} } func (this *LinkedListQueue) EnQueue(v interface{}) { node := &ListNode{v, nil} if nil == this.tail { this.tail = node this.head = node } else { this.tail.next = node this.tail = node } this.length++ } func (this *LinkedListQueue) DeQueue() interface{} { if this.head == nil { return nil } v := this.head.val this.head = this.head.next this.length-- return v } func (this *LinkedListQueue) String() string { if this.head == nil { return "empty queue" } result := "head<-" for cur := this.head; cur != nil; cur = cur.next { result += fmt.Sprintf("<-%+v", cur.val) } result += "<-tail" return result } ================================================ FILE: go/09_queue/QueueBasedOnLinkedList_test.go ================================================ package _9_queue import "testing" func TestListQueue_EnQueue(t *testing.T) { q := NewLinkedListQueue() q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) } func TestListQueue_DeQueue(t *testing.T) { q := NewLinkedListQueue() q.EnQueue(1) q.EnQueue(2) q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.EnQueue(6) t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) q.DeQueue() t.Log(q) } ================================================ FILE: go/10_recursion/Factorial.go ================================================ package Recursion // 迭代实现阶乘 type Fac struct { val map[int]int } func NewFactorial(n int) *Fac { return &Fac{ make(map[int]int, n), } } func (fac *Fac) Factorial(n int) int { if fac.val[n] != 0{ return fac.val[n] } if n <= 1{ fac.val[n] = 1 return 1 }else { res := n * fac.Factorial(n-1) fac.val[n] =res return res } } func (fac *Fac) Print(n int ) { println(fac.val[n]) } ================================================ FILE: go/10_recursion/Factorial_test.go ================================================ package Recursion import "testing" func TestFac_Factorial(t *testing.T) { fac := NewFactorial(10) for i:=1; i<15; i++{ fac.Factorial(i) fac.Print(i) } } ================================================ FILE: go/10_recursion/Fibonacci.go ================================================ package Recursion import "fmt" // 递归实现斐波那契数列 type Fibs struct { val map[int]int // 使用字典存储结果 } func NewFibs(n int) *Fibs { return &Fibs{ make(map[int]int, n), } } func (fib *Fibs)Fibonacci(n int) int { if fib.val[n] != 0{ return fib.val[n] } if n <= 1 { fib.val[1] = 1 return 1 }else if n ==2{ fib.val[2] = 1 return 1 } else { res := fib.Fibonacci(n-1) + fib.Fibonacci(n-2) fib.val[n] = res return res } } func (fib *Fibs)Print(n int) { fmt.Println(fib.val[n]) } ================================================ FILE: go/10_recursion/Fibonacci_test.go ================================================ package Recursion import "testing" func TestFibs_Fibonacci(t *testing.T) { fib := NewFibs(10) for i:=1; i<15; i++{ fib.Fibonacci(i) fib.Print(i) } } ================================================ FILE: go/10_recursion/RangAll.go ================================================ package Recursion import ( "fmt" ) // 实现一组数据集合的全排列 type RangeType struct { value []interface{} } func NewRangeArray(n int) *RangeType { return &RangeType{ make([]interface{},n), } } func (slice *RangeType)RangeALL( start int) { len := len(slice.value) if start == len-1{ // 如果已经是最后位置,直接将数组数据合并输出 fmt.Println(slice.value) } for i:=start; i a[j+1] { a[j], a[j+1] = a[j+1], a[j] //此次冒泡有数据交换 flag = true } } // 如果没有交换数据,提前退出 if !flag { break } } } // 插入排序,a表示数组,n表示数组大小 func InsertionSort(a []int, n int) { if n <= 1 { return } for i := 1; i < n; i++ { value := a[i] j := i - 1 //查找要插入的位置并移动数据 for ; j >= 0; j-- { if a[j] > value { a[j+1] = a[j] } else { break } } a[j+1] = value } } // 选择排序,a表示数组,n表示数组大小 func SelectionSort(a []int, n int) { if n <= 1 { return } for i := 0; i < n; i++ { // 查找最小值 minIndex := i for j := i + 1; j < n; j++ { if a[j] < a[minIndex] { minIndex = j } } // 交换 a[i], a[minIndex] = a[minIndex],a[i] } } ================================================ FILE: go/11_sorts/Sort_test.go ================================================ package _1_sorts import ( "fmt" "testing" ) func TestBubbleSort(t *testing.T) { arr := []int{1,5,9,6,3,7,5,10} fmt.Println("排序前:",arr) BubbleSort(arr,len(arr)) fmt.Println("排序后:",arr) } func TestInsertionSort(t *testing.T) { arr := []int{1,5,9,6,3,7,5,10} fmt.Println("排序前:",arr) InsertionSort(arr,len(arr)) fmt.Println("排序后:",arr) } func TestSelectionSort(t *testing.T) { arr := []int{1,5,9,6,3,7,5,10} fmt.Println("排序前:",arr) SelectionSort(arr,len(arr)) fmt.Println("排序后:",arr) } ================================================ FILE: go/12_sorts/MergeSort.go ================================================ package _2_sorts func MergeSort(arr []int) { arrLen := len(arr) if arrLen <= 1 { return } mergeSort(arr, 0, arrLen-1) } func mergeSort(arr []int, start, end int) { if start >= end { return } mid := (start + end) / 2 mergeSort(arr, start, mid) mergeSort(arr, mid+1, end) merge(arr, start, mid, end) } func merge(arr []int, start, mid, end int) { tmpArr := make([]int, end-start+1) i := start j := mid + 1 k := 0 for ; i <= mid && j <= end; k++ { if arr[i] <= arr[j] { tmpArr[k] = arr[i] i++ } else { tmpArr[k] = arr[j] j++ } } for ; i <= mid; i++ { tmpArr[k] = arr[i] k++ } for ; j <= end; j++ { tmpArr[k] = arr[j] k++ } copy(arr[start:end+1], tmpArr) } ================================================ FILE: go/12_sorts/MergeSort_test.go ================================================ package _2_sorts import "testing" func TestMergeSort(t *testing.T) { arr := []int{5, 4} MergeSort(arr) t.Log(arr) arr = []int{5, 4, 3, 2, 1} MergeSort(arr) t.Log(arr) } ================================================ FILE: go/12_sorts/QuickSort.go ================================================ package _2_sorts // QuickSort is quicksort methods for golang func QuickSort(arr []int) { separateSort(arr, 0, len(arr)-1) } func separateSort(arr []int, start, end int) { if start >= end { return } i := partition(arr, start, end) separateSort(arr, start, i-1) separateSort(arr, i+1, end) } func partition(arr []int, start, end int) int { // 选取最后一位当对比数字 pivot := arr[end] var i = start for j := start; j < end; j++ { if arr[j] < pivot { if !(i == j) { // 交换位置 arr[i], arr[j] = arr[j], arr[i] } i++ } } arr[i], arr[end] = arr[end], arr[i] return i } ================================================ FILE: go/12_sorts/QuickSort_test.go ================================================ package _2_sorts import ( "math/rand" "testing" ) func createRandomArr(length int) []int { arr := make([]int, length, length) for i := 0; i < length; i++ { arr[i] = rand.Intn(100) } return arr } func TestQuickSort(t *testing.T) { arr := []int{5, 4} QuickSort(arr) t.Log(arr) arr = createRandomArr(100) QuickSort(arr) t.Log(arr) } ================================================ FILE: go/13_sorts/BucketSort.go ================================================ package LinearSort import ( "algorithm/Sort" "fmt" ) // 桶排序 // 获取待排序数组中的最大值 func getMax(a []int)int{ max := a[0] for i:=1; i max{ max = a[i] } } return max } func BucketSort(a []int) { num := len(a) if num <= 1{ return } max := getMax(a) buckets := make([][]int, num) // 二维切片 index :=0 for i:=0; i< num; i++{ index = a[i]*(num -1) / max // 桶序号 buckets[index] = append(buckets[index],a[i]) // 加入对应的桶中 } tmpPos := 0 // 标记数组位置 for i := 0; i < num; i++ { bucketLen := len(buckets[i]) if bucketLen > 0{ Sort.QuickSort(buckets[i]) // 桶内做快速排序 copy(a[tmpPos:], buckets[i]) tmpPos += bucketLen } } } // 桶排序简单实现 func BucketSortSimple(source []int) { if len(source)<=1{ return } array := make([]int, getMax(source)+1) for i:=0; i max { max = a[i] } } c := make([]int, max+1) for i := range a { c[a[i]]++ } for i := 1; i <= max; i++ { c[i] += c[i-1] } r := make([]int, n) for i := n - 1; i >= 0; i-- { index := c[a[i]] - 1 r[index] = a[i] c[a[i]]-- } copy(a, r) } ================================================ FILE: go/14_sorts/CountingSort_test.go ================================================ package _4_sorts import "testing" func TestCountingSort(t *testing.T) { arr := []int{5, 4} CountingSort(arr, len(arr)) t.Log(arr) arr = []int{5, 4, 3, 2, 1} CountingSort(arr, len(arr)) t.Log(arr) } ================================================ FILE: go/15_binarysearch/binarysearch.go ================================================ package _5_binarysearch func BinarySearch(a []int, v int) int { n := len(a) if n == 0 { return -1 } low := 0 high := n - 1 for low <= high { mid := (low + high) / 2 if a[mid] == v { return mid } else if a[mid] > v { high = mid - 1 } else { low = mid + 1 } } return -1 } func BinarySearchRecursive(a []int, v int) int { n := len(a) if n == 0 { return -1 } return bs(a, v, 0, n-1) } func bs(a []int, v int, low, high int) int { if low > high { return -1 } mid := (low + high) / 2 if a[mid] == v { return mid } else if a[mid] > v { return bs(a, v, low, mid-1) } else { 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 } ================================================ FILE: go/15_binarysearch/binarysearch_test.go ================================================ package _5_binarysearch import "testing" func TestBinarySearch(t *testing.T) { var a []int a = []int{1, 3, 5, 6, 8} if BinarySearch(a, 8) != 4 { t.Fatal(BinarySearch(a, 3)) } if BinarySearch(a, 4) != -1 { t.Fatal(BinarySearch(a, 4)) } } func TestBinarySearchRecursive(t *testing.T) { var a []int a = []int{1, 3, 5, 6, 8} if BinarySearchRecursive(a, 8) != 4 { t.Fatal(BinarySearch(a, 3)) } if BinarySearchRecursive(a, 4) != -1 { 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)) } } ================================================ FILE: go/17_skiplist/skiplist.go ================================================ 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) } ================================================ FILE: go/17_skiplist/skiplist_test.go ================================================ 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("-----------------------------") } ================================================ FILE: go/20_lru/lru_cache.go ================================================ package lru_cache const ( hostbit = uint64(^uint(0)) == ^uint64(0) LENGTH = 100 ) type lruNode struct { prev *lruNode next *lruNode key int // lru key value int // lru value hnext *lruNode // 拉链 } type LRUCache struct { node []lruNode // hash list head *lruNode // lru head node tail *lruNode // lru tail node capacity int // used int // } func Constructor(capacity int) LRUCache { return LRUCache{ node: make([]lruNode, LENGTH), head: nil, tail: nil, capacity: capacity, used: 0, } } func (this *LRUCache) Get(key int) int { if this.tail == nil { return -1 } if tmp := this.searchNode(key); tmp != nil { this.moveToTail(tmp) return tmp.value } return -1 } func (this *LRUCache) Put(key int, value int) { // 1. 首次插入数据 // 2. 插入数据不在 LRU 中 // 3. 插入数据在 LRU 中 // 4. 插入数据不在 LRU 中, 并且 LRU 已满 if tmp := this.searchNode(key); tmp != nil { tmp.value = value this.moveToTail(tmp) return } this.addNode(key, value) if this.used > this.capacity { this.delNode() } } func (this *LRUCache) addNode(key int, value int) { newNode := &lruNode{ key: key, value: value, } tmp := &this.node[hash(key)] newNode.hnext = tmp.hnext tmp.hnext = newNode this.used++ if this.tail == nil { this.tail, this.head = newNode, newNode return } this.tail.next = newNode newNode.prev = this.tail this.tail = newNode } func (this *LRUCache) delNode() { if this.head == nil { return } prev := &this.node[hash(this.head.key)] tmp := prev.hnext for tmp != nil && tmp.key != this.head.key { prev = tmp tmp = tmp.hnext } if tmp == nil { return } prev.hnext = tmp.hnext this.head = this.head.next this.head.prev = nil this.used-- } func (this *LRUCache) searchNode(key int) *lruNode { if this.tail == nil { return nil } // 查找 tmp := this.node[hash(key)].hnext for tmp != nil { if tmp.key == key { return tmp } tmp = tmp.hnext } return nil } func (this *LRUCache) moveToTail(node *lruNode) { if this.tail == node { return } if this.head == node { this.head = node.next this.head.prev = nil } else { node.next.prev = node.prev node.prev.next = node.next } node.next = nil this.tail.next = node node.prev = this.tail this.tail = node } func hash(key int) int { if hostbit { return (key ^ (key >> 32)) & (LENGTH - 1) } return (key ^ (key >> 16)) & (LENGTH - 1) } ================================================ FILE: go/20_lru/lru_cache_test.go ================================================ package lru_cache import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func Test_Hostbit(t *testing.T) { fmt.Println(hostbit) } func Test_LRU(t *testing.T) { lru := Constructor(2) lru.Put(1, 1) lru.Put(2, 2) assert.Equal(t, lru.Get(1), 1) // returns 1 lru.Put(3, 3) // evicts key 2 assert.Equal(t, lru.Get(2), -1) // returns -1 (not found) lru.Put(4, 4) // evicts key 1 assert.Equal(t, lru.Get(1), -1) // returns -1 (not found) assert.Equal(t, lru.Get(3), 3) // returns 3 assert.Equal(t, lru.Get(4), 4) // returns 4 } func Test_LRU_PutGet(t *testing.T) { lru := Constructor(1) lru.Put(1, 2) assert.Equal(t, lru.Get(1), 2) // returns 2 } func Test_LRU_PutGetPutGetGet(t *testing.T) { lru := Constructor(1) lru.Put(2, 1) assert.Equal(t, lru.Get(2), 1) // returns 1 lru.Put(3, 2) assert.Equal(t, lru.Get(2), -1) // returns -1 assert.Equal(t, lru.Get(3), 2) // returns 2 } func Test_LRU_PPGPPG(t *testing.T) { lru := Constructor(2) lru.Put(2, 1) lru.Put(2, 2) assert.Equal(t, lru.Get(2), 2) // returns 2 lru.Put(1, 4) lru.Put(4, 1) assert.Equal(t, lru.Get(2), -1) // returns -1 assert.Equal(t, lru.Get(3), -1) // returns -1 } func Test_LRU_PPGPPG_2(t *testing.T) { lru := Constructor(2) lru.Put(2, 1) lru.Put(2, 2) assert.Equal(t, lru.Get(2), 2) // returns 2 lru.Put(1, 1) lru.Put(4, 1) assert.Equal(t, lru.Get(2), -1) // returns -1 assert.Equal(t, lru.Get(3), -1) // returns -1 } ================================================ FILE: go/23_binarytree/binarytree.go ================================================ package binarytree type TreeNode struct { Val int Left *TreeNode Right *TreeNode } func preOrderTraversal(root *TreeNode) []int { if root == nil { return nil } if root.Left == nil && root.Right == nil { return []int{root.Val} } var stack []*TreeNode var res []int stack = append(stack, root) for len(stack) != 0 { e := stack[len(stack)-1] stack = stack[:len(stack)-1] res = append(res, e.Val) if e.Right != nil { stack = append(stack, e.Right) } if e.Left != nil { stack = append(stack, e.Left) } } return res } func inOrderTraversal(root *TreeNode) []int { if root == nil { return nil } if root.Left == nil && root.Right == nil { return []int{root.Val} } res := inOrderTraversal(root.Left) res = append(res, root.Val) res = append(res, inOrderTraversal(root.Right)...) return res } func postOrderTraversal(root *TreeNode) []int { if root == nil { return nil } var res []int if root.Left != nil { lres := postOrderTraversal(root.Left) if len(lres) > 0 { res = append(res, lres...) } } if root.Right != nil { rres := postOrderTraversal(root.Right) if len(rres) > 0 { res = append(res, rres...) } } res = append(res, root.Val) return res } ================================================ FILE: go/23_binarytree/binarytree_test.go ================================================ package binarytree import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) var tcs = []struct { pre, in, post []int }{ { []int{1, 2, 3}, []int{1, 3, 2}, []int{3, 2, 1}, }, { []int{1, 2, 4, 5, 3, 6, 7}, []int{4, 2, 5, 1, 6, 3, 7}, []int{4, 5, 2, 6, 7, 3, 1}, }, // 可以有多个 testCase } func PreIn2Tree(pre, in []int) *TreeNode { if len(pre) != len(in) { panic("preIn2Tree 中两个切片的长度不相等") } if len(in) == 0 { return nil } res := &TreeNode{ Val: pre[0], } if len(in) == 1 { return res } idx := indexOf(res.Val, in) res.Left = PreIn2Tree(pre[1:idx+1], in[:idx]) res.Right = PreIn2Tree(pre[idx+1:], in[idx+1:]) return res } func indexOf(val int, nums []int) int { for i, v := range nums { if v == val { return i } } return 0 } func Test_preOrderTraversal(t *testing.T) { ast := assert.New(t) for _, tc := range tcs { fmt.Printf("~~%v~~\n", tc) root := PreIn2Tree(tc.pre, tc.in) ast.Equal(tc.pre, preOrderTraversal(root), "输入:%v", tc) } } func Test_inOrderTraversal(t *testing.T) { ast := assert.New(t) for _, tc := range tcs { fmt.Printf("~~%v~~\n", tc) root := PreIn2Tree(tc.pre, tc.in) ast.Equal(tc.in, inOrderTraversal(root), "输入:%v", tc) } } func Test_postOrderTraversal(t *testing.T) { ast := assert.New(t) for _, tc := range tcs { fmt.Printf("~~%v~~\n", tc) root := PreIn2Tree(tc.pre, tc.in) ast.Equal(tc.post, postOrderTraversal(root), "输入:%v", tc) } } ================================================ FILE: go/24_tree/BinarySearchTree.go ================================================ package _4_tree type BST struct { *BinaryTree //比对函数,0:v==nodeV,正数:v>nodeV,负数:v 0 { //v > nodeV p = p.right } else { //v < nodeV p = p.left } } return nil } func (this *BST) Insert(v interface{}) bool { p := this.root for nil != p { compareResult := this.compareFunc(v, p.data) if compareResult == 0 { return false } else if compareResult > 0 { if nil == p.right { p.right = NewNode(v) break } p = p.right } else { if nil == p.left { p.left = NewNode(v) break } p = p.left } } return true } func (this *BST) Delete(v interface{}) bool { var pp *Node = nil p := this.root deleteLeft := false for nil != p { compareResult := this.compareFunc(v, p.data) if compareResult > 0 { pp = p p = p.right deleteLeft = false } else if compareResult < 0 { pp = p p = p.left deleteLeft = true } else { break } } if nil == p { //需要删除的节点不存在 return false } else if nil == p.left && nil == p.right { //删除的是一个叶子节点 if nil != pp { if deleteLeft { pp.left = nil } else { pp.right = nil } } else { //根节点 this.root = nil } } else if nil != p.right { //删除的是一个有右孩子,不一定有左孩子的节点 //找到p节点右孩子的最小节点 pq := p q := p.right //向右走一步 fromRight := true for nil != q.left { //向左走到底 pq = q q = q.left fromRight = false } if fromRight { pq.right = nil } else { pq.left = nil } q.left = p.left q.right = p.right if nil == pp { //根节点被删除 this.root = q } else { if deleteLeft { pq.left = q } else { pq.right = q } } } else { //删除的是一个只有左孩子的节点 if nil != pp { if deleteLeft { pp.left = p.left } else { pp.right = p.left } } else { if deleteLeft { this.root = p.left } else { this.root = p.left } } } return true } func (this *BST) Min() *Node { p := this.root for nil != p.left { p = p.left } return p } func (this *BST) Max() *Node { p := this.root for nil != p.right { p = p.right } return p } ================================================ FILE: go/24_tree/BinarySearchTree_test.go ================================================ package _4_tree import "testing" var compareFunc = func(v, nodeV interface{}) int { vInt := v.(int) nodeVInt := nodeV.(int) if vInt > nodeVInt { return 1 } else if vInt < nodeVInt { return -1 } return 0 } func TestBST_Find(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(1) bst.Insert(2) bst.Insert(7) bst.Insert(5) t.Log(bst.Find(2)) } func TestBST_Insert(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(1) bst.Insert(2) bst.Insert(7) bst.Insert(5) bst.InOrderTraverse() } func TestBST_Min(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(1) bst.Insert(2) bst.Insert(7) bst.Insert(5) t.Log(bst.Min()) } func TestBST_Max(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(1) bst.Insert(2) bst.Insert(7) bst.Insert(5) t.Log(bst.Max()) } func TestBST_DeleteA(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(7) bst.Insert(5) t.Log(bst.Delete(7)) bst.InOrderTraverse() } func TestBST_DeleteB(t *testing.T) { bst := NewBST(1, compareFunc) t.Log(bst.Delete(1)) t.Log(bst.root) bst.InOrderTraverse() } func TestBST_DeleteC(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(7) bst.Insert(5) t.Log(bst.Delete(1)) bst.InOrderTraverse() } func TestBST_DeleteD(t *testing.T) { bst := NewBST(1, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(5) t.Log(bst.Delete(1)) bst.InOrderTraverse() } func TestBST_DeleteE(t *testing.T) { bst := NewBST(5, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(4) bst.Insert(1) t.Log(bst.Delete(5)) bst.InOrderTraverse() } func TestBST_DeleteF(t *testing.T) { bst := NewBST(5, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(4) bst.Insert(1) t.Log(bst.Delete(2)) bst.InOrderTraverse() } func TestBST_DeleteG(t *testing.T) { bst := NewBST(5, compareFunc) bst.Insert(3) bst.Insert(2) bst.Insert(4) bst.Insert(1) t.Log(bst.Delete(1)) bst.InOrderTraverse() } ================================================ FILE: go/24_tree/BinaryTree.go ================================================ package _4_tree import "fmt" type BinaryTree struct { root *Node } func NewBinaryTree(rootV interface{}) *BinaryTree { return &BinaryTree{NewNode(rootV)} } func (this *BinaryTree) InOrderTraverse() { p := this.root s := NewArrayStack() for !s.IsEmpty() || nil != p { if nil != p { s.Push(p) p = p.left } else { tmp := s.Pop().(*Node) fmt.Printf("%+v ", tmp.data) p = tmp.right } } fmt.Println() } func (this *BinaryTree) PreOrderTraverse() { p := this.root s := NewArrayStack() for !s.IsEmpty() || nil != p { if nil != p { fmt.Printf("%+v ", p.data) s.Push(p) p = p.left } else { p = s.Pop().(*Node).right } } fmt.Println() } func (this *BinaryTree) PostOrderTraverse() { s1 := NewArrayStack() s2 := NewArrayStack() s1.Push(this.root) for !s1.IsEmpty() { p := s1.Pop().(*Node) s2.Push(p) if nil != p.left { s1.Push(p.left) } if nil != p.right { s1.Push(p.right) } } for !s2.IsEmpty() { fmt.Printf("%+v ", s2.Pop().(*Node).data) } } //use one stack, pre cursor to traverse from post order func (this *BinaryTree) PostOrderTraverse2() { r := this.root s := NewArrayStack() //point to last visit node var pre *Node s.Push(r) for !s.IsEmpty() { r = s.Top().(*Node) if (r.left == nil && r.right == nil) || (pre != nil && (pre == r.left || pre == r.right)) { fmt.Printf("%+v ", r.data) s.Pop() pre = r } else { if r.right != nil { s.Push(r.right) } if r.left != nil { s.Push(r.left) } } } } ================================================ FILE: go/24_tree/BinaryTree_test.go ================================================ package _4_tree import "testing" func TestBinaryTree_InOrderTraverse(t *testing.T) { binaryTree := NewBinaryTree(1) binaryTree.root.left = NewNode(3) binaryTree.root.right = NewNode(4) binaryTree.root.right.left = NewNode(5) binaryTree.InOrderTraverse() } func TestBinaryTree_PreOrderTraverse(t *testing.T) { binaryTree := NewBinaryTree(1) binaryTree.root.left = NewNode(3) binaryTree.root.right = NewNode(4) binaryTree.root.right.left = NewNode(5) binaryTree.PreOrderTraverse() } func TestBinaryTree_PostOrderTraverse(t *testing.T) { binaryTree := NewBinaryTree(1) binaryTree.root.left = NewNode(3) binaryTree.root.right = NewNode(4) binaryTree.root.right.left = NewNode(5) binaryTree.PostOrderTraverse() } func TestBinaryTree_PostOrderTraverse2(t *testing.T) { binaryTree := NewBinaryTree(1) binaryTree.root.left = NewNode(3) binaryTree.root.right = NewNode(4) binaryTree.root.right.left = NewNode(5) binaryTree.PostOrderTraverse2() } ================================================ FILE: go/24_tree/StackBasedOnArray.go ================================================ package _4_tree import "fmt" /* 基于数组实现的栈 */ type ArrayStack struct { //数据 data []interface{} //栈顶指针 top int } func NewArrayStack() *ArrayStack { return &ArrayStack{ data: make([]interface{}, 0, 32), top: -1, } } func (this *ArrayStack) IsEmpty() bool { if this.top < 0 { return true } return false } func (this *ArrayStack) Push(v interface{}) { if this.top < 0 { this.top = 0 } else { this.top += 1 } if this.top > len(this.data)-1 { this.data = append(this.data, v) } else { this.data[this.top] = v } } func (this *ArrayStack) Pop() interface{} { if this.IsEmpty() { return nil } v := this.data[this.top] this.top -= 1 return v } func (this *ArrayStack) Top() interface{} { if this.IsEmpty() { return nil } return this.data[this.top] } func (this *ArrayStack) Flush() { this.top = -1 } func (this *ArrayStack) Print() { if this.IsEmpty() { fmt.Println("empty statck") } else { for i := this.top; i >= 0; i-- { fmt.Println(this.data[i]) } } } ================================================ FILE: go/24_tree/TreeNode.go ================================================ package _4_tree import "fmt" type Node struct { data interface{} left *Node right *Node } func NewNode(data interface{}) *Node { return &Node{data: data} } func (this *Node) String() string { return fmt.Sprintf("v:%+v, left:%+v, right:%+v", this.data, this.left, this.right) } ================================================ FILE: go/28_heap/heap.go ================================================ package heap type Heap struct { a []int n int count int } //init heap func NewHeap(capacity int) *Heap { heap := &Heap{} heap.n = capacity heap.a = make([]int, capacity+1) heap.count = 0 return heap } //top-max heap -> heapify from down to up func (heap *Heap) insert(data int) { //defensive if heap.count == heap.n { return } heap.count++ heap.a[heap.count] = data //compare with parent node i := heap.count parent := i / 2 for parent > 0 && heap.a[parent] < heap.a[i] { swap(heap.a, parent, i) i = parent parent = i / 2 } } //heapfify from up to down func (heap *Heap) removeMax() { //defensive if heap.count == 0 { return } //swap max and last swap(heap.a, 1, heap.count) heap.count-- //heapify from up to down heapifyUpToDown(heap.a, heap.count) } //heapify func heapifyUpToDown(a []int, count int) { for i := 1; i <= count/2; { maxIndex := i if a[i] < a[i*2] { maxIndex = i * 2 } if i*2+1 <= count && a[maxIndex] < a[i*2+1] { maxIndex = i*2 + 1 } if maxIndex == i { break } swap(a, i, maxIndex) i = maxIndex } } //swap two elements func swap(a []int, i int, j int) { tmp := a[i] a[i] = a[j] a[j] = tmp } ================================================ FILE: go/28_heap/heap_sort.go ================================================ package heap //build a heap func buidHeap(a []int, n int) { //heapify from the last parent node for i := n / 2; i >= 1; i-- { heapifyUpToDown(a, i, n) } } //sort by ascend, a index begin from 1, has n elements func sort(a []int, n int) { buidHeap(a, n) k := n for k >= 1 { swap(a, 1, k) heapifyUpToDown(a, 1, k-1) k-- } } //heapify from up to down , node index = top func heapifyUpToDown(a []int, top int, count int) { for i := top; i <= count/2; { maxIndex := i if a[i] < a[i*2] { maxIndex = i * 2 } if i*2+1 <= count && a[maxIndex] < a[i*2+1] { maxIndex = i*2 + 1 } if maxIndex == i { break } swap(a, i, maxIndex) i = maxIndex } } //swap two elements func swap(a []int, i int, j int) { tmp := a[i] a[i] = a[j] a[j] = tmp } ================================================ FILE: go/29_priority_queue/heap.go ================================================ package pqueue func adjustHeap(src []Node, start, end int) { if start >= end { return } // 只需要保证优先级最高的节点在 src[1] 的位置即可 for i := end / 2; i >= start; i-- { high := i if src[high].priority < src[2*i].priority { high = 2 * i } if 2*i+1 <= end && src[high].priority < src[2*i+1].priority { high = 2*i + 1 } if high == i { continue } src[high], src[i] = src[i], src[high] } } ================================================ FILE: go/29_priority_queue/heap_test.go ================================================ package pqueue import ( "testing" "github.com/stretchr/testify/assert" ) func Test_AdjustHeap(t *testing.T) { list := []Node{Node{0, 0}, Node{1, 1}, Node{2, 2}, Node{3, 3}, Node{4, 1}, Node{6, 6}} adjustHeap(list, 1, len(list)-1) assert.Equal(t, 6, list[1].value) } ================================================ FILE: go/29_priority_queue/priority_queue.go ================================================ package pqueue // Node 队列节点 type Node struct { value int priority int } // PQueue priority queue type PQueue struct { heap []Node capacity int used int } // NewPriorityQueue new func NewPriorityQueue(capacity int) PQueue { return PQueue{ heap: make([]Node, capacity+1, capacity+1), capacity: capacity, used: 0, } } // Push 入队 func (q *PQueue) Push(node Node) { if q.used > q.capacity { // 队列已满 return } q.used++ q.heap[q.used] = node // 堆化可以放在 Pop 中 // adjustHeap(q.heap, 1, q.used) } // Pop 出队列 func (q *PQueue) Pop() Node { if q.used == 0 { return Node{-1, -1} } // 先堆化, 再取堆顶元素 adjustHeap(q.heap, 1, q.used) node := q.heap[1] q.heap[1] = q.heap[q.used] q.used-- return node } // Top 获取队列顶部元素 func (q *PQueue) Top() Node { if q.used == 0 { return Node{-1, -1} } adjustHeap(q.heap, 1, q.used) return q.heap[1] } ================================================ FILE: go/29_priority_queue/priority_queue_test.go ================================================ package pqueue import ( "testing" "github.com/stretchr/testify/assert" ) func Test_Push(t *testing.T) { queue := NewPriorityQueue(100) queue.Push(Node{0, 1}) assert.Equal(t, 0, queue.Top().value) queue.Push(Node{3, 1}) assert.Equal(t, 0, queue.Top().value) queue.Push(Node{3, 2}) assert.Equal(t, 3, queue.Top().value) queue.Push(Node{6, 6}) assert.Equal(t, 6, queue.Top().value) queue.Push(Node{12, 5}) assert.Equal(t, 6, queue.Top().value) queue.Push(Node{13, 8}) assert.Equal(t, 13, queue.Top().value) } func Test_PushPop(t *testing.T) { queue := NewPriorityQueue(100) queue.Push(Node{0, 1}) queue.Push(Node{3, 1}) queue.Push(Node{3, 2}) queue.Push(Node{6, 6}) queue.Push(Node{12, 5}) queue.Push(Node{13, 8}) assert.Equal(t, 13, queue.Top().value) assert.Equal(t, 13, queue.Pop().value) assert.Equal(t, 6, queue.Pop().value) assert.Equal(t, 12, queue.Top().value) assert.Equal(t, 12, queue.Pop().value) queue.Push(Node{24, 8}) assert.Equal(t, 24, queue.Top().value) } // 无法保证入队顺序和出队顺序的一致性 // func Test_PushPop_Equal(t *testing.T) { // queue := NewPriorityQueue(9) // queue.Push(Node{0, 1}) // 8 // queue.Push(Node{3, 1}) // 9 // queue.Push(Node{3, 2}) // 3 // queue.Push(Node{6, 2}) // 4 // queue.Push(Node{11, 3}) // 2 // queue.Push(Node{12, 2}) // 5 // queue.Push(Node{13, 2}) // 6 // queue.Push(Node{19, 5}) // 1 // queue.Push(Node{17, 2}) // 7 // assert.Equal(t, 19, queue.Pop().value) // assert.Equal(t, 11, queue.Pop().value) // assert.Equal(t, 3, queue.Pop().value) // assert.Equal(t, 6, queue.Pop().value) // assert.Equal(t, 12, queue.Pop().value) // assert.Equal(t, 13, queue.Pop().value) // assert.Equal(t, 17, queue.Pop().value) // assert.Equal(t, 0, queue.Pop().value) // assert.Equal(t, 3, queue.Pop().value) // } ================================================ FILE: go/29_priority_queue/readme.md ================================================ ## TODO - 该实现方式不能保证 相同优先级的元素在出队列时 和入队列的顺序是一致的 ================================================ FILE: go/31_graph/graph_search.go ================================================ package graph import ( "container/list" "fmt" ) //adjacency table, 无向图 type Graph struct { adj []*list.List v int } //init graphh according to capacity func newGraph(v int) *Graph { graphh := &Graph{} graphh.v = v graphh.adj = make([]*list.List, v) for i := range graphh.adj { graphh.adj[i] = list.New() } return graphh } //insert as add edge,一条边存2次 func (self *Graph) addEdge(s int, t int) { self.adj[s].PushBack(t) self.adj[t].PushBack(s) } //search path by BFS func (self *Graph) BFS(s int, t int) { //todo if s == t { return } //init prev prev := make([]int, self.v) for index := range prev { prev[index] = -1 } //search by queue var queue []int visited := make([]bool, self.v) queue = append(queue, s) visited[s] = true isFound := false for len(queue) > 0 && !isFound { top := queue[0] queue = queue[1:] linkedlist := self.adj[top] for e := linkedlist.Front(); e != nil; e = e.Next() { k := e.Value.(int) if !visited[k] { prev[k] = top if k == t { isFound = true break } queue = append(queue, k) visited[k] = true } } } if isFound { printPrev(prev, s, t) } else { fmt.Printf("no path found from %d to %d\n", s, t) } } //search by DFS func (self *Graph) DFS(s int, t int) { prev := make([]int, self.v) for i := range prev { prev[i] = -1 } visited := make([]bool, self.v) visited[s] = true isFound := false self.recurse(s, t, prev, visited, isFound) printPrev(prev, s, t) } //recursivly find path func (self *Graph) recurse(s int, t int, prev []int, visited []bool, isFound bool) { if isFound { return } visited[s] = true if s == t { isFound = true return } linkedlist := self.adj[s] for e := linkedlist.Front(); e != nil; e = e.Next() { k := e.Value.(int) if !visited[k] { prev[k] = s self.recurse(k, t, prev, visited, false) } } } //print path recursively func printPrev(prev []int, s int, t int) { if t == s || prev[t] == -1 { fmt.Printf("%d ", t) } else { printPrev(prev, s, prev[t]) fmt.Printf("%d ", t) } } //func main() { // graph := newGraph(8) // graph.addEdge(0, 1) // graph.addEdge(0, 3) // graph.addEdge(1, 2) // graph.addEdge(1, 4) // graph.addEdge(2, 5) // graph.addEdge(3, 4) // graph.addEdge(4, 5) // graph.addEdge(4, 6) // graph.addEdge(5, 7) // graph.addEdge(6, 7) // // graph.BFS(0, 7) // fmt.Println() // graph.BFS(1, 3) // fmt.Println() // graph.DFS(0, 7) // fmt.Println() // graph.DFS(1, 3) // fmt.Println() //} ================================================ FILE: go/32_string/string_bf.go ================================================ package main import ( "fmt" ) //BF search pattern index, return the first match subs start index func bfSearch(main string, pattern string) int { //defensive if len(main) == 0 || len(pattern) == 0 || len(main) < len(pattern) { return -1 } for i := 0; i <= len(main)-len(pattern); i++ { subStr := main[i : i+len(pattern)] if subStr == pattern { return i } } return -1 } func main() { main := "abcd227fac" pattern := "ac" fmt.Println(bfSearch(main, pattern)) } ================================================ FILE: go/32_string/string_bm.go ================================================ package main import ( "fmt" "math" ) //bc: pattern char index hash mapping func generateBC(pattern string) []int { bc := make([]int, 256) for index := range bc { bc[index] = -1 } for index, char := range pattern { bc[int(char)] = index } return bc } //generate suffix and prefix array for pattern func generateGS(pattern string) ([]int, []bool) { m := len(pattern) suffix := make([]int, m) prefix := make([]bool, m) //init for i := 0; i < m; i++ { suffix[i] = -1 prefix[i] = false } for i := 0; i < m-1; i++ { j := i k := 0 for j >= 0 && pattern[j] == pattern[m-1-k] { j-- k++ suffix[k] = j + 1 } if j == -1 { prefix[k] = true } } return suffix, prefix } //todo func moveByGS(patternLength int, badCharStartIndex int, suffix []int, prefix []bool) int { //length of good suffix k := patternLength - badCharStartIndex - 1 //complete match if suffix[k] != -1 { return badCharStartIndex + 1 - suffix[k] } //partial match for t := patternLength - 1; t > badCharStartIndex+1; t-- { if prefix[t] { return t } } //no match return patternLength } func bmSearch(main string, pattern string) int { //defensive if len(main) == 0 || len(pattern) == 0 || len(pattern) > len(main) { return -1 } bc := generateBC(pattern) suffix, prefix := generateGS(pattern) n := len(main) m := len(pattern) // i : start index of main string step := 1 for i := 0; i <= n-m; i = i + step { subStr := main[i : i+m] k, j := findBadChar(subStr, pattern, bc) stepForBC := j - k //j is bad char occur index if j == -1 { return i } stepForGS := -1 if j < m-1 { stepForGS = moveByGS(m, j, suffix, prefix) } //k is bad char index in pattern step = int(math.Max(float64(stepForBC), float64(stepForGS))) } return -1 } func findBadChar(subStr string, pattern string, bc []int) (int, int) { j := -1 k := -1 badChar := rune(0) for index := len(subStr) - 1; index >= 0; index-- { if subStr[index] != pattern[index] { j = index badChar = rune(subStr[index]) break } } //if bad character exist, then find it's index at pattern if j > 0 { k = bc[int(badChar)] } return k, j } func main() { main := "abcacabcbcabcabc" pattern := "cabcab" fmt.Println(bmSearch(main, pattern)) } ================================================ FILE: go/34_kmp/kmp.go ================================================ package main import ( "fmt" ) func getNexts(pattern string) []int { m := len(pattern) nexts := make([]int, m) for index := range nexts { nexts[index] = -1 } for i := 1; i < m - 1; i++ { j := nexts[i - 1] for pattern[j + 1] != pattern[i] && j >= 0 { j = nexts[j] } if pattern[j + 1] == pattern[i] { j += 1 } nexts[i] = j } return nexts } func findByKMP(s string, pattern string) int { n := len(s) m := len(pattern) if n < m { return -1 } nexts := getNexts(pattern) j := 0 for i := 0; i < n; i++ { for j > 0 && s[i] != pattern[j] { j = nexts[j - 1] + 1 } if s[i] == pattern[j] { if j == m - 1 { return i - m + 1 } j += 1 } } return -1 } func main(){ s := "abc abcdab abcdabcdabde" pattern := "bcdabd" fmt.Println(findByKMP(s, pattern)) //16 s = "aabbbbaaabbababbabbbabaaabb" pattern = "abab" fmt.Println(findByKMP(s, pattern)) //11 s = "aabbbbaaabbababbabbbabaaabb" pattern = "ababacd" fmt.Println(findByKMP(s, pattern)) //-1 s = "hello" pattern = "ll" fmt.Println(findByKMP(s, pattern)) //2 } ================================================ FILE: go/41_dynamic_programming/backtracking/leastcoins.go ================================================ package backtracking import ( "fmt" ) const intMax = int(^uint(0) >> 1) var Cnt int // LeastCoins find least number of coins of which the total values are equals a given one func LeastCoins(targetTotal int, coinOptions []int) int { minNum := intMax memo := make([][]int, targetTotal+1) for i := range memo { memo[i] = make([]int, len(coinOptions)) } fmt.Println("start") leastCoins(&minNum, 0, targetTotal, len(coinOptions)-1, coinOptions, memo) fmt.Println("end") return minNum } func leastCoins(minNum *int, cNum, totalValue, opIndex int, coinOptions []int, memo [][]int) { Cnt++ if 0 == totalValue { if cNum < *minNum { *minNum = cNum } return } if opIndex < 0 { return } num4Option := 0 remaining := totalValue - coinOptions[opIndex]*num4Option for remaining >= 0 { if opIndex != 0 { if shouldSkip(memo, remaining, opIndex-1, cNum+num4Option) { goto Next } } leastCoins(minNum, cNum+num4Option, remaining, opIndex-1, coinOptions, memo) Next: num4Option++ remaining = totalValue - coinOptions[opIndex]*num4Option } } func shouldSkip(memo [][]int, totalValue, nextOpIdex, cNum int) bool { if memo[totalValue][nextOpIdex] > 0 && memo[totalValue][nextOpIdex] <= cNum { fmt.Printf("skip,%d, %d as %d <= %d \n", totalValue, nextOpIdex, memo[totalValue][nextOpIdex], cNum) return true } if memo[totalValue][nextOpIdex] == 0 || memo[totalValue][nextOpIdex] > cNum { memo[totalValue][nextOpIdex] = cNum } return false } func LeastCoins2(targetTotal int, coinOptions []int) int { minNum := intMax memo := make([][]bool, targetTotal) for i := range memo { memo[i] = make([]bool, targetTotal/coinOptions[0]) } fmt.Println("start") leastCoins2(&minNum, targetTotal, coinOptions, 0, 0, memo) fmt.Println("end") return minNum } func leastCoins2(minNum *int, targetTotal int, coinOptions []int, cNum, cValue int, memo [][]bool) { Cnt++ if cValue == targetTotal { if *minNum > cNum { *minNum = cNum } return } for _, coin := range coinOptions { if coin+cValue <= targetTotal && !memo[cValue+coin-1][cNum] { memo[cValue+coin-1][cNum] = true leastCoins2(minNum, targetTotal, coinOptions, cNum+1, cValue+coin, memo) } } } ================================================ FILE: go/41_dynamic_programming/backtracking/leastcoins_test.go ================================================ package backtracking import "testing" func TestFindLeastCoins(t *testing.T) { coinOptions := []int{1, 3, 5, 10, 50} Cnt = 0 result := LeastCoins(9, coinOptions) t.Log("test 1 =====================") if result != 3 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) Cnt = 0 t.Log("test 2 =====================") result = LeastCoins(36, coinOptions) if result != 5 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) } func TestFindLeastCoins2(t *testing.T) { coinOptions := []int{1, 3, 5, 10, 50} Cnt = 0 result := LeastCoins2(9, coinOptions) t.Log("test 1 =====================") if result != 3 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) Cnt = 0 t.Log("test 2 =====================") result = LeastCoins2(36, coinOptions) if result != 5 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) } ================================================ FILE: go/41_dynamic_programming/dp/leastcoins.go ================================================ package dp var Cnt int // minNum(v) = 1 + min(minNum(v-i)) func LeastCoins(targetTotal int, coinOptions []int) int { memo := make([]int, targetTotal) cMinNum := leastCoins(targetTotal, coinOptions, memo) return cMinNum } // State Transition Table func LeastCoins2(targetTotal int, coinOptions []int) int { memo := make([]int, targetTotal) for i := 1; i <= targetTotal; i++ { minNum := -1 for _, coin := range coinOptions { Cnt++ if i < coin { break } if i == coin { minNum = 1 break } if minNum == -1 || minNum > memo[i-coin-1]+1 { minNum = memo[i-coin-1] + 1 } } memo[i-1] = minNum } return memo[targetTotal-1] } func leastCoins(targetTotal int, coinOptions, memo []int) int { Cnt++ if targetTotal == 0 { return 0 } if memo[targetTotal-1] != 0 { return memo[targetTotal-1] } cMinNum := -1 for _, coin := range coinOptions { if targetTotal-coin < 0 { continue } cNum := 1 + leastCoins(targetTotal-coin, coinOptions, memo) if cMinNum == -1 || cNum < cMinNum { cMinNum = cNum } } memo[targetTotal-1] = cMinNum return cMinNum } ================================================ FILE: go/41_dynamic_programming/dp/leastcoins_test.go ================================================ package dp import "testing" func TestFindLeastCoins(t *testing.T) { coinOptions := []int{1, 3, 5, 10, 50} Cnt = 0 result := LeastCoins(9, coinOptions) t.Log("test 1 =====================") if result != 3 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) Cnt = 0 t.Log("test 2 =====================") result = LeastCoins(36, coinOptions) if result != 5 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) } func TestFindLeastCoins2(t *testing.T) { coinOptions := []int{1, 3, 5, 10, 50} Cnt = 0 result := LeastCoins2(9, coinOptions) t.Log("test 1 =====================") if result != 3 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) Cnt = 0 t.Log("test 2 =====================") result = LeastCoins2(36, coinOptions) if result != 5 { t.Logf("least coins %d", result) t.Error("failed") } t.Logf("cnt===%d", Cnt) } ================================================ FILE: go/42_dynamic_programming/longest_common_substring.go ================================================ package main import "fmt" func lsc(s1 string, s2 string) int { m := len(s1) n := len(s2) memo := make([][]int, m + 1) for i := 0; i < m + 1; i++ { memo[i] = make([]int, n + 1) } for i := 1; i < m + 1; i++ { for j := 1; j < n + 1; j++ { if s1[i - 1] == s2[j - 1] { memo[i][j] = memo[i - 1][j - 1] + 1 } } } fmt.Println(memo) longest := 0 for i, _ := range memo { for j, e2 := range memo[i] { if longest < memo[i][j] { longest = e2 } } } return longest } func main() { fmt.Println(lsc("blue", "clues")) //3 fmt.Println(lsc("fosh", "fish")) //2 fmt.Println(lsc("fosh", "fort")) //2 fmt.Println(lsc("hish", "fish")) //3 fmt.Println(lsc("hish", "vista")) //2 } ================================================ FILE: go/45_bitmap/bitmap.go ================================================ package bitmap // BitMap implement bitmap type BitMap []byte // New create BitMap func New(length uint) BitMap { return make([]byte, length/8+1) } // Set set value in bitmap func (b BitMap) Set(value uint) { byteIndex := value / 8 if byteIndex >= uint(len(b)) { return } bitIndex := value % 8 []byte(b)[byteIndex] |= 1 << bitIndex } // Get check whether value exist or not func (b BitMap) Get(value uint) bool { byteIndex := value / 8 if byteIndex >= uint(len(b)) { return false } bitIndex := value % 8 return []byte(b)[byteIndex]&(1<> 1) 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 - start) >> 1) 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 } ================================================ FILE: java/05_array/.gitkeep ================================================ ================================================ FILE: java/05_array/Array.java ================================================ package array; /** * 1) 数组的插入、删除、按照下标随机访问操作; * 2)数组中的数据是int类型的; * * Author: Zheng * modify: xing, Gsealy */ public class Array { //定义整型数据data保存数据 public int data[]; //定义数组长度 private int n; //定义中实际个数 private int count; //构造方法,定义数组大小 public Array(int capacity){ this.data = new int[capacity]; this.n = capacity; this.count=0;//一开始一个数都没有存所以为0 } //根据索引,找到数据中的元素并返回 public int find(int index){ if (index<0 || index>=count) return -1; return data[index]; } //插入元素:头部插入,尾部插入 public boolean insert(int index, int value){ //数组中无元素 //if (index == count && count == 0) { // data[index] = value; // ++count; // return true; //} // 数组空间已满 if (count == n) { System.out.println("没有可插入的位置"); return false; } // 如果count还没满,那么就可以插入数据到数组中 // 位置不合法 if (index < 0||index > count ) { System.out.println("位置不合法"); return false; } // 位置合法 for( int i = count; i > index; --i){ data[i] = data[i - 1]; } data[index] = value; ++count; return true; } //根据索引,删除数组中元素 public boolean delete(int index){ if (index<0 || index >=count) return false; //从删除位置开始,将后面的元素向前移动一位 for (int i=index+1; i { private T[] data; private int size; // 根据传入容量,构造Array public GenericArray(int capacity) { data = (T[]) new Object[capacity]; size = 0; } // 无参构造方法,默认数组容量为10 public GenericArray() { this(10); } // 获取数组容量 public int getCapacity() { return data.length; } // 获取当前元素个数 public int count() { return size; } // 判断数组是否为空 public boolean isEmpty() { return size == 0; } // 修改 index 位置的元素 public void set(int index, T e) { checkIndex(index); data[index] = e; } // 获取对应 index 位置的元素 public T get(int index) { checkIndex(index); return data[index]; } // 查看数组是否包含元素e public boolean contains(T e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return true; } } return false; } // 获取对应元素的下标, 未找到,返回 -1 public int find(T e) { for ( int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; } // 在 index 位置,插入元素e, 时间复杂度 O(m+n) public void add(int index, T e) { checkIndexForAdd(index); // 如果当前元素个数等于数组容量,则将数组扩容为原来的2倍 if (size == data.length) { resize(2 * data.length); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; } // 向数组头插入元素 public void addFirst(T e) { add(0, e); } // 向数组尾插入元素 public void addLast(T e) { add(size, e); } // 删除 index 位置的元素,并返回 public T remove(int index) { checkIndex(index); T ret = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size --; data[size] = null; // 缩容 if (size == data.length / 4 && data.length / 2 != 0) { resize(data.length / 2); } return ret; } // 删除第一个元素 public T removeFirst() { return remove(0); } // 删除末尾元素 public T removeLast() { return remove(size - 1); } // 从数组中删除指定元素 public void removeElement(T e) { int index = find(e); if (index != -1) { remove(index); } } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(String.format("Array size = %d, capacity = %d \n", size, data.length)); builder.append('['); for (int i = 0; i < size; i++) { builder.append(data[i]); if (i != size - 1) { builder.append(", "); } } builder.append(']'); return builder.toString(); } // 扩容方法,时间复杂度 O(n) private void resize(int capacity) { T[] newData = (T[]) new Object[capacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; } private void checkIndex(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Add failed! Require index >=0 and index < size."); } } private void checkIndexForAdd(int index) { if(index < 0 || index > size) { throw new IllegalArgumentException("remove failed! Require index >=0 and index <= size."); } } } ================================================ FILE: java/06_linkedlist/.gitkeep ================================================ ================================================ FILE: java/06_linkedlist/LRUBaseLinkedList.java ================================================ package linked.singlelist; import java.util.Scanner; /** * 基于单链表LRU算法(java) * * @author hoda * @create 2018-12-17 */ public class LRUBaseLinkedList { /** * 默认链表容量 */ private final static Integer DEFAULT_CAPACITY = 10; /** * 头结点 */ private SNode headNode; /** * 链表长度 */ private Integer length; /** * 链表容量 */ private Integer capacity; public LRUBaseLinkedList() { this.headNode = new SNode<>(); this.capacity = DEFAULT_CAPACITY; this.length = 0; } public LRUBaseLinkedList(Integer capacity) { this.headNode = new SNode<>(); this.capacity = capacity; this.length = 0; } public void add(T data) { SNode preNode = findPreNode(data); // 链表中存在,删除原数据,再插入到链表的头部 if (preNode != null) { deleteElemOptim(preNode); intsertElemAtBegin(data); } else { if (length >= this.capacity) { //删除尾结点 deleteElemAtEnd(); } intsertElemAtBegin(data); } } /** * 删除preNode结点下一个元素 * * @param preNode */ private void deleteElemOptim(SNode preNode) { SNode temp = preNode.getNext(); preNode.setNext(temp.getNext()); temp = null; length--; } /** * 链表头部插入节点 * * @param data */ private void intsertElemAtBegin(T data) { SNode next = headNode.getNext(); headNode.setNext(new SNode(data, next)); length++; } /** * 获取查找到元素的前一个结点 * * @param data * @return */ private SNode findPreNode(T data) { SNode node = headNode; while (node.getNext() != null) { if (data.equals(node.getNext().getElement())) { return node; } node = node.getNext(); } return null; } /** * 删除尾结点 */ private void deleteElemAtEnd() { SNode ptr = headNode; // 空链表直接返回 if (ptr.getNext() == null) { return; } // 倒数第二个结点 while (ptr.getNext().getNext() != null) { ptr = ptr.getNext(); } SNode tmp = ptr.getNext(); ptr.setNext(null); tmp = null; length--; } private void printAll() { SNode node = headNode.getNext(); while (node != null) { System.out.print(node.getElement() + ","); node = node.getNext(); } System.out.println(); } public class SNode { private T element; private SNode next; public SNode(T element) { this.element = element; } public SNode(T element, SNode next) { this.element = element; this.next = next; } public SNode() { this.next = null; } public T getElement() { return element; } public void setElement(T element) { this.element = element; } public SNode getNext() { return next; } public void setNext(SNode next) { this.next = next; } } public static void main(String[] args) { LRUBaseLinkedList list = new LRUBaseLinkedList(); Scanner sc = new Scanner(System.in); while (true) { list.add(sc.nextInt()); list.printAll(); } } } ================================================ FILE: java/06_linkedlist/LRUBasedArray.java ================================================ package linkedlist; import java.util.HashMap; import java.util.Map; /** * Created by SpecialYang in 2018/12/7 2:00 PM. * * 基于数组实现的LRU缓存 * 1. 空间复杂度为O(n) * 2. 时间复杂度为O(n) * 3. 不支持null的缓存 */ public class LRUBasedArray { private static final int DEFAULT_CAPACITY = (1 << 3); private int capacity; private int count; private T[] value; private Map holder; public LRUBasedArray() { this(DEFAULT_CAPACITY); } public LRUBasedArray(int capacity) { this.capacity = capacity; value = (T[]) new Object[capacity]; count = 0; holder = new HashMap(capacity); } /** * 模拟访问某个值 * @param object */ public void offer(T object) { if (object == null) { throw new IllegalArgumentException("该缓存容器不支持null!"); } Integer index = holder.get(object); if (index == null) { if (isFull()) { removeAndCache(object); } else { cache(object, count); } } else { update(index); } } /** * 若缓存中有指定的值,则更新位置 * @param end */ public void update(int end) { T target = value[end]; rightShift(end); value[0] = target; holder.put(target, 0); } /** * 缓存数据到头部,但要先右移 * @param object * @param end 数组右移的边界 */ public void cache(T object, int end) { rightShift(end); value[0] = object; holder.put(object, 0); count++; } /** * 缓存满的情况,踢出后,再缓存到数组头部 * @param object */ public void removeAndCache(T object) { T key = value[--count]; holder.remove(key); cache(object, count); } /** * end左边的数据统一右移一位 * @param end */ private void rightShift(int end) { for (int i = end - 1; i >= 0; i--) { value[i + 1] = value[i]; holder.put(value[i], i + 1); } } public boolean isContain(T object) { return holder.containsKey(object); } public boolean isEmpty() { return count == 0; } public boolean isFull() { return count == capacity; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < count; i++) { sb.append(value[i]); sb.append(" "); } return sb.toString(); } static class TestLRUBasedArray { public static void main(String[] args) { testDefaultConstructor(); testSpecifiedConstructor(4); // testWithException(); } private static void testWithException() { LRUBasedArray lru = new LRUBasedArray(); lru.offer(null); } public static void testDefaultConstructor() { System.out.println("======无参测试========"); LRUBasedArray lru = new LRUBasedArray(); lru.offer(1); lru.offer(2); lru.offer(3); lru.offer(4); lru.offer(5); System.out.println(lru); lru.offer(6); lru.offer(7); lru.offer(8); lru.offer(9); System.out.println(lru); } public static void testSpecifiedConstructor(int capacity) { System.out.println("======有参测试========"); LRUBasedArray lru = new LRUBasedArray(capacity); lru.offer(1); System.out.println(lru); lru.offer(2); System.out.println(lru); lru.offer(3); System.out.println(lru); lru.offer(4); System.out.println(lru); lru.offer(2); System.out.println(lru); lru.offer(4); System.out.println(lru); lru.offer(7); System.out.println(lru); lru.offer(1); System.out.println(lru); lru.offer(2); System.out.println(lru); } } } ================================================ FILE: java/06_linkedlist/SinglyLinkedList.java ================================================ package linkedlist; /** * 1)单链表的插入、删除、查找操作; * 2)链表中存储的是int类型的数据; * * Author:Zheng */ public class SinglyLinkedList { private Node head = null; public Node findByValue(int value) { Node p = head; while (p != null && p.data != value) { p = p.next; } return p; } public Node findByIndex(int index) { Node p = head; int pos = 0; while (p != null && pos != index) { p = p.next; ++pos; } return p; } //无头结点 //表头部插入 //这种操作将于输入的顺序相反,逆序 public void insertToHead(int value) { Node newNode = new Node(value, null); insertToHead(newNode); } public void insertToHead(Node newNode) { if (head == null) { head = newNode; } else { newNode.next = head; head = newNode; } } //顺序插入 //链表尾部插入 public void insertTail(int value){ Node newNode = new Node(value, null); //空链表,可以插入新节点作为head,也可以不操作 if (head == null){ head = newNode; }else{ Node q = head; while(q.next != null){ q = q.next; } newNode.next = q.next; q.next = newNode; } } public void insertAfter(Node p, int value) { Node newNode = new Node(value, null); insertAfter(p, newNode); } public void insertAfter(Node p, Node newNode) { if (p == null) return; newNode.next = p.next; p.next = newNode; } public void insertBefore(Node p, int value) { Node newNode = new Node(value, null); insertBefore(p, newNode); } public void insertBefore(Node p, Node newNode) { if (p == null) return; if (head == p) { insertToHead(newNode); return; } Node q = head; while (q != null && q.next != p) { q = q.next; } if (q == null) { return; } newNode.next = p; q.next = newNode; } public void deleteByNode(Node p) { if (p == null || head == null) return; if (p == head) { head = head.next; return; } Node q = head; while (q != null && q.next != p) { q = q.next; } if (q == null) { return; } q.next = q.next.next; } public void deleteByValue(int value) { if (head == null) return; Node p = head; Node q = null; while (p != null && p.data != value) { q = p; p = p.next; } if (p == null) return; if (q == null) { head = head.next; } else { q.next = q.next.next; } // 可重复删除指定value的代码 /* if (head != null && head.data == value) { head = head.next; } Node pNode = head; while (pNode != null) { if (pNode.next.data == data) { pNode.next = pNode.next.next; continue; } pNode = pNode.next; } */ } public void printAll() { Node p = head; while (p != null) { System.out.print(p.data + " "); p = p.next; } System.out.println(); } //判断true or false public boolean TFResult(Node left, Node right){ Node l = left; Node r = right; boolean flag=true; System.out.println("left_:"+l.data); System.out.println("right_:"+r.data); while(l != null && r != null){ if (l.data == r.data){ l = l.next; r = r.next; continue; }else{ flag=false; break; } } System.out.println("什么结果"); return flag; /* if (l==null && r==null){ System.out.println("什么结果"); return true; }else{ return false; }*/ } // 判断是否为回文 public boolean palindrome(){ if (head == null){ return false; }else{ System.out.println("开始执行找到中间节点"); Node p = head; Node q = head; if (p.next == null){ System.out.println("只有一个元素"); return true; } while( q.next != null && q.next.next != null){ p = p.next; q = q.next.next; } System.out.println("中间节点" + p.data); System.out.println("开始执行奇数节点的回文判断"); Node leftLink = null; Node rightLink = null; if(q.next == null){ // p 一定为整个链表的中点,且节点数目为奇数 rightLink = p.next; leftLink = inverseLinkList(p).next; System.out.println("左边第一个节点"+leftLink.data); System.out.println("右边第一个节点"+rightLink.data); }else{ //p q 均为中点 rightLink = p.next; leftLink = inverseLinkList(p); } return TFResult(leftLink, rightLink); } } //带结点的链表翻转 public Node inverseLinkList_head(Node p){ // Head 为新建的一个头结点 Node Head = new Node(9999,null); // p 为原来整个链表的头结点,现在Head指向 整个链表 Head.next = p; /* 带头结点的链表翻转等价于 从第二个元素开始重新头插法建立链表 */ Node Cur = p.next; p.next = null; Node next = null; while(Cur != null){ next = Cur.next; Cur.next = Head.next; Head.next = Cur; System.out.println("first " + Head.data); Cur = next; } // 返回左半部分的中点之前的那个节点 // 从此处开始同步像两边比较 return Head; } //无头结点的链表翻转 public Node inverseLinkList(Node p){ Node pre = null; Node r = head; System.out.println("z---" + r.data); Node next= null; while(r !=p){ next = r.next; r.next = pre; pre = r; r = next; } r.next = pre; // 返回左半部分的中点之前的那个节点 // 从此处开始同步像两边比较 return r; } public static Node createNode(int value) { return new Node(value, null); } public static class Node { private int data; private Node next; public Node(int data, Node next) { this.data = data; this.next = next; } public int getData() { return data; } } public static void main(String[]args){ SinglyLinkedList link = new SinglyLinkedList(); System.out.println("hello"); //int data[] = {1}; //int data[] = {1,2}; //int data[] = {1,2,3,1}; //int data[] = {1,2,5}; //int data[] = {1,2,2,1}; // int data[] = {1,2,5,2,1}; int data[] = {1,2,5,3,1}; for(int i =0; i < data.length; i++){ //link.insertToHead(data[i]); link.insertTail(data[i]); } // link.printAll(); // Node p = link.inverseLinkList_head(link.head); // while(p != null){ // System.out.println("aa"+p.data); // p = p.next; // } System.out.println("打印原始:"); link.printAll(); if (link.palindrome()){ System.out.println("回文"); }else{ System.out.println("不是回文"); } } } ================================================ FILE: java/07_linkedlist/.gitkeep ================================================ ================================================ FILE: java/07_linkedlist/LinkedListAlgo.java ================================================ package linkedlist; /** * 1) 单链表反转 * 2) 链表中环的检测 * 3) 两个有序的链表合并 * 4) 删除链表倒数第n个结点 * 5) 求链表的中间结点 * * Author: Zheng */ public class LinkedListAlgo { // 单链表反转 public static Node reverse(Node list) { Node curr = list, pre = null; while (curr != null) { Node next = curr.next; curr.next = pre; pre = curr; curr = next; } return pre; } // 检测环 public static boolean checkCircle(Node list) { if (list == null) return false; Node fast = list.next; Node slow = list; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (slow == fast) return true; } return false; } // 有序链表合并 // public static Node mergeSortedLists(Node la, Node lb) { // if (la == null) return lb; // if (lb == null) return la; // Node p = la; // Node q = lb; // Node head; // if (p.data < q.data) { // head = p; // p = p.next; // } else { // head = q; // q = q.next; // } // Node r = head; // while (p != null && q != null) { // if (p.data < q.data) { // r.next = p; // p = p.next; // } else { // r.next = q; // q = q.next; // } // r = r.next; // } // if (p != null) { // r.next = p; // } else { // r.next = q; // } // return head; //} //----------------------------------------- // 有序链表合并 Leetcode 21 /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode soldier = new ListNode(0); //利用哨兵结点简化实现难度 技巧三 ListNode p = soldier; while ( l1 != null && l2 != null ){ if ( l1.val < l2.val ){ p.next = l1; l1 = l1.next; } else{ p.next = l2; l2 = l2.next; } p = p.next; } if (l1 != null) { p.next = l1; } if (l2 != null) { p.next = l2; } return soldier.next; } // 删除倒数第K个结点 public static Node deleteLastKth(Node list, int k) { Node fast = list; int i = 1; while (fast != null && i < k) { fast = fast.next; ++i; } if (fast == null) return list; Node slow = list; Node prev = null; while (fast.next != null) { fast = fast.next; prev = slow; slow = slow.next; } if (prev == null) { list = list.next; } else { prev.next = prev.next.next; } return list; } // 求中间结点 public static Node findMiddleNode(Node list) { if (list == null) return null; Node fast = list; Node slow = list; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } public static void printAll(Node list) { Node p = list; while (p != null) { System.out.print(p.data + " "); p = p.next; } System.out.println(); } public static Node createNode(int value) { return new Node(value, null); } public static class Node { private int data; private Node next; public Node(int data, Node next) { this.data = data; this.next = next; } public int getData() { return data; } } } ================================================ FILE: java/08_stack/SampleBrowser.java ================================================ package algo.lesson08; /** * 使用前后栈实现浏览器的前进后退。 * * @author chinalwb */ public class SampleBrowser { public static void main(String[] args) { SampleBrowser browser = new SampleBrowser(); browser.open("http://www.baidu.com"); browser.open("http://news.baidu.com/"); browser.open("http://news.baidu.com/ent"); browser.goBack(); browser.goBack(); browser.goForward(); browser.open("http://www.qq.com"); browser.goForward(); browser.goBack(); browser.goForward(); browser.goBack(); browser.goBack(); browser.goBack(); browser.goBack(); browser.checkCurrentPage(); } private String currentPage; private LinkedListBasedStack backStack; private LinkedListBasedStack forwardStack; public SampleBrowser() { this.backStack = new LinkedListBasedStack(); this.forwardStack = new LinkedListBasedStack(); } public void open(String url) { if (this.currentPage != null) { this.backStack.push(this.currentPage); this.forwardStack.clear(); } showUrl(url, "Open"); } public boolean canGoBack() { return this.backStack.size() > 0; } public boolean canGoForward() { return this.forwardStack.size() > 0; } public String goBack() { if (this.canGoBack()) { this.forwardStack.push(this.currentPage); String backUrl = this.backStack.pop(); showUrl(backUrl, "Back"); return backUrl; } System.out.println("* Cannot go back, no pages behind."); return null; } public String goForward() { if (this.canGoForward()) { this.backStack.push(this.currentPage); String forwardUrl = this.forwardStack.pop(); showUrl(forwardUrl, "Foward"); return forwardUrl; } System.out.println("** Cannot go forward, no pages ahead."); return null; } public void showUrl(String url, String prefix) { this.currentPage = url; System.out.println(prefix + " page == " + url); } public void checkCurrentPage() { System.out.println("Current page is: " + this.currentPage); } /** * A LinkedList based Stack implementation. */ public static class LinkedListBasedStack { // public static void main(String[] args) { // LinkedListBasedStack stack = new LinkedListBasedStack(); // stack.push("A"); // stack.push("B"); // stack.push("C"); // stack.pop(); // stack.push("D"); // stack.push("E"); // stack.pop(); // stack.push("F"); // stack.print(); // //// String data = stack.getTopData(); //// System.out.println("Top data == " + data); // } private int size; private Node top; static Node createNode(String data, Node next) { return new Node(data, next); } public void clear() { this.top = null; this.size = 0; } public void push(String data) { Node node = createNode(data, this.top); this.top = node; this.size++; } public String pop() { Node popNode = this.top; if (popNode == null) { System.out.println("Stack is empty."); return null; } this.top = popNode.next; if (this.size > 0) { this.size--; } return popNode.data; } public String getTopData() { if (this.top == null) { return null; } return this.top.data; } public int size() { return this.size; } public void print() { System.out.println("Print stack:"); Node currentNode = this.top; while (currentNode != null) { String data = currentNode.getData(); System.out.print(data + "\t"); currentNode = currentNode.next; } System.out.println(); } public static class Node { private String data; private Node next; public Node(String data) { this(data, null); } public Node(String data, Node next) { this.data = data; this.next = next; } public void setData(String data) { this.data = data; } public String getData() { return this.data; } public void setNext(Node next) { this.next = next; } public Node getNext() { return this.next; } } } } ================================================ FILE: java/08_stack/StackBasedOnLinkedList.java ================================================ package stack; /** * 基于链表实现的栈。 * * Author: Zheng */ public class StackBasedOnLinkedList { private Node top = null; public void push(int value) { Node newNode = new Node(value, null); // 判断是否栈空 if (top == null) { top = newNode; } else { newNode.next = top; top = newNode; } } /** * 我用-1表示栈中没有数据。 */ public int pop() { if (top == null) return -1; int value = top.data; top = top.next; return value; } public void printAll() { Node p = top; while (p != null) { System.out.print(p.data + " "); p = p.next; } System.out.println(); } private static class Node { private int data; private Node next; public Node(int data, Node next) { this.data = data; this.next = next; } public int getData() { return data; } } } ================================================ FILE: java/09_queue/ArrayQueue.java ================================================ package queue; /** * Created by wangzheng on 2018/10/9. */ // 用数组实现的队列 public class ArrayQueue { // 数组:items,数组大小:n private String[] items; private int n = 0; // head表示队头下标,tail表示队尾下标 private int head = 0; private int tail = 0; // 申请一个大小为capacity的数组 public ArrayQueue(int capacity) { items = new String[capacity]; n = capacity; } // 入队 public boolean enqueue(String item) { // 如果tail == n 表示队列已经满了 if (tail == n) return false; items[tail] = item; ++tail; return true; } // 出队 public String dequeue() { // 如果head == tail 表示队列为空 if (head == tail) return null; // 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了 String ret = items[head]; ++head; return ret; } public void printAll() { for (int i = head; i < tail; ++i) { System.out.print(items[i] + " "); } System.out.println(); } } ================================================ FILE: java/09_queue/CircularQueue.java ================================================ package queue; /** * Created by wangzheng on 2018/10/9. */ public class CircularQueue { // 数组:items,数组大小:n private String[] items; private int n = 0; // head表示队头下标,tail表示队尾下标 private int head = 0; private int tail = 0; // 申请一个大小为capacity的数组 public CircularQueue(int capacity) { items = new String[capacity]; n = capacity; } // 入队 public boolean enqueue(String item) { // 队列满了 if ((tail + 1) % n == head) return false; items[tail] = item; tail = (tail + 1) % n; return true; } // 出队 public String dequeue() { // 如果head == tail 表示队列为空 if (head == tail) return null; String ret = items[head]; head = (head + 1) % n; return ret; } public void printAll() { if (0 == n) return; for (int i = head; i % n != tail; i = (i + 1) % n) { System.out.print(items[i] + " "); } System.out.println(); } } ================================================ FILE: java/09_queue/DynamicArrayQueue.java ================================================ package queue; /** * Created by wangzheng on 2018/10/9. */ public class DynamicArrayQueue { // 数组:items,数组大小:n private String[] items; private int n = 0; // head表示队头下标,tail表示队尾下标 private int head = 0; private int tail = 0; // 申请一个大小为capacity的数组 public DynamicArrayQueue(int capacity) { items = new String[capacity]; n = capacity; } // 入队操作,将item放入队尾 public boolean enqueue(String item) { // tail == n表示队列末尾没有空间了 if (tail == n) { // tail ==n && head==0,表示整个队列都占满了 if (head == 0) return false; // 数据搬移 for (int i = head; i < tail; ++i) { items[i-head] = items[i]; } // 搬移完之后重新更新head和tail tail -= head; head = 0; } items[tail] = item; tail++; return true; } // 出队 public String dequeue() { // 如果head == tail 表示队列为空 if (head == tail) return null; // 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了 String ret = items[head]; ++head; return ret; } public void printAll() { for (int i = head; i < tail; ++i) { System.out.print(items[i] + " "); } System.out.println(); } } ================================================ FILE: java/09_queue/QueueBasedOnLinkedList.java ================================================ package queue; /** * 基于链表实现的队列 * * Author: Zheng */ public class QueueBasedOnLinkedList { // 队列的队首和队尾 private Node head = null; private Node tail = null; // 入队 public void enqueue(String value) { if (tail == null) { Node newNode = new Node(value, null); head = newNode; tail = newNode; } else { tail.next = new Node(value, null); tail = tail.next; } } // 出队 public String dequeue() { if (head == null) return null; String value = head.data; head = head.next; if (head == null) { tail = null; } return value; } public void printAll() { Node p = head; while (p != null) { System.out.print(p.data + " "); p = p.next; } System.out.println(); } private static class Node { private String data; private Node next; public Node(String data, Node next) { this.data = data; this.next = next; } public String getData() { return data; } } } ================================================ FILE: java/11_sorts/InsertionSortAdd.java ================================================ package sorts; import java.util.Arrays; /** * 插入排序(插入位置,从头至尾搜索) * @Author: ooooor */ public class InsertionSortAdd { public static void main(String[] args) { int[] data = new int[]{4, 6, 5, 3, 7, 1, 2}; fromStartToEnd(Arrays.copyOf(data, data.length)); System.out.println(Arrays.toString(data)); } /** * 查询插入位置时, 从头至尾搜索 * @param data */ private static void fromStartToEnd(int[] data) { for (int i=1; i < data.length; i++) { int value = data[i]; int[] tmp = new int[2]; int change = i; for (int j=0; j < i; j++) { if(value >= data[j]) { continue; } int index = j%2; if (change == i) { tmp[Math.abs(index-1)] = data[j]; change = j; } tmp[index] = data[j+1]; if (0 == index) { data[j+1] = tmp[index+1]; } else { data[j+1] = tmp[index-1]; } } data[change] = value; } } } ================================================ FILE: java/11_sorts/Sorts.java ================================================ package sorts; import java.util.Arrays; /** * 冒泡排序、插入排序、选择排序 *

* Author: Zheng */ public class Sorts { // 冒泡排序,a是数组,n表示数组大小 public static void bubbleSort(int[] a, int n) { if (n <= 1) return; for (int i = 0; i < n; ++i) { // 提前退出标志位 boolean flag = false; for (int j = 0; j < n - i - 1; ++j) { if (a[j] > a[j + 1]) { // 交换 int tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp; // 此次冒泡有数据交换 flag = true; } } if (!flag) break; // 没有数据交换,提前退出 } } /** * 冒泡排序改进:在每一轮排序后记录最后一次元素交换的位置,作为下次比较的边界, * 对于边界外的元素在下次循环中无需比较. */ public static void bubbleSort2(int[] a, int n) { if (n <= 1) return; // 最后一次交换的位置 int lastExchange = 0; // 无序数据的边界,每次只需要比较到这里即可退出 int sortBorder = n - 1; for (int i = 0; i < n; i++) { // 提前退出标志位 boolean flag = false; for (int j = 0; j < sortBorder; j++) { if (a[j] > a[j + 1]) { int tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp; // 此次冒泡有数据交换 flag = true; // 更新最后一次交换的位置 lastExchange = j; } } sortBorder = lastExchange; if (!flag) break; // 没有数据交换,提前退出 } } // 插入排序,a表示数组,n表示数组大小 public static void insertionSort(int[] a, int n) { if (n <= 1) return; for (int i = 1; i < n; ++i) { int value = a[i]; int j = i - 1; // 查找要插入的位置并移动数据 for (; j >= 0; --j) { if (a[j] > value) { a[j + 1] = a[j]; } else { break; } } a[j + 1] = value; } } // 选择排序,a表示数组,n表示数组大小 public static void selectionSort(int[] a, int n) { if (n <= 1) return; for (int i = 0; i < n - 1; ++i) { // 查找最小值 int minIndex = i; for (int j = i + 1; j < n; ++j) { if (a[j] < a[minIndex]) { minIndex = j; } } // 交换 int tmp = a[i]; a[i] = a[minIndex]; a[minIndex] = tmp; } } public static void main(String[] args) { int[] array = new int[]{3, 4, 2, 1, 5, 6, 7, 8}; bubbleSort2(array, array.length); System.out.println(Arrays.toString(array)); } } ================================================ FILE: java/11_sorts/SortsAddOn.java ================================================ package sorts; /** * 向下冒泡算法 (或许比冒泡更易懂的排序算法?) * 希尔排序 * * Author: wliu */ public class SortsAddOn { public static void main(String[] args) { int[] arr = {3, 2, 6, 4, 5, 1, 9, 20, 13, 16}; // bubbleDownSort(arr); shellSort(arr); print(arr); } /** * 向下冒泡。可能比冒泡更易懂? * * 算法概要: * 从0开始,用这个元素去跟后面的所有元素比较,如果发现这个元素大于后面的某个元素,则交换。 * 3 2 6 4 5 1 * 第一趟是从 index=0 也就是 3, 开始跟index=1及其后面的数字比较 * 3 大于 2,交换,变为 2 3 6 4 5 1,此时index=0的位置变为了2 * 接下来将用2跟index=2比较 * 2 不大于 6 不交换 * 2 不大于 4 不交换 * 2 不大于 5 不交换 * 2 大于 1,交换,变为 1 3 6 4 5 2,第一趟排序完成。 * * 第二趟是从 index=1 也就是 3,开始跟index=2及其后面的数字比较 * 3 不大于 6 不交换 * 3 不大于 4 不交换 * 3 不大于 5 不交换 * 3 大于 2,交换,变为 1 2 6 4 5 3,第二趟排序完成。 * * 第三趟是从 index=2 也就是 6,开始跟index=3及其后面的数字比较 * 6 大于 4,交换,变为 1 2 4 6 5 3, 此时 index = 2 的位置变为了4 * 接下来将用4跟index=4比较 * 4 不大于 5 不交换 * 4 大于 3,交换,变为 1 2 3 6 5 4,第三趟排序完成。 * * 第四趟是从 index=3 也就是 6,开始跟index=4及其后面的数字比较 * 6 大于 5,交换,变为 1 2 3 5 6 4, 此时 index = 3 的位置变为了5 * 接下来将用5跟index=5比较 * 5 大于 4,交换,变为 1 2 3 4 6 5, 第四趟排序完成。 * * 第五趟是从 index=4 也就是 6,开始跟index=5及其后面的数字比较 * 6 大于 5,交换,变为 1 2 3 4 5 6, 此时 index = 4 的位置变为了5 * 接下来将用5跟index=6比较 * index = 6 已经不满足 index < length 的条件,整个排序完成。 */ private static void bubbleDownSort(int[] arr) { int len = arr.length; if (len == 1) return; for (int i = 0; i < len; i++) { for (int j = i + 1; j < len; j++) { if (arr[i] > arr[j]) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } } } private static void shellSort(int[] arr) { int len = arr.length; if (len == 1) return; int step = len / 2; while (step >= 1) { for (int i = step; i < len; i++) { int value = arr[i]; int j = i - step; for (; j >= 0; j -= step) { if (value < arr[j]) { arr[j+step] = arr[j]; } else { break; } } arr[j+step] = value; } step = step / 2; } } private static void print(int[] arr) { System.out.println("Print array:"); for (int x : arr) { System.out.print(x + "\t"); } System.out.println(""); } } ================================================ FILE: java/12_sorts/KthSmallest.java ================================================ package sort; /** * @author wangjunwei87 * @since 2019-03-10 */ public class KthSmallest { public static int kthSmallest(int[] arr, int k) { if (arr == null || arr.length < k) { return -1; } int partition = partition(arr, 0, arr.length - 1); while (partition + 1 != k) { if (partition + 1 < k) { partition = partition(arr, partition + 1, arr.length - 1); } else { partition = partition(arr, 0, partition - 1); } } return arr[partition]; } private static int partition(int[] arr, int p, int r) { int pivot = arr[r]; int i = p; for (int j = p; j < r; j++) { // 这里要是 <= ,不然会出现死循环,比如查找数组 [1,1,2] 的第二小的元素 if (arr[j] <= pivot) { swap(arr, i, j); i++; } } swap(arr, i, r); return i; } private static void swap(int[] arr, int i, int j) { if (i == j) { return; } int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } ================================================ FILE: java/12_sorts/MergeSort.java ================================================ package sorts; /** * Created by wangzheng on 2018/10/16. */ public class MergeSort { // 归并排序算法, a是数组,n表示数组大小 public static void mergeSort(int[] a, int n) { mergeSortInternally(a, 0, n-1); } // 递归调用函数 private static void mergeSortInternally(int[] a, int p, int r) { // 递归终止条件 if (p >= r) return; // 取p到r之间的中间位置q,防止(p+r)的和超过int类型最大值 int q = p + (r - p)/2; // 分治递归 mergeSortInternally(a, p, q); mergeSortInternally(a, q+1, r); // 将A[p...q]和A[q+1...r]合并为A[p...r] merge(a, p, q, r); } private static void merge(int[] a, int p, int q, int r) { int i = p; int j = q+1; int k = 0; // 初始化变量i, j, k int[] tmp = new int[r-p+1]; // 申请一个大小跟a[p...r]一样的临时数组 while (i<=q && j<=r) { if (a[i] <= a[j]) { tmp[k++] = a[i++]; // i++等于i:=i+1 } else { tmp[k++] = a[j++]; } } // 判断哪个子数组中有剩余的数据 int start = i; int end = q; if (j <= r) { start = j; end = r; } // 将剩余的数据拷贝到临时数组tmp while (start <= end) { tmp[k++] = a[start++]; } // 将tmp中的数组拷贝回a[p...r] for (i = 0; i <= r-p; ++i) { a[p+i] = tmp[i]; } } /** * 合并(哨兵) * * @param arr * @param p * @param q * @param r */ private static void mergeBySentry(int[] arr, int p, int q, int r) { int[] leftArr = new int[q - p + 2]; int[] rightArr = new int[r - q + 1]; for (int i = 0; i <= q - p; i++) { leftArr[i] = arr[p + i]; } // 第一个数组添加哨兵(最大值) leftArr[q - p + 1] = Integer.MAX_VALUE; for (int i = 0; i < r - q; i++) { rightArr[i] = arr[q + 1 + i]; } // 第二个数组添加哨兵(最大值) rightArr[r-q] = Integer.MAX_VALUE; int i = 0; int j = 0; int k = p; while (k <= r) { // 当左边数组到达哨兵值时,i不再增加,直到右边数组读取完剩余值,同理右边数组也一样 if (leftArr[i] <= rightArr[j]) { arr[k++] = leftArr[i++]; } else { arr[k++] = rightArr[j++]; } } } } ================================================ FILE: java/12_sorts/QuickSort.java ================================================ package sorts; /** * Created by wangzheng on 2018/10/16. */ public class QuickSort { // 快速排序,a是数组,n表示数组的大小 public static void quickSort(int[] a, int n) { quickSortInternally(a, 0, n-1); } // 快速排序递归函数,p,r为下标 private static void quickSortInternally(int[] a, int p, int r) { if (p >= r) return; int q = partition(a, p, r); // 获取分区点 quickSortInternally(a, p, q-1); quickSortInternally(a, q+1, r); } private static int partition(int[] a, int p, int r) { int pivot = a[r]; int i = p; for(int j = p; j < r; ++j) { if (a[j] < pivot) { if (i == j) { ++i; } else { int tmp = a[i]; a[i++] = a[j]; a[j] = tmp; } } } int tmp = a[i]; a[i] = a[r]; a[r] = tmp; System.out.println("i=" + i); return i; } } ================================================ FILE: java/12_sorts/Sorts.java ================================================ package com.study.sort; import java.util.Arrays; /** * 冒泡,选择,插入,快速,归并 * * @author ldb * @date 2019-10-08 16:09 */ public class Sorts { /** * 冒泡排序 * * @param arr */ public static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } /** * 优化冒泡排序 * * @param arr */ public static void bubbleSort2(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { boolean flag = true; for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; flag = false; } } if (flag) { break; } } } /** * 插入排序 * * @param arr */ public static void insertSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int val = arr[i]; int index = i - 1; while (index >= 0 && arr[index] > val) { arr[index + 1] = arr[index]; index--; } arr[index + 1] = val; } } /** * 插入排序 * * @param arr * @param n 表示数组有用大小 */ public static void insertSort(int[] arr, int n) { for (int i = 1; i < n; i++) { int val = arr[i]; int index = i - 1; while (index >= 0 && arr[index] > val) { arr[index + 1] = arr[index]; index--; } arr[index + 1] = val; } } /** * 选择排序 * * @param arr */ public static void selectSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[minIndex] > arr[j]) { minIndex = j; } } // 交换 int temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } /** * 归并排序 * * @param arr */ public static void mergeSort(int[] arr, int left, int right) { if (left >= right) { return; } int q = (left + right) / 2; mergeSort(arr, left, q); mergeSort(arr, q + 1, right); merge2(arr, left, q, right); } private static void merge2(int[] arr, int left, int q, int right) { int[] leftArr = new int[q - left + 2]; int[] rightArr = new int[right - q + 1]; for (int i = 0; i <= q - left; i++) { leftArr[i] = arr[left + i]; } // 第一个数组添加哨兵(最大值) leftArr[q - left + 1] = Integer.MAX_VALUE; for (int i = 0; i < right - q; i++) { rightArr[i] = arr[q + 1 + i]; } // 第二个数组添加哨兵(最大值) rightArr[right - q] = Integer.MAX_VALUE; int i = 0; int j = 0; int k = left; while (k <= right) { // 当左边数组到达哨兵值时,i不再增加,直到右边数组读取完剩余值,同理右边数组也一样 if (leftArr[i] <= rightArr[j]) { arr[k++] = leftArr[i++]; } else { arr[k++] = rightArr[j++]; } } } private static void merge(int[] arr, int left, int q, int right) { int i = left; int j = q + 1; int k = 0; int[] tmp = new int[right - left + 1]; while (i <= q && j <= right) { if (arr[i] <= arr[j]) { tmp[k++] = arr[i++]; } else { tmp[k++] = arr[j++]; } } int start = i; int end = q; if (j <= right) { start = j; end = right; } while (start <= end) { tmp[k++] = arr[start++]; } for (int l = 0; l <= right - left; l++) { arr[l + left] = tmp[l]; } } /** * 快速排序 * * @param arr */ public static void quickSort(int[] arr, int left, int right) { if (left >= right) { return; } int q = partition2(arr, left, right); quickSort(arr, left, q - 1); quickSort(arr, q + 1, right); } private static int partition(int[] arr, int left, int right) { int pivot = arr[right]; int i = left; for (int j = left; j < right; j++) { if (arr[j] < pivot) { if (i == j) { ++i; } else { int tmp = arr[i]; arr[i++] = arr[j]; arr[j] = tmp; } } } int tmp = arr[i]; arr[i] = arr[right]; arr[right] = tmp; return i; } private static int partition2(int[] arr, int left, int right) { // 三数取中法 , 随机数在这里写 int middle = (left + right) / 2; int pivot = arr[middle]; // 交换到最右边 int val = arr[right]; arr[right] = pivot; arr[middle] = val; int i = left; for (int j = left; j < right; j++) { if (arr[j] < pivot) { if (i == j) { ++i; } else { int tmp = arr[i]; arr[i++] = arr[j]; arr[j] = tmp; } } } int tmp = arr[i]; arr[i] = arr[right]; arr[right] = tmp; return i; } /** * 三向切分快速排序 * * @param arr * @param left * @param right * @return */ private static void quickSort3(int[] arr, int left, int right) { if (left >= right) { return; } int l = left; int k = left + 1; int r = right; int pivot = arr[l]; while (k <= r) { if (arr[k] < pivot) { int tmp = arr[l]; arr[l] = arr[k]; arr[k] = tmp; l++; k++; } else if (arr[k] == pivot) { k++; } else { if (arr[r] > pivot) { r--; } else if (arr[r] == pivot) { int tmp = arr[k]; arr[k] = arr[r]; arr[r] = tmp; k++; r--; } else { int tmp = arr[l]; arr[l] = arr[r]; arr[r] = arr[k]; arr[k] = tmp; l++; k++; r--; } } } quickSort(arr, left, l - 1); quickSort(arr, r + 1, right); } /** * 双轴快速排序 * * @param arr * @param left * @param right */ private static void quickSort4(int[] arr, int left, int right) { if (left >= right) { return; } int l = left; int k = left + 1; int r = right; // 判断pivot1 与 pivot2 大小 if (arr[l] > arr[r]) { int tmp = arr[l]; arr[l] = arr[r]; arr[r] = tmp; } int pivot1 = arr[l]; int pivot2 = arr[r]; while (k < r) { if (arr[k] < pivot1) { l++; if (l != k) { int tmp = arr[l]; arr[l] = arr[k]; arr[k] = tmp; } k++; } else if (arr[k] >= pivot1 && arr[k] <= pivot2) { k++; } else { --r; if (arr[r] > pivot2) { } else if (arr[r] >= pivot1 && arr[r] <= pivot2) { int tmp = arr[k]; arr[k] = arr[r]; arr[r] = tmp; k++; } else { l++; int tmp = arr[l]; arr[l] = arr[r]; arr[r] = arr[k]; arr[k] = tmp; k++; } } } // 交换pivot1 和 pivot2 arr[left] = arr[l]; arr[l] = pivot1; arr[right] = arr[r]; arr[r] = pivot2; quickSort(arr, left, l - 1); quickSort(arr, l + 1, r - 1); quickSort(arr, r + 1, right); } /** * O(n) 时间复杂度内求无序数组中的第 K 大元素。比如, 4 , 2 , 5 , 12 , 3 这样一组数据,第 3 大元素就是 4 。 * * @param arr */ public static int sort(int[] arr, int l, int r, int k) { if (l >= r) { return 0; } int p = partition(arr, l, r); if ((p + 1) == k) { return arr[p]; } else if ((p + 1) < k) { return sort(arr, p + 1, r, k); } else { return sort(arr, l, p - 1, k); } } public static void main(String[] args) { int[] arr = {2, 1, 5, 6, 8, 4, 12, 11, 13, 15, 7, 9, 0, -1}; // bubbleSort(arr); // bubbleSort2(arr); // selectSort(arr); // mergeSort(arr, 0, arr.length - 1); // quickSort4(arr, 0, arr.length - 1); Arrays.sort(arr); print(arr); } public static void print(int[] arr) { for (int i : arr) { System.out.print(i + " "); } System.out.println(); } } ================================================ FILE: java/13_sorts/BucketSort.java ================================================ /** * @Description:桶排序算法 * @Author: Hoda * @Date: Create in 2019-06-01 * @Modified By: * @Modified Date: */ public class BucketSort { /** * 桶排序 * * @param arr 数组 * @param bucketSize 桶容量 */ public static void bucketSort(int[] arr, int bucketSize) { if (arr.length < 2) { return; } // 数组最小值 int minValue = arr[0]; // 数组最大值 int maxValue = arr[1]; for (int i = 0; i < arr.length; i++) { if (arr[i] < minValue) { minValue = arr[i]; } else if (arr[i] > maxValue) { maxValue = arr[i]; } } // 桶数量 int bucketCount = (maxValue - minValue) / bucketSize + 1; int[][] buckets = new int[bucketCount][bucketSize]; int[] indexArr = new int[bucketCount]; // 将数组中值分配到各个桶里 for (int i = 0; i < arr.length; i++) { int bucketIndex = (arr[i] - minValue) / bucketSize; if (indexArr[bucketIndex] == buckets[bucketIndex].length) { ensureCapacity(buckets, bucketIndex); } buckets[bucketIndex][indexArr[bucketIndex]++] = arr[i]; } // 对每个桶进行排序,这里使用了快速排序 int k = 0; for (int i = 0; i < buckets.length; i++) { if (indexArr[i] == 0) { continue; } quickSortC(buckets[i], 0, indexArr[i] - 1); for (int j = 0; j < indexArr[i]; j++) { arr[k++] = buckets[i][j]; } } } /** * 数组扩容 * * @param buckets * @param bucketIndex */ private static void ensureCapacity(int[][] buckets, int bucketIndex) { int[] tempArr = buckets[bucketIndex]; int[] newArr = new int[tempArr.length * 2]; for (int j = 0; j < tempArr.length; j++) { newArr[j] = tempArr[j]; } buckets[bucketIndex] = newArr; } /** * 快速排序递归函数 * * @param arr * @param p * @param r */ private static void quickSortC(int[] arr, int p, int r) { if (p >= r) { return; } int q = partition(arr, p, r); quickSortC(arr, p, q - 1); quickSortC(arr, q + 1, r); } /** * 分区函数 * * @param arr * @param p * @param r * @return 分区点位置 */ private static int partition(int[] arr, int p, int r) { int pivot = arr[r]; int i = p; for (int j = p; j < r; j++) { if (arr[j] <= pivot) { swap(arr, i, j); i++; } } swap(arr, i, r); return i; } /** * 交换 * * @param arr * @param i * @param j */ private static void swap(int[] arr, int i, int j) { if (i == j) { return; } int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } ================================================ FILE: java/13_sorts/CountingSort.java ================================================ package sorts; /** * 计数排序 * * Author: ZHENG */ public class CountingSort { // 计数排序,a是数组,n是数组大小。假设数组中存储的都是非负整数。 public static void countingSort(int[] a, int n) { if (n <= 1) return; // 查找数组中数据的范围 int max = a[0]; for (int i = 1; i < n; ++i) { if (max < a[i]) { max = a[i]; } } // 申请一个计数数组c,下标大小[0,max] int[] c = new int[max + 1]; // 计算每个元素的个数,放入c中 for (int i = 0; i < n; ++i) { c[a[i]]++; } // 依次累加 for (int i = 1; i < max + 1; ++i) { c[i] = c[i-1] + c[i]; } // 临时数组r,存储排序之后的结果 int[] r = new int[n]; // 计算排序的关键步骤了,有点难理解 for (int i = n - 1; i >= 0; --i) { int index = c[a[i]]-1; r[index] = a[i]; c[a[i]]--; } // 将结果拷贝会a数组 for (int i = 0; i < n; ++i) { a[i] = r[i]; } } } ================================================ FILE: java/13_sorts/RadixSort.java ================================================ /** * @Description:基数排序 * @Author: Hoda * @Date: Create in 2019-07-25 * @Modified By: * @Modified Date: */ public class RadixSort { /** * 基数排序 * * @param arr */ public static void radixSort(int[] arr) { int max = arr[0]; for (int i = 0; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } // 从个位开始,对数组arr按"指数"进行排序 for (int exp = 1; max / exp > 0; exp *= 10) { countingSort(arr, exp); } } /** * 计数排序-对数组按照"某个位数"进行排序 * * @param arr * @param exp 指数 */ public static void countingSort(int[] arr, int exp) { if (arr.length <= 1) { return; } // 计算每个元素的个数 int[] c = new int[10]; for (int i = 0; i < arr.length; i++) { c[(arr[i] / exp) % 10]++; } // 计算排序后的位置 for (int i = 1; i < c.length; i++) { c[i] += c[i - 1]; } // 临时数组r,存储排序之后的结果 int[] r = new int[arr.length]; for (int i = arr.length - 1; i >= 0; i--) { r[c[(arr[i] / exp) % 10] - 1] = arr[i]; c[(arr[i] / exp) % 10]--; } for (int i = 0; i < arr.length; i++) { arr[i] = r[i]; } } } ================================================ FILE: java/17_skiplist/SkipList.java ================================================ package skiplist; /** * 跳表的一种实现方法。 * 跳表中存储的是正整数,并且存储的是不重复的。 * * Author:ZHENG */ public class SkipList { private static final float SKIPLIST_P = 0.5f; private static final int MAX_LEVEL = 16; private int levelCount = 1; private Node head = new Node(); // 带头链表 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; } // record every level largest value which smaller than insert value in update[] 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;// use update save node in search path } // in search path node next node become new node forwords(next) for (int i = 0; i < level; ++i) { newNode.forwards[i] = update[i].forwards[i]; update[i].forwards[i] = newNode; } // update node hight 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]; } } } while (levelCount>1&&head.forwards[levelCount]==null){ levelCount--; } } // 理论来讲,一级索引中元素个数应该占原始数据的 50%,二级索引中元素个数占 25%,三级索引12.5% ,一直到最顶层。 // 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。 // 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且 : // 50%的概率返回 1 // 25%的概率返回 2 // 12.5%的概率返回 3 ... private int randomLevel() { int level = 1; while (Math.random() < SKIPLIST_P && level < MAX_LEVEL) level += 1; 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(); } } } ================================================ FILE: java/17_skiplist/SkipList2.java ================================================ package com.study.skiplist; import java.util.Random; /** * 1,跳表的一种实现方法,用于练习。跳表中存储的是正整数,并且存储的是不重复的。 * 2,本类是参考作者zheng ,自己学习,优化了添加方法 * 3,看完这个,我觉得再看ConcurrentSkipListMap 源码,会有很大收获 * Author:ldb */ public class SkipList2 { private static final int MAX_LEVEL = 16; private int levelCount = 1; /** * 带头链表 */ private Node head = new Node(MAX_LEVEL); private Random r = new Random(); public Node find(int value) { Node p = head; // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找 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; } } /** * 优化了作者zheng的插入方法 * * @param value 值 */ public void insert(int value) { int level = head.forwards[0] == null ? 1 : randomLevel(); // 每次只增加一层,如果条件满足 if (level > levelCount) { level = ++levelCount; } Node newNode = new Node(level); newNode.data = value; Node update[] = new Node[level]; for (int i = 0; i < level; ++i) { update[i] = head; } Node p = head; // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找 for (int i = levelCount - 1; i >= 0; --i) { while (p.forwards[i] != null && p.forwards[i].data < value) { // 找到前一节点 p = p.forwards[i]; } // levelCount 会 > level,所以加上判断 if (level > i) { update[i] = p; } } for (int i = 0; i < level; ++i) { newNode.forwards[i] = update[i].forwards[i]; update[i].forwards[i] = newNode; } } /** * 优化了作者zheng的插入方法2 * * @param value 值 */ public void insert2(int value) { int level = head.forwards[0] == null ? 1 : randomLevel(); // 每次只增加一层,如果条件满足 if (level > levelCount) { level = ++levelCount; } Node newNode = new Node(level); newNode.data = value; Node p = head; // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找 for (int i = levelCount - 1; i >= 0; --i) { while (p.forwards[i] != null && p.forwards[i].data < value) { // 找到前一节点 p = p.forwards[i]; } // levelCount 会 > level,所以加上判断 if (level > i) { if (p.forwards[i] == null) { p.forwards[i] = newNode; } else { Node next = p.forwards[i]; p.forwards[i] = newNode; newNode.forwards[i] = next; } } } } /** * 作者zheng的插入方法,未优化前,优化后参见上面insert() * * @param value * @param level 0 表示随机层数,不为0,表示指定层数,指定层数 * 可以让每次打印结果不变动,这里是为了便于学习理解 */ public void insert(int value, int level) { // 随机一个层数 if (level == 0) { level = randomLevel(); } // 创建新节点 Node newNode = new Node(level); newNode.data = value; // 表示从最大层到低层,都要有节点数据 newNode.maxLevel = level; // 记录要更新的层数,表示新节点要更新到哪几层 Node update[] = new Node[level]; for (int i = 0; i < level; ++i) { update[i] = head; } /** * * 1,说明:层是从下到上的,这里最下层编号是0,最上层编号是15 * 2,这里没有从已有数据最大层(编号最大)开始找,(而是随机层的最大层)导致有些问题。 * 如果数据量为1亿,随机level=1 ,那么插入时间复杂度为O(n) */ 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]表示当前层节点的前一节点,因为要找到前一节点,才好插入数据 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]; } } } } /** * 随机 level 次,如果是奇数层数 +1,防止伪随机 * * @return */ 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 void printAll_beautiful() { Node p = head; Node[] c = p.forwards; Node[] d = c; int maxLevel = c.length; for (int i = maxLevel - 1; i >= 0; i--) { do { System.out.print((d[i] != null ? d[i].data : null) + ":" + i + "-------"); } while (d[i] != null && (d = d[i].forwards)[i] != null); System.out.println(); d = c; } } /** * 跳表的节点,每个节点记录了当前节点数据和所在层数数据 */ public class Node { private int data = -1; /** * 表示当前节点位置的下一个节点所有层的数据,从上层切换到下层,就是数组下标-1, * forwards[3]表示当前节点在第三层的下一个节点。 */ private Node forwards[]; /** * 这个值其实可以不用,看优化insert() */ private int maxLevel = 0; public Node(int level) { forwards = new Node[level]; } @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(); } } public static void main(String[] args) { SkipList2 list = new SkipList2(); list.insert(1, 3); list.insert(2, 3); list.insert(3, 2); list.insert(4, 4); list.insert(5, 10); list.insert(6, 4); list.insert(8, 5); list.insert(7, 4); list.printAll_beautiful(); list.printAll(); /** * 结果如下: * null:15------- * null:14------- * null:13------- * null:12------- * null:11------- * null:10------- * 5:9------- * 5:8------- * 5:7------- * 5:6------- * 5:5------- * 5:4------- 8:4------- * 4:3-------5:3-------6:3-------7:3-------8:3------- * 1:2-------2:2------- 4:2-------5:2-------6:2-------7:2-------8:2------- * 1:1-------2:1-------3:1-------4:1-------5:1-------6:1-------7:1-------8:1------- * 1:0-------2:0-------3:0-------4:0-------5:0-------6:0-------7:0-------8:0------- * { data: 1; levels: 3 } { data: 2; levels: 3 } { data: 3; levels: 2 } { data: 4; levels: 4 } * { data: 5; levels: 10 } { data: 6; levels: 4 } { data: 7; levels: 4 } { data: 8; levels: 5 } */ // 优化后insert() SkipList2 list2 = new SkipList2(); list2.insert2(1); list2.insert2(2); list2.insert2(6); list2.insert2(7); list2.insert2(8); list2.insert2(3); list2.insert2(4); list2.insert2(5); System.out.println(); list2.printAll_beautiful(); } } ================================================ FILE: java/18_hashtable/HashTable.java ================================================ /** * @Description:散列表实现 * @Author: Hoda * @Date: Create in 2019-08-07 * @Modified By: * @Modified Date: */ public class HashTable { /** * 散列表默认长度 */ private static final int DEFAULT_INITAL_CAPACITY = 8; /** * 装载因子 */ private static final float LOAD_FACTOR = 0.75f; /** * 初始化散列表数组 */ private Entry[] table; /** * 实际元素数量 */ private int size = 0; /** * 散列表索引数量 */ private int use = 0; public HashTable() { table = (Entry[]) new Entry[DEFAULT_INITAL_CAPACITY]; } static class Entry { K key; V value; Entry next; Entry(K key, V value, Entry next) { this.key = key; this.value = value; this.next = next; } } /** * 新增 * * @param key * @param value */ public void put(K key, V value) { int index = hash(key); // 位置未被引用,创建哨兵节点 if (table[index] == null) { table[index] = new Entry<>(null, null, null); } Entry tmp = table[index]; // 新增节点 if (tmp.next == null) { tmp.next = new Entry<>(key, value, null); size++; use++; // 动态扩容 if (use >= table.length * LOAD_FACTOR) { resize(); } } // 解决散列冲突,使用链表法 else { do { tmp = tmp.next; // key相同,覆盖旧的数据 if (tmp.key == key) { tmp.value = value; return; } } while (tmp.next != null); Entry temp = table[index].next; table[index].next = new Entry<>(key, value, temp); size++; } } /** * 散列函数 *

* 参考hashmap散列函数 * * @param key * @return */ private int hash(Object key) { int h; return (key == null) ? 0 : ((h = key.hashCode()) ^ (h >>> 16)) % table.length; } /** * 扩容 */ private void resize() { Entry[] oldTable = table; table = (Entry[]) new Entry[table.length * 2]; use = 0; for (int i = 0; i < oldTable.length; i++) { if (oldTable[i] == null || oldTable[i].next == null) { continue; } Entry e = oldTable[i]; while (e.next != null) { e = e.next; int index = hash(e.key); if (table[index] == null) { use++; // 创建哨兵节点 table[index] = new Entry<>(null, null, null); } table[index].next = new Entry<>(e.key, e.value, table[index].next); } } } /** * 删除 * * @param key */ public void remove(K key) { int index = hash(key); Entry e = table[index]; if (e == null || e.next == null) { return; } Entry pre; Entry headNode = table[index]; do { pre = e; e = e.next; if (key == e.key) { pre.next = e.next; size--; if (headNode.next == null) use--; return; } } while (e.next != null); } /** * 获取 * * @param key * @return */ public V get(K key) { int index = hash(key); Entry e = table[index]; if (e == null || e.next == null) { return null; } while (e.next != null) { e = e.next; if (key == e.key) { return e.value; } } return null; } } ================================================ FILE: java/20_hashtable/LRUBaseHashTable.java ================================================ import java.util.HashMap; /** * @Description:基于散列表的LRU算法 * @Author: Hoda * @Date: Create in 2019-08-09 * @Modified By: * @Modified Date: */ public class LRUBaseHashTable { /** * 默认链表容量 */ private final static Integer DEFAULT_CAPACITY = 10; /** * 头结点 */ private DNode headNode; /** * 尾节点 */ private DNode tailNode; /** * 链表长度 */ private Integer length; /** * 链表容量 */ private Integer capacity; /** * 散列表存储key */ private HashMap> table; /** * 双向链表 */ static class DNode { private K key; /** * 数据 */ private V value; /** * 前驱指针 */ private DNode prev; /** * 后继指针 */ private DNode next; DNode() { } DNode(K key, V value) { this.key = key; this.value = value; } } public LRUBaseHashTable(int capacity) { this.length = 0; this.capacity = capacity; headNode = new DNode<>(); tailNode = new DNode<>(); headNode.next = tailNode; tailNode.prev = headNode; table = new HashMap<>(); } public LRUBaseHashTable() { this(DEFAULT_CAPACITY); } /** * 新增 * * @param key * @param value */ public void add(K key, V value) { DNode node = table.get(key); if (node == null) { DNode newNode = new DNode<>(key, value); table.put(key, newNode); addNode(newNode); if (++length > capacity) { DNode tail = popTail(); table.remove(tail.key); length--; } } else { node.value = value; moveToHead(node); } } /** * 将新节点加到头部 * * @param newNode */ private void addNode(DNode newNode) { newNode.next = headNode.next; newNode.prev = headNode; headNode.next.prev = newNode; headNode.next = newNode; } /** * 弹出尾部数据节点 */ private DNode popTail() { DNode node = tailNode.prev; removeNode(node); return node; } /** * 移除节点 * * @param node */ private void removeNode(DNode node) { node.prev.next = node.next; node.next.prev = node.prev; } /** * 将节点移动到头部 * * @param node */ private void moveToHead(DNode node) { removeNode(node); addNode(node); } /** * 获取节点数据 * * @param key * @return */ public V get(K key) { DNode node = table.get(key); if (node == null) { return null; } moveToHead(node); return node.value; } /** * 移除节点数据 * * @param key */ public void remove(K key) { DNode node = table.get(key); if (node == null) { return; } removeNode(node); length--; table.remove(node.key); } private void printAll() { DNode node = headNode.next; while (node.next != null) { System.out.print(node.value + ","); node = node.next; } System.out.println(); } } ================================================ FILE: java/24_tree/BinarySearchTree.java ================================================ public class BinarySearchTree { private Node tree; public Node find(int data) { Node p = tree; while (p != null) { if (data < p.data) p = p.left; else if (data > p.data) p = p.right; else return p; } return null; } public void insert(int data) { if (tree == null) { tree = new Node(data); return; } Node p = tree; while (p != null) { if (data > p.data) { if (p.right == null) { p.right = new Node(data); return; } p = p.right; } else { // data < p.data if (p.left == null) { p.left = new Node(data); return; } p = p.left; } } } public void delete(int data) { Node p = tree; // p指向要删除的节点,初始化指向根节点 Node pp = null; // pp记录的是p的父节点 while (p != null && p.data != data) { pp = p; if (data > p.data) p = p.right; else p = p.left; } if (p == null) return; // 没有找到 // 要删除的节点有两个子节点 if (p.left != null && p.right != null) { // 查找右子树中最小节点 Node minP = p.right; Node minPP = p; // minPP表示minP的父节点 while (minP.left != null) { minPP = minP; minP = minP.left; } p.data = minP.data; // 将minP的数据替换到p中 p = minP; // 下面就变成了删除minP了 pp = minPP; } // 删除节点是叶子节点或者仅有一个子节点 Node child; // p的子节点 if (p.left != null) child = p.left; else if (p.right != null) child = p.right; else child = null; if (pp == null) tree = child; // 删除的是根节点 else if (pp.left == p) pp.left = child; else pp.right = child; } public Node findMin() { if (tree == null) return null; Node p = tree; while (p.left != null) { p = p.left; } return p; } public Node findMax() { if (tree == null) return null; Node p = tree; while (p.right != null) { p = p.right; } return p; } public static class Node { private int data; private Node left; private Node right; public Node(int data) { this.data = data; } } } ================================================ FILE: java/28_sorts/HeapSort.java ================================================ /** * 堆排序 */ public class HeapSort { /** * 排序 *

* 堆元素是从数组下标0开始 * * @param arr */ public static void sort(int[] arr) { if (arr.length <= 1) { return; } // 1、建堆 buildHeap(arr); // 2、排序 int k = arr.length - 1; while (k > 0) { // 将堆顶元素(最大)与最后一个元素交换位置 swap(arr, 0, k); // 将剩下元素重新堆化,堆顶元素变成最大元素 heapify(arr, --k, 0); } } /** * 建堆 * * @param arr */ private static void buildHeap(int[] arr) { // (arr.length - 1) / 2 为最后一个叶子节点的父节点 // 也就是最后一个非叶子节点,依次堆化直到根节点 for (int i = (arr.length - 1) / 2; i >= 0; i--) { heapify(arr, arr.length - 1, i); } } /** * 堆化 * * @param arr 要堆化的数组 * @param n 最后堆元素下标 * @param i 要堆化的元素下标 */ private static void heapify(int[] arr, int n, int i) { while (true) { // 最大值位置 int maxPos = i; // 与左子节点(i * 2 + 1)比较,获取最大值位置 if (i * 2 + 1 <= n && arr[i] < arr[i * 2 + 1]) { maxPos = i * 2 + 1; } // 最大值与右子节点(i * 2 + 2)比较,获取最大值位置 if (i * 2 + 2 <= n && arr[maxPos] < arr[i * 2 + 2]) { maxPos = i * 2 + 2; } // 最大值是当前位置结束循环 if (maxPos == i) { break; } // 与子节点交换位置 swap(arr, i, maxPos); // 以交换后子节点位置接着往下查找 i = maxPos; } } } ================================================ FILE: java/30_graph/Graph.java ================================================ package com.study.graph; import java.util.LinkedList; import java.util.Queue; /** * @author ldb * @date 2019-10-23 15:10 */ public class Graph { private int v; private LinkedList adj[]; // 邻接表 public Graph(int v) { this.v = v; adj = new LinkedList[v]; for (int i = 0; i < v; ++i) { adj[i] = new LinkedList<>(); } } /** * 添加边 * * @param s 顶点 * @param t 顶点 */ public void addEdge(int s, int t) { // 无向图一条边存两次 adj[s].add(t); adj[t].add(s); } public void bfs(int s, int t) { if (s == t) return; // visited是用来记录已经被访问的顶点,用来避免顶点被重复访问。 boolean[] visited = new boolean[v]; visited[s] = true; // queue是一个队列,用来存储已经被访问、但相连的顶点还没有被访问的顶点。 Queue queue = new LinkedList<>(); queue.add(s); // prev用来记录搜索路径。 int[] prev = new int[v]; for (int i = 0; i < v; ++i) { prev[i] = -1; } while (queue.size() != 0) { int w = queue.poll(); for (int i = 0; i < adj[w].size(); ++i) { int q = adj[w].get(i); if (!visited[q]) { prev[q] = w; if (q == t) { print(prev, s, t); return; } visited[q] = true; queue.add(q); } } } } private void print(int[] prev, int s, int t) { // 递归打印 s->t 的路径 if (prev[t] != -1 && t != s) { print(prev, s, prev[t]); } System.out.print(t + " "); } public static void main(String[] args) { Graph graph = new Graph(8); graph.addEdge(0,1); graph.addEdge(0,3); graph.addEdge(1,2); graph.addEdge(1,4); graph.addEdge(2,5); graph.addEdge(4,5); graph.addEdge(4,6); graph.addEdge(5,7); graph.addEdge(6,7); // graph.bfs(0,6); // 深度优先 graph.dfs(0, 6); } boolean found = false; // 全局变量或者类成员变量 public void dfs(int s, int t) { found = false; boolean[] visited = new boolean[v]; int[] prev = new int[v]; for (int i = 0; i < v; ++i) { prev[i] = -1; } recurDfs(s, t, visited, prev); print(prev, s, t); } private void recurDfs(int w, int t, boolean[] visited, int[] prev) { if (found == true) return; visited[w] = true; if (w == t) { found = true; return; } for (int i = 0; i < adj[w].size(); ++i) { int q = adj[w].get(i); if (!visited[q]) { prev[q] = w; recurDfs(q, t, visited, prev); } } } } ================================================ FILE: java/32_BFRK ================================================ public static int bF(String a,String b) { int m=a.length(),n=b.length(),k; char[] a1=a.toCharArray(); char[] b1=b.toCharArray(); for(int i=0;i<=m-n;i++) { k=0; for(int j=0;j queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { ACNode p = queue.pop(); for(ACNode pc: p.children.values()){ if (Objects.isNull(pc)) { continue; } if(p == root) { pc.fail = root; } else { ACNode q = p.fail; while (Objects.nonNull(q)) { ACNode qc = q.children.get(pc.data); if(Objects.nonNull(qc)) { pc.fail = qc; break; } q = q.fail; } if(Objects.isNull(q)) { pc.fail = root; } } queue.add(pc); } } } private Boolean match (String text) { ACNode root = this.root; ACNode p = root; int n = text.length(); for(int i = 0; i < n; i++) { String c = text.charAt(i) + ""; while(Objects.isNull(p.children.get(c)) && p != root){ p = p.fail; } p = p.children.get(c); if(Objects.isNull(p)) { p = root; } ACNode tmp = p; while ( tmp != root) { if (tmp.isEndingChar == true) { System.out.println("Start from " + (i - p.length + 1)); return true; } tmp = tmp.fail; } } return false; } public static boolean match(String text, String[] patterns) { ACAutoMata automata = new ACAutoMata(); for (String pattern: patterns) { automata.insert(pattern); } automata.buildFailurePointer(); return automata.match(text); } public class ACNode { private String data; private Map children; private Boolean isEndingChar; private Integer length; private ACNode fail; public ACNode(String data) { this.data = data; this.children = new HashMap<>(); this.isEndingChar = false; this.length = 0; this.fail = null; } } public static void main(String[] args) { String[] patterns = {"at", "art", "oars", "soar"}; String text = "soarsoars"; System.out.println(match(text, patterns)); String[] patterns2 = {"Fxtec Pro1", "谷歌Pixel"}; String text2 = "一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机,该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠"; System.out.println(match(text2, patterns2)); } } ================================================ FILE: javascript/.gitkeep ================================================ ================================================ FILE: javascript/05_array/.gitkeep ================================================ ================================================ FILE: javascript/05_array/Array.md ================================================ 与 Java 、PHP 等语言不同,在 JavaScript 中,数组其实是一种特殊的对象。 #### 数组的创建与读写 以下两种方式都可创建数组: ``` // 字面量方式,常用 var num = [1,5,6,10]; print(num.length); // 4 // 构造函数方式 var num = new Array(1,5,6,10); print(num.length); // 4 ``` 值得注意的是,JavaScript 中的数组数据可以是不同类型,它的语法相对宽松,例如可以指定不同类型数据`var example = [1,"Mike",true,null];`另外,可以通过`Array.isArray()`来判断一个对象是否是数组,例如: ``` var num = [1,5,6,10]; print(Array.isArray(num)); // true ``` 如何读写数组呢?可以使用循环。 ``` var num = [1,5,6,10]; for (var i = 0; i < num.length; i++) { console.log(num[i]+" "); } ``` #### 数组的深复制与浅复制 当我们把数组赋给另外一个数组,然后改变其中一个数组的值,另一数组也会随之改变,这就是数组的浅复制。而深复制指的就是不改变原来的数组而去创建一个新的数组,这种情况是经常使用的,为了不破坏原数组。下面的代码展示了这两种复制 ``` // 浅复制 var num = [1,2,3,4,5]; var newNum = num; num[0] = 10; console.log(newNum[0]); // 10 // 深复制 function copy (arr1,arr2) { for(var i=0;i= 60; } var grades = []; for(var i = 0;i < 11;i++){ grade[i] = Math.floor(Math.random() * 101); } var pass = grades.filter(passing); console.log("随机产生的 10 个同学的分数为:"); console.log(grades); console.log("及格的分数有:"); console.log(pass); ``` 上述代码的输出结果为 > 随机产生的 10 个同学的分数为: 21, 4, 89, 45, 5, 51, 71, 7, 46, 53, 47 及格的分数有: 89, 71 #### 二维数组 JavaScript 可以通过在数组里在嵌套一个数组来形成二维数组。 ``` var grades = [ [88,86,82], [91,82,83], [77,72,79], [86,80,82] ]; console.log(grades[1][2]); // 83 ``` #### 处理二维数组 对于二维数组的处理可以分为两种,一种按列访问,一种是按行访问。 按列访问,外层循环对应行,内层循环对应列。例如,上述的数组,每一行对应一个学生三门科目的成绩记录,可以通过相加所有成绩,然后除以科目数来得到该生的平均成绩。 ``` var grades = [ [88,86,82], [91,82,83], [77,72,79], [86,80,82] ]; var total = 0; var average = 0.0; for(var row = 0;row student 1 average: 85.33 student 2 average: 85.33 student 3 average: 76.00 student 4 average: 82.67 对于按行访问,则外层循环对应列,内层循环对应行,例如还是上述数组,现在的数组表示一个学生三场考试四门科目的各科分数,我们来求每场考试的平均成绩 ``` var grades = [ [88,86,82], [91,82,83], [77,72,79], [86,80,82] ]; var total = 0; var average = 0.0; //这里假设每场考试的科目都一样,所以可以通过grades[0].length来获取考试数量 for(var col = 0;col exam 1 average: 85.50 exam 2 average: 80.00 exam 3 average: 81.50 其实只要调整 for 循环的顺序就可以控制是按行还是按列来输出,此外,JavaScript 还可以处理一些参差不齐的数组,比如一个二维数组中的数组,有的是两个元素,有的是四个元素,并不是都相同,在这种情况下,JavaScript 依然可以处理运行而不报错,这是因为不管多或少,都可以通过 length 属性来计算。 #### 对象数组 如果你有阅读到这里,你应该可以发现上面的所有例子里数据类型都是基本数据类型,不是数字就是字符串。对象数组,顾名思义,就是数组里面的元素可以是对象,这个与 java 的语法很相似,基本上所有的编程语言都是相通的。看看下面这个例子: ``` function point(x,y){ this.x = x; this.y = y; } function show(arr){ for(var i=0;i Point 1: 1, 2 Point 2: 2, 4 Point 3: 8, 1 Point 4: 2, 9 也可以用之前的 push() 等操作方法来操作对象数组 ``` var p5 = new Point(11,13); point.push(p5); console.log("添加了 p5 后:"); show(point); point.shift(); console.log("删除第一个元素后:") show(point); ``` 输出结果为: > 添加了 p5 后: 1,2 2,4 8,1 2,9 11,13 删除第一个元素后: 2,4 8,1 2,9 11,13 ================================================ FILE: javascript/06_linkedlist/.gitkeep ================================================ ================================================ FILE: javascript/06_linkedlist/SinglyLinkedList.js ================================================ /** * 1)单链表的插入、删除、查找操作; * 2)链表中存储的是int类型的数据; */ class Node { constructor (element) { this.element = element this.next = null } } class LinkedList { constructor () { this.head = new Node('head') } // 根据value查找节点 findByValue (item) { let currentNode = this.head.next while (currentNode !== null && currentNode.element !== item) { currentNode = currentNode.next } console.log(currentNode) return currentNode === null ? -1 : currentNode } // 根据index查找节点,下标从0开始 findByIndex (index) { let currentNode = this.head.next let pos = 0 while (currentNode !== null && pos !== index) { currentNode = currentNode.next pos++ } console.log(currentNode) return currentNode === null ? -1 : currentNode } // 向链表末尾追加节点 append(newElement) { const newNode = new Node(newElement) let currentNode = this.head while(currentNode.next) { currentNode = currentNode.next } currentNode.next = newNode } // 指定元素向后插入 insert (newElement, element) { const currentNode = this.findByValue(element) if (currentNode === -1) { console.log('未找到插入位置') return } const newNode = new Node(newElement) newNode.next = currentNode.next currentNode.next = newNode } // 查找前一个 findPrev (item) { let currentNode = this.head while (currentNode.next !== null && currentNode.next.element !== item) { currentNode = currentNode.next } if (currentNode.next === null) { return -1 } return currentNode } // 根据值删除 remove (item) { const prevNode = this.findPrev(item) if (prevNode === -1) { console.log('未找到元素') return } prevNode.next = prevNode.next.next } // 遍历显示所有节点 display () { let currentNode = this.head.next // 忽略头指针的值 while (currentNode !== null) { console.log(currentNode.element) currentNode = currentNode.next } } } // Test const LList = new LinkedList() LList.append('chen') LList.append('curry') LList.append('sang') LList.append('zhao') // chen -> curry -> sang -> zhao console.log('-------------insert item------------') LList.insert('qian', 'chen') // 首元素后插入 LList.insert('zhou', 'zhao') // 尾元素后插入 LList.display() // chen -> qian -> curry -> sang -> zhao -> zhou console.log('-------------remove item------------') LList.remove('curry') LList.display() // chen -> qian -> sang -> zhao -> zhou console.log('-------------find by item------------') LList.findByValue('chen') console.log('-------------find by index------------') LList.findByIndex(2) console.log('-------------与头结点同值元素测试------------') LList.insert('head', 'sang') LList.display() // chen -> qian -> sang -> head -> zhao -> zhou LList.findPrev('head') // sang LList.remove('head') LList.display() // chen -> qian -> sang -> zhao -> zhou ================================================ FILE: javascript/07_linkedlist/.gitkeep ================================================ ================================================ FILE: javascript/07_linkedlist/LinkedListAlgo.js ================================================ /** * 1) 单链表反转 * 2) 链表中环的检测 * 3) 两个有序的链表合并 * 4) 删除链表倒数第n个结点 * 5) 求链表的中间结点 * */ class Node { constructor(element) { this.element = element this.next = null } } class LinkedList { constructor() { this.head = new Node('head') } // 根据value查找节点 findByValue(item) { let currentNode = this.head while (currentNode !== null && currentNode.element !== item) { currentNode = currentNode.next } return currentNode === null ? -1 : currentNode } // 根据index查找节点 findByIndex(index) { let currentNode = this.head let pos = 0 while (currentNode !== null && pos !== index) { currentNode = currentNode.next pos++ } return currentNode === null ? -1 : currentNode } // 指定元素向后插入 insert(newElement, element) { const currentNode = this.findByValue(element) if (currentNode === -1) { console.log('未找到插入位置') return } const newNode = new Node(newElement) newNode.next = currentNode.next currentNode.next = newNode } // 查找前一个 findPrev(item) { let currentNode = this.head while (currentNode.next !== null && currentNode.next.element !== item) { currentNode = currentNode.next } if (currentNode.next === null) { return -1 } return currentNode } // 根据值删除 remove(item) { const desNode = this.findByValue(item) if (desNode === -1) { console.log('未找到元素') return } const prevNode = this.findPrev(item) prevNode.next = desNode.next } // 遍历显示所有节点 display() { //先检查是否为环 if(this.checkCircle()) return false let currentNode = this.head while (currentNode !== null) { console.log(currentNode.element) currentNode = currentNode.next } } // 尾插法 反转单链表 reverseList() { const root = new Node('head') let currentNode = this.head.next while (currentNode !== null) { const next = currentNode.next currentNode.next = root.next root.next = currentNode currentNode = next } this.head = root } //增强尾插法可读性,便于初学者理解 reverseList1(){ //head节点即哨兵,作用就是使所有链表, // 包括空链表的头节点不为null,并使对单链表的插入、删除操作不需要区分是否为空表或是否在第一个位置进行, // 从而与其他位置的插入、删除操作一致 //所以反转链表的时候不需要带上head节点 let currentNode=this.head.next //第一个节点头结点让其指向null let previousNode=null while(currentNode!==null){ //务必先保留下一节点的指针地址 let nextNode=currentNode.next //第一次是null currentNode.next=previousNode //此时将previousNode赋值为当前节点, // 那么下次循环的时候,方便下次的currentNode指向previousNode previousNode=currentNode //抬走,下一个! currentNode=nextNode } //最后将反转好的链表加上头节点 this.head.next=previousNode } // 自己一开始瞎想的。差距啊 reverseList2() { let currentNode = this.head.next let reverseList = new LinkedList() const tempArr = [] if (currentNode === null || currentNode.next === null) { console.log('节点数小于3,不反转') return } while (currentNode !== null) { tempArr.push(currentNode) currentNode = currentNode.next } for (let i = tempArr.length - 1; i >= 0; i--) { const key = i === tempArr.length - 1 ? 'head' : tempArr[i + 1].element reverseList.insert(tempArr[i].element, key) } reverseList.display() return reverseList } // 环验证 checkCircle() { let fast = this.head.next let slow = this.head while (fast !== null && fast.next !== null) { fast = fast.next.next slow = slow.next if (slow === fast) return true } return false } // 删除倒数第k个节点 removeByIndexFromEnd(index) { //务必先判断是否是 环链表 if(this.checkCircle()) return false let pos = 1 this.reverseList() let currentNode = this.head.next while (currentNode !== null && pos < index) { currentNode = currentNode.next pos++ } if (currentNode === null) { console.log('无法删除最后一个节点或者该节点不存在') return false } this.remove(currentNode.element) this.reverseList() } // 求中间节点 findMiddleNode() { let fast = this.head let slow = this.head while (fast.next !== null && fast.next.next !== null) { fast = fast.next.next slow = slow.next } console.log(slow) return slow } } const mergeSortedLists = (listA, listB) => { if (!listA) { return listB } if (!listB) { return listA } let a = listA let b = listB let resultList = undefined if (a.element < b.element) { resultList = a a = a.next } else { resultList = b b = b.next } let currentNode = resultList while (a !== null && b !== null) { if (a.element < b.element) { currentNode.next = a a = a.next } else { currentNode.next = b b = b.next } currentNode = currentNode.next } if (a != null) { currentNode.next = a } else { currentNode.next = b } return resultList } // Test const LList = new LinkedList() LList.insert('chen', 'head') LList.insert('curry', 'chen') LList.insert('sang', 'head') LList.insert('zhao', 'head') console.log('-------------start reverse------------') LList.reverseList() LList.display() console.log('-------------check circle------------') console.log(LList.checkCircle()) console.log('-------------remove the one before last ------------') LList.removeByIndexFromEnd(2) LList.display() const sortedList1 = new LinkedList() sortedList1.insert(9, 'head') sortedList1.insert(8, 'head') sortedList1.insert(7, 'head') sortedList1.insert(6, 'head') const sortedList2 = new LinkedList() sortedList2.insert(21, 'head') sortedList2.insert(20, 'head') sortedList2.insert(19, 'head') sortedList2.insert(18, 'head') console.log('-------------sort two list ------------') let sortedList = mergeSortedLists(sortedList1.head.next, sortedList2.head.next) while (sortedList !== null) { console.log(sortedList.element) sortedList = sortedList.next } ================================================ FILE: javascript/08_stack/SampleBrowser.js ================================================ /** * 使用前后栈实现浏览器的前进后退。 * * Author nameczz */ const stack = require('./StackBasedOnLinkedList') class SampleBrowser { constructor() { this.normalStack = new stack.CreatedStack() this.backStack = new stack.CreatedStack() } // 正常浏览页面 pushNormal(name) { this.normalStack.push(name) this.backStack.clear() this.displayAllStack() } // 后退 back() { const value = this.normalStack.pop() if (value !== -1) { this.backStack.push(value) this.displayAllStack() } else { console.log('无法后退') } } // 前进 front() { const value = this.backStack.pop() if (value !== -1) { this.normalStack.push(value) this.displayAllStack() } else { console.log('无法前进') } } // 打印栈内数据 displayAllStack() { console.log('---后退页面---') this.backStack.display() console.log('---浏览页面---') this.normalStack.display() } } // Test const browser = new SampleBrowser() browser.pushNormal('www.google.com') browser.pushNormal('www.baidu.com') browser.pushNormal('www.github.com') // 后退 browser.back() browser.back() browser.front() browser.pushNormal('www.new.com') ================================================ FILE: javascript/08_stack/StackBasedOnLinkedList.js ================================================ /** * 基于链表实现的栈。 * * Author: nameczz */ class Node { constructor(element) { this.element = element this.next = null } } class StackBasedLinkedList { constructor() { this.top = null } push(value) { const node = new Node(value) if (this.top === null) { this.top = node } else { node.next = this.top this.top = node } } pop() { if (this.top === null) { return -1 } const value = this.top.element this.top = this.top.next return value } // 为了实现浏览器前进后退 clear() { this.top = null } display() { if (this.top !== null) { let temp = this.top while (temp !== null) { console.log(temp.element) temp = temp.next } } } } // Test const newStack = new StackBasedLinkedList() newStack.push(1) newStack.push(2) newStack.push(3) // 获取元素 let res = 0 console.log('-------获取pop元素------') while (res !== -1) { res = newStack.pop() console.log(res) } exports.CreatedStack = StackBasedLinkedList ================================================ FILE: javascript/09_queue/CircularQueueBasedOnLinkedList.js ================================================ /** * 基于链表实现的循环队列。 * * Author: nameczz */ class Node { constructor(element) { this.element = element this.next = null } } class CircularQueue { constructor() { this.head = null this.tail = null } enqueue(value) { if (this.head === null) { this.head = new Node(value) this.head.next = this.head this.tail = this.head } else { const flag = this.head === this.tail this.tail.next = new Node(value) this.tail.next.next = this.head this.tail = this.tail.next if (flag) { this.head.next = this.tail } } } dequeue() { if(this.head == null) return -1 if (this.head === this.tail) { const value = this.head.element this.head = null return value } else { const value = this.head.element this.head = this.head.next this.tail.next = this.head return value } } display() { let res = 0 console.log('-------获取dequeue元素------') while (res !== -1) { res = this.dequeue() console.log(res) } } } // Test const newCircularQueue = new CircularQueue() // 插入元素 newCircularQueue.enqueue(1) newCircularQueue.enqueue(2) newCircularQueue.enqueue(3) // 获取元素 newCircularQueue.display() newCircularQueue.enqueue(1) newCircularQueue.display() // exports.CreatedStack = StackBasedLinkedList ================================================ FILE: javascript/09_queue/QueueBasedOnLinkedList.js ================================================ /** * 基于链表实现的队列。 * * Author: nameczz */ class Node { constructor(element) { this.element = element this.next = null } } class QueueBasedOnLinkedList { constructor() { this.head = null this.tail = null } enqueue(value) { if (this.head === null) { this.head = new Node(value) this.tail = this.head } else { this.tail.next = new Node(value) this.tail = this.tail.next } } dequeue() { if (this.head !== null) { const value = this.head.element this.head = this.head.next return value } else { return -1 } } } // Test const newQueue = new QueueBasedOnLinkedList() // 插入元素 newQueue.enqueue(1) newQueue.enqueue(2) newQueue.enqueue(3) // 获取元素 let res = 0 console.log('-------获取dequeue元素------') while (res !== -1) { res = newQueue.dequeue() console.log(res) } ================================================ FILE: javascript/11_sorts/sort.js ================================================ /** * 冒泡,插入,选择排序 * * Author: nameczz */ // 冒泡排序 const bubbleSort = (arr) => { if (arr.length <= 1) return for (let i = 0; i < arr.length; i++) { let hasChange = false for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) { const temp = arr[j] arr[j] = arr[j + 1] arr[j + 1] = temp hasChange = true } } // 如果false 说明所有元素已经到位 if (!hasChange) break } console.log(arr) } // 插入排序 const insertionSort = (arr) => { if (arr.length <= 1) return for (let i = 1; i < arr.length; i++) { const temp = arr[i] let j = i - 1 // 若arr[i]前有大于arr[i]的值的化,向后移位,腾出空间,直到一个<=arr[i]的值 for (j; j >= 0; j--) { if (arr[j] > temp) { arr[j + 1] = arr[j] } else { break } } arr[j + 1] = temp } console.log(arr) } // 选择排序 const selectionSort = (arr) => { if (arr.length <= 1) return // 需要注意这里的边界, 因为需要在内层进行 i+1后的循环,所以外层需要 数组长度-1 for (let i = 0; i < arr.length - 1; i++) { let minIndex = i for (let j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minIndex]) { minIndex = j // 找到整个数组的最小值 } } const temp = arr[i] arr[i] = arr[minIndex] arr[minIndex] = temp } console.log(arr) } const test = [4, 5, 6, 3, 2, 1] bubbleSort(test) const testSort = [4, 1, 6, 3, 2, 1] insertionSort(testSort) const testSelect = [4, 8, 6, 3, 2, 1, 0, 12] selectionSort(testSelect) ================================================ FILE: javascript/12_sorts/KthNum.js ================================================ /** * 第k大的数 * @param {array} arr * @param {number} k */ function kthNum(arr, k) { const len = arr.length; if (k > len) { return -1; } let p = partition(arr, 0, len - 1); while (p + 1 !== k) { if (p + 1 > k) { p = partition(arr, 0, p - 1); } else { p = partition(arr, p + 1, len - 1); } } return arr[p]; } function partition(arr, start, end) { let i = start; let pivot = arr[end]; for (let j = start; j < end; j++) { if (arr[j] < pivot) { swap(arr, i, j); i += 1; } } swap(arr, i, end); return i; } function swap(arr, i, j) { if (i === j) return; let tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } ================================================ FILE: javascript/12_sorts/MergeSort.js ================================================ /** * 归并排序 * * Author: nameczz */ const mergeArr = (left, right) => { let temp = [] let leftIndex = 0 let rightIndex = 0 // 判断2个数组中元素大小,依次插入数组 while (left.length > leftIndex && right.length > rightIndex) { if (left[leftIndex] <= right[rightIndex]) { temp.push(left[leftIndex]) leftIndex++ } else { temp.push(right[rightIndex]) rightIndex++ } } // 合并 多余数组 return temp.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)) } const mergeSort = (arr) => { // 当任意数组分解到只有一个时返回。 if (arr.length <= 1) return arr const middle = Math.floor(arr.length / 2) // 找到中间值 const left = arr.slice(0, middle) // 分割数组 const right = arr.slice(middle) // 递归 分解 合并 return mergeArr(mergeSort(left), mergeSort(right)) } const testArr = [] let i = 0 while (i < 100) { testArr.push(Math.floor(Math.random() * 1000)) i++ } const res = mergeSort(testArr) console.log(res) ================================================ FILE: javascript/12_sorts/QuickSort.js ================================================ /** * 快速排序 * * Author: nameczz */ const swap = (arr, i, j) => { const temp = arr[i] arr[i] = arr[j] arr[j] = temp } // 获取 pivot 交换完后的index const partition = (arr, pivot, left, right) => { const pivotVal = arr[pivot] let startIndex = left for (let i = left; i < right; i++) { if (arr[i] < pivotVal) { swap(arr, i, startIndex) startIndex++ } } swap(arr, startIndex, pivot) return startIndex } const quickSort = (arr, left, right) => { if (left < right) { let pivot = right let partitionIndex = partition(arr, pivot, left, right) quickSort(arr, left, partitionIndex - 1 < left ? left : partitionIndex - 1) quickSort(arr, partitionIndex + 1 > right ? right : partitionIndex + 1, right) } } const testArr = [] let i = 0 while (i < 10) { testArr.push(Math.floor(Math.random() * 1000)) i++ } console.log('unsort', testArr) quickSort(testArr, 0, testArr.length - 1); console.log('sort', testArr) ================================================ FILE: javascript/13_sorts/bucketSort.js ================================================ // 思路: // 将数组中的数据,按桶进行划分,将相邻的数据划分在同一个桶中 // 每个桶用插入排序算法(或者快速排序)进行排序 // 最后整合每个桶中的数据 function bucketSort(array, bucketSize = 5) { if (array.length < 2) { return array } const buckets = createBuckets(array, bucketSize) return sortBuckets(buckets) } function createBuckets(array, bucketSize) { let minValue = array[0] let maxValue = array[0] // 遍历数组,找到数组最小值与数组最大值 for (let i = 1; i < array.length; i++) { if (array[i] < minValue) { minValue = array[i] } else if (array[i] > maxValue) { maxValue = array[i] } } // 根据最小值、最大值、桶的大小,计算得到桶的个数 const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1 // 建立一个二维数组,将桶放入buckets中 const buckets = [] for (let i = 0; i < bucketCount; i++) { buckets[i] = [] } // 计算每一个值应该放在哪一个桶中 for (let i = 0; i < array.length; i++) { const bucketIndex = Math.floor((array[i] - minValue) / bucketSize) buckets[bucketIndex].push(array[i]) } return buckets } function sortBuckets(buckets) { const sortedArray = [] for (let i = 0; i < buckets.length; i++) { if (buckets[i] != null) { insertionSort(buckets[i]) sortedArray.push(...buckets[i]) } } return sortedArray } // 插入排序 function insertionSort(array) { const { length } = array if (length <= 1) return for (let i = 1; i < length; i++) { let value = array[i] let j = i - 1 while (j >= 0) { if (array[j] > value) { array[j + 1] = array[j] // 移动 j-- } else { break } } array[j + 1] = value // 插入数据 } } ================================================ FILE: javascript/13_sorts/countingSort.js ================================================ const countingSort = array => { if (array.length <= 1) return const max = findMaxValue(array) const counts = new Array(max + 1) // 计算每个元素的个数,放入到counts桶中 // counts下标是元素,值是元素个数 array.forEach(element => { if (!counts[element]) { counts[element] = 0 } counts[element]++ }) // counts下标是元素,值是元素个数 // 例如: array: [6, 4, 3, 1], counts: [empty, 1, empty, 1, 1, empty, 1] // i是元素, count是元素个数 let sortedIndex = 0 counts.forEach((count, i) => { while (count > 0) { array[sortedIndex] = i sortedIndex++ count-- } }) // return array } function findMaxValue(array) { let max = array[0] for (let i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i] } } return max } ================================================ FILE: javascript/15_binary/binaryFind.js ================================================ /** * 二分查找 * * 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)) ================================================ FILE: javascript/16_binary/binary-find.js ================================================ /** * 二分查找 * * 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 (target < sortedArr[mid]) { 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}`) ================================================ FILE: javascript/17_skiplist/SkipList.js ================================================ /** * author dreamapplehappy * 关于代码的一些解释可以看一下这里:https://github.com/dreamapplehappy/blog/tree/master/2018/12/02 */ const MAX_LEVEL = 16; class Node { constructor({ data = -1, maxLevel = 0, refer = new Array(MAX_LEVEL) } = {}) { this.data = data; this.maxLevel = maxLevel; this.refer = refer } } class SkipList{ constructor() { this.head = new Node(); this.levelCount = 1; } randomLevel() { let level = 1; for (let i = 1; i < MAX_LEVEL; i++) { if (Math.random() < 0.5) { level++; } } return level; } insert(value) { const level = this.randomLevel(); const newNode = new Node(); newNode.data = value; newNode.maxLevel = level; const update = new Array(level).fill(new Node()); let p = this.head; for(let i = level - 1; i >= 0; i--) { while(p.refer[i] !== undefined && p.refer[i].data < value) { p = p.refer[i]; } update[i] = p; } for(let i = 0; i < level; i++) { newNode.refer[i] = update[i].refer[i]; update[i].refer[i] = newNode; } if(this.levelCount < level) { this.levelCount = level; } } find(value) { if(!value){return null} let p = this.head; for(let i = this.levelCount - 1; i >= 0; i--) { while(p.refer[i] !== undefined && p.refer[i].data < value) { p = p.refer[i]; } } if(p.refer[0] !== undefined && p.refer[0].data === value) { return p.refer[0]; } return null; } remove(value) { let _node; let p = this.head; const update = new Array(new Node()); for(let i = this.levelCount - 1; i >= 0; i--) { while(p.refer[i] !== undefined && p.refer[i].data < value){ p = p.refer[i]; } update[i] = p; } if(p.refer[0] !== undefined && p.refer[0].data === value) { _node = p.refer[0]; for(let i = 0; i <= this.levelCount - 1; i++) { if(update[i].refer[i] !== undefined && update[i].refer[i].data === value) { update[i].refer[i] = update[i].refer[i].refer[i]; } } return _node; } return null; } printAll() { let p = this.head; while(p.refer[0] !== undefined) { console.log(p.refer[0].data) p = p.refer[0]; } } } test(); function test() { let list = new SkipList(); let length = 20000; //顺序插入 for (let i = 1; i <= 10; i++) { list.insert(i); } //输出一次 list.printAll(); console.time('create length-10') //插入剩下的 for (let i = 11; i <= length - 10; i++) { list.insert(i); } console.timeEnd('create length-10') //搜索 10次 for (let j = 0; j < 10; j++) { let key = Math.floor(Math.random() * length + 1); console.log(key, list.find(key)) } //搜索不存在的值 console.log('null:', list.find(length + 1)); //搜索5000次统计时间 console.time('search 5000'); for (let j = 0; j < 5000; j++) { let key = Math.floor(Math.random() * length + 1); } console.timeEnd('search 5000'); } ================================================ FILE: javascript/18_hashmap/HashTable.html ================================================ Title ================================================ FILE: javascript/19_hashTable/hashtable.js ================================================ /**** * 带碰撞处理的Hash表 * 实际上在js中,单独实现一个Hash表感觉不是很有实用价值 * 如果需要通常是直接将Object,Map,Set来当Hash表用 * * 总结: * 我写的这个实现把store 从Object换成Array不会有运行性能上的区别 * 把hash函数改成生成一定范围的值的类型,然后初始化一个指定长度的数组因该会有一定的性能提升 * 把store换成Map,然后修改相关实现会获得飞越性的提升,因为在js中Map的实现对这种类型的操作做了优化 */ class HashTable { constructor() { //创建一个没有原型链的对象 this.store = Object.create(null); } /** * Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。 * @param {*} string * 翻译自别的语言的实现 * 需要注意的是由于js中没有int类型,number是dobule的标准实现 * 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同 */ hash(string) { let len = string.length; let hash = len; for (let i = 0; i < len; i++) { hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i); } return hash & 0x7FFFFFFF; } isCresh(item) { return Object.prototype.toString.call(item) === "[object Map]" } /** * 约定item必须要有key * @param {*} item */ put(item) { if (typeof item.key !== 'string') { throw 'item must have key!' } let hash = this.hash(item.key); //碰撞处理 let cresh = this.store[hash]; if (cresh) { if (cresh.key === item.key) { this.store[hash] = item; return } if (!this.isCresh(cresh)) { this.store[hash] = new Map(); } this.store[hash].set(item.key, item); } else { this.store[hash] = item; } } get(key) { let hash = this.hash(key); let value = this.store[hash] || null; if (this.isCresh(value)) { return value.get(key); } else { return value } } remove(key) { let hash = this.hash(key); let value = this.store[hash]; if (!value) { return null; } if (this.isCresh(value)) { value.delete(key); } else { delete this.store[hash]; } } clear() { this.store = {}; } print() { let values = Object.values(this.store); values.forEach(element => { if (this.isCresh(element)) { element.forEach(item => { console.log(item); }); } else { console.log(element) } }); } } /** * 相比使用Object和Array做store 运行时的性能提升了三分之一 * 但当前这种用法没有直接使用Map方便,而且直接使用Map会快的多 */ class HashTableBaseMap { constructor() { this.store = new Map(); } /** * Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。 * @param {*} string * 翻译自别的语言的实现 * 需要注意的是由于js中没有int类型,number是dobule的标准实现 * 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同 */ hash(string) { let len = string.length; let hash = len; for (let i = 0; i < len; i++) { hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i); } return hash & 0x7FFFFFFF; } isCresh(item) { return Object.prototype.toString.call(item) === "[object Map]" } /** * 约定item必须要有key * @param {*} item */ put(item) { if (typeof item.key !== 'string') { throw 'item must have key!' } let hash = this.hash(item.key); //碰撞处理 let cresh = this.store.get(hash); if (cresh) { if (cresh.key === item.key) { this.store.set(hash, item); return } if (!this.isCresh(cresh)) { this.store[hash] = new Map(); } this.store[hash].set(item.key, item); } else { this.store.set(hash, item); } } get(key) { let hash = this.hash(key); let value = this.store.get(hash); if (this.isCresh(value)) { return value.get(key); } else { return value } } remove(key) { let hash = this.hash(key); let value = this.store.get(hash); if (!value) { return null; } if (this.isCresh(value)) { value.delete(key); } else { this.store.delete(hash) } } clear() { this.store = {}; } print() { this.store.forEach(element => { if (this.isCresh(element)) { element.forEach(item => { console.log(item); }); } else { console.log(element) } }); } } /** * 基础测试 */ function baseTest() { let hashTable = new HashTable(); for (let i = 0; i < 10; i++) { hashTable.put({ key: 'test' + i, value: 'some value' + i }); } console.log('step1:') //随机获取5次 for (let j = 0; j < 5; j++) { let key = 'test' + Math.floor(Math.random() * 10); console.log(key); console.log(hashTable.get(key)) } //获得一次空值 console.log('get null:', hashTable.get('test10')) //修改一次值 hashTable.put({ key: 'test1', value: 'change' }); //删除一次值 hashTable.remove('test2'); console.log('step2:') //输出修改后所有的 hashTable.print(); } /** * 有序key存取,性能测试 */ function ordKeyTest() { let length = 1000000; console.time('create') let hashTable = new HashTable(); for (let i = 0; i < length; i++) { //24位长度有序key hashTable.put({ key: 'someTestSoSoSoSoLongKey' + i, value: 'some value' + i }); } console.timeEnd('create') let get = 100000; console.time('get') for (let j = 0; j < get; j++) { let key = 'test' + Math.floor(Math.random() * 999999); hashTable.get(key) } console.timeEnd('get') } /** * 无序key性能测试 * 这个查找稍微有点不准,会有一定量随机字符串重复 * 实际结果,创建没有区别,大数据量下由于无序key有一些会碰撞,get的总体用的时间会多不少。 */ function randKeyTest() { let length = 1000000; let keyList = []; for (let i = 0; i < length; i++) { keyList.push(randomString()); } console.time('create') let hashTable = new HashTable(); for (let i = 0; i < length; i++) { hashTable.put({ key: keyList[i], value: 'some value' + i }); } console.timeEnd('create') let get = 100000; console.time('get') for (let j = 0; j < get; j++) { let key = keyList[Math.floor(Math.random() * 999999)]; hashTable.get(key) } console.timeEnd('get') } /** * 直接使用Object的性能测试 * 有序就不测了,估计不会有区别,只看不使用hash的无序key * 结果:想达到同样的结果创建会比hash后的慢接近四分之三,获取用时差不多 */ function randKeyTestFromObj() { let length = 1000000; let keyList = []; for (let i = 0; i < length; i++) { keyList.push(randomString()); } console.time('create') let hashTable = {}; for (let i = 0; i < length; i++) { let key = keyList[i]; hashTable[key] = { key: key, value: 'some value' + i } } console.timeEnd('create') let get = 100000; console.time('get') for (let j = 0; j < get; j++) { let key = keyList[Math.floor(Math.random() * 999999)]; hashTable[key] } console.timeEnd('get') } /** * 直接使用Map的性能测试 * 结果:创建用时差不多,但是获取快了一个数量级(十倍不止) */ function randKeyTestFromMap() { let length = 1000000; let keyList = []; for (let i = 0; i < length; i++) { keyList.push(randomString()); } console.time('create') let hashTable = new Map(); for (let i = 0; i < length; i++) { let key = keyList[i]; hashTable.set(key, { key: key, value: 'some value' + i }) } console.timeEnd('create') let get = 100000; console.time('get') for (let j = 0; j < get; j++) { let key = keyList[Math.floor(Math.random() * 999999)]; hashTable.get(key); } console.timeEnd('get') } //生成指定长度的字符串 function randomString(len) { len = len || 24; var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; var maxPos = chars.length; var pwd = ''; for (i = 0; i < len; i++) { pwd += chars.charAt(Math.floor(Math.random() * maxPos)); } return pwd; } ================================================ FILE: javascript/23_tree/binary_tree.js ================================================ class Node { constructor(value) { this.value = value; this.left = null; this.right = null; } } /** * 搜索二叉树 * 允许重复值添加 * 实现有点弯弯绕 */ class SearchTree { constructor() { this.root = null; } insert(num) { let node = new Node(num); if (this.root === null) { this.root = node; return } let prent = this.getPrev(num); if (num < prent.value) { prent.left = node; } else { prent.right = node; } } remove(num) { let point = this.root; let prent = null; let tree = this; let res = null; while (true) { if (point.left) { if (num < point.left.value || num < point.value) { prent = point; point = point.left; continue } } if (point.right) { if (num >= point.right.value || num >= point.value) { if (num === point.value) { delMethod(point, prent); if (prent === null) { point = this.root; } else { prent = prent; point = prent.right; } res = true; continue } prent = point; point = point.right; continue } } if (point.value === num) { res = true; delMethod(point, prent); } break; } return res; function delMethod(delNode, parent) { let p = delNode; // p指向要删除的节点 let pp = parent; // pp记录的是p的父节点 // 要删除的节点有两个子节点 if (p.left != null && p.right != null) { // 查找右子树中最小节点 let minP = p.right; let minPP = p; // minPP表示minP的父节点 while (minP.left != null) { minPP = minP; minP = minP.left; } p.value = minP.value; // 将minP的数据替换到p中 p = minP; // 下面就变成了删除minP了 pp = minPP; } // 删除节点是叶子节点或者仅有一个子节点 let child; // p的子节点 if (p.left != null) child = p.left; else if (p.right != null) child = p.right; else child = null; if (pp == null) { tree.root = child } else if (pp.left == p) { pp.left = child; } else { pp.right = child; } } } //中序遍历 print() { let point = this.root; if (point) { printAll(point.left) console.log(point.value); printAll(point.right) } function printAll(point) { if (point == null) { return } printAll(point.left); console.log(point.value); printAll(point.right) } } find(num) { if (this.root === null) { this.root = node; return } return this.getPrev(num, true); } //添加和查找的公用部分 getPrev(num, find = false) { let point = this.root; let res = []; while (true) { if (point.left) { if (num < point.left.value || num < point.value) { point = point.left continue } } if (point.right) { if (num >= point.right.value || num >= point.value) { //搜索时如果有多个值则缓存 if (find && num === point.value) { res.push(point.value); } point = point.right; continue } } //如果是搜索 if (find) { if (point.value === num) { res.push(point.value); } if (res.length === 0) { return null } if (res.length === 1) { return res[0]; } return res; } //如果是添加 返回的是应该添加的那各节点的父节点 return point; } } } function baseTest() { let searchTree = new SearchTree(); console.log('step 1:') searchTree.insert(4); searchTree.insert(1); searchTree.insert(2); searchTree.insert(5); searchTree.print(); //1 2 4 5 console.log('step 2:') console.log('5', searchTree.find(5)) //5 console.log('null:', searchTree.find(6)) //null searchTree.insert(5); searchTree.insert(5); console.log('5,5,5:', searchTree.find(5)) } //删除测试 function delTest() { let searchTree = new SearchTree(); console.log('add: 4 1 2 5 ') searchTree.insert(4); searchTree.insert(1); searchTree.insert(2); searchTree.insert(5); searchTree.print(); //1 2 4 5 //console.log('del 3 null:', searchTree.remove(3)); console.log('del 1 true:', searchTree.remove(1)); // console.log('print 2 4 5:') // searchTree.print(); // console.log('del 4 root true:', searchTree.remove(4)); // console.log('print 2 5:') // searchTree.print(); // console.log('add: 3 7 1 5 5 5 ') // searchTree.insert(3); // searchTree.insert(7); // searchTree.insert(1); // searchTree.insert(5); // searchTree.insert(5); // searchTree.insert(5); // console.log('print: 1 2 3 5 5 5 5 7 ') // searchTree.print(); // console.log('del 5 true:', searchTree.remove(5)); // console.log('print: 1 2 3 7 ') // searchTree.print(); } delTest(); ================================================ FILE: javascript/28_heapsort/heap-sort.js ================================================ /** * 堆排序 * * Author: nameczz */ // 忽视数组的第一位 class HeapSort { constructor(originArray) { this.originArray = originArray console.log(this.originArray) } buildHeap() { const arr = this.originArray const startIndex = Math.floor(arr.length) for (let i = startIndex; i >= 1; i--) { this.heapify(arr, arr.length, i) } return arr } heapify(arr, len, i) { while (true) { let maxPos = i // 如果index i拥有叶左节点 并且左节点较大 if (i * 2 <= len && arr[i] < arr[i * 2]) { maxPos = i * 2 } // 如果index i拥有叶右节点 与Max节点比较大小,选出父/左/右中最大的一个 if (i * 2 + 1 <= len && arr[maxPos] < arr[i * 2 + 1]) { maxPos = i * 2 + 1 } if (maxPos === i) break // 循环直到i节点为最大值 this.swap(arr, i, maxPos) // 交换位置, 父节点为父/左/右中最大的一个 i = maxPos // i为左/右节点,并尝试向下查找更大的值 } } sort() { const arr = this.buildHeap() // 先建堆 let len = arr.length - 1 while (len > 1) { this.swap(arr, 1, len) // 交换顶元素和最后一位。顶元素永远是最大的。 len-- this.heapify(arr, len, 1) //剩下的元素重新建堆 直到len === 1 停止 } console.log(arr) } swap(arr, i, max) { let temp = arr[i] arr[i] = arr[max] arr[max] = temp } } const arr = [null] let i = 0 while (i <= 10) { const num = Math.floor(Math.random() * 100) arr.push(num) i++ } const testHeap = new HeapSort(arr) testHeap.sort() ================================================ FILE: javascript/28_heapsort/heap.js ================================================ /** * 优先队列的 堆实现 */ class HeapNode { constructor(num, item) { this.sortNum = num; this.content = item; } } class Heap { constructor(arr = []) { this.PRIVATE = { swap(arr, i, j) { let temp = arr[i] arr[i] = arr[j] arr[j] = temp }, //从point往下 堆化 heapify(point = 1) { let { swap, store } = this; while (true) { let lPoint = point * 2; let rPoint = point * 2 + 1; if (store[lPoint] && store[point].sortNum < store[lPoint].sortNum) { swap(store, point, lPoint); point = lPoint; continue } if (store[rPoint] && store[point].sortNum < store[rPoint].sortNum) { swap(store, point, rPoint); point = rPoint; continue } break; } }, store: [null].concat(arr) } //建堆 //从最后一个非子叶节点遍历 for (let i = (this.PRIVATE.store.length / 2 | 0); i > 1; i--) { this.PRIVATE.heapify(i); } } insert(node) { let store = this.PRIVATE.store; let HeapUtil = this.PRIVATE; store.push(node); let point = store.length - 1; let sub = point / 2 | 0; while (sub > 0 && store[point].sortNum > store[sub].sortNum) { // 自下往上堆化 HeapUtil.swap(store, point, sub); // swap()函数作用:交换下标为i和i/2的两个元素 point = sub; sub = sub / 2 | 0; } } getMax() { let store = this.PRIVATE.store; let point = store.length - 1; if (point === 0) { return null; } let HeapUtil = this.PRIVATE; //最大与末尾元素交换 HeapUtil.swap(store, point, 1); let max = store.pop(); HeapUtil.heapify(); return max; } } function HeapTest() { let maxHeap = new Heap(); console.log('偶数个') maxHeap.insert(new HeapNode(2, 'c')) maxHeap.insert(new HeapNode(1, 'c')) maxHeap.insert(new HeapNode(7, 'a')) maxHeap.insert(new HeapNode(4, 'c')) console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); console.log('奇数个') maxHeap.insert(new HeapNode(5, 'b')) maxHeap.insert(new HeapNode(6, 'c')) maxHeap.insert(new HeapNode(10, 'a')) console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); console.log('获取最大值:', maxHeap.getMax()); console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); console.log('获取最大值:', maxHeap.getMax()); console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); } function createTest() { console.log('随机创建测试:') let arr = []; let i = 0 while (i <= 10) { const num = Math.floor(Math.random() * 100) arr.push(new HeapNode(num, i)) i++ } let heap = new Heap(arr); console.log('check:', isHeapArr(heap.PRIVATE.store)) } function isHeapArr(arr) { for (let i = 1; i < arr.length; i++) { let p = arr[i]; let l = arr[i * 2]; let r = arr[i * 2 + 1]; if (l > p || r > p) { return false; } } return true; } ================================================ FILE: javascript/35_trie/trie.js ================================================ class TrieNode { constructor(data){ this.data = data; this.children = new Array(26); this.isEndingChar = false } } class TrieTree { constructor(data){ this.root = new TrieNode('/') } insert (text) { let node = this.root; for (let char of text) { let index = char.charCodeAt() - 'a'.charCodeAt(); if(!node.children[index]) { node.children[index] = new TrieNode(char); } node = node.children[index]; } node.isEndingChar = true; } find (text) { let node = this.root; for(let char of text) { let index = char.charCodeAt() - 'a'.charCodeAt(); if(node.children[index]) { node = node.children[index]; } else { return false; } } return node.isEndingChar; } } var tree = new TrieTree(); var strs = ["how", "hi", "her", "hello", "so", "see"]; for(let str of strs) { tree.insert(str); } for(let str of strs) { console.log(tree.find(str)); } console.log(tree.find('world')); ================================================ FILE: javascript/36_ac_automata/ac_automata.js ================================================ MAX_LEN = 128; class ACNode { constructor(data){ this.data = data; this.children = new Array(MAX_LEN); this.isEndingChar = false; this.length = 0; this.fail = null; } } class ACTree { constructor(data){ this.root = new ACNode('/') } insert (text) { let node = this.root; for (let char of text) { let index = char.charCodeAt() + 1; if(!node.children[index]) { node.children[index] = new ACNode(char); } node = node.children[index]; } node.isEndingChar = true; node.length = text.length; } buildFailurePointer() { let root = this.root; let queue = []; queue.push(root); while (queue.length > 0) { let p = queue.shift(); for (let i = 0; i < MAX_LEN; i++) { let pc = p.children[i]; if (!pc) { continue; } if(p == root) { pc.fail = root; } else { let q = p.fail; while (q) { let qc = q.children[pc.data.charCodeAt() + 1]; if(qc) { pc.fail = qc; break; } q = q.fail; } if(!q) { pc.fail = root; } } queue.push(pc); } } } match (text) { let root = this.root; let n = text.length; let p = root; for(let i = 0; i < n; i++) { let idx = text[i].charCodeAt() + 1; while(!p.children[idx] && p != root){ p = p.fail; } p = p.children[idx]; if(!p) { p = root; } let tmp = p; while ( tmp != root) { if (tmp.isEndingChar == true) { console.log(`Start from ${i - p.length + 1}, length: ${p.length}`); } tmp = tmp.fail; } } } } let automata = new ACTree(); let patterns = ["at", "art", "oars", "soar"]; for (let pattern of patterns) { automata.insert(pattern); } automata.buildFailurePointer() automata.match("soarsoars"); ================================================ FILE: javascript/36_ac_automata/ac_automata_unicode.js ================================================ class ACNode { constructor(data){ this.data = data; this.children = new Map(); this.isEndingChar = false; this.length = 0; this.fail = null; } } class ACTree { constructor(data){ this.root = new ACNode('/') } insert (text) { let node = this.root; for (let char of text) { if(!node.children.get(char)) { node.children.set(char, new ACNode(char)); } node = node.children.get(char); } node.isEndingChar = true; node.length = text.length; } buildFailurePointer() { let root = this.root; let queue = []; queue.push(root); while (queue.length > 0) { let p = queue.shift(); for(var pc of p.children.values()){ if (!pc) { continue; } if(p == root) { pc.fail = root; } else { let q = p.fail; while (q) { let qc = q.children.get(pc.data); if(qc) { pc.fail = qc; break; } q = q.fail; } if(!q) { pc.fail = root; } } queue.push(pc); } } } match (text) { let root = this.root; let n = text.length; let p = root; for(let i = 0; i < n; i++) { let char = text[i]; while(!p.children.get(char) && p != root){ p = p.fail; } p = p.children.get(char); if(!p) { p = root; } let tmp = p; while ( tmp != root) { if (tmp.isEndingChar == true) { console.log(`Start from ${i - p.length + 1}, length: ${p.length}`); } tmp = tmp.fail; } } } } function match( text, patterns) { let automata = new ACTree(); for (let pattern of patterns) { automata.insert(pattern); } automata.buildFailurePointer(); automata.match(text); } let patterns = ["at", "art", "oars", "soar"]; let text = "soarsoars"; match(text, patterns); let patterns2 = ["Fxtec Pro1", "谷歌Pixel"]; let text2 = "一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机,该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠"; match(text2, patterns2); ================================================ FILE: javascript/42_dynamic_programming/levenshtein_distance.js ================================================ function lsDist(str1, str2) { var n = str1.length; var m = str2.length; var memo = new Array(n); for(let i = 0; i < n; i++) { memo[i] = new Array(m); if (str1[i] === str2[0]) { memo[i][0] = i - 0; } else if(i === 0) { memo[i][0] = 1; } else { memo[i][0] = memo[i - 1][0] + 1; } } for(let j = 0; j < m; j++) { if(str1[0] === str2[j]) { memo[0][j] = j - 0; } else if(j === 0) { memo[0][j] = 1; } else { memo[0][j] = memo[0][j - 1] + 1; } } for(let i = 1; i < n; i++) { for(let j = 1; j < m; j++) { if(str1[i] === str2[j]) { memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ]); } else { memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ] + 1); } } } console.log(memo); return memo[n - 1][m - 1]; } var s = "mitcmu"; var t = "mtacnu"; console.log( lsDist(s, t) ); var s = "kitten"; var t = "sitting"; console.log( lsDist(s, t) ); var s = "flaw"; var t = "lawn"; console.log( lsDist(s, t) ); ================================================ FILE: javascript/43_topological_sorting/dsf.js ================================================ function Graph() { var graph = { adj: new Map(), addEdge: function (from, to){ if(!this.adj.get(from)) { this.adj.set(from, [ to ]); } else { this.adj.get(from).push(to); } }, sortingByDsf: function(){ var inverseAdj = new Map(); var keys = this.adj.keys(); for(let key of keys) { let blk = this.adj.get(key); if(blk) { for(let v of blk) { if(!inverseAdj.get(v)) { inverseAdj.set(v, [key]); } else { inverseAdj.get(v).push(key); } } } } let inKeys = inverseAdj.keys(); let vertexes = new Set([...keys, ...inKeys]); let visited = []; for(let vertex of vertexes) { if(!visited.includes(vertex)) { visited.push(vertex); this.dsf(vertex, inverseAdj, visited); } } }, dsf: function(vertex, inverseAdj, visited) { if(!inverseAdj.get(vertex)) { inverseAdj.set(vertex, []); } for(let v of inverseAdj.get(vertex)) { if(visited.includes(v)) { continue; } visited.push(v); this.dsf(v, inverseAdj, visited); } console.log("->" + vertex); } } return graph; } var dag = new Graph(); dag.addEdge(2, 1); dag.addEdge(3, 2); dag.addEdge(2, 4); dag.addEdge(4, 1); dag.sortingByDsf(); var dag2 = new Graph(); dag2.addEdge("main", "parse_options"); dag2.addEdge("main", "tail_file"); dag2.addEdge("main", "tail_forever"); dag2.addEdge("tail_file", "pretty_name"); dag2.addEdge("tail_file", "write_header"); dag2.addEdge("tail_file", "tail"); dag2.addEdge("tail_forever", "recheck"); dag2.addEdge("tail_forever", "pretty_name"); dag2.addEdge("tail_forever", "write_header"); dag2.addEdge("tail_forever", "dump_remainder"); dag2.addEdge("tail", "tail_lines"); dag2.addEdge("tail", "tail_bytes"); dag2.addEdge("tail_lines", "start_lines"); dag2.addEdge("tail_lines", "dump_remainder"); dag2.addEdge("tail_lines", "file_lines"); dag2.addEdge("tail_lines", "pipe_lines"); dag2.addEdge("tail_bytes", "xlseek"); dag2.addEdge("tail_bytes", "start_bytes"); dag2.addEdge("tail_bytes", "dump_remainder"); dag2.addEdge("tail_bytes", "pipe_bytes"); dag2.addEdge("file_lines", "dump_remainder"); dag2.addEdge("recheck", "pretty_name"); dag2.sortingByDsf(); ================================================ FILE: javascript/45_bitmap/bitmap.js ================================================ class BitMap { constructor(n) { this.nbits = n; this.blk = new Array(Math.floor(n / 16) + 1); this.blk.fill(0); } get(k) { if( k > this.nbits) return false; let byteIndex = Math.floor(k / 16); let bitIndex = k % 16; return !((this.blk[byteIndex] & (1 << bitIndex)) === 0); } set(k) { if( k > this.nbits) return; let byteIndex = Math.floor(k / 16); let bitIndex = k % 16; this.blk[byteIndex] = this.blk[byteIndex] | (1 << bitIndex); } } let aBitMap = new BitMap(20); aBitMap.set(1); aBitMap.set(3); aBitMap.set(5); aBitMap.set(7); aBitMap.set(9); aBitMap.set(11); aBitMap.set(13); aBitMap.set(15); aBitMap.set(17); aBitMap.set(19); for(let i = 0; i < 21; i++) { console.log(aBitMap.get(i)); } ================================================ FILE: kotlin/05_array/ArrayKt.kt ================================================ import kotlin.Array /** * 1) 数组的插入、删除、按照下标随机访问操作; * 2)数组中的数据是int类型的; * * Author: Zackratos */ class ArrayKt constructor(private val capacity: Int) { // 定义整型数据data保存数据 private val data: IntArray = IntArray(capacity) // 定义数组中实际个数 private var count: Int = 0 companion object { @JvmStatic fun main(args: Array) { val array = ArrayKt(5) array.printAll() array.insert(0, 3) array.insert(0, 4) array.insert(1, 5) array.insert(3, 9) array.insert(3, 10) array.printAll() } } // 根据索引,找到数据中的元素并返回 fun find(index: Int): Int { if (index !in 0..(count - 1)) return -1 return data[index] } // 插入元素:头部插入,尾部插入 fun insert(index: Int, value: Int): Boolean { // 数组空间已满 if (count == capacity) { System.out.println("没有可插入的位置") return false } // 如果count还没满,那么就可以插入数据到数组中 // 位置不合法 if (index !in 0..count) { System.out.println("位置不合法") return false } // 位置合法 (count downTo index + 1).forEach { data[it] = data[it - 1] } data[index] = value ++count return true } // 根据索引,删除数组中元素 fun delete(index: Int): Boolean { if (index !in 0..(count - 1)) return false (index + 1 until count).forEach { data[it - 1] = data[it] } --count return true } fun printAll() { (0 until count).forEach { System.out.println("${data[it]} ") } } } ================================================ FILE: kotlin/05_array/DynamicArray.kt ================================================ /** * 动态扩容的数组 */ class DynamicArray { companion object { // 默认容量 const val DEFAULT_CAPACITY = 10 // 最大容量 const val MAX_CAPACITY = Int.MAX_VALUE } // 当前已使用大小 private var usedSize = 0 // 当前容量大小 private var capacity = 0 // 数组容器 private var data: Array init { this.capacity = DEFAULT_CAPACITY this.data = Array(this.capacity) { 0 } } /** * 增加元素 */ fun add(value: Int) { if (this.usedSize == this.capacity - 1) { this.doubleCapacity() } this.data[this.usedSize] = value ++this.usedSize } /** * 移除元素 */ fun remove(value: Int) { if (this.usedSize >= 0) { var target = -1 // 查找目标所在位置 for (i in 0 until this.usedSize) { if (this.data[i] == value) { target = i break } } // 找到了 if (target >= 0) { val size = this.usedSize - 1 // 把后续元素往前搬 for (i in target until size) { this.data[i] = this.data[i + 1] } // 最后一个元素位置置为空 this.data[size] = 0 // 更新已使用大小 this.usedSize = size } } } /** * 通过索引设置元素的值 */ fun set(index: Int, value: Int) { if (this.checkIndex(index)) { this.data[index] = value return } throw IllegalArgumentException("index must be in rang of 0..${this.usedSize}") } /** * 获取元素 */ fun get(index: Int): Int? { if (this.checkIndex(index)) { return this.data[index] } throw IllegalArgumentException("index must be in rang of 0..${this.usedSize}") } /** * 获取当前数组的大小 */ fun getSize(): Int = this.usedSize private fun checkIndex(index: Int): Boolean { return index >= 0 && index < this.usedSize } /** * 按原容量的两倍进行扩容 */ private fun doubleCapacity() { if (this.capacity < MAX_CAPACITY) { this.capacity = Math.min(this.capacity * 2, MAX_CAPACITY) val newArray = Array(this.capacity) { 0 } for (i in 0 until this.usedSize) { newArray[i] = this.data[i] } this.data = newArray } } } ================================================ FILE: kotlin/06_linkedlist/SinglyLinkedList.kt ================================================ /** * 1)单链表的插入、删除、查找操作; * 2)链表中存储的是int类型的数据; * * Author:Zackratos */ class SinglyLinkedList { private var head: Node? = null companion object { @JvmStatic fun main(args: Array) { val link = SinglyLinkedList() println("hello") val data = intArrayOf(1, 2, 5, 3, 1) for (i in data.indices) { //link.insertToHead(data[i]); link.insertTail(data[i]) } println("打印原始:") link.printAll() if (link.palindrome()) { println("回文") } else { println("不是回文") } } } fun findByValue(value: Int): Node? { var p = head while (p != null && p.data != value) { p = p.next } return p } fun findByIndex(index: Int): Node? { var p = head var pos = 0 while (p != null && pos != index) { p = p.next ++pos } return p } //无头结点 //表头部插入 //这种操作将于输入的顺序相反,逆序 fun insertToHead(value: Int) { val newNode = Node(value, null) insertToHead(newNode) } fun insertToHead(newNode: Node) { if (head == null) { head = newNode } else { newNode.next = head head = newNode } } //顺序插入 //链表尾部插入 fun insertTail(value: Int) { val newNode = Node(value, null) //空链表,可以插入新节点作为head,也可以不操作 if (head == null) { head = newNode } else { var q = head while (q?.next != null) { q = q.next } newNode.next = q?.next q?.next = newNode } } fun insertAfter(p: Node?, value: Int) { val newNode = Node(value, null) insertAfter(p, newNode) } fun insertAfter(p: Node?, newNode: Node) { if (p == null) return newNode.next = p.next p.next = newNode } fun insertBefore(p: Node?, value: Int) { val newNode = Node(value, null) insertBefore(p, newNode) } fun insertBefore(p: Node?, newNode: Node) { if (p == null) return if (head === p) { insertToHead(newNode) return } var q = head while (q != null && q.next !== p) { q = q.next } if (q == null) { return } newNode.next = p q.next = newNode } fun deleteByNode(p: Node?) { if (p == null || head == null) return if (p === head) { head = head?.next return } var q = head while (q != null && q.next !== p) { q = q.next } if (q == null) { return } q.next = q.next?.next } fun deleteByValue(value: Int) { if (head == null) return var p = head var q: Node? = null while (p != null && p.data != value) { q = p p = p.next } if (p == null) return if (q == null) { head = head?.next } else { q.next = q.next?.next } } fun printAll() { var p = head while (p != null) { print("${p.data} ") p = p.next } println() } //判断true or false fun TFResult(left: Node?, right: Node?): Boolean { var l: Node? = left var r: Node? = right println("left_:${l?.data}") println("right_:${r?.data}") while (l != null && r != null) { if (l.data == r.data) { l = l.next r = r.next continue } else { break } } println("什么结果") return l == null && r == null } // 判断是否为回文 fun palindrome(): Boolean { if (head == null) { return false } else { println("开始执行找到中间节点") var p = head var q = head if (p?.next == null) { println("只有一个元素") return true } while (q?.next != null && q.next?.next != null) { p = p?.next q = q.next?.next } println("中间节点${p?.data}") println("开始执行奇数节点的回文判断") val leftLink: Node? val rightLink: Node? if (q?.next == null) { // p 一定为整个链表的中点,且节点数目为奇数 rightLink = p?.next leftLink = inverseLinkList(p)?.next println("左边第一个节点${leftLink?.data}") println("右边第一个节点${rightLink?.data}") } else { //p q 均为中点 rightLink = p?.next leftLink = inverseLinkList(p) } return TFResult(leftLink, rightLink) } } //带结点的链表翻转 fun inverseLinkList_head(p: Node): Node { // Head 为新建的一个头结点 val Head = Node(9999, null) // p 为原来整个链表的头结点,现在Head指向 整个链表 Head.next = p /* 带头结点的链表翻转等价于 从第二个元素开始重新头插法建立链表 */ var Cur = p.next p.next = null var next: Node? while (Cur != null) { next = Cur.next Cur.next = Head.next Head.next = Cur println("first " + Head.data) Cur = next } // 返回左半部分的中点之前的那个节点 // 从此处开始同步像两边比较 return Head } //无头结点的链表翻转 fun inverseLinkList(p: Node?): Node? { var pre: Node? = null var r = head println("z---${r?.data}") var next: Node? while (r !== p) { next = r?.next r?.next = pre pre = r r = next } r?.next = pre // 返回左半部分的中点之前的那个节点 // 从此处开始同步像两边比较 return r } fun createNode(value: Int): Node = Node(value, null) class Node(var data: Int, var next: Node?) } ================================================ FILE: kotlin/07_linkedlist/LinkedListAlgo.kt ================================================ /** * 1) 单链表反转 * 2) 链表中环的检测 * 3) 两个有序的链表合并 * 4) 删除链表倒数第n个结点 * 5) 求链表的中间结点 * * Author: Zackratos */ object LinkedListAlgo { // 单链表反转 fun reverse(list: Node): Node? { var curr: Node? = list var pre: Node? = null while (curr != null) { val next = curr.next curr.next = pre pre = curr curr = next } return pre } // 检测环 fun checkCircle(list: Node): Boolean { var fast = list.next var slow: Node? = list while (fast != null && slow != null) { fast = fast.next?.next slow = slow.next if (fast === slow) return true } return false } // 有序链表合并 fun mergeSortedLists(la: Node, lb: Node): Node? { var p: Node? = la var q: Node? = lb val head: Node? if (p?.data ?: 0 < q?.data ?: 0) { head = p p = p?.next } else { head = q q = q?.next } var r = head while (p != null && q != null) { if (p.data < q.data) { r?.next = p p = p.next } else { r?.next = q q = q.next } r = r?.next } if (p != null) { r?.next = p } else { r?.next = q } return head } // 删除倒数第 k 个结点 fun deleteLastKth(list: Node, k: Int): Node? { var fast: Node? = list var i = 1 while (fast != null && i < k) { fast = fast.next i++ } if (fast == null) { return when (i) { k -> list.next else -> list } } var slow: Node? = list var pre: Node? = null while (fast != null) { fast = fast.next pre = slow slow = slow?.next } pre?.next = pre?.next?.next return list } // 求中间结点 fun findMiddleNode(list: Node): Node? { var fast: Node? = list var slow: Node? = list while (fast?.next != null && fast.next?.next != null) { fast = fast.next?.next slow = slow?.next } return slow } class Node(var data: Int, var next: Node?) } ================================================ FILE: kotlin/08_stack/StackBasedOnLinkedList.kt ================================================ /** * 基于链表实现的栈。 * * Author: Zackratos */ class StackBasedOnLinkedList { private var top: Node? = null fun push(value: Int) { val newNode = Node(value, null) // 不管 top 是不是 null,都可以这么写 newNode.next = top top = newNode } fun pop(): Int { if (top == null) return -1 val node = top top = top!!.next return node!!.data } fun printAll() { var p = top while (p != null) { print("${p.data} ") p = p.next } println() } class Node(var data: Int, var next: Node?) } ================================================ FILE: notes/.gitkeep ================================================ ================================================ FILE: notes/10_recursion/readme.md ================================================ # 递归 ## 三个条件 * 可分解为子问题 * 子问题与原问题解法一致,只有规模上的不同 * 有终止条件 ## 写递归代码 * 整理出递推公式 * 确定好终止条件 * 「翻译」成代码 关键: > 只要遇到递归,我们就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑去分解每一个步骤。 ## 警惕 * 堆栈溢出 <- 递归深度过大 * 重复计算 <- 递归过程中的不同分支,重复计算相同子问题 * 保存子问题结果(map/dict) * 空间复杂度高 <- 递归函数调用带来的消耗 ## 递归改写非递归 本质:人肉模拟函数调用堆栈。 ================================================ FILE: notes/11_sorts/readme.md ================================================ # 排序(平方时间复杂度排序算法) | 排序算法 | 时间复杂度 | 是否基于比较 | |---------|----|----| | 冒泡、插入、选择 | $O(n^2)$ | [y] | | 快排、归并 | $O(n\log n)$ | [y] | | 桶、基数、计数 | $O(n) | [x] | 开篇问题:插入排序和冒泡排序的时间复杂度相同,都是 $O(n^2)$,在实际软件开发中,为什么我们更倾向于使用插入排序而不是冒泡排序? ## 如何分析「排序算法」? ### 算法执行效率 1. 最好、最坏、平均情况的时间复杂度 2. 时间复杂度的系数、低阶、常数——在渐进复杂度相同的情况下,需要比较系数、低阶和常数 3. 比较和交换(移动)的次数——基于比较的排序算法的两种基本操作 ### 算法的内存消耗 是否为原地排序算法(In-place sort algorithm),即算法的空间复杂度是否为 $O(1)$。 ### 排序的稳定性 经过排序算法处理后,值相同的元素,在原序列和排序后序列中的相对位置保持不变,则称该排序算法是稳定的。 > 待排序的 `item` 并不是简单的值,而是一个基于对象中的某个 `key` 进行排序时,排序的稳定性就有意义了。 ## 冒泡排序 * 每次循环都从序列起始位置开始 * 循环中的每个动作,都对比相邻两个元素的大小是否满足偏序要求,若不满足,则交换顺序 ![冒泡排序例图](https://static001.geekbang.org/resource/image/88/34/8890cbf63ea80455ce82490a23361134.jpg) 分析: * 原地排序 * 稳定排序(偏序关系是严格的偏序关系,如 `<` 或 `>`) * 时间复杂度 * 最好 $O(n)$ * 最坏 $O(n^2)$ * 平均 $O(n^2)$ ### 冒泡排序的平均时间复杂度非严格分析 * 有序度:序列中满足偏序关系的两两组合的元素对的个数 * 满有序度:排序完成的序列的有序度,它等于 $n(n - 1) / 2$ * 逆序度:序列中不满足偏序关系的亮亮组合的元素对的个数 显然,$\text{逆序度} = \text{满有序度} - \text{有序度}$。 在冒泡排序中,每产生一次「交换」操作,$\text{逆序度}--$。于是,平均情况下,需要 $n(n - 1)/4$ 次交换操作,它已经是 $O(n^2)$ 了。因此,尽管比较操作的数量会大于交换操作的数量,但我们依然能说,冒泡排序的平均时间复杂度是 $O(n^2)$。 > 分析过程不严格,但足够说明问题。 ## 插入排序 1. 将待排序数列分为已排序区间和未排序区间 2. 取未排序区间的第一个元素 3. 遍历已排序区间,按照偏序关系,寻找合适的位置,插入未排序区间的第一个元素 4. 重复 2 -- 3 直至未排序区间长度为零 ![插入排序例图](https://static001.geekbang.org/resource/image/fd/01/fd6582d5e5927173ee35d7cc74d9c401.jpg) 分析: * 原地排序 * 稳定排序(值相同的元素,往后插) * 时间复杂度 * 最好 $O(n)$ * 最坏 $O(n^2)$ * 平均 $O(n^2)$(乘法法则) ## 选择排序 1. 将待排序数列分为已排序区间和未排序区间 2. 遍历未排序区间,取未排序区间的最小元素 3. 交换上述最小元素与未排序区间中的第一个元素的位置 4. 重复 2 -- 3 直至未排序区间长度为零 ![选择排序例图](https://static001.geekbang.org/resource/image/32/1d/32371475a0b08f0db9861d102474181d.jpg) 分析: * 非原地排序 * 非稳定排序 * 时间复杂度 * 最好 $O(n^2)$ * 最坏 $O(n^2)$ * 平均 $O(n^2)$(乘法法则) ## 开篇问题 * 对同一份未排序序列数据,冒泡排序和插入排序所需的交换(移动)次数是一定的,且是相等的 * 单次数据交换,冒泡排序所需的时间更长(三次赋值操作,插排只需要一次) 另有插入排序的优化版本[希尔排序](https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F)。 ![小结](https://static001.geekbang.org/resource/image/34/50/348604caaf0a1b1d7fee0512822f0e50.jpg) ================================================ FILE: notes/12_sorts/.gitkeep ================================================ ================================================ FILE: notes/12_sorts/readme.md ================================================ # 排序(线性对数时间复杂度排序算法) 开篇问题:如何在 $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,前半段末位元素即是所求 ================================================ FILE: notes/13_sorts/.gitkeep ================================================ ================================================ FILE: notes/13_sorts/readme.md ================================================ # 线性排序 ## 开篇问题 如何按年龄给 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)$ * 使用条件 * 等长数据 ## 解答开篇 桶排序。 ================================================ FILE: notes/14_sorts/.gitkeep ================================================ ================================================ FILE: notes/14_sorts/readme.md ================================================ # 排序优化 ## 如何取舍排序算法? * 排序规模小 —— $O(n^2)$ 的算法(通常是插排) * 排序规模大 —— $O(n\log n)$ 的算法(通常不用归并排序) ## 如何优化快速排序? 参考:[谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/) ================================================ FILE: notes/15_bsearch/.gitkeep ================================================ ================================================ FILE: notes/15_bsearch/readme.md ================================================ # 二分查找(上) ## 算法描述 二分查找(Binary Search)也叫折半查找,是针对有序数据集合的查找算法。其描述十分简单: * 折半取中,判断元素与目标元素的大小关系 * 小于——往前继续折半 * 大于——往后继续折半 * 等于——返回 关于它的复杂度分析,参见[谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/)中的相关信息。它的复杂度是 $O(\log n)$。 ## $O(\log n)$ 的惊人之处 在 42 亿个数据中用二分查找一个数据,最多需要比较 32 次。 ## 适用场景 * 依赖顺序表结构 * 数据本身必须有序 * 数据量相对比较元素的开销要足够大——不然遍历即可 * 数据量相对内存空间不能太大——不然顺序表装不下 ================================================ FILE: notes/16_bsearch/.gitkeep ================================================ ================================================ FILE: notes/16_bsearch/readme.md ================================================ # 二分查找(下) 本节课讨论二分的各种变体。实际上在针对上一节的代码中,已经实现了两个变体。本次实现四个变体: * 第一个等于给定值的元素 * 最后一个等于给定值的元素 * 第一个不小于给定值的元素 * 最后一个不大于给定值的元素 ```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_ ``` ================================================ FILE: notes/17_skiplist/.gitkeep ================================================ ================================================ FILE: notes/17_skiplist/readme.md ================================================ # 跳表(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) ================================================ FILE: notes/18_hashtable/.gitkeep ================================================ ================================================ FILE: notes/18_hashtable/readme.md ================================================ # 散列表 散列表是数组的一种扩展,利用数组下标的随机访问特性。 ## 散列思想 * 键/关键字/Key:用来标识一个数据 * 散列函数/哈希函数/Hash:将 Key 映射到数组下标的函数 * 散列值/哈希值:Key 经过散列函数得到的数值 ![](https://static001.geekbang.org/resource/image/92/73/92c89a57e21f49d2f14f4424343a2773.jpg) 本质:利用散列函数将关键字映射到数组下标,而后利用数组随机访问时间复杂度为 $\Theta(1)$ 的特性快速访问。 ## 散列函数 * 形式:`hash(key)` * 基本要求 1. 散列值是非负整数 1. 如果 `key1 == key2`,那么 `hash(key1) == hash(key2)` 1. 如果 `key1 != key2`,那么 `hash(key1) != hash(key2)` 第 3 个要求,实际上不可能对任意的 `key1` 和 `key2` 都成立。因为通常散列函数的输出范围有限而输入范围无限。 ## 散列冲突 * 散列冲突:`key1 != key2` 但 `hash(key1) == hash(key2)` 散列冲突会导致不同键值映射到散列表的同一个位置。为此,我们需要解决散列冲突带来的问题。 ### 开放寻址法 如果遇到冲突,那就继续寻找下一个空闲的槽位。 #### 线性探测 插入时,如果遇到冲突,那就依次往下寻找下一个空闲的槽位。(橙色表示已被占用的槽位,黄色表示空闲槽位) ![](https://static001.geekbang.org/resource/image/5c/d5/5c31a3127cbc00f0c63409bbe1fbd0d5.jpg) 查找时,如果目标槽位上不是目标数据,则依次往下寻找;直至遇见目标数据或空槽位。 ![](https://static001.geekbang.org/resource/image/91/ff/9126b0d33476777e7371b96e676e90ff.jpg) 删除时,标记为 `deleted`,而不是直接删除。 #### 平方探测(Quadratic probing) 插入时,如果遇到冲突,那就往后寻找下一个空闲的槽位,其步长为 $1^2$, $2^2$, $3^2$, $\ldots$。 查找时,如果目标槽位上不是目标数据,则依次往下寻找,其步长为 $1^2$, $2^2$, $3^2$, $\ldots$;直至遇见目标数据或空槽位。 删除时,标记为 `deleted`,而不是直接删除。 #### 装载因子(load factor) $\text{load factor} = \frac{size()}{capacity()}$ ### 链表法 所有散列值相同的 key 以链表的形式存储在同一个槽位中。 ![](https://static001.geekbang.org/resource/image/a4/7f/a4b77d593e4cb76acb2b0689294ec17f.jpg) 插入时,不论是否有冲突,直接插入目标位置的链表。 查找时,遍历目标位置的链表来查询。 删除时,遍历目标位置的链表来删除。 ================================================ FILE: notes/19_hashtable/.gitkeep ================================================ ================================================ FILE: notes/19_hashtable/readme.md ================================================ # 散列表 核心:散列表的效率并不总是 $O(1)$,仅仅是在理论上能达到 $O(1)$。实际情况中,恶意攻击者可以通过精心构造数据,使得散列表的性能急剧下降。 如何设计一个工业级的散列表? ## 散列函数 * 不能过于复杂——避免散列过程耗时 * 散列函数的结果要尽可能均匀——最小化散列冲突 ## 装载因子过大怎么办 动态扩容。涉及到 rehash,效率可能很低。 ![](https://static001.geekbang.org/resource/image/67/43/67d12e07a7d673a9c1d14354ad029443.jpg) 如何避免低效扩容? ——将 rehash 的步骤,均摊到每一次插入中去: * 申请新的空间 * 不立即使用 * 每次来了新的数据,往新表插入数据 * 同时,取出旧表的一个数据,插入新表 ![](https://static001.geekbang.org/resource/image/6d/cb/6d6736f986ec4b75dabc5472965fb9cb.jpg) ## 解决冲突 开放寻址法,优点: * 不需要额外空间 * 有效利用 CPU 缓存 * 方便序列化 开放寻址法,缺点: * 查找、删除数据时,涉及到 `delete` 标志,相对麻烦 * 冲突的代价更高 * 对装载因子敏感 链表法,优点: * 内存利用率较高——链表的优点 * 对装载因子不敏感 链表法,缺点: * 需要额外的空间(保存指针) * 对 CPU 缓存不友好 ——将链表改造成更高效的数据结构,例如跳表、红黑树 ## 举个栗子(JAVA 中的 HashMap) * 初始大小:16 * 装载因子:超过 0.75 时动态扩容 * 散列冲突:优化版的链表法(当槽位冲突元素超过 8 时使用红黑树,否则使用链表) ================================================ FILE: notes/20_hashtable/.gitkeep ================================================ ================================================ FILE: notes/20_hashtable/readme.md ================================================ # 散列表 散列表和链表的组合?为什么呢? * 链表:涉及查找的操作慢,不连续存储; * 顺序表:支持随机访问,连续存储。 散列表 + 链表:结合优点、规避缺点。 ## 结合散列表的 LRU 缓存淘汰算法 缓存的操作接口: * 向缓存添加数据 * 从缓存删除数据 * 在缓存中查找数据 然而——不管是添加还是删除,都涉及到查找数据。因此,单纯的链表效率低下。 魔改一把! ![](https://static001.geekbang.org/resource/image/ea/6e/eaefd5f4028cc7d4cfbb56b24ce8ae6e.jpg) * `prev` 和 `next`:双向链表——LRU 的链表 * `hnext`:单向链表——解决散列冲突的链表 操作: * 在缓存中查找数据:利用散列表 * 从缓存中删除数据:先利用散列表寻找数据,然后删除——改链表就好了,效率很高 * 向缓存中添加数据:先利用散列表寻找数据,如果找到了,LRU 更新;如果没找到,直接添加在 LRU 链表尾部 ## Java: LinkedHashMap 遍历时,按照访问顺序遍历。实现结构,与上述 LRU 的结构完全相同——只不过它不是缓存,不限制容量大小。 ================================================ FILE: object-c/.gitkeep ================================================ ================================================ FILE: object-c/05_array/.gitkeep ================================================ ================================================ FILE: object-c/05_array/MyArray.h ================================================ // // MyArray.h // algo // // Created by Wenru Dong on 2018/10/3. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import @interface MyArray : NSObject - (instancetype)initWithCapacity:(NSUInteger)capacity; - (id)objectAtIndexedSubscript:(NSUInteger)index; - (void)removeObjectAtIndex:(NSUInteger)index; - (void)insertObject:(id)anObject atIndex:(NSUInteger)index; - (void)addObject:(id)anObject; - (void)printAll; @end ================================================ FILE: object-c/05_array/MyArray.m ================================================ // // MyArray.m // algo // // Created by Wenru Dong on 2018/10/3. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import "MyArray.h" @implementation MyArray { @private NSMutableArray *_data; NSUInteger _capacity; NSUInteger _count; } - (instancetype)initWithCapacity:(NSUInteger)capacity { self = [super init]; if (self) { _data = [NSMutableArray arrayWithCapacity:capacity]; _capacity = capacity; _count = 0; } return self; } - (id)objectAtIndexedSubscript:(NSUInteger)index { if (index >= _count) return nil; return _data[index]; } - (void)removeObjectAtIndex:(NSUInteger)index { if (index >= _count) { [NSException raise:NSRangeException format:@"Index out of range."]; } for (NSUInteger i = index + 1; i < _data.count; i++) { _data[i-1] = _data[i]; } _count--; } - (void)insertObject:(nonnull id)anObject atIndex:(NSUInteger)index { if (index >= _count || _count == _capacity) { [NSException raise:NSRangeException format:@"Index out of range."]; } for (NSUInteger i = _count - 1; i >= index; i--) { _data[i+1] = _data[i]; } _data[index] = anObject; _count++; } // insertToTail - (void)addObject:(nonnull id)anObject { if (_count == _capacity) { [NSException raise:NSRangeException format:@"Array is full."]; } [_data addObject:anObject]; _count++; } - (void)printAll { for (id obj in _data) { NSLog(@"%@", obj); } } @end ================================================ FILE: object-c/06_linkedlist/.gitkeep ================================================ ================================================ FILE: object-c/06_linkedlist/ListNode.h ================================================ // // ListNode.h // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import @interface ListNode : NSObject @property int value; @property ListNode *next; - (instancetype)initWithValue:(int)value; + (instancetype)nodeWithValue:(int)value; @end ================================================ FILE: object-c/06_linkedlist/ListNode.m ================================================ // // ListNode.m // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import "ListNode.h" @implementation ListNode - (instancetype)initWithValue:(int)value { if (self = [super init]) { _value = value; } return self; } + (instancetype)nodeWithValue:(int)value { return [[self alloc] initWithValue:value]; } - (NSString*)debugDescription { return [NSString stringWithFormat:@"%d", _value]; } @end ================================================ FILE: object-c/06_linkedlist/SinglyLinkedList.h ================================================ // // SinglyLinkedList.h // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import #import "ListNode.h" @interface SinglyLinkedList : NSObject @property ListNode* head; - (ListNode*)nodeWithValue:(int)value; - (ListNode*)nodeAtIndex:(NSUInteger)index; - (void)insertNodeWithValue:(int)value; - (void)insertNode:(nonnull ListNode*)node; + (void)insertNodeWithValue:(int)value afterNode:(nonnull ListNode*)node; + (void)insertNode:(nonnull ListNode*)aNode afterNode:(nonnull ListNode*)node; - (void)insertNodeWithValue:(int)value beforeNode:(nonnull ListNode*)node; - (void)insertNode:(nonnull ListNode*)aNode beforeNode:(nonnull ListNode*)node; - (void)deleteNode:(nonnull ListNode*)node; - (void)deleteNodesWithValue:(int)value; @end ================================================ FILE: object-c/06_linkedlist/SinglyLinkedList.m ================================================ // // SinglyLinkedList.m // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import "SinglyLinkedList.h" @implementation SinglyLinkedList - (ListNode*)nodeWithValue:(int)value { ListNode* current = _head; while (current && current.value != value) { current = current.next; } return current; } - (ListNode*)nodeAtIndex:(NSUInteger)index { ListNode* current = _head; NSUInteger position = 0; while (current && position != index) { current = current.next; position++; } return current; } - (void)insertNodeWithValue:(int)value { ListNode* aNode = [ListNode nodeWithValue:value]; [self insertNode:aNode]; } - (void)insertNode:(nonnull ListNode *)node { node.next = _head; _head = node; } + (void)insertNodeWithValue:(int)value afterNode:(nonnull ListNode *)node { ListNode* aNode = [ListNode nodeWithValue:value]; [SinglyLinkedList insertNode:aNode afterNode:node]; } + (void)insertNode:(nonnull ListNode *)aNode afterNode:(nonnull ListNode *)node { aNode.next = node.next; node.next = aNode; } - (void)insertNodeWithValue:(int)value beforeNode:(nonnull ListNode *)node { ListNode* aNode = [ListNode nodeWithValue:value]; [self insertNode:aNode beforeNode:node]; } - (void)insertNode:(nonnull ListNode *)aNode beforeNode:(nonnull ListNode *)node { ListNode* fakeHead = [ListNode nodeWithValue:0]; fakeHead.next = _head; ListNode* current = fakeHead; while (current.next && current.next != node) { current = current.next; } if (current.next == nil) { return; } aNode.next = node; current.next = aNode; } - (void)deleteNode:(nonnull ListNode *)node { if (node.next) { node.value = node.next.value; node.next = node.next.next; return; } if (_head == nil) return; ListNode* current = _head; while (current.next && current.next != node) { current = current.next; } current.next = nil; } - (void)deleteNodesWithValue:(int)value { ListNode* fakeHead = [ListNode nodeWithValue:value+1]; fakeHead.next = _head; ListNode* prev = fakeHead; ListNode* current = _head; while (current) { if (current.value != value) { prev.next = current; prev = prev.next; } current = current.next; } if (prev.next) { prev.next = nil; } _head = fakeHead.next; } - (NSString*)debugDescription { NSMutableString* info = [[NSMutableString alloc] init]; ListNode* current = _head; if (current) { [info appendString:current.debugDescription]; } current = current.next; while (current) { [info appendString:@"->"]; [info appendString:current.debugDescription]; current = current.next; } return [NSString stringWithString:info]; } @end ================================================ FILE: object-c/06_linkedlist/SinglyLinkedListTests.m ================================================ // // SinglyLinkedListTests.m // SinglyLinkedListTests // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import #import "ListNode.h" #import "SinglyLinkedList.h" @interface SinglyLinkedListTests : XCTestCase @end @implementation SinglyLinkedListTests { SinglyLinkedList* _list; NSArray* _nodes; } - (void)setUp { [super setUp]; ListNode* node1 = [ListNode nodeWithValue:1]; ListNode* node2 = [ListNode nodeWithValue:2]; ListNode* node3 = [ListNode nodeWithValue:3]; ListNode* node4 = [ListNode nodeWithValue:4]; ListNode* node5 = [ListNode nodeWithValue:5]; ListNode* node6 = [ListNode nodeWithValue:6]; node1.next = node2; node2.next = node3; node3.next = node4; node4.next = node5; node5.next = node6; _list = [[SinglyLinkedList alloc] init]; _list.head = node1; _nodes = [NSArray arrayWithObjects:node1, node2, node3, node4, node5, node6, nil]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testNodeWithValue { XCTAssertEqualObjects([_list nodeWithValue:1], _list.head); XCTAssertNil([_list nodeWithValue:10]); } - (void)testNodeAtIndex { XCTAssertEqualObjects([_list nodeAtIndex:4], _nodes[4]); XCTAssertNil([_list nodeAtIndex:10]); } - (void)testInsertNodeWithValue { [_list insertNodeWithValue:9]; XCTAssertEqual(_list.head.value, 9); XCTAssertEqual(_list.head.next.value, 1); } - (void)testInsertNode { ListNode* aNode = [ListNode nodeWithValue:7]; [_list insertNode:aNode]; XCTAssertEqualObjects(_list.head, aNode); } - (void)testInsertNodeWithValueAfterNode { [SinglyLinkedList insertNodeWithValue:12 afterNode:_nodes[3]]; XCTAssertEqual([[_list nodeAtIndex:4] value], 12); } - (void)testInsertNodeAfterNode { ListNode* aNode = [ListNode nodeWithValue:28]; [SinglyLinkedList insertNode:aNode afterNode:_nodes[5]]; ListNode* prevNode = (ListNode *)_nodes[5]; XCTAssertEqualObjects(aNode, prevNode.next); } - (void)testInsertNodeBeforeNode { ListNode* aNode = [ListNode nodeWithValue:27]; ListNode* prevNode = (ListNode *)_nodes[3]; [_list insertNode:aNode beforeNode:_nodes[4]]; XCTAssertEqualObjects(aNode, prevNode.next); } - (void)testInsertNodeBeforeUnconnectedNode { ListNode* aNode = [ListNode nodeWithValue:27]; ListNode* floatingNode = [ListNode nodeWithValue:36]; [_list insertNode:aNode beforeNode:floatingNode]; for (NSUInteger i = 0; i < 6; i++) { XCTAssertEqualObjects([_list nodeAtIndex:i], _nodes[i]); } } - (void)testDeleteNode { [_list deleteNode:_nodes[0]]; XCTAssertEqual(_list.head.value, 2); [_list deleteNode:_nodes[5]]; ListNode* lastNode = (ListNode *)_nodes[4]; XCTAssertNil(lastNode.next); } - (void)testDeleteNodesWithValue { ListNode* firstNode = [ListNode nodeWithValue:1]; ListNode* secondNode = [ListNode nodeWithValue:1]; [_list insertNode:firstNode]; [_list insertNode:secondNode]; [_list deleteNodesWithValue:1]; for (NSUInteger i = 1; i < 6; i++) { XCTAssertEqualObjects([_list nodeAtIndex:i-1], _nodes[i]); } } - (void)testDebugDescription { XCTAssertEqualObjects(_list.debugDescription, @"1->2->3->4->5->6"); } //- (void)testPerformanceExample { // // This is an example of a performance test case. // [self measureBlock:^{ // // Put the code you want to measure the time of here. // }]; //} @end ================================================ FILE: object-c/07_linkedlist/.gitkeep ================================================ ================================================ FILE: object-c/08_stack/LinkedStack.h ================================================ // // LinkedStack.h // algo // // Created by Wenru Dong on 2018/10/8. // Copyright © 2018年 Wenru Dong. All rights reserved. // // Stack based upon linked list // 基于链表实现的栈 #import @interface LinkedStack : NSObject - (BOOL)isEmpty; - (void)push:(int)value; - (int)pop; @end ================================================ FILE: object-c/08_stack/LinkedStack.m ================================================ // // LinkedStack.m // algo // // Created by Wenru Dong on 2018/10/8. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import "LinkedStack.h" #import "ListNode.h" @implementation LinkedStack { @private ListNode* _top; } - (BOOL)isEmpty { return _top == nil; } - (void)push:(int)value { ListNode *newTop = [ListNode nodeWithValue:value]; newTop.next = _top; _top = newTop; } - (int)pop { if ([self isEmpty]) { [NSException raise:NSRangeException format:@"The stack is empty."]; } int value = _top.value; _top = _top.next; return value; } - (NSString *)debugDescription { NSMutableString *info = [[NSMutableString alloc] init]; ListNode *current = _top; while (current) { [info appendString:[NSString stringWithFormat:@"%d]", current.value]]; current = current.next; } return [NSString stringWithString:info]; } @end ================================================ FILE: object-c/08_stack/LinkedStackTests.m ================================================ // // LinkedStackTests.m // LinkedStackTests // // Created by Wenru Dong on 2018/10/8. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import #import "LinkedStack.h" @interface LinkedStackTests : XCTestCase @end @implementation LinkedStackTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testPush { LinkedStack *stack = [[LinkedStack alloc] init]; for (int i = 0; i < 10; i++) { [stack push:i]; } XCTAssertEqualObjects([stack debugDescription], @"9]8]7]6]5]4]3]2]1]0]"); } - (void)testPop { LinkedStack *stack = [[LinkedStack alloc] init]; for (int i = 0; i < 10; i++) { [stack push:i]; } [stack pop]; XCTAssertEqualObjects([stack debugDescription], @"8]7]6]5]4]3]2]1]0]"); for (int i = 0; i < 9; i++) { [stack pop]; } XCTAssertThrowsSpecificNamed([stack pop], NSException, NSRangeException, @"The stack is empty."); } //- (void)testPerformanceExample { // // This is an example of a performance test case. // [self measureBlock:^{ // // Put the code you want to measure the time of here. // }]; //} @end ================================================ FILE: object-c/08_stack/ListNode.h ================================================ // // ListNode.h // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import @interface ListNode : NSObject @property int value; @property ListNode *next; - (instancetype)initWithValue:(int)value; + (instancetype)nodeWithValue:(int)value; @end ================================================ FILE: object-c/08_stack/ListNode.m ================================================ // // ListNode.m // algo // // Created by Wenru Dong on 2018/10/6. // Copyright © 2018年 Wenru Dong. All rights reserved. // #import "ListNode.h" @implementation ListNode - (instancetype)initWithValue:(int)value { if (self = [super init]) { _value = value; } return self; } + (instancetype)nodeWithValue:(int)value { return [[self alloc] initWithValue:value]; } - (NSString*)debugDescription { return [NSString stringWithFormat:@"%d", _value]; } @end ================================================ FILE: object-c/08_stack/stack_practice/ArrayStack.h ================================================ /** 栈实现 Author: Smallfly */ #import @interface Stack : NSObject - (id)initWithCapacity:(NSUInteger)count; - (BOOL)isEmpty; - (id)top; - (NSUInteger)size; - (BOOL)push:(id)obj; - (id)pop; @end ================================================ FILE: object-c/08_stack/stack_practice/ArrayStack.m ================================================ #import "ArrayStack.h" @implementation Stack { NSMutableArray *_arr; NSUInteger _capacity; NSUInteger _count; } - (id)initWithCapacity:(NSUInteger)capacity { self = [super init]; _capacity = capacity; _arr = [[NSMutableArray alloc] initWithCapacity:capacity]; return self; } - (BOOL)isEmpty { return _arr.count == 0; } - (BOOL)isFull { return _arr.count == _capacity; } - (id)top { if ([self isEmpty]) return nil; NSUInteger index = _arr.count - 1; return _arr[index]; } - (NSUInteger)size { return _arr.count; } - (BOOL)push:(id)obj { if (!obj) return NO; if (_arr.count == _capacity) return NO; [_arr addObject:obj]; return YES; } - (id)pop { if ([self isEmpty]) return nil; NSUInteger index = _arr.count - 1; id obj = _arr[index]; [_arr removeLastObject]; return obj; } @end ================================================ FILE: object-c/08_stack/stack_practice/BalancedParentheses.h ================================================ /** 判断括号是否匹配 {} [] () Author: Smallfly */ #import @interface BalancedParentheses : NSObject - (BOOL)checkForParenthessBlanced:(NSString *)express; @end ================================================ FILE: object-c/08_stack/stack_practice/BalancedParentheses.m ================================================ #import "BalancedParentheses.h" #import "ArrayStack.h" const NSDictionary *parenthesesDict() { return @{@"(": @")", @"{": @"}", @"[": @"]"}; } @implementation BalancedParentheses { Stack *_stack; } - (instancetype)init { self = [super init]; _stack = [[Stack alloc] initWithCapacity:100]; return self; } - (BOOL)checkForParenthessBlanced:(NSString *)express { NSInteger midIndex = express.length / 2; for (int i = 0; i < express.length; ++i) { NSString *ele = [express substringWithRange:NSMakeRange(i, 1)]; if (i < midIndex) { // 前半部分把与 ele 匹配的括号加入栈 [_stack push:parenthesesDict()[ele]]; } else { // 后半部分检查栈顶的元素与当前元素是否相同 NSString *topEle = [_stack pop]; if (![topEle isEqualToString:ele]) { return NO; } } } return YES; } @end ================================================ FILE: object-c/08_stack/stack_practice/FourOperation.h ================================================ /** 整型四则运算 Author: Smallfly */ #import @interface FourOperation : NSObject + (FourOperation *)shared; /** 整型四则运算 @param expression 运算表达式,注意操作数和运算符之间要有空格 @return 计算结果 */ - (NSNumber *)caculateExpression:(NSString *)expression; @end ================================================ FILE: object-c/08_stack/stack_practice/FourOperation.m ================================================ #import "FourOperation.h" #import "ArrayStack.h" const NSDictionary *operationPriorityDict() { return @{@"*": @1, @"/": @1, @"+": @0, @"-": @0}; } @implementation FourOperation { @private Stack *_optStack; Stack *_numStack; NSNumberFormatter *_numFormatter; } + (FourOperation *)shared { static FourOperation* single = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ single = [FourOperation new]; }); return single; } - (instancetype)init { self = [super init]; _optStack = [[Stack alloc] initWithCapacity:100]; _numStack = [[Stack alloc] initWithCapacity:100]; _numFormatter = [NSNumberFormatter new]; return self; } - (id)caculateExpression:(NSString *)expression { NSArray *elements = [expression componentsSeparatedByString:@" "]; for (NSString *obj in elements) { NSNumber *numb = [_numFormatter numberFromString:obj]; if (numb) { // 运算数 [_numStack push:numb]; } else { // 操作符 // 如果栈顶操作符优先级大于等于当前操作符 while ([self _topOperationPriorityIsHigherOrEqualToOperation:obj]) { // 取出栈顶的操作符和两个操作数做一次运算 NSNumber *res = [self _excuteOnceCaculate]; // 计算结果存入栈 [_numStack push:res]; } [_optStack push:obj]; } } // 如果操作符存在栈中,依次取出做运算 NSNumber *res = nil; while ([_optStack top]) { res = [self _excuteOnceCaculate]; [_numStack push:res]; } return res; } - (NSInteger)_getPriority:(NSString *)opt { return [[operationPriorityDict() objectForKey:opt] integerValue]; } - (BOOL)_topOperationPriorityIsHigherOrEqualToOperation:(NSString *)opt { NSString *topOpt = [_optStack top]; if (!topOpt) return NO; NSInteger curPriority = [self _getPriority:opt]; NSInteger topPriority = [self _getPriority:topOpt]; return curPriority <= topPriority; } - (NSNumber *)_excuteOnceCaculate { NSNumber *numRight = [_numStack pop]; NSNumber *numLeft = [_numStack pop]; NSString *topOpt = [_optStack pop]; NSInteger result = [self _caculeteWithNumberLeft:numLeft numberRight:numRight operation:topOpt]; NSNumber *res = [NSNumber numberWithInteger:result]; return res; } - (NSInteger)_caculeteWithNumberLeft:(NSNumber *)numLeft numberRight:(NSNumber *)numRight operation:(NSString *)opt { if (!numLeft || !numRight || !opt) return 0; NSInteger left = [numLeft integerValue]; NSInteger right = [numRight integerValue]; if ([opt isEqualToString:@"+"]) { return left + right; } else if ([opt isEqualToString:@"-"]) { return left - right; } else if ([opt isEqualToString:@"*"]) { return left * right; } else if ([opt isEqualToString:@"/"]) { return left / right; } else { return 0; } } @end ================================================ FILE: object-c/08_stack/stack_practice/main.m ================================================ #import #import "FourOperation.h" #import "BalancedParentheses.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 测试四则运算 NSNumber *a = [[FourOperation shared] caculateExpression:@"10 - 4 / 2 * 3 + 3 - 6 / 2"]; NSNumber *b = [[FourOperation shared] caculateExpression:@"10 - 3"]; NSNumber *c = [[FourOperation shared] caculateExpression:@"2 * 3"]; NSLog(@"FourOperation: %ld\t%ld\t%ld\t", a.integerValue, b.integerValue, c.integerValue); // 测试括号匹配 BalancedParentheses *balancedCheck = [BalancedParentheses new]; BOOL result = [balancedCheck checkForParenthessBlanced:@"([{{{}}}])"]; NSLog(@"BalancedParentheses: %d", result); } return 0; } ================================================ FILE: object-c/11_Sort/Sort.h ================================================ // // Sort.h // test1231231 // // Created by Scarlett Che on 2018/12/12. // Copyright © 2018 Scarlett Che. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface Sort : NSObject // 冒泡排序 + (NSArray *)bubbleSortWithArray:(NSArray *)array; // 插入排序 + (NSArray *)insertionSortWithArray:(NSArray *)array; // 选择排序 + (NSArray *)selectionSortWithArray:(NSArray *)array; @end NS_ASSUME_NONNULL_END ================================================ FILE: object-c/11_Sort/Sort.m ================================================ // // Sort.m // test1231231 // // Created by Scarlett Che on 2018/12/12. // Copyright © 2018 Scarlett Che. All rights reserved. // #import "Sort.h" @implementation Sort // 冒泡排序 + (NSArray *)bubbleSortWithArray:(NSArray *)array { if (array.count <= 1) { return array; } NSMutableArray *aryM = array.mutableCopy; for (int i = 0; i < aryM.count - 1; i++) { BOOL flag = NO; // 提前结束标记 for (int j = 0; j < aryM.count - i - 1; j++) { NSInteger value1 = [aryM[j] integerValue]; NSInteger value2 = [aryM[j + 1] integerValue]; if (value1 > value2) { flag = YES; [aryM exchangeObjectAtIndex:j withObjectAtIndex:j+1]; } } if (flag == NO) { // 提前结束 break; } } return aryM.copy; } // 插入排序 + (NSArray *)insertionSortWithArray:(NSArray *)array { NSMutableArray *aryU = array.mutableCopy; for (int i = 1; i < aryU.count; i++) { NSInteger value = [aryU[i] integerValue]; for (int j = 0; j < i; j ++) { NSInteger sortedValue = [aryU[j] integerValue]; if (value < sortedValue) { id obj = aryU[i]; [aryU removeObjectAtIndex:i]; [aryU insertObject:obj atIndex:j]; break; } } } return aryU.copy; } // 选择排序 + (NSArray *)selectionSortWithArray:(NSArray *)array { if (array.count <= 1) { return array; } NSMutableArray *aryM = array.mutableCopy; for (int i = 0; i < array.count - 1; i++) { NSInteger minIndex = NSNotFound; NSInteger minValue = NSNotFound; for (int j = i + 1; j < array.count - 1; j++) { NSInteger tmp = [array[j] integerValue]; if (tmp < minValue) { minValue = tmp; minIndex = j; } } if (minIndex != NSNotFound && minValue != NSNotFound && minValue < [array[i] integerValue]) { [aryM exchangeObjectAtIndex:minIndex withObjectAtIndex:i]; } } return array; } @end ================================================ FILE: object-c/33_bm_match/BM.h ================================================ // // BM.h // BM-Match // // Created by Smallfly on 2018/12/9. // Copyright © 2018 Smallfly. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface BM : NSObject - (instancetype)initWithA:(NSString *)a andB:(NSString *)b; - (NSInteger)startMatch; - (void)startMatchCompeletion:(void (^)(NSInteger))completion; @end NS_ASSUME_NONNULL_END ================================================ FILE: object-c/33_bm_match/BM.m ================================================ // // BM.m // BM-Match // // Created by Smallfly on 2018/12/9. // Copyright © 2018 Smallfly. All rights reserved. // #import "BM.h" #define SIZE 256 @interface BM() @property (nonatomic, strong) NSString *a; // 主串 @property (nonatomic, strong) NSString *b; // 匹配串 @property (nonatomic, strong) NSMutableArray *bc; // 匹配串,哈希表,存储字符在匹配串中的下标 @property (nonatomic, strong) NSMutableArray *suffix; @property (nonatomic, strong) NSMutableArray *prifix; @end @implementation BM - (instancetype)initWithA:(NSString *)a andB:(NSString *)b { self = [super init]; if (!self) return nil; _a = a; _b = b; _bc = [NSMutableArray new]; _suffix = [NSMutableArray new]; _prifix = [NSMutableArray new]; [self generateBC]; [self generateGS]; return self; } // 构建坏字符哈希表,记录每个字符在匹配串中最后出现的位置 - (void)generateBC { for (int i = 0; i < SIZE; ++i) { [_bc addObject:@-1]; } for (int i = 0; i < _b.length; ++i) { int ascii = (int)[_b characterAtIndex:i]; // char to ASCII _bc[ascii] = [NSNumber numberWithInteger:i]; // save b's char index } } - (NSInteger)bm { NSInteger i = 0; // 主串和匹配串对齐的第一个字符 NSUInteger n = _a.length; NSUInteger m = _b.length; while (i <= n - m) { NSInteger j; // 从后往前匹配 for (j = m - 1; j >= 0; --j) { int aValue = (int)[_a characterAtIndex:(i + j)]; int bValue = (int)[_b characterAtIndex:j]; if (aValue != bValue) break; // 找到坏字符下标 j 停止 } if (j < 0) { return i; // 匹配成功,返回所在的位置 } // 坏字符在匹配串中最后出现的位置 id numberInHashTableBC = _bc[(int)[_a characterAtIndex:(i + j)]]; NSInteger x = j - [numberInHashTableBC integerValue]; NSInteger y = 0; if (j < m - 1) { y = [self moveByGSBy:j]; } i = i + MAX(x, y); // 这一步比较难理解,不直接滑到过 j,是因为在 j 之前可能存在于坏字符相同的字符 // 这个于坏字符相同的字符,在匹配串中的最大下标是 numberInHashTableBC // i = i + (j - [numberInHashTableBC integerValue]); } return -1; } // 好后缀匹配移动 - (NSInteger)moveByGSBy:(NSInteger)j { NSUInteger m = _b.length; NSInteger k = m - 1 - j; // 好后缀的长度 NSInteger t = [_suffix[k] integerValue]; if (t != -1) return j - t + 1; // 匹配串的前缀,是否匹配好后缀,关键 for (NSInteger r = j+1; r <= m-1; ++r) { if ([_prifix[m-r] boolValue]) { // 关键 return r; } } return m; } - (void)generateGS { NSUInteger m = _b.length; for (NSInteger i = 0; i < m; ++i) { _prifix[i] = @(NO); _suffix[i] = @(-1); } for (NSInteger i = 0; i < m - 1; ++i) { // 从 b 中取两个字符对比 NSInteger j = i; NSInteger k = 0; // 公共后缀的长度 int jValue = (int)[_b characterAtIndex:j]; int bmValue = (int)[_b characterAtIndex:(m-1-k)]; while (j >= 0 && jValue == bmValue) { // 与 b[0, m-1] 求公共子串 ++k; --j; _suffix[k] = [NSNumber numberWithInteger:(j+1)]; //j+1 代表公共子串在 b 中的起始下标 } if (j == -1) _prifix[k] = @(YES); } } #pragma mark - // 同步 - (NSInteger)startMatch { return [self bm]; } // 异步 - (void)startMatchCompeletion:(void (^)(NSInteger))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ completion([self bm]); }); } @end ================================================ FILE: object-c/33_bm_match/main.m ================================================ // // main.m // BM-Match // // Created by Smallfly on 2018/12/9. // Copyright © 2018 Smallfly. All rights reserved. // #import #import "BM.h" int main(int argc, const char * argv[]) { @autoreleasepool { BM *bm = [[BM alloc] initWithA:@"abacadc" andB:@"adc"]; [bm startMatchCompeletion:^(NSInteger index) { NSLog(@"异步查找到下标:%ld\n", index); }]; NSLog(@"同步查找到下标:%ld\n", [bm startMatch]); } return 0; } ================================================ FILE: php/.gitignore ================================================ .idea vendor *my* Queue ================================================ FILE: php/05_array/.gitkeep ================================================ ================================================ FILE: php/05_array/array.php ================================================ data = array(); $this->capacity = $capacity; $this->length = 0; } /** * 数组是否已满 * @return bool */ public function checkIfFull() { if($this->length == $this->capacity) { return true; } return false; } /** * 判断索引index是否超出数组范围 * @param $index * @return bool */ private function checkOutOfRange($index) { if($index >= $this->length) { return true; } return false; } /** * 在索引index位置插入值value,返回错误码,0为插入成功 * @param $index * @param $value * @return int */ public function insert($index, $value) { $index = intval($index); $value = intval($value); if ($index < 0) { return 1; } if ($this->checkIfFull()) { return 2; } for ($i = $this->length - 1; $i >= $index; $i--) { $this->data[$i + 1] = $this->data[$i]; } $this->data[$index] = $value; $this->length++; return 0; } /** * 删除索引index上的值,并返回 * @param $index * @return array */ public function delete($index) { $value = 0; $index = intval($index); if ($index < 0) { $code = 1; return [$code, $value]; } if ($this->checkOutOfRange($index)) { $code = 2; return [$code, $value]; } $value = $this->data[$index]; for ($i = $index; $i < $this->length - 1; $i++) { $this->data[$i] = $this->data[$i + 1]; } $this->length--; return [0, $value]; } /** * 查找索引index的值 * @param $index * @return array */ public function find($index) { $value = 0; $index = intval($index); if ($index < 0) { $code = 1; return [$code, $value]; } if ($this->checkOutOfRange($index)) { $code = 2; return [$code, $value]; } return [0, $this->data[$index]]; } public function printData() { $format = ""; for ($i = 0; $i < $this->length; $i++) { $format .= "|" . $this->data[$i]; } print($format . "\n"); } } ================================================ FILE: php/05_array/array_test.php ================================================ insert($i, $i+1); } $myArr1->printData(); $code = $myArr1->insert(6, 999); echo "insert at 6: code:{$code}\n"; $myArr1->printData(); list($code, $value) = $myArr1->delete(6); echo "delete at 6: code:{$code}, value:{$value}\n"; $myArr1->printData(); $code = $myArr1->insert(11, 999); echo "insert at 11: code:{$code}\n"; $myArr1->printData(); list($code, $value) = $myArr1->delete(0); echo "delete at 0: code:{$code}, value:{$value}\n"; $myArr1->printData(); list($code, $value) = $myArr1->find(0); echo "find at 0: code:{$code}, value:{$value}\n"; ================================================ FILE: php/06_linkedlist/.gitkeep ================================================ ================================================ FILE: php/06_linkedlist/SingleLinkedList.php ================================================ head = new SingleLinkedListNode(); } else { $this->head = $head; } $this->length = 0; } /** * 获取链表长度 * * @return int */ public function getLength() { return $this->length; } /** * 插入数据 采用头插法 插入新数据 * * @param $data * * @return SingleLinkedListNode|bool */ public function insert($data) { return $this->insertDataAfter($this->head, $data); } /** * 删除节点 * * @param SingleLinkedListNode $node * * @return bool */ public function delete(SingleLinkedListNode $node) { if (null == $node) { return false; } // 获取待删除节点的前置节点 $preNode = $this->getPreNode($node); if (empty($preNode)) { return false; } // 修改指针指向 $preNode->next = $node->next; unset($node); $this->length--; return true; } /** * 通过索引获取节点 * * @param int $index * * @return SingleLinkedListNode|null */ public function getNodeByIndex($index) { if ($index >= $this->length) { return null; } $cur = $this->head->next; for ($i = 0; $i < $index; ++$i) { $cur = $cur->next; } return $cur; } /** * 获取某个节点的前置节点 * * @param SingleLinkedListNode $node * * @return SingleLinkedListNode|bool|null */ public function getPreNode(SingleLinkedListNode $node) { if (null == $node) { return false; } $curNode = $this->head; $preNode = $this->head; // 遍历找到前置节点 要用全等判断是否是同一个对象 // http://php.net/manual/zh/language.oop5.object-comparison.php while ($curNode !== $node) { if ($curNode == null) { return null; } $preNode = $curNode; $curNode = $curNode->next; } return $preNode; } /** * 输出单链表 当data的数据为可输出类型 * * @return bool */ public function printList() { if (null == $this->head->next) { return false; } $curNode = $this->head; // 防止链表带环,控制遍历次数 $listLength = $this->getLength(); while ($curNode->next != null && $listLength--) { echo $curNode->next->data . ' -> '; $curNode = $curNode->next; } echo 'NULL' . PHP_EOL; return true; } /** * 输出单链表 当data的数据为可输出类型 * * @return bool */ public function printListSimple() { if (null == $this->head->next) { return false; } $curNode = $this->head; while ($curNode->next != null) { echo $curNode->next->data . ' -> '; $curNode = $curNode->next; } echo 'NULL' . PHP_EOL; return true; } /** * 在某个节点后插入新的节点 (直接插入数据) * * @param SingleLinkedListNode $originNode * @param $data * * @return SingleLinkedListNode|bool */ public function insertDataAfter(SingleLinkedListNode $originNode, $data) { // 如果originNode为空,插入失败 if (null == $originNode) { return false; } // 新建单链表节点 $newNode = new SingleLinkedListNode(); // 新节点的数据 $newNode->data = $data; // 新节点的下一个节点为源节点的下一个节点 $newNode->next = $originNode->next; // 在originNode后插入newNode $originNode->next = $newNode; // 链表长度++ $this->length++; return $newNode; } /** * 在某个节点前插入新的节点(很少使用) * * @param SingleLinkedListNode $originNode * @param $data * * @return SingleLinkedListNode|bool */ public function insertDataBefore(SingleLinkedListNode $originNode, $data) { // 如果originNode为空,插入失败 if (null == $originNode) { return false; } // 先找到originNode的前置节点,然后通过insertDataAfter插入 $preNode = $this->getPreNode($originNode); return $this->insertDataAfter($preNode, $data); } /** * 在某个节点后插入新的节点 * * @param SingleLinkedListNode $originNode * @param SingleLinkedListNode $node * * @return SingleLinkedListNode|bool */ public function insertNodeAfter(SingleLinkedListNode $originNode, SingleLinkedListNode $node) { // 如果originNode为空,插入失败 if (null == $originNode) { return false; } $node->next = $originNode->next; $originNode->next = $node; $this->length++; return $node; } /** * 构造一个有环的链表 */ public function buildHasCircleList() { $data = [1, 2, 3, 4, 5, 6, 7, 8]; $node0 = new SingleLinkedListNode($data[0]); $node1 = new SingleLinkedListNode($data[1]); $node2 = new SingleLinkedListNode($data[2]); $node3 = new SingleLinkedListNode($data[3]); $node4 = new SingleLinkedListNode($data[4]); $node5 = new SingleLinkedListNode($data[5]); $node6 = new SingleLinkedListNode($data[6]); $node7 = new SingleLinkedListNode($data[7]); $this->insertNodeAfter($this->head, $node0); $this->insertNodeAfter($node0, $node1); $this->insertNodeAfter($node1, $node2); $this->insertNodeAfter($node2, $node3); $this->insertNodeAfter($node3, $node4); $this->insertNodeAfter($node4, $node5); $this->insertNodeAfter($node5, $node6); $this->insertNodeAfter($node6, $node7); $node7->next = $node4; } } ================================================ FILE: php/06_linkedlist/SingleLinkedListNode.php ================================================ data = $data; $this->next = null; } } ================================================ FILE: php/06_linkedlist/main.php ================================================ getLength() <= 1) { return true; } $pre = null; $slow = $list->head->next; $fast = $list->head->next; $remainNode = null; // 找单链表中点 以及 反转前半部分链表 while ($fast != null && $fast->next != null) { $fast = $fast->next->next; // 单链表反转关键代码 三个指针 $remainNode = $slow->next; $slow->next = $pre; $pre = $slow; $slow = $remainNode; } // 链表长度为偶数的情况 if ($fast != null) { $slow = $slow->next; } // 开始逐个比较 while ($slow != null) { if ($slow->data != $pre->data) { return false; } $slow = $slow->next; $pre = $pre->next; } return true; } $list = new SingleLinkedList(); $list->insert('a'); $list->insert('b'); $list->insert('c'); $list->insert('c'); $list->insert('b'); $list->insert('a'); var_dump(isPalindrome($list)); ================================================ FILE: php/07_linkedlist/.gitkeep ================================================ ================================================ FILE: php/07_linkedlist/main.php ================================================ list = $list; } /** * 设置单链表 * * @param SingleLinkedList $list */ public function setList(SingleLinkedList $list) { $this->list = $list; } /** * 单链表反转 * * 三个指针反转 * preNode 指向前一个结点 * curNode 指向当前结点 * remainNode 指向当前结点的下一个节点(保存未逆序的链表,为了在断开curNode的next指针后能找到后续节点) * * @return bool */ public function reverse() { if (null == $this->list || null == $this->list->head || null == $this->list->head->next) { return false; } $preNode = null; $curNode = $this->list->head->next; $remainNode = null; // 保存头结点,稍后指向反转后的链表 $headNode = $this->list->head; // 断开头结点的next指针 $this->list->head->next = null; while ($curNode != null) { $remainNode = $curNode->next; $curNode->next = $preNode; $preNode = $curNode; $curNode = $remainNode; } // 头结点指向反转后的链表 $headNode->next = $preNode; return true; } /** * 判断链表是否有环 * * 快慢指针判断是否有环 * @link http://t.cn/ROxpgQ1 * * @return bool */ public function checkCircle() { if (null == $this->list || null == $this->list->head || null == $this->list->head->next) { return false; } $slow = $this->list->head->next; $fast = $this->list->head->next; while ($fast != null && $fast->next != null) { $fast = $fast->next->next; $slow = $slow->next; // 如果慢指针跟快指针相遇了说明有环 解释在上面的链接中 if ($slow === $fast) { return true; } } return false; } /** * 合并两个有序链表 * * @param SingleLinkedList $listA * @param SingleLinkedList $listB * * @return SingleLinkedList|\Algo_06\SingleLinkedListNode */ public function mergerSortedList(SingleLinkedList $listA, SingleLinkedList $listB) { if (null == $listA) { return $listB; } if (null == $listB) { return $listA; } $pListA = $listA->head->next; $pListB = $listB->head->next; $newList = new SingleLinkedList(); $newHead = $newList->head; $newRootNode = $newHead; while ($pListA != null && $pListB != null) { if ($pListA->data <= $pListB->data) { $newRootNode->next = $pListA; $pListA = $pListA->next; } else { $newRootNode->next = $pListB; $pListB = $pListB->next; } $newRootNode = $newRootNode->next; } // 如果第一个链表未处理完,拼接到新链表后面 if ($pListA != null) { $newRootNode->next = $pListA; } // 如果第二个链表未处理完,拼接到新链表后面 if ($pListB != null) { $newRootNode->next = $pListB; } return $newList; } /** * 删除链表倒数第n个结点 * * @param $index * * @return bool */ public function deleteLastKth($index) { if (null == $this->list || null == $this->list->head || null == $this->list->head->next) { return false; } $i = 1; $slow = $this->list->head; $fast = $this->list->head; while ($fast != null && $i < $index) { $fast = $fast->next; ++$i; } if (null == $fast) { return true; } $pre = null; while($fast->next != null) { $pre = $slow; $slow = $slow->next; $fast = $fast->next; } if (null == $pre) { $this->list->head->next = $slow->next; } else { $pre->next = $pre->next->next; } return true; } /** * 寻找中间节点 * * 快慢指针遍历 * * @return \Algo_06\SingleLinkedListNode|bool|null */ public function findMiddleNode() { if (null == $this->list || null == $this->list->head || null == $this->list->head->next) { return false; } $slow = $this->list->head->next; $fast = $this->list->head->next; while ($fast != null && $fast->next != null) { $fast = $fast->next->next; $slow = $slow->next; } return $slow; } } echo '---------------------- 单链表反转 ----------------------' . PHP_EOL . PHP_EOL; $list = new SingleLinkedList(); $list->insert(1); $list->insert(2); $list->insert(3); $list->insert(4); $list->insert(5); $list->insert(6); $list->insert(7); // 单链表反转 $listAlgo = new SingleLinkedListAlgo($list); $listAlgo->list->printList(); $listAlgo->reverse(); $listAlgo->list->printList(); echo '--------------------------------------------------------' . PHP_EOL . PHP_EOL; echo '---------------------- 链表中环的检测 ----------------------'. PHP_EOL . PHP_EOL; // 链表中环的检测 $listCircle = new SingleLinkedList(); $listCircle->buildHasCircleList(); $listAlgo->setList($listCircle); var_dump($listAlgo->checkCircle()); echo '------------------------------------------------------------' . PHP_EOL . PHP_EOL; echo '---------------------- 两个有序的链表合并 ----------------------' . PHP_EOL . PHP_EOL; // 两个有序的链表合并 $listA = new SingleLinkedList(); $listA->insert(9); $listA->insert(7); $listA->insert(5); $listA->insert(3); $listA->insert(1); $listA->printList(); $listB = new SingleLinkedList(); $listB->insert(10); $listB->insert(8); $listB->insert(6); $listB->insert(4); $listB->insert(2); $listB->printList(); $listAlgoMerge = new SingleLinkedListAlgo(); $newList = $listAlgoMerge->mergerSortedList($listA, $listB); $newList->printListSimple(); echo '----------------------------------------------------------------'. PHP_EOL . PHP_EOL; echo '---------------------- 删除链表倒数第n个结点 ----------------------' . PHP_EOL . PHP_EOL; // 删除链表倒数第n个结点 $listDelete = new SingleLinkedList(); $listDelete->insert(1); $listDelete->insert(2); $listDelete->insert(3); $listDelete->insert(4); $listDelete->insert(5); $listDelete->insert(6); $listDelete->insert(7); $listDelete->printList(); $listAlgo->setList($listDelete); $listAlgo->deleteLastKth(3); var_dump($listAlgo->list->printListSimple()); echo '------------------------------------------------------------------'. PHP_EOL . PHP_EOL; echo '---------------------- 求链表的中间结点 ----------------------' . PHP_EOL . PHP_EOL; // 求链表的中间结点 $listAlgo->setList($list); $middleNode = $listAlgo->findMiddleNode(); var_dump($middleNode->data); echo '-------------------------------------------------------------'. PHP_EOL . PHP_EOL; ================================================ FILE: php/08_stack/.gitkeep ================================================ ================================================ FILE: php/08_stack/Compute.php ================================================ = 48 && ord($arr[$i]) <= 57){ array_push($numStack, $arr[$i]); continue; } switch ($arr[$i]){ case '+': case '-': $arrLen = count($operStack); while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){ compute($numStack, $operStack); $arrLen--; } array_push($operStack, $arr[$i]); break; case '*': $arrLen = count($operStack); while ($operStack[$arrLen-1] === '/'){ compute($numStack, $operStack); $arrLen--; } array_push($operStack, $arr[$i]); break; case '/': case '(': array_push($operStack, $arr[$i]); break; case ')': $arrLen = count($operStack); while ($operStack[$arrLen-1] !== '('){ compute($numStack, $operStack); $arrLen--; } array_pop($operStack); break; default: throw new \Exception("不支持的运算符", 1); break; } } $arrLen = count($operStack); while ($operStack[$arrLen-1] !== NULL){ compute($numStack, $operStack); $arrLen--; } echo array_pop($numStack); } //数字栈长度减一,运算符栈长度减一 function compute(&$numStack, &$operStack){ $num = array_pop($numStack); switch (array_pop($operStack)) { case '*': array_push($numStack, array_pop($numStack) * $num); break; case '/': array_push($numStack, array_pop($numStack) / $num); break; case '+': array_push($numStack, array_pop($numStack) + $num); break; case '-': array_push($numStack, array_pop($numStack) - $num); break; case '(': throw new \Exception("不匹配的(", 2); break; } } expression('-1+2-(1+2*3)'); echo PHP_EOL; eval('echo -1+2-(1+2*3);'); ================================================ FILE: php/08_stack/StackOnLinkedList.php ================================================ head = new SingleLinkedListNode(); $this->length = 0; } /** * 出栈 * * @return bool */ public function pop() { if (0 == $this->length) { return false; } $this->head->next = $this->head->next->next; $this->length--; return true; } /** * 入栈 * * @param $data * * @return SingleLinkedListNode|bool */ public function push($data) { return $this->pushData($data); } /** * 入栈 node * * @param SingleLinkedListNode $node * * @return bool */ public function pushNode(SingleLinkedListNode $node) { if (null == $node) { return false; } $node->next = $this->head->next; $this->head->next = $node; $this->length++; return true; } /** * 入栈 data * * @param $data * * @return SingleLinkedListNode|bool */ public function pushData($data) { $node = new SingleLinkedListNode($data); if (!$this->pushNode($node)) { return false; } return $node; } /** * 获取栈顶元素 * * @return SingleLinkedListNode|bool|null */ public function top() { if (0 == $this->length) { return false; } return $this->head->next; } /** * 打印栈 */ public function printSelf() { if (0 == $this->length) { echo 'empty stack' . PHP_EOL; return; } echo 'head.next -> '; $curNode = $this->head; while ($curNode->next) { echo $curNode->next->data . ' -> '; $curNode = $curNode->next; } echo 'NULL' . PHP_EOL; } /** * 获取栈长度 * * @return int */ public function getLength() { return $this->length; } /** * 判断栈是否为空 * * @return bool */ public function isEmpty() { return $this->length > 0 ? false : true; } } ================================================ FILE: php/08_stack/main.php ================================================ pushData(1); $stack->pushData(2); $stack->pushData(3); $stack->pushData(4); var_dump($stack->getLength()); $stack->printSelf(); $topNode = $stack->top(); var_dump($topNode->data); $stack->pop(); $stack->printSelf(); $stack->pop(); $stack->printSelf(); var_dump($stack->getLength()); $stack->pop(); $stack->pop(); $stack->printSelf(); ================================================ FILE: php/09_queue/QueueOnLinkedList.php ================================================ head = new SingleLinkedListNode(); $this->tail = $this->head; $this->length = 0; } /** * 入队 * * @param $data */ public function enqueue($data) { $newNode = new SingleLinkedListNode(); $newNode->data = $data; $this->tail->next = $newNode; $this->tail = $newNode; $this->length++; } /** * 出队 * * @return SingleLinkedListNode|bool|null */ public function dequeue() { if (0 == $this->length) { return false; } $node = $this->head->next; $this->head->next = $this->head->next->next; $this->length--; return $node; } /** * 获取队列长度 * * @return int */ public function getLength() { return $this->length; } /** * 打印队列 */ public function printSelf() { if (0 == $this->length) { echo 'empty queue' . PHP_EOL; return; } echo 'head.next -> '; $curNode = $this->head; while ($curNode->next) { echo $curNode->next->data . ' -> '; $curNode = $curNode->next; } echo 'NULL' . PHP_EOL; } } ================================================ FILE: php/09_queue/Sequential.php ================================================ MaxSzie = ++$size; } /** * 队列满条件 ($this->tail+1) % $this->MaxSzie == $this->head */ public function enQueue($data) { if (($this->tail+1) % $this->MaxSzie == $this->head) return -1; $this->data[$this->tail] = $data; $this->tail = (++$this->tail) % $this->MaxSzie; } public function deQueue() { if ($this->head == $this->tail) return NULL; $data = $this->data[$this->head]; unset($this->data[$this->head]); $this->head = (++$this->head) % $this->MaxSzie; return $data; } public function getLength() { return ($this->tail - $this->head + $this->MaxSzie) % $this->MaxSzie; } } $queue = new LoopQueue(4); // var_dump($queue); $queue->enQueue(1); $queue->enQueue(2); $queue->enQueue(3); $queue->enQueue(4); // $queue->enQueue(5); var_dump($queue->getLength()); $queue->deQueue(); $queue->deQueue(); $queue->deQueue(); $queue->deQueue(); $queue->deQueue(); var_dump($queue); ================================================ FILE: php/09_queue/main.php ================================================ enqueue(1); $queue->enqueue(2); $queue->enqueue(3); $queue->enqueue(4); $queue->enqueue(5); $queue->printSelf(); var_dump($queue->getLength()); $queue->dequeue(); $queue->printSelf(); $queue->dequeue(); $queue->dequeue(); $queue->dequeue(); $queue->printSelf(); $queue->dequeue(); $queue->printSelf(); ================================================ FILE: php/10_heap/Heap.php ================================================ size = $size; $this->heapType = $heapType; } /** * @param $data * 插入并堆化 */ public function insert($data) { if ($this->isFull()) { return false; } $this->dataArr[$this->count + 1] = $data; $this->count++; if ($this->heapType) { $this->bigHeapLast(); } else { $this->smallHeapLast(); } } /** * @return bool * 堆是否满 */ public function isFull() { if ($this->size == 0) { return false; } if ($this->count >= $this->size) { return true; } return false; } public function isEmpty(){ return empty($this->count)?true:false; } //返回堆顶的元素 public function peak(){ if($this->isEmpty()){ return null; } return $this->dataArr[1]; } //只插入 public function insertOnly($data) { if ($this->isFull()) { return false; } $this->dataArr[$this->count + 1] = $data; $this->count++; } /** * 删除堆顶的元素 * 把最后1个元素插入到堆顶 * 然后从堆顶开始堆化 * 返回堆化后的堆顶元素 */ public function deleteFirst() { $first = $this->dataArr[1]; $last = array_pop($this->dataArr); if($this->isEmpty()){ return null; } $this->count--; $i = 1; $this->dataArr[$i] = $last; if ($this->heapType) { $this->bigHeapFirst(); } else { $this->smallHeapFirst(); } return $first; } /** * 从某一个结点开始向下堆化 */ protected function heapFromOneToDown($i) { //大根堆 if ($this->heapType) { $maxPos = $i; while (true) { if (2 * $i <= $this->count) { if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i]) { $maxPos = 2 * $i; } } if (2 * $i + 1 <= $this->count) { if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) { $maxPos = 2 * $i + 1; } } //不需要交换 if ($i == $maxPos) { break; } $tmp = $this->dataArr[$maxPos]; $this->dataArr[$maxPos] = $this->dataArr[$i]; $this->dataArr[$i] = $tmp; //继续往下堆化 $i = $maxPos; } } else { //小根堆 $minPos = $i; while (true) { if (2 * $i <= $this->count) { if ($this->dataArr[$minPos] > $this->dataArr[2 * $i]) { $minPos = 2 * $i; } } if (2 * $i + 1 <= $this->count) { if ($this->dataArr[$minPos] > $this->dataArr[2 * $i + 1]) { $minPos = 2 * $i + 1; } } //不需要交换 if ($i == $minPos) { break; } $tmp = $this->dataArr[$minPos]; $this->dataArr[$minPos] = $this->dataArr[$i]; $this->dataArr[$i] = $tmp; //继续往下堆化 $i = $minPos; } } } /** * 对于1个完全不符合堆性质的 整体堆化 */ public function heapAll() { for ($i = intval($this->count / 2); $i >= 1; $i--) { $this->heapFromOneToDown($i); } } /** * 堆排序 * 把堆顶部的元素和数组尾部元素交换 */ public function heapSort() { $sorted = 0;//已经有序的个数 while ($sorted < $this->count) { $i = 1; $head = $this->dataArr[$i]; $this->dataArr[$i] = $this->dataArr[$this->count - $sorted]; $this->dataArr[$this->count - $sorted] = $head; $sorted++; while (true) { $maxPos = $i; if (2 * $i <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i]) { $maxPos = 2 * $i; } if (2 * $i + 1 <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) { $maxPos = 2 * $i + 1; } if ($i == $maxPos) { break; } $tmp = $this->dataArr[$i]; $this->dataArr[$i] = $this->dataArr[$maxPos]; $this->dataArr[$maxPos] = $tmp; $i = $maxPos; } } } /** *小顶堆 堆化 * 插入时 * 堆化最后1个元素 */ public function smallHeapLast() { $i = $this->count; while (true) { $smallPos = $i; $parent = intval($i / 2); if ($parent >= 1) { if ($this->dataArr[$smallPos] < $this->dataArr[$parent]) { $smallPos = $parent; } } if ($smallPos == $i) { break; } $tmp = $this->dataArr[$smallPos]; $this->dataArr[$smallPos] = $this->dataArr[$i]; $this->dataArr[$i] = $tmp; $i = $smallPos; } } /** * 小根堆 * 堆化根部元素(第一个元素) */ public function smallHeapFirst() { $i = 1; while (true) { $smallpos = $i; $left = 2 * $i; if ($left <= $this->count) { if ($this->dataArr[$smallpos] > $this->dataArr[$left]) { $smallpos = $left; } } $right = $left + 1; if ($right <= $this->count) { if ($this->dataArr[$smallpos] > $this->dataArr[$right]) { $smallpos = $right; } } if ($smallpos == $i) { break; } $tmp = $this->dataArr[$i]; $this->dataArr[$i] = $this->dataArr[$smallpos]; $this->dataArr[$smallpos] = $tmp; $i = $smallpos; } } /** * 大根堆 * 堆化根部元素(第一个元素) */ public function bigHeapFirst() { $i = 1; while (true) { $maxpos = $i; $left = 2 * $i; if ($left <= $this->count) { if ($this->dataArr[$maxpos] < $this->dataArr[$left]) { $maxpos = $left; } } $right = $left + 1; if ($right <= $this->count) { if ($this->dataArr[$maxpos] < $this->dataArr[$right]) { $maxpos = $right; } } if ($maxpos == $i) { break; } $tmp = $this->dataArr[$i]; $this->dataArr[$i] = $this->dataArr[$maxpos]; $this->dataArr[$maxpos] = $tmp; $i = $maxpos; } } //大根堆, 插入节点后放到数组最后面,然后从插入的节点自下而上开始堆化 //这里只堆化插入元素相关的节点(就是说,如果没插入这个元素,这个是一个堆) public function bigHeapLast() { $i = $this->count; while (intval($i / 2) > 0 && $this->dataArr[$i] > $this->dataArr[intval($i / 2)]) { $tmp = $this->dataArr[$i]; $this->dataArr[$i] = $this->dataArr[intval($i / 2)]; $this->dataArr[intval($i / 2)] = $tmp; $i = $i / 2; } } /** * @param $data */ public function topn($data) { //堆满了 if ($this->isFull()) { if ($data > $this->dataArr[1]) { $this->dataArr[1] = $data; $this->smallHeapFirst(); } } else { $this->dataArr[$this->count + 1] = $data; $this->count++; $this->smallHeapLast(); } return $this->dataArr[1]; } } ================================================ FILE: php/10_heap/findmiddle.php ================================================ $v) { if ($bigHeap->isEmpty()) { $bigHeap->insert($v); } else { $bigPeak = $bigHeap->peak(); if ($v < $bigPeak) { $bigHeap->insert($v); } else { $smallHeap->insert($v); } if ($bigHeap->count - $smallHeap->count > 1) { $bigPeak = $bigHeap->deleteFirst(); $smallHeap->insert($bigPeak); } elseif ($smallHeap->count - $bigHeap->count > 1) { $smallPeak = $smallHeap->deleteFirst(); $bigHeap->insert($smallPeak); } } //实时获取中位数 echo "现在的中位数为:".implode(',', midPeak($bigHeap, $smallHeap)) . PHP_EOL; } } function midPeak($heap1, $heap2) { if ($heap1->count == $heap2->count) { $midArr = [$heap1->peak(), $heap2->peak()]; } elseif ($heap2->count > $heap1->count) { $midArr = [$heap2->peak()]; } else { $midArr = [$heap1->peak()]; } return $midArr; } ================================================ FILE: php/10_heap/main.php ================================================ insert($v); } while(($r=$heap->deleteFirst())!==null){ echo $r." "; } echo PHP_EOL; $heap1=new Heap(10); foreach ($arr as $v){ $heap1->insertOnly($v); } $heap1->heapAll(); //堆化后的 print_r($heap1->dataArr); //堆排序 $heap1->heapSort(); print_r($heap1->dataArr); ================================================ FILE: php/10_heap/topn.php ================================================ ".$heap->topn($v).PHP_EOL; } ================================================ FILE: php/11_sort/Sort.php ================================================ $arr[$j + 1]) { $tmp = $arr[$j]; $arr[$j] = $arr[$j + 1]; $arr[$j + 1] = $tmp; $flag = true; } } if (!$flag) { break; } } } // 插入排序 function insertionSort(&$arr) { $n = count($arr); if ($n <= 1) return; for ($i = 1; $i < $n; ++$i) { $value = $arr[$i]; $j = $i - 1; // 查找插入的位置 for (; $j >= 0; --$j) { if ($arr[$j] > $value) { $arr[$j + 1] = $arr[$j]; // 数据移动 } else { break; } } $arr[$j + 1] = $value; // 插入数据 } } // 选择排序 function selectionSort(&$arr) { $length = count($arr); if ($length <= 1) return; for ($i = 0; $i < $length - 1; $i++) { //先假设最小的值的位置 $p = $i; for ($j = $i + 1; $j < $length; $j++) { if ($arr[$p] > $arr[$j]) { $p = $j; } } $tmp = $arr[$p]; $arr[$p] = $arr[$i]; $arr[$i] = $tmp; } } $arr = [1,4,6,2,3,5,4]; insertionSort($arr); var_dump($arr); ================================================ FILE: php/12_sort/mergeSort.php ================================================ = $r) { return [$arr[$r]]; } // 取 p 到 r 之间的中间位置 q $q = (int)(($p + $r) / 2); // 分治递归 $left = mergeSortRecursive($arr, $p, $q); $right = mergeSortRecursive($arr, $q + 1, $r); return merge($left, $right); } // 合并 function merge(array $left, array $right) { $tmp = []; $i = $j = 0; $leftLength = count($left); $rightLength = count($right); do { if ($left[$i] <= $right[$j]) { $tmp[] = $left[$i++]; } else { $tmp[] = $right[$j++]; } } while ($i < $leftLength && $j < $rightLength); $start = $i; $end = $leftLength; $copyArr = $left; // 判断哪个子数组中有剩余的数据 if ($j < $rightLength) { $start = $j; $end = $rightLength; $copyArr = $right; } // 将剩余的数据拷贝到临时数组 tmp do { $tmp[] = $copyArr[$start++]; } while ($start < $end); return $tmp; } ================================================ FILE: php/12_sort/quicksort.php ================================================ = $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); ================================================ FILE: php/13_sort/bucketSort.php ================================================ $value) { $index = ceil(($value-$min)/$length); $buckets[$index][] = $value; } $result = []; for($i=0;$i<$bucketNumber;$i++) { $bucket = $buckets[$i]; $length = count($bucket); //如果桶内元素为空,跳过这个桶 if($length == 0) { continue; } if( $length > 10) { $bucket = bucketSort($bucket); } quickSort($bucket,0,count($bucket)-1); $result = array_merge($result,$bucket); } return $result; } ================================================ FILE: php/13_sort/countingSort.php ================================================ $value) { $countScore[$value]++; } /** * 顺序求和 */ for($i=1;$i<=5;$i++) { $countScore[$i] += $countScore[$i-1]; } /** * 排序 */ foreach ($score as $key => $value) { $countScore[$value] --; $temp[$countScore[$value]] = $value; } //copy for($i=0;$i<$length;$i++) { $score[$i] = $temp[$i]; } return $score; } ================================================ FILE: php/13_sort/radixSort.php ================================================ toArray(); foreach ($numbers as $key => $value) { $index = ($value/$divisor)%10;//计算该数字在哪个桶中 $buckets[$index][] = $value; } /** * 从桶中取出数字 */ $k=0; for($i=0;$i<10;$i++) { while(count($buckets[$i]) > 0) { $numbers[$k++] = array_shift($buckets[$i]); } } } ================================================ FILE: php/15_binary/binary.php ================================================ $high) { return -1; } /** * notice2 mid计算 */ $mid = $low + (($high - $low) >> 1); if ($numbers[$mid] > $find) { //notice3 high值更新 return search($numbers, $low, $mid -1, $find); } elseif ($numbers[$mid] < $find) { //notice4 low值更新 return search($numbers, $mid + 1, $high, $find); } else { return $mid; } } /** * 求数字的平方根,保留6位小数 * @param [type] $number * * @return void * @date 2018/11/26 * @author yuanliandu */ function squareRoot($number) { if ($number < 0) { return -1; } elseif ($number < 1) { $min = $number; $max = 1; } else { $min = 1; $max = $number; } $mid = $min + ($max - $min) / 2; while (getDecimalPlaces($mid) < 6) { $square = $mid * $mid; if ($square > $number) { $max = $mid; } elseif ($square == $number) { return $mid; } else { $min = $mid; } $mid = $min + ($max - $min) / 2; } return $mid; } /** * 计算数字小数点后有几位数字 * @param [type] $number * * @return void * @date 2018/11/27 * @author yuanliandu */ function getDecimalPlaces($number) { $temp = explode('.', $number); if (isset($temp[1])) { return strlen($temp[1]); } return 0; } // 测试二分查找给定值 $numbers = [0, 1, 2, 3, 3, 4, 5, 6, 7, 9]; $find = 1; var_dump(binarySearch($numbers,$find)); //测试求平方根 var_dump(squareRoot(3)); ================================================ FILE: php/16_binary/binary.php ================================================ */ function findFirstEqual(array $numbers,$find) { $length = count($numbers); $low = 0; $high = $length - 1; while($low <= $high) { $mid = $low + (($high-$low)>>1); if($numbers[$mid] > $find) { $high = $mid - 1; }else if($numbers[$mid] < $find) { $low = $mid + 1; }else { /** * 如果是第一个元素,或之前一个元素不等于我们要找的值 * 我们就找到了第一个=find的element */ if($mid==0 || $numbers[$mid-1]!=$find) { return $mid; }else { $high = $mid - 1; } } } return -1; } /** * 找到最后一个=find的元素 * @param array $numbers * @param [type] $find * * @return void * @date 2018/11/27 * @author yuanliandu */ function findLastEqual(array $numbers,$find) { $length = count($numbers); $low = 0; $high = $length - 1; while($low <= $high) { $mid = $low + (($high-$low)>>1); if($numbers[$mid] > $find) { $high = $mid - 1; }else if($numbers[$mid] < $find) { $low = $mid + 1; }else { /** * 如果mid是最后一个元素的index * 或mid后一个元素!=我们要找的值 * 则找到了最后一个=find的value */ if($mid==$length-1 || $numbers[$mid+1]!=$find) { return $mid; }else { $low = $mid + 1; } } } return -1; } /** * 找到第一个大于等于find的元素 * @param array $numbers * @param [type] $find * * @return void * @date 2018/11/27 * @author yuanliandu */ function findFirstGreaterEqual(array $numbers,$find) { $length = count($numbers); $low = 0; $high = $length - 1; while($low <= $high) { $mid = $low + (($high-$low)>>1); if($numbers[$mid] >= $find) { if ($mid == 0 || $numbers[$mid-1] < $find) { return $mid; }else { $high = $mid - 1; } }else { $low = $mid + 1; } } return -1; } /** * 找到最后一个小于等于find的元素 * @param array $numbers * @param [type] $find * * @return void * @date 2018/11/27 * @author yuanliandu */ function findLastLessEqual(array $numbers,$find) { $length = count($numbers); $low = 0; $high = $length - 1; while($low <= $high) { $mid = $low + (($high-$low)>>1); if($numbers[$mid] <= $find) { if($mid==$length-1 || $numbers[$mid+1]> $find) { return $mid; } $low = $mid + 1; }else { $high = $mid - 1; } } return -1; } /** * 循环数组中找指定元素 * @param array $numbers * @param [type] $find * * @return void * @date 2018/11/27 * @author yuanliandu */ function searchCircularArray(array $numbers,$find) { $length = count($numbers); $low = 0; $high = $length - 1; while($low <= $high) { $mid = $low + (($high-$low)>>1); if($numbers[$mid] === $find) { return $mid; } if($numbers[$low] > $numbers[$mid]) { // 后半部分是有序数组 if(($numbers[$mid] < $find) && ($numbers[$high] >= $find)) { if($numbers[$high] === $find) return $high; //在后半个区间内 $low = $mid + 1; }else { $high = $mid - 1; } }else { // 前半部分是有序的 if(($numbers[$low] <= $find) && ($numbers[$mid] > $find)) { // 在有序区间内 if($numbers[$low] === $find) return $low; $high = $mid - 1; }else { $low = $mid + 1; } } } return -1; } /*** * 测试 */ $numbers = [1,2,3,3,3,4,5,6,8,11,13]; $find = 3; var_dump(findFirstEqual($numbers,$find));//找到第一个等于find的元素 var_dump(findFirstGreaterEqual($numbers,$find));//找到第一个大于等于find的元素 var_dump(findLastEqual($numbers,$find));//找到最后一个=find的元素 var_dump(findLastLessEqual($numbers,$find));//找到最后一个小于等于find的元素 //测试在循环数组中找到指定数字 $numbers = [9,10,1,2,3,4,5,6,7,8]; $find = 2; var_dump(searchCircularArray($numbers,$find)); ================================================ FILE: php/17_skiplist/skipList.php ================================================ data = $data; } //获取当前节点索引层数 public function getMaxLevel() { return count($this->next) - 1; } } class SkipList { //索引最大层数 public $indexLevel; //头节点 protected $head; public function __construct(int $indexLevel) { $this->indexLevel = max($indexLevel, 0); $this->head = new SNode(); } public function addData($data) { $newNode = new SNode($data); for ($level = $this->getRandomLevel(), $node = $this->head; $level >= 0; $level--) { while (isset($node->next[$level]) && $data < $node->next[$level]->data) { $node = $node->next[$level]; } if (isset($node->next[$level])) { $newNode->next[$level] = $node->next[$level]; } $node->next[$level] = $newNode; } return $newNode; } public function deleteData($data) { $deleted = false; for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) { while (isset($node->next[$level]) && $data < $node->next[$level]->data) { $node = $node->next[$level]; } if (isset($node->next[$level]) && $data == $node->next[$level]->data) { $node->next[$level] = isset($node->next[$level]->next[$level]) ? $node->next[$level]->next[$level] : null; $deleted = true; } } return $deleted; } public function findData($data) { for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) { while (isset($node->next[$level]) && $data < $node->next[$level]->data) { $node = $node->next[$level]; } if (isset($node->next[$level]) && $data == $node->next[$level]->data) { return $node->next[$level]; } } return false; } protected function getRandomLevel() { return mt_rand(0, $this->indexLevel); } } /** * 示例 */ $indexLevel = 2; $skipList = new SkipList($indexLevel); for ($i = 10; $i >= 0; $i--) { $skipList->addData($i); } //打印0到10组成的跳表 var_dump($skipList); //返回SNode对象 var_dump($skipList->findData(5)); $skipList->deleteData(5); //返回false var_dump($skipList->findData(5)); ================================================ FILE: php/24_tree/Tree.php ================================================ head = new TreeNode($headData); } } /** * 查找数据 * @param [type] $data [数据] * @return [type] [description] */ public function find($data) { if ($this->head == null) { return null; } $node = $this->head; while ($node != null) { if ($node->data == $data) { return $node; } elseif ($data > $node->data) { $node = $node->right; } else { $node = $node->left; } } return null; } /** * 插入数据 * @param [type] $data [数据] * @return [type] [description] */ public function insert($data) { if ($this->head == null) { $this->head = new TreeNode($data); return true; } $node = $this->head; while ($node != null) { if ($data > $node->data) { if ($node->right == null) { $node->right = new TreeNode($data); return true; } $node = $node->right; } else { if ($node->left == null) { $node->left = new TreeNode($data); return true; } $node = $node->left; } } } /** * 删除节点 * @param [type] $data [节点] * @return [type] [description] */ public function delete($data) { // 找到需要删除节点 $node = $this->head; $pnode = null; while ($node != null) { if ($node->data == $data) { break; } elseif ($data > $node->data) { $pnode = $node; $node = $node->right; } else { $pnode = $node; $node = $node->left; } } if ($node == null) { return false; } // 要删除的节点有两个子节点 // 查找右子树中最小节点 if ($node->left != null && $node->right != null) { $minPP = $node; $minP = $node->right; while ($minP->left != null) { $minPP = $minP; $minP = $minP->left; } $node->data = $minP->data; $node = $minP; // 删除掉右子树中的最小节点 $minPP->left = null; } if ($node->left != null) { $child = $node->left; } elseif ($node->right != null) { $child = $node->right; } else { $child = null; } if ($pnode == null) { // 删除的是根节点 $node = $child; } elseif ($pnode->left == $node) { $pnode->left = $child; } else { $pnode->right = $child; } } /** * 前序遍历 * @return [type] [description] */ public function preOrder($node) { if ($node == null) { return ; } echo $node->data . '->'; $this->preOrder($node->left); $this->preOrder($node->right); } /**中序遍历 * @param $node * */ public function inOrder($node){ if(empty($node)){ return; } $this->inOrder($node->left); echo $node->data . ' '; $this->inOrder($node->right); } /** * @param $node * 后续遍历 */ public function postOrder($node){ if(empty($node)){ return; } $this->postOrder($node->left); $this->postOrder($node->right); echo $node->data . ' '; } /** * @param $queue * @param int $index 从队列(数组)的那个位置开始处理 * 层级遍历 * 首先把节点放入数组,记录放入数组的根节点个数index,把节点的左右子放入数组 * 开始遍历数组queue(从index开始,子节点已经入队列的节点元素不再处理),把左右子节点放入queue,index++ * 持续上述过程,当节点没有子节点时,入队列过程结束,queue里节点的顺序即为层级遍历元素节点的顺序 * * 完全二叉树 */ public function levelOrder($queue, $index = 0) { for ($i = $index; $i < count($queue); $i++) { $node = $queue[$i]; if ($node->left) { $queue[] = $node->left; } else { return $queue; } if ($node->right) { $queue[] = $node->right; } else { return $queue; } $index++; } return $queue; } } ================================================ FILE: php/24_tree/TreeNode.php ================================================ data = $data; $this->left = null; $this->right = null; } } ================================================ FILE: php/24_tree/levelOrder.php ================================================ insert(16); $tree->insert(30); $tree->insert(12); $tree->insert(19); $tree->insert(10); $tree->insert(15); $tree->insert(18); $tree->insert(21); $tree->insert(38); $q=$tree->levelOrder([$tree->head]); foreach ($q as $n){ echo $n->data." "; } echo PHP_EOL; ================================================ FILE: php/24_tree/main.php ================================================ insert(20); $tree->insert(30); $tree->insert(40); $tree->insert(10); $tree->insert(21); $tree->insert(22); $tree->preOrder($tree->head); echo PHP_EOL; $tree->inOrder($tree->head); echo PHP_EOL; $tree->postOrder($tree->head); echo PHP_EOL; print_r($tree->find(30)); echo PHP_EOL; $tree->delete(30); $tree->preOrder($tree->head); echo PHP_EOL; ================================================ FILE: php/38_divide_and_conquer/matrix_production.php ================================================ &$row) { $data = array_shift($row); if (empty($data) || empty($row)) { unset($matrix[$key]); } $column[] = [$data]; } return $column; } function countProduction($row, $column) { for ($i = 0, $sum = 0; $i < count($row[0]); $i++) { $sum += $row[0][$i] * $column[$i][0]; } return $sum; } function merger($value1, $value2, $value3, $value4) { if (empty($value2) && empty($value3)) { return $value1; } else { $array12 = array_merge([$value1], !is_array($value2) ? [$value2] : $value2); if (!is_array($value3)) { $array34 = array_merge([$value3], !is_array($value4) ? [$value4] : $value4); return [$array12, $array34]; } else { for ($i = 0, $array34 = []; $i < count($value3); $i++) { $array34[] = array_merge($value3[$i], $value4[$i]); } return array_merge([$array12], $array34); } } } function matrixProduction($matrix1, $matrix2) { $row = popRow($matrix1); $column = popColumn($matrix2); if (empty($row) || empty($column)) { return []; } $value1 = countProduction($row, $column); $value2 = matrixProduction($row, $matrix2); $value3 = matrixProduction($matrix1, $column); $value4 = matrixProduction($matrix1, $matrix2); return merger($value1, $value2, $value3, $value4); } $matrix1 = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]; $matrix2 = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]; var_dump(matrixProduction($matrix1, $matrix2)); ================================================ FILE: php/39_backtracking/queens.php ================================================ printQueens(); return; } //每一行有8中放法 for($column = 0; $column < 8; $column++) { if ($this->isOk($row, $column)) { $this->result[$row] = $column; $this->cal8queens($row + 1); } } } //row行的column列是否合适 function isOk($row, $column) { $leftup = $column - 1; $rightdown = $column + 1; for ($i = $row - 1; $i >= 0; $i--) { //判断上一行的 column 列是否有值 if ($this->result[$i] == $column) { return false; } //左上角是否有值 if ($leftup >= 0 && $this->result[$i] == $leftup) { return false; } //右下角是否有值 if ($rightdown < 8 && $this->result[$i] == $rightdown) { return false; } $leftup--; $rightdown++; } return true; } //打印 function printQueens() { for ($row = 0; $row < 8; $row++) { for ($column = 0; $column < 8; $column++) { if ($this->result[$row] == $column) { echo 'Q'; } else { echo '*'; } } echo '
'; } } } $queen = new Queen(); $queen->cal8queens(0); ================================================ FILE: php/README.md ================================================ ## 数据结构与算法之美PHP实现 ### 项目运行 * 依赖composer自动加载,php目录下执行`composer dump-autoload` || `sh buildAutoLoad.sh` * 项目代码均在mac&php7环境下跑通 ### 项目实现 #### 06_linkedlist * 单链表php实现 * 回文判断 #### 07_linkedlist * reverse 单链表反转 * checkCircle 链表中环的检测 * mergerSortedList 两个有序的链表合并 * deleteLastKth 删除链表倒数第n个结点 * findMiddleNode 求链表的中间结点 #### 08_stack * 链栈实现 #### 09_stack * 队列链表实现 #### 10_heap * main 堆的基本操作,堆排序 * findmiddle 动态数据流求中位数 * topn 动态数据流求top k #### 24_tree * main 二叉树的基本操作 前中后序遍历 * levelOrder 二叉树的层级遍历 ================================================ FILE: php/Stack/Compute.php ================================================ = 48 && ord($arr[$i] <= 57)){ array_push($numStack, $arr[$i]); continue; } switch ($arr[$i]){ case '+': case '-': $arrLen = count($operStack); while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){ compute($numStack, $operStack); $arrLen--; } array_push($operStack, $arr[$i]); break; case '*': case '/': case '(': array_push($operStack, $arr[$i]); break; case ')': $arrLen = count($operStack); while ($operStack[$arrLen-1] !== '('){ compute($numStack, $operStack); $arrLen--; } array_pop($operStack); break; default: throw new \Exception("不支持的运算符", 1); break; } } $arrLen = count($operStack); while ($operStack[$arrLen-1] !== NULL){ compute($numStack, $operStack); $arrLen--; } echo array_pop($numStack); } //数字栈长度减一,运算符栈长度减一 function compute(&$numStack, &$operStack){ $num = array_pop($numStack); switch (array_pop($operStack)) { case '*': array_push($numStack, array_pop($numStack) * $num); break; case '/': array_push($numStack, array_pop($numStack) / $num); break; case '+': array_push($numStack, array_pop($numStack) + $num); break; case '-': array_push($numStack, array_pop($numStack) - $num); break; } } expression('-1+2-(1+2*3)'); echo PHP_EOL; eval('echo -1+2-(1+2*3);'); ================================================ FILE: php/buildAutoLoad.sh ================================================ #!/bin/bash composer dump-autoload ================================================ FILE: php/composer.json ================================================ { "name": "algo/php", "description": "数据结构与算法之美php实现", "type": "project", "require": {}, "autoload": { "psr-4": { "Algo_06\\": "06_linkedlist/", "Algo_07\\": "07_linkedlist/", "Algo_08\\": "08_stack/", "Algo_09\\": "09_queue/", "Algo_24\\": "24_tree/", "Algo_10\\": "10_heap/" } } } ================================================ FILE: python/.gitkeep ================================================ ================================================ FILE: python/05_array/.gitkeep ================================================ ================================================ FILE: python/05_array/myarray.py ================================================ # # 1) Insertion, deletion and random access of array # 2) Assumes int for element type # # Author: Wenru # class MyArray: """A simple wrapper around List. You cannot have -1 in the array. """ def __init__(self, capacity: int): self._data = [] self._capacity = capacity def __getitem__(self, position: int) -> object: return self._data[position] def __setitem__(self, index: int, value: object): self._data[index] = value def __len__(self) -> int: return len(self._data) def __iter__(self): for item in self._data: yield item def find(self, index: int) -> object: try: return self._data[index] except IndexError: return None def delete(self, index: int) -> bool: try: self._data.pop(index) return True except IndexError: return False def insert(self, index: int, value: int) -> bool: if len(self) >= self._capacity: return False else: return self._data.insert(index, value) def print_all(self): for item in self: print(item) def test_myarray(): array = MyArray(5) array.insert(0, 3) array.insert(0, 4) array.insert(1, 5) array.insert(3, 9) array.insert(3, 10) assert array.insert(0, 100) is False assert len(array) == 5 assert array.find(1) == 5 assert array.delete(4) is True array.print_all() if __name__ == "__main__": test_myarray() ================================================ FILE: python/06_linkedlist/.gitkeep ================================================ ================================================ FILE: python/06_linkedlist/LRUCache.py ================================================ # Definition for singly-linked list. class DbListNode(object): def __init__(self, x, y): self.key = x self.val = y self.next = None self.prev = None class LRUCache: ''' leet code: 146 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。 它应该支持以下操作: 获取数据 get 和 写入数据 put 。 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。 当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间 哈希表+双向链表 哈希表: 查询 O(1) 双向链表: 有序, 增删操作 O(1) Author: Ben ''' def __init__(self, capacity: int): self.cap = capacity self.hkeys = {} # self.top和self.tail作为哨兵节点, 避免越界 self.top = DbListNode(None, -1) self.tail = DbListNode(None, -1) self.top.next = self.tail self.tail.prev = self.top def get(self, key: int) -> int: if key in self.hkeys.keys(): # 更新结点顺序 cur = self.hkeys[key] # 跳出原位置 cur.next.prev = cur.prev cur.prev.next = cur.next # 最近用过的置于链表首部 top_node = self.top.next self.top.next = cur cur.prev = self.top cur.next = top_node top_node.prev = cur return self.hkeys[key].val return -1 def put(self, key: int, value: int) -> None: if key in self.hkeys.keys(): cur = self.hkeys[key] cur.val = value # 跳出原位置 cur.prev.next = cur.next cur.next.prev = cur.prev # 最近用过的置于链表首部 top_node = self.top.next self.top.next = cur cur.prev = self.top cur.next = top_node top_node.prev = cur else: # 增加新结点至首部 cur = DbListNode(key, value) self.hkeys[key] = cur # 最近用过的置于链表首部 top_node = self.top.next self.top.next = cur cur.prev = self.top cur.next = top_node top_node.prev = cur if len(self.hkeys.keys()) > self.cap: self.hkeys.pop(self.tail.prev.key) # 去掉原尾结点 self.tail.prev.prev.next = self.tail self.tail.prev = self.tail.prev.prev def __repr__(self): vals = [] p = self.top.next while p.next: vals.append(str(p.val)) p = p.next return '->'.join(vals) if __name__ == '__main__': cache = LRUCache(2) cache.put(1, 1) cache.put(2, 2) print(cache) cache.get(1) # 返回 1 cache.put(3, 3) # 该操作会使得密钥 2 作废 print(cache) cache.get(2) # 返回 -1 (未找到) cache.put(4, 4) # 该操作会使得密钥 1 作废 print(cache) cache.get(1) # 返回 -1 (未找到) cache.get(3) # 返回 3 print(cache) cache.get(4) # 返回 4 print(cache) ================================================ FILE: python/06_linkedlist/palindrome.py ================================================ """ check a single-linked list whether a palindrome """ import sys # 引用当前文件夹下的single_linked_list sys.path.append('singly_linked_list') from singly_linked_list import SinglyLinkedList def reverse(head): reverse_head = None while head: next = head._next head._next = reverse_head reverse_head = head head = next return reverse_head def is_palindrome(l): l.print_all() slow = l._head fast = l._head position = 0 while fast and fast._next: slow = slow._next fast = fast._next._next position += 1 reverse_node = reverse(slow) head_node = l._head is_palin = True while (head_node and reverse_node): if (head_node.data == reverse_node.data): head_node = head_node._next reverse_node = reverse_node._next else: is_palin = False break return is_palin if __name__ == '__main__': # the result should be False, True, True, True, True test_str_arr = ['ab', 'aa', 'aba', 'abba', 'abcba'] for str in test_str_arr: l = SinglyLinkedList() for i in str: l.insert_value_to_head(i) print(is_palindrome(l)) ================================================ FILE: python/06_linkedlist/singlyLinkedList.py ================================================ # 1.单链表的插入、删除、查找操作; # 2.链表中存储的数据类型是Int # # Author:Lee class Node(object): """链表结构的Node节点""" def __init__(self, data, next_node=None): """Node节点的初始化方法. 参数: data:存储的数据 next:下一个Node节点的引用地址 """ self.__data = data self.__next = next_node @property def data(self): """Node节点存储数据的获取. 返回: 当前Node节点存储的数据 """ return self.__data @data.setter def data(self, data): """Node节点存储数据的设置方法. 参数: data:新的存储数据 """ self.__data = data @property def next_node(self): """获取Node节点的next指针值. 返回: next指针数据 """ return self.__next @next_node.setter def next_node(self, next_node): """Node节点next指针的修改方法. 参数: next:新的下一个Node节点的引用 """ self.__next = next_node class SinglyLinkedList(object): """单向链表""" def __init__(self): """单向列表的初始化方法.""" self.__head = None def find_by_value(self, value): """按照数据值在单向列表中查找. 参数: value:查找的数据 返回: Node """ node = self.__head while (node is not None) and (node.data != value): node = node.next_node return node def find_by_index(self, index): """按照索引值在列表中查找. 参数: index:索引值 返回: Node """ node = self.__head pos = 0 while (node is not None) and (pos != index): node = node.next_node pos += 1 return node def insert_to_head(self, value): """在链表的头部插入一个存储value数值的Node节点. 参数: value:将要存储的数据 """ node = Node(value) node.next_node = self.__head self.__head = node def insert_after(self, node, value): """在链表的某个指定Node节点之后插入一个存储value数据的Node节点. 参数: node:指定的一个Node节点 value:将要存储在新Node节点中的数据 """ if node is None: # 如果指定在一个空节点之后插入数据节点,则什么都不做 return new_node = Node(value) new_node.next_node = node.next node.next = new_node def insert_before(self, node, value): """在链表的某个指定Node节点之前插入一个存储value数据的Node节点. 参数: node:指定的一个Node节点 value:将要存储在新的Node节点中的数据 """ if (node is None) or (self.__head is None): # 如果指定在一个空节点之前或者空链表之前插入数据节点,则什么都不做 return if node == self.__head: # 如果是在链表头之前插入数据节点,则直接插入 self.insert_to_head(value) return new_node = Node(value) pro = self.__head not_found = False # 如果在整个链表中都没有找到指定插入的Node节点,则该标记量设置为True while pro.next_node != node: # 寻找指定Node之前的一个Node if pro.next_node is None: # 如果已经到了链表的最后一个节点,则表明该链表中没有找到指定插入的Node节点 not_found = True break else: pro = pro.next_node if not not_found: pro.next_node = new_node new_node.next_node = node def delete_by_node(self, node): """在链表中删除指定Node的节点. 参数: node:指定的Node节点 """ if self.__head is None: # 如果链表是空的,则什么都不做 return if node == self.__head: # 如果指定删除的Node节点是链表的头节点 self.__head = node.next_node return pro = self.__head not_found = False # 如果在整个链表中都没有找到指定删除的Node节点,则该标记量设置为True while pro.next_node != node: if pro.next_node is None: # 如果已经到链表的最后一个节点,则表明该链表中没有找到指定删除的Node节点 not_found = True break else: pro = pro.next_node if not not_found: pro.next_node = node.next_node def delete_by_value(self, value): """在链表中删除指定存储数据的Node节点. 参数: value:指定的存储数据 """ if self.__head is None: # 如果链表是空的,则什么都不做 return if self.__head.data == value: # 如果链表的头Node节点就是指定删除的Node节点 self.__head = self.__head.next_node pro = self.__head node = self.__head.next_node not_found = False while node.data != value: if node.next_node is None: # 如果已经到链表的最后一个节点,则表明该链表中没有找到执行Value值的Node节点 not_found = True break else: pro = node node = node.next_node if not_found is False: pro.next_node = node.next_node def delete_last_n_node(self, n): """删除链表中倒数第N个节点. 主体思路: 设置快、慢两个指针,快指针先行,慢指针不动;当快指针跨了N步以后,快、慢指针同时往链表尾部移动, 当快指针到达链表尾部的时候,慢指针所指向的就是链表的倒数第N个节点 参数: n:需要删除的倒数第N个序数 """ fast = self.__head slow = self.__head step = 0 while step <= n: fast = fast.next_node step += 1 while fast.next_node is not None: tmp = slow fast = fast.next_node slow = slow.next_node tmp.next_node = slow.next_node def find_mid_node(self): """查找链表中的中间节点. 主体思想: 设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,则当快指针到达链表尾部的时候,慢指针指向链表的中间节点 返回: node:链表的中间节点 """ fast = self.__head slow = self.__head while fast.next_node is not None: fast = fast.next_node.next_node slow = slow.next_node return slow def create_node(self, value): """创建一个存储value值的Node节点. 参数: value:将要存储在Node节点中的数据 返回: 一个新的Node节点 """ return Node(value) def print_all(self): """打印当前链表所有节点数据.""" pos = self.__head if pos is None: print("当前链表还没有数据") return while pos.next_node is not None: print(str(pos.data) + " --> ", end="") pos = pos.next_node print(str(pos.data)) def reversed_self(self): """翻转链表自身.""" if self.__head is None or self.__head.next is None: # 如果链表为空,或者链表只有一个节点 return pre = self.__head node = self.__head.next while node is not None: pre, node = self.__reversed_with_two_node(pre, node) self.__head.next = None self.__head = pre def __reversed_with_two_node(self, pre, node): """翻转相邻两个节点. 参数: pre:前一个节点 node:当前节点 返回: (pre,node):下一个相邻节点的元组 """ tmp = node.next_node node.next_node = pre pre = node # 这样写有点啰嗦,但是能让人更能看明白 node = tmp return pre, node def has_ring(self): """检查链表中是否有环. 主体思想: 设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,如果快指针没有与慢指针相遇而是顺利到达链表尾部 说明没有环;否则,存在环 返回: True:有环 False:没有环 """ fast = self.__head slow = self.__head while (fast.next_node is not None) and (fast is not None): fast = fast.next_node slow = slow.next_node if fast == slow: return True return False ================================================ FILE: python/06_linkedlist/singly_linked_list.py ================================================ """ 1) Insertion, deletion and search of singly-linked list; 2) Assumes int type for data in list nodes. Author: Wenru """ from typing import Optional class Node: def __init__(self, data: int, next_node=None): self.data = data self._next = next_node class SinglyLinkedList: def __init__(self): self._head = None def find_by_value(self, value: int) -> Optional[Node]: p = self._head while p and p.data != value: p = p._next return p def find_by_index(self, index: int) -> Optional[Node]: p = self._head position = 0 while p and position != index: p = p._next position += 1 return p def insert_value_to_head(self, value: int): new_node = Node(value) self.insert_node_to_head(new_node) def insert_node_to_head(self, new_node: Node): if new_node: new_node._next = self._head self._head = new_node def insert_value_after(self, node: Node, value: int): new_node = Node(value) self.insert_node_after(node, new_node) def insert_node_after(self, node: Node, new_node: Node): if not node or not new_node: return new_node._next = node._next node._next = new_node def insert_value_before(self, node: Node, value: int): new_node = Node(value) self.insert_node_before(node, new_node) def insert_node_before(self, node: Node, new_node: Node): if not self._head or not node or not new_node: return if self._head == node: self.insert_node_to_head(new_node) return current = self._head while current._next and current._next != node: current = current._next if not current._next: # node is not even in the list return new_node._next = node current._next = new_node def delete_by_node(self, node: Node): if not self._head or not node: return if node._next: node.data = node._next.data node._next = node._next._next return # node is the last one or not in the list current = self._head while current and current._next != node: current = current._next if not current: # node not in the list return current._next = node._next def delete_by_value(self, value: int): if not self._head or not value: return fake_head = Node(value + 1) fake_head._next = self._head prev, current = fake_head, self._head while current: if current.data != value: prev._next = current prev = prev._next current = current._next if prev._next: prev._next = None self._head = fake_head._next # in case head.data == value def __repr__(self) -> str: nums = [] current = self._head while current: nums.append(current.data) current = current._next return "->".join(str(num) for num in nums) # 重写__iter__方法,方便for关键字调用打印值 def __iter__(self): node = self._head while node: yield node.data node = node._next def print_all(self): current = self._head if current: print(f"{current.data}", end="") current = current._next while current: print(f"->{current.data}", end="") current = current._next print("\n", flush=True) if __name__ == "__main__": l = SinglyLinkedList() for i in range(15): l.insert_value_to_head(i) node9 = l.find_by_value(9) l.insert_value_before(node9, 20) l.insert_value_before(node9, 16) l.insert_value_before(node9, 16) l.delete_by_value(16) node11 = l.find_by_index(3) l.delete_by_node(node11) l.delete_by_node(l._head) l.delete_by_value(13) print(l) for value in l: print(value) ================================================ FILE: python/07_linkedlist/.gitkeep ================================================ ================================================ FILE: python/07_linkedlist/linked_list_algo.py ================================================ """ 1) Reverse singly-linked list 2) Detect cycle in a list 3) Merge two sorted lists 4) Remove nth node from the end 5) Find middle node Author: Wenru """ from typing import Optional class Node: def __init__(self, data: int, next=None): self.data = data self._next = next # Reverse singly-linked list # 单链表反转 # Note that the input is assumed to be a Node, not a linked list. def reverse(head: Node) -> Optional[Node]: reversed_head = None current = head while current: reversed_head, reversed_head._next, current = current, reversed_head, current._next return reversed_head # Detect cycle in a list # 检测环 def has_cycle(head: Node) -> bool: slow, fast = head, head while fast and fast._next: slow = slow._next fast = fast._next._next if slow == fast: return True return False # Merge two sorted linked list # 有序链表合并 def merge_sorted_list(l1: Node, l2: Node) -> Optional[Node]: if l1 and l2: p1, p2 = l1, l2 fake_head = Node(None) current = fake_head while p1 and p2: if p1.data <= p2.data: current._next = p1 p1 = p1._next else: current._next = p2 p2 = p2._next current = current._next current._next = p1 if p1 else p2 return fake_head._next return l1 or l2 # Remove nth node from the end # 删除倒数第n个节点。假设n大于0 def remove_nth_from_end(head: Node, n: int) -> Optional[Node]: fast = head count = 0 while fast and count < n: fast = fast._next count += 1 if not fast and count < n: # not that many nodes return head if not fast and count == n: return head._next slow = head while fast._next: fast, slow = fast._next, slow._next slow._next = slow._next._next return head def find_middle_node(head: Node) -> Optional[Node]: slow, fast = head, head fast = fast._next if fast else None while fast and fast._next: slow, fast = slow._next, fast._next._next return slow def print_all(head: Node): nums = [] current = head while current: nums.append(current.data) current = current._next print("->".join(str(num) for num in nums)) ================================================ FILE: python/08_stack/linked_stack.py ================================================ """ Stack based upon linked list 基于链表实现的栈 Author: Wenru """ from typing import Optional class Node: def __init__(self, data: int, next=None): self._data = data self._next = next class LinkedStack: """A stack based upon singly-linked list. """ def __init__(self): self._top: Node = None def push(self, value: int): new_top = Node(value) new_top._next = self._top self._top = new_top def pop(self) -> Optional[int]: if self._top: value = self._top._data self._top = self._top._next return value def __repr__(self) -> str: current = self._top nums = [] while current: nums.append(current._data) current = current._next return " ".join(f"{num}]" for num in nums) if __name__ == "__main__": stack = LinkedStack() for i in range(9): stack.push(i) print(stack) for _ in range(3): stack.pop() print(stack) ================================================ FILE: python/08_stack/simple_browser.py ================================================ """ 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() ================================================ FILE: python/09_queue/array_queue.py ================================================ """ Queue based upon array 用数组实现的队列 Author: Wenru """ from typing import Optional class ArrayQueue: def __init__(self, capacity: int): self._items = [] self._capacity = capacity self._head = 0 self._tail = 0 def enqueue(self, item: str) -> bool: if self._tail == self._capacity: if self._head == 0: return False else: for i in range(0, self._tail - self._head): self._items[i] = self._items[i + self._head] self._tail = self._tail - self._head self._head = 0 self._items.insert(self._tail, item) self._tail += 1 return True def dequeue(self) -> Optional[str]: if self._head != self._tail: item = self._items[self._head] self._head += 1 return item else: return None def __repr__(self) -> str: return " ".join(item for item in self._items[self._head : self._tail]) ================================================ FILE: python/09_queue/circular_queue.py ================================================ """ Author: Wenru """ from typing import Optional from itertools import chain class CircularQueue: def __init__(self, capacity): self._items = [] self._capacity = capacity + 1 self._head = 0 self._tail = 0 def enqueue(self, item: str) -> bool: if (self._tail + 1) % self._capacity == self._head: return False self._items.append(item) self._tail = (self._tail + 1) % self._capacity return True def dequeue(self) -> Optional[str]: if self._head != self._tail: item = self._items[self._head] self._head = (self._head + 1) % self._capacity return item def __repr__(self) -> str: if self._tail >= self._head: return " ".join(item for item in self._items[self._head : self._tail]) else: return " ".join(item for item in chain(self._items[self._head:], self._items[:self._tail])) if __name__ == "__main__": q = CircularQueue(5) for i in range(5): q.enqueue(str(i)) q.dequeue() q.dequeue() q.enqueue(str(5)) print(q) ================================================ FILE: python/09_queue/dynamic_array_queue.py ================================================ """ Author: Wenru """ from typing import Optional class DynamicArrayQueue: def __init__(self, capacity: int): self._items = [] self._capacity = capacity self._head = 0 self._tail = 0 def enqueue(self, item: str) -> bool: if self._tail == self._capacity: if self._head == 0: return False self._items[0 : self._tail - self._head] = self._items[self._head : self._tail] self._tail -= self._head self._head = 0 if self._tail == len(self._items): self._items.append(item) else: self._items[self._tail] = item self._tail += 1 return True def dequeue(self) -> Optional[str]: if self._head != self._tail: item = self._items[self._head] self._head += 1 return item def __repr__(self) -> str: return " ".join(item for item in self._items[self._head:self._tail]) if __name__ == "__main__": q = DynamicArrayQueue(10) for i in range(10): q.enqueue(str(i)) print(q) for _ in range(3): q.dequeue() print(q) q.enqueue("7") q.enqueue("8") print(q) ================================================ FILE: python/09_queue/linked_queue.py ================================================ """ Queue based upon linked list Author: Wenru """ from typing import Optional class Node: def __init__(self, data: str, next=None): self.data = data self._next = next class LinkedQueue: def __init__(self): self._head: Optional[Node] = None self._tail: Optional[Node] = None def enqueue(self, value: str): new_node = Node(value) if self._tail: self._tail._next = new_node else: self._head = new_node self._tail = new_node def dequeue(self) -> Optional[str]: if self._head: value = self._head.data self._head = self._head._next if not self._head: self._tail = None return value def __repr__(self) -> str: values = [] current = self._head while current: values.append(current.data) current = current._next return "->".join(value for value in values) if __name__ == "__main__": q = LinkedQueue() for i in range(10): q.enqueue(str(i)) print(q) for _ in range(3): q.dequeue() print(q) q.enqueue("7") q.enqueue("8") print(q) ================================================ FILE: python/11_sorts/sorts.py ================================================ """ Bubble sort, insertion sort and selection sort 冒泡排序、插入排序、选择排序 Author: Wenru """ from typing import List # 冒泡排序 def bubble_sort(a: List[int]): length = len(a) if length <= 1: return for i in range(length): made_swap = False for j in range(length - i - 1): if a[j] > a[j + 1]: a[j], a[j + 1] = a[j + 1], a[j] made_swap = True if not made_swap: break # 插入排序 def insertion_sort(a: List[int]): length = len(a) if length <= 1: return for i in range(1, length): value = a[i] j = i - 1 while j >= 0 and a[j] > value: a[j + 1] = a[j] j -= 1 a[j + 1] = value # 选择排序 def selection_sort(a: List[int]): length = len(a) if length <= 1: return for i in range(length): min_index = i min_val = a[i] for j in range(i, length): if a[j] < min_val: min_val = a[j] min_index = j a[i], a[min_index] = a[min_index], a[i] def test_bubble_sort(): test_array = [1, 1, 1, 1] bubble_sort(test_array) assert test_array == [1, 1, 1, 1] test_array = [4, 1, 2, 3] bubble_sort(test_array) assert test_array == [1, 2, 3, 4] test_array = [4, 3, 2, 1] bubble_sort(test_array) assert test_array == [1, 2, 3, 4] def test_insertion_sort(): test_array = [1, 1, 1, 1] insertion_sort(test_array) assert test_array == [1, 1, 1, 1] test_array = [4, 1, 2, 3] insertion_sort(test_array) assert test_array == [1, 2, 3, 4] test_array = [4, 3, 2, 1] insertion_sort(test_array) assert test_array == [1, 2, 3, 4] def test_selection_sort(): test_array = [1, 1, 1, 1] selection_sort(test_array) assert test_array == [1, 1, 1, 1] test_array = [4, 1, 2, 3] selection_sort(test_array) assert test_array == [1, 2, 3, 4] test_array = [4, 3, 2, 1] selection_sort(test_array) assert test_array == [1, 2, 3, 4] if __name__ == "__main__": array = [5, 6, -1, 4, 2, 8, 10, 7, 6] bubble_sort(array) print(array) array = [5, 6, -1, 4, 2, 8, 10, 7, 6] insertion_sort(array) print(array) array = [5, 6, -1, 4, 2, 8, 10, 7, 6] selection_sort(array) print(array) ================================================ FILE: python/12_sorts/merge_sort.py ================================================ """ Author: Wenru """ from typing import List def merge_sort(a: List[int]): _merge_sort_between(a, 0, len(a) - 1) def _merge_sort_between(a: List[int], low: int, high: int): # The indices are inclusive for both low and high. if low < high: mid = low + (high - low) // 2 _merge_sort_between(a, low, mid) _merge_sort_between(a, mid + 1, high) _merge(a, low, mid, high) def _merge(a: List[int], low: int, mid: int, high: int): # a[low:mid], a[mid+1, high] are sorted. i, j = low, mid + 1 tmp = [] while i <= mid and j <= high: if a[i] <= a[j]: tmp.append(a[i]) i += 1 else: tmp.append(a[j]) j += 1 start = i if i <= mid else j end = mid if i <= mid else high tmp.extend(a[start:end + 1]) a[low:high + 1] = tmp def test_merge_sort(): a1 = [3, 5, 6, 7, 8] merge_sort(a1) assert a1 == [3, 5, 6, 7, 8] a2 = [2, 2, 2, 2] merge_sort(a2) assert a2 == [2, 2, 2, 2] a3 = [4, 3, 2, 1] merge_sort(a3) assert a3 == [1, 2, 3, 4] a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] merge_sort(a4) assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9] if __name__ == "__main__": a1 = [3, 5, 6, 7, 8] a2 = [2, 2, 2, 2] a3 = [4, 3, 2, 1] a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] merge_sort(a1) print(a1) merge_sort(a2) print(a2) merge_sort(a3) print(a3) merge_sort(a4) print(a4) ================================================ FILE: python/12_sorts/quick_sort.py ================================================ """ Author: Wenru """ from typing import List import random def quick_sort(a: List[int]): _quick_sort_between(a, 0, len(a) - 1) def _quick_sort_between(a: List[int], low: int, high: int): if low < high: # get a random position as the pivot k = random.randint(low, high) a[low], a[k] = a[k], a[low] m = _partition(a, low, high) # a[m] is in final position _quick_sort_between(a, low, m - 1) _quick_sort_between(a, m + 1, high) def _partition(a: List[int], low: int, high: int): pivot, j = a[low], low for i in range(low + 1, high + 1): if a[i] <= pivot: j += 1 a[j], a[i] = a[i], a[j] # swap a[low], a[j] = a[j], a[low] return j def test_quick_sort(): a1 = [3, 5, 6, 7, 8] quick_sort(a1) assert a1 == [3, 5, 6, 7, 8] a2 = [2, 2, 2, 2] quick_sort(a2) assert a2 == [2, 2, 2, 2] a3 = [4, 3, 2, 1] quick_sort(a3) assert a3 == [1, 2, 3, 4] a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] quick_sort(a4) assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9] if __name__ == "__main__": a1 = [3, 5, 6, 7, 8] a2 = [2, 2, 2, 2] a3 = [4, 3, 2, 1] a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] quick_sort(a1) print(a1) quick_sort(a2) print(a2) quick_sort(a3) print(a3) quick_sort(a4) print(a4) ================================================ FILE: python/12_sorts/quicksort_twoway.py ================================================ import random def QuickSort(arr): # 双向排序: 提高非随机输入的性能 # 不需要额外的空间,在待排序数组本身内部进行排序 # 基准值通过random随机选取 # 入参: 待排序数组, 数组开始索引 0, 数组结束索引 len(array)-1 if arr is None or len(arr) < 1: return arr def swap(arr, low, upper): tmp = arr[low] arr[low] = arr[upper] arr[upper] = tmp return arr def QuickSort_TwoWay(arr, low, upper): # 小数组排序i可以用插入或选择排序 # if upper-low < 50 : return arr # 基线条件: low index = upper index; 也就是只有一个值的区间 if low >= upper: return arr # 随机选取基准值, 并将基准值替换到数组第一个元素 swap(arr, low, int(random.uniform(low, upper))) temp = arr[low] # 缓存边界值, 从上下边界同时排序 i, j = low, upper while True: # 第一个元素是基准值,所以要跳过 i += 1 # 在小区间中, 进行排序 # 从下边界开始寻找大于基准值的索引 while i <= upper and arr[i] <= temp: i += 1 # 从上边界开始寻找小于基准值的索引 # 因为j肯定大于i, 所以索引值肯定在小区间中 while arr[j] > temp: j -= 1 # 如果小索引大于等于大索引, 说明排序完成, 退出排序 if i >= j: break swap(arr, i, j) # 将基准值的索引从下边界调换到索引分割点 swap(arr, low, j) QuickSort_TwoWay(arr, low, j - 1) QuickSort_TwoWay(arr, j + 1, upper) return arr return QuickSort_TwoWay(arr, 0, len(arr) - 1) if __name__ == "__main__": a1 = [3, 5, 6, 7, 8] a2 = [2, 2, 2, 2] a3 = [4, 3, 2, 1] a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] QuickSort(a1) print(a1) QuickSort(a2) print(a2) QuickSort(a3) print(a3) QuickSort(a4) print(a4) ================================================ FILE: python/14_sorts/counting_sort.py ================================================ """ 计数排序 Author: Wenru """ from typing import List import itertools def counting_sort(a: List[int]): if len(a) <= 1: return # a中有counts[i]个数不大于i counts = [0] * (max(a) + 1) for num in a: counts[num] += 1 counts = list(itertools.accumulate(counts)) # 临时数组,储存排序之后的结果 a_sorted = [0] * len(a) for num in reversed(a): index = counts[num] - 1 a_sorted[index] = num counts[num] -= 1 a[:] = a_sorted if __name__ == "__main__": a1 = [1, 2, 3, 4] counting_sort(a1) print(a1) a2 = [1, 1, 1, 1] counting_sort(a2) print(a2) a3 = [4, 5, 0, 9, 3, 3, 1, 9, 8, 7] counting_sort(a3) print(a3) ================================================ FILE: python/15_bsearch/bsearch.py ================================================ """ Author: Wenru """ from typing import List def bsearch(nums: List[int], target: int) -> int: """Binary search of a target in a sorted array without duplicates. If such a target does not exist, return -1, othewise, return its index. """ low, high = 0, len(nums) - 1 while low <= high: mid = low + (high - low) // 2 if nums[mid] == target: return mid elif nums[mid] < target: low = mid + 1 else: high = mid - 1 return -1 ================================================ FILE: python/15_bsearch/bsearch_recursion.py ================================================ """ Author: dreamkong """ from typing import List def bsearch(nums: List[int], target: int) -> int: return bsearch_internally(nums, 0, len(nums)-1, target) def bsearch_internally(nums: List[int], low: int, high: int, target: int) -> int: if low > high: return -1 mid = low+int((high-low) >> 2) if nums[mid] == target: return mid elif nums[mid] < target: return bsearch_internally(nums, mid+1, high, target) else: return bsearch_internally(nums, low, mid-1, target) ================================================ FILE: python/16_bsearch/bsearch_variants.py ================================================ """ Author: Wenru Fix: nzjia """ from typing import List def bsearch_left(nums: List[int], target: int) -> int: """Binary search of the index of the first element equal to a given target in the ascending sorted array. If not found, return -1. """ low, high = 0, len(nums) - 1 while low <= high: mid = low + (high - low) // 2 if nums[mid] < target: low = mid + 1 else: high = mid - 1 if low < len(nums) and nums[low] == target: return low else: return -1 def bsearch_right(nums: List[int], target: int) -> int: """Binary search of the index of the last element equal to a given target in the ascending sorted array. If not found, return -1. """ low, high = 0, len(nums) - 1 while low <= high: mid = low + (high - low) // 2 if nums[mid] <= target: low = mid + 1 else: high = mid - 1 if high >= 0 and nums[high] == target: return high else: return -1 def bsearch_left_not_less(nums: List[int], target: int) -> int: """Binary search of the index of the first element not less than a given target in the ascending sorted array. If not found, return -1. """ low, high = 0, len(nums) - 1 while low <= high: mid = low + (high - low) // 2 if nums[mid] < target: low = mid + 1 else: high = mid - 1 if low < len(nums) and nums[low] >= target: return low else: return -1 def bsearch_right_not_greater(nums: List[int], target: int) -> int: """Binary search of the index of the last element not greater than a given target in the ascending sorted array. If not found, return -1. """ low, high = 0, len(nums) - 1 while low <= high: mid = low + (high - low) // 2 if nums[mid] <= target: low = mid + 1 else: high = mid - 1 if high >= 0 and nums[high] <= target: return high else: return -1 if __name__ == "__main__": a = [1, 1, 2, 3, 4, 6, 7, 7, 7, 7, 10, 22] print(bsearch_left(a, 0) == -1) print(bsearch_left(a, 7) == 6) print(bsearch_left(a, 30) == -1) print(bsearch_right(a, 0) == -1) print(bsearch_right(a, 7) == 9) print(bsearch_right(a, 30) == -1) print(bsearch_left_not_less(a, 0) == 0) print(bsearch_left_not_less(a, 5) == 5) print(bsearch_left_not_less(a, 30) == -1) print(bsearch_right_not_greater(a, 0) == -1) print(bsearch_right_not_greater(a, 6) == 5) print(bsearch_right_not_greater(a, 30) == 11) ================================================ FILE: python/17_skiplist/skip_list.py ================================================ """ 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) ================================================ FILE: python/17_skiplist/skip_list_comments.py ================================================ import random class SkipListNode(object): def __init__(self, val, high=1): # 节点存储的值 self.data = val # 节点对应索引层的深度 self.deeps = [None] * high class SkipList(object): """ An implementation of skip list. The list stores positive integers without duplicates. 跳表的一种实现方法。 跳表中储存的是正整数,并且储存的是不重复的。 Author: Ben """ def __init__(self): # 索引层的最大深度 self.__MAX_LEVEL = 16 # 跳表的高度 self._high = 1 # 每一索引层的首节点, 默认值为None self._head = SkipListNode(None, self.__MAX_LEVEL) def find(self, val): cur = self._head # 从索引的顶层, 逐层定位要查找的值 # 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点 for i in range(self._high - 1, -1, -1): # 同一索引层内, 查找小于插入值的最大值对应的节点 while cur.deeps[i] and cur.deeps[i].data < val: cur = cur.deeps[i] if cur.deeps[0] and cur.deeps[0].data == val: return cur.deeps[0] return None def insert(self, val): ''' 新增时, 通过随机函数获取要更新的索引层数, 要对低于给定高度的索引层添加新结点的指针 ''' high = self.randomLevel() if self._high < high: self._high = high # 申请新结点 newNode = SkipListNode(val, high) # cache用来缓存对应索引层中小于插入值的最大节点 cache = [self._head] * high cur = self._head # 在低于随机高度的每一个索引层寻找小于插入值的节点 for i in range(high - 1, -1, -1): # 每个索引层内寻找小于带插入值的节点 # ! 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点 while cur.deeps[i] and cur.deeps[i].data < val: cur = cur.deeps[i] cache[i] = cur # 在小于高度的每个索引层中插入新结点 for i in range(high): # new.next = prev.next \ prev.next = new.next newNode.deeps[i] = cache[i].deeps[i] cache[i].deeps[i] = newNode def delete(self, val): ''' 删除时, 要将每个索引层中对应的节点都删掉 ''' # cache用来缓存对应索引层中小于插入值的最大节点 cache = [None] * self._high cur = self._head # 缓存每一个索引层定位小于插入值的节点 for i in range(self._high - 1, -1, -1): while cur.deeps[i] and cur.deeps[i].data < val: cur = cur.deeps[i] cache[i] = cur # 如果给定的值存在, 更新索引层中对应的节点 if cur.deeps[0] and cur.deeps[0].data == val: for i in range(self._high): if cache[i].deeps[i] and cache[i].deeps[i].data == val: cache[i].deeps[i] = cache[i].deeps[i].deeps[i] def randomLevel(self, p=0.25): ''' #define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */ https://github.com/antirez/redis/blob/unstable/src/t_zset.c ''' high = 1 for _ in range(self.__MAX_LEVEL - 1): if random.random() < p: high += 1 return high def __repr__(self): vals = [] p = self._head while p.deeps[0]: vals.append(str(p.deeps[0].data)) p = p.deeps[0] return '->'.join(vals) if __name__ == '__main__': sl = SkipList() for i in range(100): sl.insert(i) print(sl) p = sl.find(7) print(p.data) sl.delete(37) print(sl) sl.delete(37.5) print(sl) ================================================ FILE: python/23_binarytree/binary_search_tree.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from queue import Queue import math class TreeNode: def __init__(self, val=None): self.val = val self.left = None self.right = None self.parent = None class BinarySearchTree: def __init__(self, val_list=[]): self.root = None for n in val_list: self.insert(n) def insert(self, data): """ 插入 :param data: :return: """ assert(isinstance(data, int)) if self.root is None: self.root = TreeNode(data) else: n = self.root while n: p = n if data < n.val: n = n.left else: n = n.right new_node = TreeNode(data) new_node.parent = p if data < p.val: p.left = new_node else: p.right = new_node return True def search(self, data): """ 搜索 返回bst中所有值为data的节点列表 :param data: :return: """ assert(isinstance(data, int)) # 所有搜索到的节点 ret = [] n = self.root while n: if data < n.val: n = n.left else: if data == n.val: ret.append(n) n = n.right return ret def delete(self, data): """ 删除 :param data: :return: """ assert (isinstance(data, int)) # 通过搜索得到需要删除的节点 del_list = self.search(data) for n in del_list: # 父节点为空,又不是根节点,已经不在树上,不用再删除 if n.parent is None and n != self.root: continue else: self._del(n) def _del(self, node): """ 删除 所删除的节点N存在以下情况: 1. 没有子节点:直接删除N的父节点指针 2. 有一个子节点:将N父节点指针指向N的子节点 3. 有两个子节点:找到右子树的最小节点M,将值赋给N,然后删除M :param data: :return: """ # 1 if node.left is None and node.right is None: # 情况1和2,根节点和普通节点的处理方式不同 if node == self.root: self.root = None else: if node.val < node.parent.val: node.parent.left = None else: node.parent.right = None node.parent = None # 2 elif node.left is None and node.right is not None: if node == self.root: self.root = node.right self.root.parent = None node.right = None else: if node.val < node.parent.val: node.parent.left = node.right else: node.parent.right = node.right node.right.parent = node.parent node.parent = None node.right = None elif node.left is not None and node.right is None: if node == self.root: self.root = node.left self.root.parent = None node.left = None else: if node.val < node.parent.val: node.parent.left = node.left else: node.parent.right = node.left node.left.parent = node.parent node.parent = None node.left = None # 3 else: min_node = node.right # 找到右子树的最小值节点 if min_node.left: min_node = min_node.left if node.val != min_node.val: node.val = min_node.val self._del(min_node) # 右子树的最小值节点与被删除节点的值相等,再次删除原节点 else: self._del(min_node) self._del(node) def get_min(self): """ 返回最小值节点 :return: """ if self.root is None: return None n = self.root while n.left: n = n.left return n.val def get_max(self): """ 返回最大值节点 :return: """ if self.root is None: return None n = self.root while n.right: n = n.right return n.val def in_order(self): """ 中序遍历 :return: """ if self.root is None: return [] return self._in_order(self.root) def _in_order(self, node): if node is None: return [] ret = [] n = node ret.extend(self._in_order(n.left)) ret.append(n.val) ret.extend(self._in_order(n.right)) return ret def __repr__(self): # return str(self.in_order()) print(str(self.in_order())) return self._draw_tree() def _bfs(self): """ bfs 通过父子关系记录节点编号 :return: """ if self.root is None: return [] ret = [] q = Queue() # 队列[节点,编号] q.put((self.root, 1)) while not q.empty(): n = q.get() if n[0] is not None: ret.append((n[0].val, n[1])) q.put((n[0].left, n[1]*2)) q.put((n[0].right, n[1]*2+1)) return ret def _draw_tree(self): """ 可视化 :return: """ nodes = self._bfs() if not nodes: print('This tree has no nodes.') return layer_num = int(math.log(nodes[-1][1], 2)) + 1 prt_nums = [] for i in range(layer_num): prt_nums.append([None]*2**i) for v, p in nodes: row = int(math.log(p ,2)) col = p % 2**row prt_nums[row][col] = v prt_str = '' for l in prt_nums: prt_str += str(l)[1:-1] + '\n' return prt_str if __name__ == '__main__': nums = [4, 2, 5, 6, 1, 7, 3] bst = BinarySearchTree(nums) print(bst) # 插入 bst.insert(1) bst.insert(4) print(bst) # 搜索 for n in bst.search(2): print(n.parent.val, n.val) # 删除 bst.insert(6) bst.insert(7) print(bst) bst.delete(7) print(bst) bst.delete(6) print(bst) bst.delete(4) print(bst) # min max print(bst.get_max()) print(bst.get_min()) ================================================ FILE: python/23_binarytree/binary_tree.py ================================================ """ Pre-order, in-order and post-order traversal of binary trees. Author: Wenru Dong """ from typing import TypeVar, Generic, Generator, Optional T = TypeVar("T") class TreeNode(Generic[T]): def __init__(self, value: T): self.val = value self.left = None self.right = None # Pre-order traversal def pre_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: if root: yield root.val yield from pre_order(root.left) yield from pre_order(root.right) # In-order traversal def in_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: if root: yield from in_order(root.left) yield root.val yield from in_order(root.right) # Post-order traversal def post_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: if root: yield from post_order(root.left) yield from post_order(root.right) yield root.val if __name__ == "__main__": singer = TreeNode("Taylor Swift") genre_country = TreeNode("Country") genre_pop = TreeNode("Pop") album_fearless = TreeNode("Fearless") album_red = TreeNode("Red") album_1989 = TreeNode("1989") album_reputation = TreeNode("Reputation") song_ls = TreeNode("Love Story") song_wh = TreeNode("White Horse") song_wanegbt = TreeNode("We Are Never Ever Getting Back Together") song_ikywt = TreeNode("I Knew You Were Trouble") song_sio = TreeNode("Shake It Off") song_bb = TreeNode("Bad Blood") song_lwymmd = TreeNode("Look What You Made Me Do") song_g = TreeNode("Gorgeous") singer.left, singer.right = genre_country, genre_pop genre_country.left, genre_country.right = album_fearless, album_red genre_pop.left, genre_pop.right = album_1989, album_reputation album_fearless.left, album_fearless.right = song_ls, song_wh album_red.left, album_red.right = song_wanegbt, song_ikywt album_1989.left, album_1989.right = song_sio, song_bb album_reputation.left, album_reputation.right = song_lwymmd, song_g print(list(pre_order(singer))) print(list(in_order(singer))) print(list(post_order(singer))) ================================================ FILE: python/24_tree/binary_search_tree.py ================================================ """ Binary search tree Author: Wenru Dong """ from typing import Optional class TreeNode: def __init__(self, value: int): self.val = value self.left = None self.right = None class BinarySearchTree: def __init__(self): self._root = None def find(self, value: int) -> Optional[TreeNode]: node = self._root while node and node.val != value: node = node.left if node.val > value else node.right return node def insert(self, value: int): if not self._root: self._root = TreeNode(value) return parent = None node = self._root while node: parent = node node = node.left if node.val > value else node.right new_node = TreeNode(value) if parent.val > value: parent.left = new_node else: parent.right = new_node def delete(self, value: int): node = self._root parent = None while node and node.val != value: parent = node node = node.left if node.val > value else node.right if not node: return # 要删除的节点有两个子节点 if node.left and node.right: successor = node.right successor_parent = node while successor.left: successor_parent = successor successor = successor.left node.val = successor.val parent, node = successor_parent, successor # 删除节点是叶子节点或者仅有一个子节点 child = node.left if node.left else node.right if not parent: self._root = child elif parent.left == node: parent.left = child else: parent.right = child ================================================ FILE: python/26_red_black_tree/red_black_tree.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from queue import Queue import pygraphviz as pgv import random OUTPUT_PATH = 'E:/' class TreeNode: def __init__(self, val=None, color=None): self.val = val assert color in ['r', 'b'] self.color = 'red' if color == 'r' else 'black' self.left = None self.right = None self.parent = None def is_black(self): return self.color == 'black' def set_black(self): self.color = 'black' return def set_red(self): self.color = 'red' class RedBlackTree: """ 红黑树实现 参考资料: 1. 《算法导论》 第13章 红黑树 13.3 插入 p178 13.4 删除 p183 2. 红黑树(二):删除 https://zhuanlan.zhihu.com/p/25402654 """ def __init__(self, val_list=None): self.root = None self.black_leaf = TreeNode(color='b') # 共用的黑色叶子节点 # 可用数组初始化 if type(val_list) is list: for n in val_list: assert type(n) is int self.insert(n) def search(self, val): """ 搜索 :param val: :return: """ if self.root is None: return None n = self.root while n != self.black_leaf: if val < n.val: n = n.left elif val > n.val: n = n.right else: return n return None def insert(self, val): """ 插入 :param val: :return: """ assert type(val) is int new_node = TreeNode(val, 'r') # 新插入的节点为红色 # 根节点 if self.root is None: self.root = new_node else: n = self.root while n != self.black_leaf: # 黑色叶子节点 p = n if val < n.val: n = n.left elif val > n.val: n = n.right else: raise KeyError('val:{} already exists') # 该值已存在,插入失败 if val < p.val: p.left = new_node else: p.right = new_node new_node.parent = p new_node.left = new_node.right = self.black_leaf # 插入后调整 self._insert_fixup(new_node) def _insert_fixup(self, node): """ 插入调整 参考资料:《算法导论》 13.3 p178-179 :param node: :return: """ n = node while n is not self.root and not n.parent.is_black(): # 父p 叔u 祖父g p = self.parent(n) u = self.bro(p) g = self.parent(p) if not u.is_black(): # case 1 p.set_black() # case 1 u.set_black() # case 1 g.set_red() # case 1 n = g # case 1 continue if p == g.left: # p为左结点 if n == p.right: # case 2 self.rotate_l(p) # case 2 n, p = p, n # case 2 p.set_black() # case 3 g.set_red() # case 3 self.rotate_r(g) # case 3 else: # p为右节点 if n == p.left: # case 2 self.rotate_r(p) # case 2 n, p = p, n # case 2 p.set_black() # case 3 g.set_red() # case 3 self.rotate_l(g) # case 3 # 根节点强制置黑,有两种情况根节点是红色: # 1. 新插入时是红色 # 2. 经过case 1调整过后变红色 self.root.color = 'black' def delete(self, val): """ 删除 :param val: :return: """ assert type(val) is int n = self.search(val) if n is None: print('can not find any nodes with value: {}'.format(val)) return self._delete_node(n) def _delete_node(self, node): """ 删除节点内部实现 参考资料:《算法导论》 13.4 p183-184 实现方式有微调,当n有2个子节点时,将s拷贝至n,转为删除s(s最多有一个子节点) :param node: :return: """ n = node # n的子节点个数等于2 if self.children_count(n) == 2: # 寻找n的后继s s = n.right while s.left != self.black_leaf: s = s.left n.val = s.val # 将删除n转化为删除s n = s # n的子节点个数小于2 if n.left == self.black_leaf: c = n.right else: c = n.left self._transplant(n, c) # 删除的节点是黑色,需要调整 if n.is_black(): self._delete_fixup(c) return def _delete_fixup(self, node): """ 删除调整 参考资料:《算法导论》 13.4 p185-187 :param node: :return: """ n = node while n != self.root and n.is_black(): p = self.parent(n) b = self.bro(n) # 左右节点对称 if p.left == n: if not b.is_black(): b.set_black() # case 1 p.set_red() # case 1 self.rotate_l(p) # case 1 # new bro after rotate b = self.bro(n) # case 1 if b.left.is_black() and b.right.is_black(): b.set_red() # case 2 n = p # case 2 else: if b.right.is_black(): b.left.set_black() # case 3 b.set_red() # case 3 self.rotate_r(b) # case 3 # new bro after rotate b = self.bro(n) # case 3 # 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copy b.color = p.color # case 4 p.set_black() # case 4 b.right.set_black() # case 4 self.rotate_l(p) # case 4 # trick, 调整结束跳出while n = self.root # case 4 else: if not b.is_black(): b.set_black() # case 1 p.set_red() # case 1 self.rotate_r(p) # case 1 # new bro after rotate b = self.bro(n) # case 1 if b.left.is_black() and b.right.is_black(): b.set_red() # case 2 n = p # case 2 else: if b.left.is_black(): b.right.set_black() # case 3 b.set_red() # case 3 self.rotate_l(b) # case 3 # new bro after rotate b = self.bro(n) # case 3 # 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copy b.color = p.color # case 4 p.set_black() # case 4 b.left.set_black() # case 4 self.rotate_r(p) # case 4 # trick, 调整结束跳出while n = self.root # case 4 # 将n设为黑色,从上面while循环跳出,情况有两种 # 1. n是根节点,直接无视附加的黑色 # 2. n是红色的节点,则染黑 n.set_black() def _transplant(self, n1, n2): """ 节点移植, n2 -> n1 :param n1: 原节点 :param n2: 移植节点 :return: """ if n1 == self.root: if n2 != self.black_leaf: self.root = n2 n2.parent = None else: self.root = None # 只有删除根节点时会进来 else: p = self.parent(n1) if p.left == n1: p.left = n2 else: p.right = n2 n2.parent = p def rotate_l(self, node): """ 左旋 :param node: :return: """ if node is None: return if node.right is self.black_leaf: return # raise Exception('try rotate left , but the node "{}" has no right child'.format(node.val)) p = self.parent(node) x = node y = node.right # node为根节点时,p为None,旋转后要更新根节点指向 if p is not None: if x == p.left: p.left = y else: p.right = y else: self.root = y x.parent, y.parent = y, p if y.left != self.black_leaf: y.left.parent = x x.right, y.left = y.left, x def rotate_r(self, node): """ 右旋 :param node: :return: """ if node is None: return if node.left is self.black_leaf: return # raise Exception('try rotate right , but the node "{}" has no left child'.format(node.val)) p = self.parent(node) x = node y = node.left # 同左旋 if p is not None: if x == p.left: p.left = y else: p.right = y else: self.root = y x.parent, y.parent = y, p if y.right is not None: y.right.parent = x x.left, y.right = y.right, x @staticmethod def bro(node): """ 获取兄弟节点 :param node: :return: """ if node is None or node.parent is None: return None else: p = node.parent if node == p.left: return p.right else: return p.left @staticmethod def parent(node): """ 获取父节点 :param node: :return: """ if node is None: return None else: return node.parent def children_count(self, node): """ 获取子节点个数 :param node: :return: """ return 2 - [node.left, node.right].count(self.black_leaf) def draw_img(self, img_name='Red_Black_Tree.png'): """ 画图 用pygraphviz画出节点和箭头 箭头的红色和黑色分别代表左和右 :param img_name: :return: """ if self.root is None: return tree = pgv.AGraph(directed=True, strict=True) q = Queue() q.put(self.root) while not q.empty(): n = q.get() if n != self.black_leaf: # 黑色叶子的连线由各个节点自己画 tree.add_node(n.val, color=n.color) # 画父节点箭头 # if n.parent is not None: # tree.add_edge(n.val, n.parent.val) for c in [n.left, n.right]: q.put(c) color = 'red' if c == n.left else 'black' if c != self.black_leaf: tree.add_edge(n.val, c.val, color=color) else: tree.add_edge(n.val, 'None', color=color) tree.graph_attr['epsilon'] = '0.01' tree.layout('dot') tree.draw(OUTPUT_PATH + img_name) return True if __name__ == '__main__': rbt = RedBlackTree() # insert nums = list(range(1, 25)) # random.shuffle(nums) for num in nums: rbt.insert(num) # search search_num = 23 n = rbt.search(search_num) if n is not None: print(n) else: print('node {} not found'.format(search_num)) # delete rbt.delete(4) # draw image rbt.draw_img('rbt.png') ================================================ FILE: python/28_binary_heap/binary_heap.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- import math import random class BinaryHeap: """ 大顶堆 """ def __init__(self, data=None, capacity=100): self._data = [] self._capacity = capacity if type(data) is list: if len(data) > self._capacity: raise Exception('Heap oversize, capacity:{}, data size:{}'.format(self._capacity, len(data))) self._type_assert(data) self._data = data self._length = len(self._data) def heapify(self): """ 堆化 :return: """ self._heapify(self._data, self._length-1) def _heapify(self, data, tail_idx): """ 堆化内部实现 :param data: 需要堆化的数据 :param tail_idx: 尾元素的索引 :return: """ # heapify data[:tail_idx+1] if tail_idx <= 0: return # idx of the Last Parent node lp = (tail_idx - 1) // 2 for i in range(lp, -1, -1): self._heap_down(data, i, tail_idx) @staticmethod def _heap_down(data, idx, tail_idx): """ 将指定的位置堆化 :param data: 需要堆化的数据 :param idx: data: 中需要堆化的位置 :param tail_idx: 尾元素的索引 :return: """ assert type(data) is list lp = (tail_idx - 1) // 2 # top-down while idx <= lp: # Left and Right Child index lc = 2 * idx + 1 rc = lc + 1 # right child exists if rc <= tail_idx: tmp = lc if data[lc] > data[rc] else rc else: tmp = lc if data[tmp] > data[idx]: data[tmp], data[idx] = data[idx], data[tmp] idx = tmp else: break def insert(self, num): """ 插入 :param num: :return: """ if self._length < self._capacity: if self._insert(self._data, num): self._length += 1 return True return False @staticmethod def _insert(data, num): """ 堆中插入元素的内部实现 :param data: :param num: :return: """ assert type(data) is list assert type(num) is int data.append(num) length = len(data) # idx of New Node nn = length - 1 # bottom-up while nn > 0: p = (nn-1) // 2 if data[nn] > data[p]: data[nn], data[p] = data[p], data[nn] nn = p else: break return True def get_top(self): """ 取堆顶 :return: """ if self._length <= 0: return None return self._data[0] def remove_top(self): """ 取堆顶 :return: """ ret = None if self._length > 0: ret = self._remove_top(self._data) self._length -= 1 return ret @staticmethod def _remove_top(data): """ 取堆顶内部实现 :param data: :return: """ assert type(data) is list length = len(data) if length == 0: return None data[0], data[-1] = data[-1], data[0] ret = data.pop() length -= 1 # length == 0 or == 1, return if length > 1: BinaryHeap._heap_down(data, 0, length-1) return ret @staticmethod def _type_assert(nums): assert type(nums) is list for n in nums: assert type(n) is int @staticmethod def _draw_heap(data): """ 格式化打印 :param data: :return: """ length = len(data) if length == 0: return 'empty heap' ret = '' for i, n in enumerate(data): ret += str(n) # 每行最后一个换行 if i == 2**int(math.log(i+1, 2)+1) - 2 or i == len(data) - 1: ret += '\n' else: ret += ', ' return ret def __repr__(self): return self._draw_heap(self._data) if __name__ == '__main__': nums = list(range(10)) random.shuffle(nums) bh = BinaryHeap(nums) print('--- before heapify ---') print(bh) # heapify bh.heapify() print('--- after heapify ---') print(bh) # insert print('--- insert ---') if bh.insert(8): print('insert success') else: print('insert fail') print(bh) # get top print('--- get top ---') print('get top of the heap: {}'.format(bh.get_top())) bh.remove_top() print(bh) ================================================ FILE: python/28_binary_heap/binary_heap_sort.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from binary_heap import BinaryHeap class BinaryHeapSort(BinaryHeap): def __init__(self): super(BinaryHeapSort, self).__init__() def sort(self, nums): """ 排序 1. 堆化,大顶堆 2. 排序,从后往前遍历,首尾元素互换,子数组堆化 :param nums: :return: """ assert type(nums) is list length = len(nums) if length <= 1: return self._type_assert(nums) # heapify self._heapify(nums, length-1) # sort for i in range(length-1, 0, -1): nums[0], nums[i] = nums[i], nums[0] self._heap_down(nums, 0, i-1) return if __name__ == '__main__': bhs = BinaryHeapSort() nums = [3, 5, 2, 6, 1, 7, 6] print('--- before sort ---') print(nums) bhs.sort(nums) print('--- after sort ---') print(nums) ================================================ FILE: python/28_binary_heap/heap.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- import math import random class Heap: def __init__(self, nums=None, capacity=100): self._data = [] self._capacity = capacity if type(nums) == list and len(nums) <= self._capacity: for n in nums: assert type(n) is int self._data.append(n) self._length = len(self._data) self._heapify() def _heapify(self): if self._length <= 1: return # idx of the Last Parent node lp = (self._length - 2) // 2 for i in range(lp, -1, -1): self._heap_down(i) def _heap_down(self, idx): pass def insert(self, num): pass def get_top(self): if self._length <= 0: return None return self._data[0] def remove_top(self): if self._length <= 0: return None self._data[0], self._data[-1] = self._data[-1], self._data[0] ret = self._data.pop() self._length -= 1 self._heap_down(0) return ret def get_data(self): return self._data def get_length(self): return self._length @staticmethod def _draw_heap(data): """ 格式化打印 :param data: :return: """ length = len(data) if length == 0: return 'empty heap' ret = '' for i, n in enumerate(data): ret += str(n) # 每行最后一个换行 if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1: ret += '\n' else: ret += ', ' return ret def __repr__(self): return self._draw_heap(self._data) class MaxHeap(Heap): def _heap_down(self, idx): if self._length <= 1: return lp = (self._length - 2) // 2 while idx <= lp: lc = 2 * idx + 1 rc = lc + 1 if rc <= self._length-1: tmp = lc if self._data[lc] > self._data[rc] else rc else: tmp = lc if self._data[tmp] > self._data[idx]: self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp] idx = tmp else: break def insert(self, num): if self._length >= self._capacity: return False self._data.append(num) self._length += 1 nn = self._length - 1 while nn > 0: p = (nn-1) // 2 if self._data[nn] > self._data[p]: self._data[nn], self._data[p] = self._data[p], self._data[nn] nn = p else: break return True class MinHeap(Heap): def _heap_down(self, idx): if self._length <= 1: return lp = (self._length - 2) // 2 while idx <= lp: lc = 2 * idx + 1 rc = lc + 1 if rc <= self._length-1: tmp = lc if self._data[lc] < self._data[rc] else rc else: tmp = lc if self._data[tmp] < self._data[idx]: self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp] idx = tmp else: break def insert(self, num): if self._length >= self._capacity: return False self._data.append(num) self._length += 1 nn = self._length - 1 while nn > 0: p = (nn-1) // 2 if self._data[nn] < self._data[p]: self._data[nn], self._data[p] = self._data[p], self._data[nn] nn = p else: break return True if __name__ == '__main__': nums = list(range(10)) random.shuffle(nums) max_h = MaxHeap(nums) print('--- max heap ---') print(max_h) print('--- min heap ---') min_h = MinHeap(nums) print(min_h) ================================================ FILE: python/28_binary_heap/priority_queue.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- import math class QueueNode: def __init__(self, priority, data=None): assert type(priority) is int and priority >= 0 self.priority = priority self.data = data def __repr__(self): return str((self.priority, self.data)) class PriorityQueue: def __init__(self, capacity=100): self._capacity = capacity self._q = [] self._length = 0 def enqueue(self, priority, data=None): if self._length >= self._capacity: return False new_node = QueueNode(priority, data) self._q.append(new_node) self._length += 1 # update queue nn = self._length - 1 while nn > 0: p = (nn - 1) // 2 if self._q[nn].priority < self._q[p].priority: self._q[nn], self._q[p] = self._q[p], self._q[nn] nn = p else: break return True def dequeue(self): if self._length <= 0: raise Exception('the queue is empty....') self._q[0], self._q[-1] = self._q[-1], self._q[0] ret = self._q.pop() self._length -= 1 if self._length > 1: # update queue lp = (self._length - 2) // 2 idx = 0 while idx <= lp: lc = 2 * idx + 1 rc = lc + 1 if rc <= self._length - 1: tmp = lc if self._q[lc].priority < self._q[rc].priority else rc else: tmp = lc if self._q[tmp].priority < self._q[idx].priority: self._q[tmp], self._q[idx] = self._q[idx], self._q[tmp] idx = tmp else: break return ret def get_length(self): return self._length @staticmethod def _draw_heap(data): """ 格式化打印 :param data: :return: """ length = len(data) if length == 0: return 'empty' ret = '' for i, n in enumerate(data): ret += str(n) # 每行最后一个换行 if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1: ret += '\n' else: ret += ', ' return ret def __repr__(self): def formater(node): assert type(node) is QueueNode return node.priority, node.data data = list(map(formater, self._q)) return self._draw_heap(data) if __name__ == '__main__': pq = PriorityQueue() pq.enqueue(5, 'Watch TV') pq.enqueue(2, 'Learning') pq.enqueue(10, 'Go Sleep') pq.enqueue(0, 'Go Home') pq.enqueue(7, 'Mobile Games') print(pq) while pq.get_length() > 0: print(pq.dequeue()) ================================================ FILE: python/28_binary_heap/top_k.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- import random from heap import MinHeap def top_k(nums, k): """ 返回数组的前k大元素 :param nums: :param k: :return: """ if len(nums) <= k: return nums min_h = MinHeap(nums[:k], k) for i in range(k, len(nums)): tmp = min_h.get_top() if nums[i] > tmp: min_h.remove_top() min_h.insert(nums[i]) return min_h.get_data() if __name__ == '__main__': nums = [] k = 3 for i in range(20): nums.append(random.randint(1, 100)) print('--- nums ---') print(nums) print('--- top {} ---'.format(k)) print(top_k(nums, k)) ================================================ FILE: python/28_heap/heap.py ================================================ """ Max-heap Author: Wenru Dong """ from typing import Optional, List class Heap: def __init__(self, capacity: int): self._data = [0] * (capacity + 1) self._capacity = capacity self._count = 0 @classmethod def _parent(cls, child_index: int) -> int: """The parent index.""" return child_index // 2 @classmethod def _left(cls, parent_index: int) -> int: """The left child index.""" return 2 * parent_index @classmethod def _right(cls, parent_index: int) -> int: """The right child index.""" return 2 * parent_index + 1 def _siftup(self) -> None: i, parent = self._count, Heap._parent(self._count) while parent and self._data[i] > self._data[parent]: self._data[i], self._data[parent] = self._data[parent], self._data[i] i, parent = parent, Heap._parent(parent) @classmethod def _siftdown(cls, a: List[int], count: int, root_index: int = 1) -> None: i = larger_child_index = root_index while True: left, right = cls._left(i), cls._right(i) if left <= count and a[i] < a[left]: larger_child_index = left if right <= count and a[larger_child_index] < a[right]: larger_child_index = right if larger_child_index == i: break a[i], a[larger_child_index] = a[larger_child_index], a[i] i = larger_child_index def insert(self, value: int) -> None: if self._count >= self._capacity: return self._count += 1 self._data[self._count] = value self._siftup() def remove_max(self) -> Optional[int]: if self._count: result = self._data[1] self._data[1] = self._data[self._count] self._count -= 1 Heap._siftdown(self._data, self._count) return result @classmethod def build_heap(cls, a: List[int]) -> None: """Data in a needs to start from index 1.""" for i in range((len(a) - 1)//2, 0, -1): cls._siftdown(a, len(a) - 1, i) @classmethod def sort(cls, a: List[int]) -> None: """Data in a needs to start from index 1.""" cls.build_heap(a) k = len(a) - 1 while k > 1: a[1], a[k] = a[k], a[1] k -= 1 cls._siftdown(a, k) def __repr__(self): return self._data[1 : self._count + 1].__repr__() if __name__ == "__main__": hp = Heap(10) hp.insert(3) hp.insert(9) hp.insert(1) hp.insert(8) hp.insert(7) hp.insert(3) print(hp) for _ in range(6): print(hp.remove_max()) a = [0, 6, 3, 4, 0, 9, 2, 7, 5, -2, 8, 1, 6, 10] Heap.sort(a) print(a[1:]) ================================================ FILE: python/28_heap/min_heap.py ================================================ class Heap(object): ''' 索引从0开始的小顶堆 参考: https://github.com/python/cpython/blob/master/Lib/heapq.py author: Ben ''' def __init__(self, nums): self._heap = nums def _siftup(self, pos): ''' 从上向下的堆化 将pos节点的子节点中的最值提升到pos位置 ''' start = pos startval = self._heap[pos] n = len(self._heap) # 完全二叉树特性 child = pos * 2 + 1 # 比较叶子节点 while child < n: right = child + 1 # 平衡二叉树的特性, 大的都在右边 if right < n and not self._heap[right] > self._heap[child]: child = right self._heap[pos] = self._heap[child] pos = child child = pos * 2 + 1 self._heap[pos] = startval # 此时只有pos是不确定的 self._siftdown(start, pos) def _siftdown(self, start, pos): ''' 最小堆: 大于start的节点, 除pos外已经是最小堆 以pos为叶子节点, start为根节点之间的元素进行排序. 将pos叶子节点交换到正确的排序位置 操作: 从叶子节点开始, 当父节点的值大于子节点时, 父节点的值降低到子节点 ''' startval = self._heap[pos] while pos > start: parent = (pos - 1) >> 1 parentval = self._heap[parent] if parentval > startval: self._heap[pos] = parentval pos = parent continue break self._heap[pos] = startval def heapify(self): ''' 堆化: 从后向前(从下向上)的方式堆化, _siftup中pos节点的子树已经是有序的, 这样要排序的节点在慢慢减少 1. 因为n/2+1到n的节点是叶子节点(完全二叉树的特性), 它们没有子节点, 所以, 只需要堆化n/2到0的节点, 以对应的父节点为根节点, 将最值向上筛选, 然后交换对应的根节点和查找到的最值 2. 因为开始时待排序树的根节点还没有排序, 为了保证根节点的有序, 需要将子树中根节点交换到正确顺序 ''' n = len(self._heap) for i in reversed(range(n // 2)): self._siftup(i) def heappop(self): ''' 弹出堆首的最值 O(logn) ''' tail = self._heap.pop() # 为避免破环完全二叉树特性, 将堆尾元素填充到堆首 # 此时, 只有堆首是未排序的, 只需要一次从上向下的堆化 if self._heap: peak = self._heap[0] self._heap[0] = tail self._siftup(0) return peak return tail def heappush(self, val): ''' 添加元素到堆尾 O(logn) ''' n = len(self._heap) self._heap.append(val) # 此时只有堆尾的节点是未排序的, 将添加的节点迭代到正确的位置 self._siftdown(0, n) def __repr__(self): vals = [str(i) for i in self._heap] return '>'.join(vals) if __name__ == '__main__': h = Heap([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) h.heapify() print(h) print(h.heappop()) print(h) h.heappush(3.5) print(h) h.heappush(0.1) print(h) h.heappush(0.5) print(h) print(h.heappop()) print(h) ================================================ FILE: python/31_bfs_dfs/bfs_dfs.py ================================================ """ Breadth-first search and depth-first search. Author: Wenru Dong """ from typing import List, Optional, Generator, IO from collections import deque class Graph: """Undirected graph.""" def __init__(self, num_vertices: int): self._num_vertices = num_vertices self._adjacency = [[] for _ in range(num_vertices)] def add_edge(self, s: int, t: int) -> None: self._adjacency[s].append(t) self._adjacency[t].append(s) def _generate_path(self, s: int, t: int, prev: List[Optional[int]]) -> Generator[str, None, None]: if prev[t] or s != t: yield from self._generate_path(s, prev[t], prev) yield str(t) def bfs(self, s: int, t: int) -> IO[str]: """Print out the path from Vertex s to Vertex t using bfs. """ if s == t: return visited = [False] * self._num_vertices visited[s] = True q = deque() q.append(s) prev = [None] * self._num_vertices while q: v = q.popleft() for neighbour in self._adjacency[v]: if not visited[neighbour]: prev[neighbour] = v if neighbour == t: print("->".join(self._generate_path(s, t, prev))) return visited[neighbour] = True q.append(neighbour) def dfs(self, s: int, t: int) -> IO[str]: """Print out a path from Vertex s to Vertex t using dfs. """ found = False visited = [False] * self._num_vertices prev = [None] * self._num_vertices def _dfs(from_vertex: int) -> None: nonlocal found if found: return visited[from_vertex] = True if from_vertex == t: found = True return for neighbour in self._adjacency[from_vertex]: if not visited[neighbour]: prev[neighbour] = from_vertex _dfs(neighbour) _dfs(s) print("->".join(self._generate_path(s, t, prev))) if __name__ == "__main__": graph = Graph(8) graph.add_edge(0, 1) graph.add_edge(0, 3) graph.add_edge(1, 2) graph.add_edge(1, 4) graph.add_edge(2, 5) graph.add_edge(3, 4) graph.add_edge(4, 5) graph.add_edge(4, 6) graph.add_edge(5, 7) graph.add_edge(6, 7) graph.bfs(0, 7) graph.dfs(0, 7) ================================================ FILE: python/31_bfs_dfs/graph.py ================================================ # -*- coding:utf-8 -*- class Undigraph(object): def __init__(self, vertex_num): self.v_num = vertex_num self.adj_tbl = [] for i in range(self.v_num + 1): self.adj_tbl.append([]) def add_edge(self, s, t): if s > self.v_num or t > self.v_num: return False self.adj_tbl[s].append(t) self.adj_tbl[t].append(s) return True def __len__(self): return self.v_num def __getitem__(self, ind): if ind > self.v_num: raise IndexError("No Such Vertex!") return self.adj_tbl[ind] def __repr__(self): return str(self.adj_tbl) def __str__(self): return str(self.adj_tbl) class Digraph(object): def __init__(self, vertex_num): self.v_num = vertex_num self.adj_tbl = [] for i in range(self.v_num + 1): self.adj_tbl.append([]) def add_edge(self, frm, to): if frm > self.v_num or to > self.v_num: return False self.adj_tbl[frm].append(to) def __len__(self): return self.v_num def __getitem__(self, ind): if ind > self.v_num: raise IndexError("No such vertex!") return self.ajd_tbl[ind] def __repr__(self): return str(self.adj_tbl) def __str__(self): return str(self.adj_tbl) if __name__ == '__main__': ug = Undigraph(10) ug.add_edge(1, 9) ug.add_edge(1, 3) ug.add_edge(3, 2) print(ug.adj_tbl) dg = Digraph(10) dg.add_edge(1, 9) dg.add_edge(1, 3) dg.add_edge(3, 4) print(dg.adj_tbl) ================================================ FILE: python/31_bfs_dfs/graph_application.py ================================================ # -*- coding:utf-8 -*- from collections import deque from graph import Undigraph def find_vertex_by_degree(graph, s, degree): if len(graph) <= 1: return [] if degree == 0: return [s] d_vertices = [] queue = deque() prev = [-1] * len(graph) visited = [False] * len(graph) visited[s] = True queue.append(s) while len(queue) > 0: sz = len(queue) for i in range(sz): v = queue.popleft() for adj_v in graph[v]: if not visited[adj_v]: prev[adj_v] = v visited[adj_v] = True queue.append(adj_v) degree -= 1 if degree == 0 and len(queue) != 0: return queue if __name__ == '__main__': g = Undigraph(8) g.add_edge(0, 1) g.add_edge(0, 3) g.add_edge(1, 2) g.add_edge(1, 4) g.add_edge(2, 5) g.add_edge(3, 4) g.add_edge(4, 5) g.add_edge(4, 6) g.add_edge(5, 7) g.add_edge(6, 7) print(find_vertex_by_degree(g, 0, 4)) ================================================ FILE: python/32_bf_rk/bf_rk.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from time import time def bf(main, pattern): """ 字符串匹配,bf暴搜 :param main: 主串 :param pattern: 模式串 :return: """ n = len(main) m = len(pattern) if n <= m: return 0 if pattern == main else -1 for i in range(n-m+1): for j in range(m): if main[i+j] == pattern[j]: if j == m-1: return i else: continue else: break return -1 def simple_hash(s, start, end): """ 计算子串的哈希值 每个字符取acs-ii码后求和 :param s: :param start: :param end: :return: """ assert start <= end ret = 0 for c in s[start: end+1]: ret += ord(c) return ret def rk(main, pattern): n = len(main) m = len(pattern) if n <= m: return 0 if pattern == main else -1 # 子串哈希值表 hash_memo = [None] * (n-m+1) hash_memo[0] = simple_hash(main, 0, m-1) for i in range(1, n-m+1): hash_memo[i] = hash_memo[i-1] - simple_hash(main, i-1, i-1) + simple_hash(main, i+m-1, i+m-1) # 模式串哈希值 hash_p = simple_hash(pattern, 0, m-1) for i, h in enumerate(hash_memo): # 可能存在哈希冲突 if h == hash_p: if pattern == main[i:i+m]: return i else: continue return -1 if __name__ == '__main__': m_str = 'a'*10000 p_str = 'a'*200+'b' print('--- time consume ---') t = time() print('[bf] result:', bf(m_str, p_str)) print('[bf] time cost: {0:.5}s'.format(time()-t)) t = time() print('[rk] result:', rk(m_str, p_str)) print('[rk] time cost: {0:.5}s'.format(time()-t)) print('') print('--- search ---') m_str = 'thequickbrownfoxjumpsoverthelazydog' p_str = 'jump' print('[bf] result:', bf(m_str, p_str)) print('[rk] result:', rk(m_str, p_str)) ================================================ FILE: python/33_bm/bm.py ================================================ """ Boyer-Moore string-search algorithm. Author: Wenru Dong """ from typing import List, Tuple SIZE = 256 def _generate_bad_character_table(pattern: str) -> List[int]: bc = [-1] * SIZE for i, char in enumerate(pattern): bc[ord(char)] = i return bc def _generate_good_suffix_table(pattern: str) -> Tuple[List[bool], List[int]]: m = len(pattern) # prefix[k] records whether the last k-character suffix of pattern # can match with the first k-character prefix of pattern. # suffix[k] records the starting index of the last substring of # pattern that can match with the last k-character suffix of pattern. prefix, suffix = [False] * m, [-1] * m # For each substring patter[:i+1], we find the common suffix with # pattern, and the starting index of this common suffix. # This way we can re-write previous suffix[k] to record the index # as large as possible, hence the last substring. for i in range(m - 1): j = i # starting index of the common suffix k = 0 # length of the common suffix while j >= 0 and pattern[j] == pattern[~k]: j -= 1 k += 1 suffix[k] = j + 1 if j == -1: prefix[k] = True return (prefix, suffix) def _move_by_good_suffix(bad_character_index: int, suffix: List[int], prefix: List[bool]) -> int: k = len(suffix) - 1 - bad_character_index if suffix[k] != -1: return bad_character_index - suffix[k] + 1 # Test from k - 1 for r, can_match_prefix in enumerate(reversed(prefix[:k]), bad_character_index + 2): if can_match_prefix: return r return len(suffix) def bm(s: str, pattern: str) -> int: bc = _generate_bad_character_table(pattern) prefix, suffix = _generate_good_suffix_table(pattern) n, m = len(s), len(pattern) i = 0 while i <= n - m: j = m - 1 # bad character index in pattern while j >= 0: if s[i + j] != pattern[j]: break j -= 1 if j < 0: return i x = j - bc[ord(s[i + j])] y = 0 if j < m - 1: y = _move_by_good_suffix(j, suffix, prefix) i += max(x, y) return -1 if __name__ == "__main__": s = "Here is a simple example" pattern = "example" print(bm(s, pattern)) s = "abcdcccdc" pattern = "cccd" print(s.find(pattern) == bm(s, pattern)) ================================================ FILE: python/33_bm/bm_.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- SIZE = 256 def bm(main, pattern): """ BM算法 匹配规则: 1. 坏字符规则 2. 好字符规则 :param main: :param pattern: :return: """ assert type(main) is str and type(pattern) is str n, m = len(main), len(pattern) if n <= m: return 0 if main == pattern else -1 # bc bc = [-1] * SIZE generate_bc(pattern, m, bc) # gs suffix = [-1] * m prefix = [False] * m generate_gs(pattern, m, suffix, prefix) i = 0 while i < n-m+1: j = m - 1 while j >= 0: if main[i+j] != pattern[j]: break else: j -= 1 # pattern整个已被匹配,返回 if j == -1: return i # 1. bc规则计算后移位数 x = j - bc[ord(main[i+j])] # 2. gs规则计算后移位数 y = 0 if j != m - 1: # 存在gs y = move_by_gs(j, m, suffix, prefix) i += max(x, y) return -1 def generate_bc(pattern, m, bc): """ 生成坏字符哈希表 :param pattern: :param m: :param bc: :return: """ for i in range(m): bc[ord(pattern[i])] = i def generate_gs(pattern, m, suffix, prefix): """ 好后缀预处理 :param pattern: :param m: :param suffix: :param prefix: :return: """ for i in range(m-1): k = 0 # pattern[:i+1]和pattern的公共后缀长度 for j in range(i, -1, -1): if pattern[j] == pattern[m-1-k]: k += 1 suffix[k] = j if j == 0: prefix[k] = True else: break def move_by_gs(j, m, suffix, prefix): """ 通过好后缀计算移动值 需要处理三种情况: 1. 整个好后缀在pattern仍能找到 2. 好后缀里存在 *后缀子串* 能和pattern的 *前缀* 匹配 3. 其他 :param j: :param m: :param suffix: :param prefix: :return: """ k = m - 1 - j # j指向从后往前的第一个坏字符,k是此次匹配的好后缀的长度 if suffix[k] != -1: # 1. 整个好后缀在pattern剩余字符中仍有出现 return j - suffix[k] + 1 else: for r in range(j+2, m): # 2. 后缀子串从长到短搜索 if prefix[m-r]: return r return m # 3. 其他情况 if __name__ == '__main__': print('--- search ---') m_str = 'dfasdeeeetewtweyyyhtruuueyytewtweyyhtrhrth' p_str = 'eyytewtweyy' print('[Built-in Functions] result:', m_str.find(p_str)) print('[bm] result:', bm(m_str, p_str)) ================================================ FILE: python/34_kmp/kmp.py ================================================ """ KMP algorithm Author: Wenru Dong """ from typing import List def kmp(s: int, pattern: int) -> int: m = len(pattern) partial_match_table = _get_partial_match_table(pattern) j = 0 for i in range(len(s)): while j >= 0 and s[i] != pattern[j]: j = partial_match_table[j] j += 1 if j == m: return i - m + 1 return -1 def _get_partial_match_table(pattern: int) -> List[int]: # Denote πᵏ(i) as π applied to i for k times, # i.e., π²(i) = π(π(i)). # Then we have the result: # π(i) = πᵏ(i-1) + 1, # where k is the smallest integer such that # pattern[πᵏ(i-1)+1] == pattern[i]. # The value of π means the maximum length # of proper prefix/suffix. # The index of π means the length of the prefix # considered for pattern. # For example, π[2] means we are considering the first 2 characters # of the pattern. # If π[2] == 1, it means for the prefix of the pattern, P[0]P[1], # it has a maximum length proper prefix of 1, which is also the # suffix of P[0]P[1]. # We also add a π[0] == -1 for easier handling of boundary # condition. m = len(pattern) π = [0] * (m + 1) π[0] = k = -1 # We use k here to represent πᵏ(i) for i in range(1, m + 1): while k >= 0 and pattern[k] != pattern[i - 1]: k = π[k] k += 1 π[i] = k return π if __name__ == "__main__": s = "abc abcdab abcdabcdabde" pattern = "bcdabd" print(kmp(s, pattern), s.find(pattern)) s = "hello" pattern = "ll" print(kmp(s, pattern), s.find(pattern)) ================================================ FILE: python/34_kmp/kmp_.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- def kmp(main, pattern): """ kmp字符串匹配 :param main: :param pattern: :return: """ assert type(main) is str and type(pattern) is str n, m = len(main), len(pattern) if m == 0: return 0 if n <= m: return 0 if main == pattern else -1 # 求解next数组 next = get_next(pattern) j = 0 for i in range(n): # 在pattern[:j]中,从长到短递归去找最长的和后缀子串匹配的前缀子串 while j > 0 and main[i] != pattern[j]: j = next[j-1] + 1 # 如果next[j-1] = -1,则要从起始字符取匹配 if main[i] == pattern[j]: if j == m-1: return i-m+1 else: j += 1 return -1 def get_next(pattern): """ next数组生成 注意: 理解的难点在于next[i]根据next[0], next[1]…… next[i-1]的求解 next[i]的值依赖于前面的next数组的值,求解思路: 1. 首先取出前一个最长的匹配的前缀子串,其下标就是next[i-1] 2. 对比下一个字符,如果匹配,直接赋值next[i]为next[i-1]+1,因为i-1的时候已经是最长 *3. 如果不匹配,需要递归去找次长的匹配的前缀子串,这里难理解的就是递归地方式,next[i-1] 是i-1的最长匹配前缀子串的下标结尾,则 *next[next[i-1]]* 是其次长匹配前缀子串的下标 结尾 *4. 递归的出口,就是在次长前缀子串的下一个字符和当前匹配 或 遇到-1,遇到-1则说明没找到任 何匹配的前缀子串,这时需要找pattern的第一个字符对比 ps: next[m-1]的数值其实没有任何意义,求解时可以不理。网上也有将next数组往右平移的做法。 :param pattern: :return: """ m = len(pattern) next = [-1] * m next[0] = -1 # for i in range(1, m): for i in range(1, m-1): j = next[i-1] # 取i-1时匹配到的最长前缀子串 while j != -1 and pattern[j+1] != pattern[i]: j = next[j] # 次长的前缀子串的下标,即是next[next[i-1]] # 根据上面跳出while的条件,当j=-1时,需要比较pattern[0]和当前字符 # 如果j!=-1,则pattern[j+1]和pattern[i]一定是相等的 if pattern[j+1] == pattern[i]: # 如果接下来的字符也是匹配的,那i的最长前缀子串下标是next[i-1]+1 j += 1 next[i] = j return next if __name__ == '__main__': m_str = "aabbbbaaabbababbabbbabaaabb" p_str = "abbabbbabaa" print('--- search ---') print('[Built-in Functions] result:', m_str.find(p_str)) print('[kmp] result:', kmp(m_str, p_str)) ================================================ FILE: python/35_trie/trie.py ================================================ """ Author: Wenru Dong """ class TrieNode: def __init__(self, data: str): self._data = data self._children = [None] * 26 self._is_ending_char = False class Trie: def __init__(self): self._root = TrieNode("/") def insert(self, text: str) -> None: node = self._root for index, char in map(lambda x: (ord(x) - ord("a"), x), text): if not node._children[index]: node._children[index] = TrieNode(char) node = node._children[index] node._is_ending_char = True def find(self, pattern: str) -> bool: node = self._root for index in map(lambda x: ord(x) - ord("a"), pattern): if not node._children[index]: return False node = node._children[index] return node._is_ending_char if __name__ == "__main__": strs = ["how", "hi", "her", "hello", "so", "see"] trie = Trie() for s in strs: trie.insert(s) for s in strs: print(trie.find(s)) print(trie.find("swift")) ================================================ FILE: python/35_trie/trie_.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from queue import Queue import pygraphviz as pgv OUTPUT_PATH = 'E:/' class Node: def __init__(self, c): self.data = c self.is_ending_char = False # 使用有序数组,降低空间消耗,支持更多字符 self.children = [] def insert_child(self, c): self._insert_child(Node(c)) def _insert_child(self, node): """ 插入一个子节点 :param c: :return: """ v = ord(node.data) idx = self._find_insert_idx(v) length = len(self.children) if idx == length: self.children.append(node) else: self.children.append(None) for i in range(length, idx, -1): self.children[i] = self.children[i-1] self.children[idx] = node def has_child(self, c): return True if self.get_child(c) is not None else False def get_child(self, c): """ 搜索子节点并返回 :param c: :return: """ start = 0 end = len(self.children) - 1 v = ord(c) while start <= end: mid = (start + end)//2 if v == ord(self.children[mid].data): return self.children[mid] elif v < ord(self.children[mid].data): end = mid - 1 else: start = mid + 1 # 找不到返回None return None def _find_insert_idx(self, v): """ 二分查找,找到有序数组的插入位置 :param v: :return: """ start = 0 end = len(self.children) - 1 while start <= end: mid = (start + end)//2 if v < ord(self.children[mid].data): end = mid - 1 else: if mid + 1 == len(self.children) or v < ord(self.children[mid+1].data): return mid + 1 else: start = mid + 1 # v < self.children[0] return 0 def __repr__(self): return 'node value: {}'.format(self.data) + '\n' \ + 'children:{}'.format([n.data for n in self.children]) class Trie: def __init__(self): self.root = Node(None) def gen_tree(self, string_list): """ 创建trie树 1. 遍历每个字符串的字符,从根节点开始,如果没有对应子节点,则创建 2. 每一个串的末尾节点标注为红色(is_ending_char) :param string_list: :return: """ for string in string_list: n = self.root for c in string: if n.get_child(c) is None: n.insert_child(c) n = n.get_child(c) n.is_ending_char = True def search(self, pattern): """ 搜索 1. 遍历模式串的字符,从根节点开始搜索,如果途中子节点不存在,返回False 2. 遍历完模式串,则说明模式串存在,再检查树中最后一个节点是否为红色,是 则返回True,否则False :param pattern: :return: """ assert type(pattern) is str and len(pattern) > 0 n = self.root for c in pattern: if n.get_child(c) is None: return False n = n.get_child(c) return True if n.is_ending_char is True else False def draw_img(self, img_name='Trie.png'): """ 画出trie树 :param img_name: :return: """ if self.root is None: return tree = pgv.AGraph('graph foo {}', strict=False, directed=False) # root nid = 0 color = 'black' tree.add_node(nid, color=color, label='None') q = Queue() q.put((self.root, nid)) while not q.empty(): n, pid = q.get() for c in n.children: nid += 1 q.put((c, nid)) color = 'red' if c.is_ending_char is True else 'black' tree.add_node(nid, color=color, label=c.data) tree.add_edge(pid, nid) tree.graph_attr['epsilon'] = '0.01' tree.layout('dot') tree.draw(OUTPUT_PATH + img_name) return True if __name__ == '__main__': string_list = ['abc', 'abd', 'abcc', 'accd', 'acml', 'P@trick', 'data', 'structure', 'algorithm'] print('--- gen trie ---') print(string_list) trie = Trie() trie.gen_tree(string_list) # trie.draw_img() print('\n') print('--- search result ---') search_string = ['a', 'ab', 'abc', 'abcc', 'abe', 'P@trick', 'P@tric', 'Patrick'] for ss in search_string: print('[pattern]: {}'.format(ss), '[result]: {}'.format(trie.search(ss))) ================================================ FILE: python/36_ac_automata/ac_automata.py ================================================ """ Aho-Corasick Algorithm Author: Wenru Dong """ from collections import deque from typing import List class ACNode: def __init__(self, data: str): self._data = data self._children = [None] * 26 self._is_ending_char = False self._length = -1 self._suffix = None class ACAutomata: def __init__(self): self._root = ACNode("/") def _build_suffix_link(self) -> None: q = deque() q.append(self._root) while q: node = q.popleft() for child in node._children: if child: if node == self._root: child._suffix = self._root else: suffix = node._suffix while suffix: suffix_child = suffix._children[ord(child._data) - ord("a")] if suffix_child: child._suffix = suffix_child break suffix = suffix._suffix if not suffix: child._suffix = self._root q.append(child) def _insert(self, text: str) -> None: node = self._root for index, char in map(lambda x: (ord(x) - ord("a"), x), text): if not node._children[index]: node._children[index] = ACNode(char) node = node._children[index] node._is_ending_char = True node._length = len(text) def insert(self, patterns: List[str]) -> None: for pattern in patterns: self._insert(pattern) self._build_suffix_link() def match(self, text: str) -> None: node = self._root for i, char in enumerate(text): index = ord(char) - ord("a") while not node._children[index] and node != self._root: node = node._suffix node = node._children[index] if not node: node = self._root tmp = node while tmp != self._root: if tmp._is_ending_char: print(f"匹配起始下标{i - tmp._length + 1},长度{tmp._length}") tmp = tmp._suffix if __name__ == "__main__": patterns = ["at", "art", "oars", "soar"] ac = ACAutomata() ac.insert(patterns) ac.match("soarsoars") ================================================ FILE: python/36_ac_automata/ac_automata_.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from trie_ import Node, Trie from queue import Queue class ACNode(Node): def __init__(self, c: str): super(ACNode, self).__init__(c) self.fail = None self.length = 0 def insert_child(self, c: str): self._insert_child(ACNode(c)) class ACTrie(Trie): def __init__(self): self.root = ACNode(None) def ac_automata(main: str, ac_trie: ACTrie) -> list: root = ac_trie.root build_failure_pointer(ac_trie) ret = [] p = root for i, c in enumerate(main): while p != root and not p.has_child(c): p = p.fail if p.has_child(c): # a char matched, try to find all potential pattern matched q = p.get_child(c) while q != root: if q.is_ending_char: ret.append((i-q.length+1, i)) # ret.append(main[i-q.length+1:i+1]) q = q.fail p = p.get_child(c) return ret def build_failure_pointer(ac_trie: ACTrie) -> None: root = ac_trie.root # queue: [(node, node.length) ....] node_queue = Queue() node_queue.put((root, root.length)) root.fail = None while not node_queue.empty(): p, length = node_queue.get() for pc in p.children: pc.length = length + 1 if p == root: pc.fail = root else: q = p.fail # same as kmp while q != root and not q.has_child(pc.data): q = q.fail # cases now: # 1. q == root # 2. q != root and q.has_child(pc.data) if q.has_child(pc.data): pc.fail = q.get_child(pc.data) else: pc.fail = root node_queue.put((pc, pc.length)) if __name__ == '__main__': ac_trie = ACTrie() ac_trie.gen_tree(['fuck', 'shit', 'TMD', '傻叉']) print('--- ac automata ---') m_str = 'fuck you, what is that shit, TMD你就是个傻叉傻叉傻叉叉' print('original str : {}'.format(m_str)) filter_range_list = ac_automata(m_str, ac_trie) str_filtered = m_str for start, end in filter_range_list: str_filtered = str_filtered.replace(str_filtered[start:end+1], '*'*(end+1-start)) print('after filtered: {}'.format(str_filtered)) ================================================ FILE: python/38_divide_and_conquer/merge_sort_counting.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- inversion_num = 0 def merge_sort_counting(nums, start, end): if start >= end: return mid = (start + end)//2 merge_sort_counting(nums, start, mid) merge_sort_counting(nums, mid+1, end) merge(nums, start, mid, end) def merge(nums, start, mid, end): global inversion_num i = start j = mid+1 tmp = [] while i <= mid and j <= end: if nums[i] <= nums[j]: inversion_num += j - mid - 1 tmp.append(nums[i]) i += 1 else: tmp.append(nums[j]) j += 1 while i <= mid: # 这时nums[i]的逆序数是整个nums[mid+1: end+1]的长度 inversion_num += end - mid tmp.append(nums[i]) i += 1 while j <= end: tmp.append(nums[j]) j += 1 nums[start: end+1] = tmp if __name__ == '__main__': print('--- count inversion number using merge sort ---') # nums = [5, 0, 4, 2, 3, 1, 6, 8, 7] nums = [5, 0, 4, 2, 3, 1, 3, 3, 3, 6, 8, 7] print('nums : {}'.format(nums)) merge_sort_counting(nums, 0, len(nums)-1) print('sorted: {}'.format(nums)) print('inversion number: {}'.format(inversion_num)) ================================================ FILE: python/39_back_track/01_bag.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List # 背包选取的物品列表 picks = [] picks_with_max_value = [] def bag(capacity: int, cur_weight: int, items_info: List, pick_idx: int): """ 回溯法解01背包,穷举 :param capacity: 背包容量 :param cur_weight: 背包当前重量 :param items_info: 物品的重量和价值信息 :param pick_idx: 当前物品的索引 :return: """ # 考察完所有物品,或者在中途已经装满 if pick_idx >= len(items_info) or cur_weight == capacity: global picks_with_max_value if get_value(items_info, picks) > \ get_value(items_info, picks_with_max_value): picks_with_max_value = picks.copy() else: item_weight = items_info[pick_idx][0] if cur_weight + item_weight <= capacity: # 选 picks[pick_idx] = 1 bag(capacity, cur_weight + item_weight, items_info, pick_idx + 1) picks[pick_idx] = 0 # 不选 bag(capacity, cur_weight, items_info, pick_idx + 1) def get_value(items_info: List, pick_items: List): values = [_[1] for _ in items_info] return sum([a*b for a, b in zip(values, pick_items)]) if __name__ == '__main__': # [(weight, value), ...] items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)] capacity = 8 print('--- items info ---') print(items_info) print('\n--- capacity ---') print(capacity) picks = [0] * len(items_info) bag(capacity, 0, items_info, 0) print('\n--- picks ---') print(picks_with_max_value) print('\n--- value ---') print(get_value(items_info, picks_with_max_value)) ================================================ FILE: python/39_back_track/eight_queens.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- # 棋盘尺寸 BOARD_SIZE = 8 solution_count = 0 queen_list = [0] * BOARD_SIZE def eight_queens(cur_column: int): """ 输出所有符合要求的八皇后序列 用一个长度为8的数组代表棋盘的列,数组的数字则为当前列上皇后所在的行数 :return: """ if cur_column >= BOARD_SIZE: global solution_count solution_count += 1 # 解 print(queen_list) else: for i in range(BOARD_SIZE): if is_valid_pos(cur_column, i): queen_list[cur_column] = i eight_queens(cur_column + 1) def is_valid_pos(cur_column: int, pos: int) -> bool: """ 因为采取的是每列放置1个皇后的做法 所以检查的时候不必检查列的合法性,只需要检查行和对角 1. 行:检查数组在下标为cur_column之前的元素是否已存在pos 2. 对角:检查数组在下标为cur_column之前的元素,其行的间距pos - QUEEN_LIST[i] 和列的间距cur_column - i是否一致 :param cur_column: :param pos: :return: """ i = 0 while i < cur_column: # 同行 if queen_list[i] == pos: return False # 对角线 if cur_column - i == abs(pos - queen_list[i]): return False i += 1 return True if __name__ == '__main__': print('--- eight queens sequence ---') eight_queens(0) print('\n--- solution count ---') print(solution_count) ================================================ FILE: python/39_back_track/permutations.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List permutations_list = [] # 全局变量,用于记录每个输出 def permutations(nums: List, n: int, pick_count: int): """ 从nums选取n个数的全排列 回溯法,用一个栈记录当前路径信息 当满足n==0时,说明栈中的数已足够,输出并终止遍历 :param nums: :param n: :param pick_count: :return: """ if n == 0: print(permutations_list) else: for i in range(len(nums) - pick_count): permutations_list[pick_count] = nums[i] nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i] permutations(nums, n-1, pick_count+1) nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i] if __name__ == '__main__': nums = [1, 2, 3, 4] n = 3 print('--- list ---') print(nums) print('\n--- pick num ---') print(n) print('\n--- permutation list ---') permutations_list = [0] * n permutations(nums, n, 0) ================================================ FILE: python/39_back_track/regex.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- is_match = False def rmatch(r_idx: int, m_idx: int, regex: str, main: str): global is_match if is_match: return if r_idx >= len(regex): # 正则串全部匹配好了 is_match = True return if m_idx >= len(main) and r_idx < len(regex): # 正则串没匹配完,但是主串已经没得匹配了 is_match = False return if regex[r_idx] == '*': # * 匹配1个或多个任意字符,递归搜索每一种情况 for i in range(m_idx, len(main)): rmatch(r_idx+1, i+1, regex, main) elif regex[r_idx] == '?': # ? 匹配0个或1个任意字符,两种情况 rmatch(r_idx+1, m_idx+1, regex, main) rmatch(r_idx+1, m_idx, regex, main) else: # 非特殊字符需要精确匹配 if regex[r_idx] == main[m_idx]: rmatch(r_idx+1, m_idx+1, regex, main) if __name__ == '__main__': regex = 'ab*eee?d' main = 'abcdsadfkjlekjoiwjiojieeecd' rmatch(0, 0, regex, main) print(is_match) ================================================ FILE: python/39_backtracking/backtracking.py ================================================ """ Author: Wenru Dong """ from typing import List def eight_queens() -> None: solutions = [] def backtracking(queens_at_column: List[int], index_sums: List[int], index_diffs: List[int]) -> None: row = len(queens_at_column) if row == 8: solutions.append(queens_at_column) return for col in range(8): if col in queens_at_column or row + col in index_sums or row - col in index_diffs: continue backtracking(queens_at_column + [col], index_sums + [row + col], index_diffs + [row - col]) backtracking([], [], []) print(*(" " + " ".join("*" * i + "Q" + "*" * (8 - i - 1) + "\n" for i in solution) for solution in solutions), sep="\n") if __name__ == "__main__": eight_queens() ================================================ FILE: python/40_dynamic_programming/01_bag.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List, Tuple def bag(items_info: List[int], capacity: int) -> int: """ 固定容量的背包,计算能装进背包的物品组合的最大重量 :param items_info: 每个物品的重量 :param capacity: 背包容量 :return: 最大装载重量 """ n = len(items_info) memo = [[-1]*(capacity+1) for i in range(n)] memo[0][0] = 1 if items_info[0] <= capacity: memo[0][items_info[0]] = 1 for i in range(1, n): for cur_weight in range(capacity+1): if memo[i-1][cur_weight] != -1: memo[i][cur_weight] = memo[i-1][cur_weight] # 不选 if cur_weight + items_info[i] <= capacity: # 选 memo[i][cur_weight + items_info[i]] = 1 for w in range(capacity, -1, -1): if memo[-1][w] != -1: return w def bag_with_max_value(items_info: List[Tuple[int, int]], capacity: int) -> int: """ 固定容量的背包,计算能装进背包的物品组合的最大价值 :param items_info: 物品的重量和价值 :param capacity: 背包容量 :return: 最大装载价值 """ n = len(items_info) memo = [[-1]*(capacity+1) for i in range(n)] memo[0][0] = 0 if items_info[0][0] <= capacity: memo[0][items_info[0][0]] = items_info[0][1] for i in range(1, n): for cur_weight in range(capacity+1): if memo[i-1][cur_weight] != -1: memo[i][cur_weight] = memo[i-1][cur_weight] if cur_weight + items_info[i][0] <= capacity: memo[i][cur_weight + items_info[i][0]] = max(memo[i][cur_weight + items_info[i][0]], memo[i-1][cur_weight] + items_info[i][1]) return max(memo[-1]) if __name__ == '__main__': # [weight, ...] items_info = [2, 2, 4, 6, 3] capacity = 9 print(bag(items_info, capacity)) # [(weight, value), ...] items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)] capacity = 8 print(bag_with_max_value(items_info, capacity)) ================================================ FILE: python/40_dynamic_programming/knapsack.py ================================================ """ Author: Wenru Dong """ from typing import List def knapsack01(weights: List[int], values: List[int], capacity: int) -> int: # Denote the state as (i, c), where i is the stage number, # and c is the capacity available. Denote f(i, c) to be the # maximum value when the capacity available is c, and Item 0 # to Item i-1 are to be packed. # The goal is to find f(n-1, W), where W is the total capacity. # Then the DP functional equation is: # f(i, c) = max(xᵢvᵢ + f(i-1, c-xᵢwᵢ)), xᵢ ∈ D, i ≥ 0, # f(-1, c) = 0, 0 ≤ c ≤ W, # where # / {0}, if wᵢ > c # D = D(i, c) = # \ {0, 1}, if wᵢ ≤ c prev = [0] * (capacity + 1) for w, v in zip(weights, values): prev = [c >= w and max(prev[c], prev[c-w] + v) for c in range(capacity + 1)] return prev[-1] if __name__ == "__main__": # To find the maximum weight that can be packed, # set values equal to the weights print(knapsack01([2, 2, 4, 6, 3], [2, 2, 4, 6, 3], 9)) ================================================ FILE: python/40_dynamic_programming/yh_triangle.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List Layer_nums = List[int] def yh_triangle(nums: List[Layer_nums]) -> int: """ 从根节点开始向下走,过程中经过的节点,只需存储经过它时最小的路径和 :param nums: :return: """ assert len(nums) > 0 n = len(nums) # 层数 memo = [[0]*n for i in range(n)] memo[0][0] = nums[0][0] for i in range(1, n): for j in range(i+1): # 每一层首尾两个数字,只有一条路径可以到达 if j == 0: memo[i][j] = memo[i-1][j] + nums[i][j] elif j == i: memo[i][j] = memo[i-1][j-1] + nums[i][j] else: memo[i][j] = min(memo[i-1][j-1] + nums[i][j], memo[i-1][j] + nums[i][j]) return min(memo[n-1]) def yh_triangle_space_optimization(nums: List[Layer_nums]) -> int: assert len(nums) > 0 n = len(nums) memo = [0] * n memo[0] = nums[0][0] for i in range(1, n): for j in range(i, -1, -1): if j == i: memo[j] = memo[j-1] + nums[i][j] elif j == 0: memo[j] = memo[j] + nums[i][j] else: memo[j] = min(memo[j-1] + nums[i][j], memo[j] + nums[i][j]) return min(memo) def yh_triangle_bottom_up(nums: List[Layer_nums]) -> int: assert len(nums) > 0 n = len(nums) memo = nums[-1].copy() for i in range(n-1, 0, -1): for j in range(i): memo[j] = min(memo[j] + nums[i-1][j], memo[j+1] + nums[i-1][j]) return memo[0] if __name__ == '__main__': nums = [[3], [2, 6], [5, 4, 2], [6, 0, 3, 2]] print(yh_triangle(nums)) print(yh_triangle_space_optimization(nums)) print(yh_triangle_bottom_up(nums)) ================================================ FILE: python/41_dynamic_programming/coins_problem.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List def coins_dp(values: List[int], target: int) -> int: # memo[i]表示target为i的时候,所需的最少硬币数 memo = [0] * (target+1) # 0元的时候为0个 memo[0] = 0 for i in range(1, target+1): min_num = 999999 # 对于values中的所有n # memo[i]为 min(memo[i-n1], memo[i-n2], ...) + 1 for n in values: if i >= n: min_num = min(min_num, 1 + memo[i-n]) else: # values中的数值要从小到大排序 break memo[i] = min_num # print(memo) return memo[-1] min_num = 999999 def coins_backtracking(values: List[int], target: int, cur_value: int, coins_count: int): if cur_value == target: global min_num min_num = min(coins_count, min_num) else: for n in values: if cur_value + n <= target: coins_backtracking(values, target, cur_value+n, coins_count+1) if __name__ == '__main__': values = [1, 3, 5] target = 23 print(coins_dp(values, target)) coins_backtracking(values, target, 0, 0) print(min_num) ================================================ FILE: python/41_dynamic_programming/min_dist.py ================================================ """ Author: Wenru Dong """ from typing import List from itertools import accumulate def min_dist(weights: List[List[int]]) -> int: """Find the minimum weight path from the weights matrix.""" m, n = len(weights), len(weights[0]) table = [[0] * n for _ in range(m)] # table[i][j] is the minimum distance (weight) when # there are i vertical moves and j horizontal moves # left. table[0] = list(accumulate(reversed(weights[-1]))) for i, v in enumerate(accumulate(row[-1] for row in reversed(weights))): table[i][0] = v for i in range(1, m): for j in range(1, n): table[i][j] = weights[~i][~j] + min(table[i - 1][j], table[i][j - 1]) return table[-1][-1] def min_dist_recur(weights: List[List[int]]) -> int: m, n = len(weights), len(weights[0]) table = [[0] * n for _ in range(m)] def min_dist_to(i: int, j: int) -> int: if i == j == 0: return weights[0][0] if table[i][j]: return table[i][j] min_left = float("inf") if j - 1 < 0 else min_dist_to(i, j - 1) min_up = float("inf") if i - 1 < 0 else min_dist_to(i - 1, j) return weights[i][j] + min(min_left, min_up) return min_dist_to(m - 1, n - 1) if __name__ == "__main__": weights = [[1, 3, 5, 9], [2, 1, 3, 4], [5, 2, 6, 7], [6, 8, 4, 3]] print(min_dist(weights)) print(min_dist_recur(weights)) ================================================ FILE: python/42_dynamic_programming/longest_increasing_subsequence.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List def longest_increasing_subsequence(nums: List[int]) -> int: """ 最长子上升序列的一种DP解法,从回溯解法转化,思路类似于有限物品的背包问题 每一次决策都算出当前可能的lis的长度,重复子问题合并,合并策略是lis的末尾元素最小 时间复杂度:O(n^2) 空间复杂度:O(n^2),可优化至O(n) 没leetcode上的参考答案高效,提供另一种思路作为参考 https://leetcode.com/problems/longest-increasing-subsequence/solution/ :param nums: :return: """ if not nums: return 0 n = len(nums) # memo[i][j] 表示第i次决策,长度为j的lis的 最小的 末尾元素数值 # 每次决策都根据上次决策的所有可能转化,空间上可以类似背包优化为O(n) memo = [[-1] * (n+1) for _ in range(n)] # 第一列全赋值为0,表示每次决策都不选任何数 for i in range(n): memo[i][0] = 0 # 第一次决策选数组中的第一个数 memo[0][1] = nums[0] for i in range(1, n): for j in range(1, n+1): # case 1: 长度为j的lis在上次决策后存在,nums[i]比长度为j-1的lis末尾元素大 if memo[i-1][j] != -1 and nums[i] > memo[i-1][j-1]: memo[i][j] = min(nums[i], memo[i-1][j]) # case 2: 长度为j的lis在上次决策后存在,nums[i]比长度为j-1的lis末尾元素小/等 if memo[i-1][j] != -1 and nums[i] <= memo[i-1][j-1]: memo[i][j] = memo[i-1][j] if memo[i-1][j] == -1: # case 3: 长度为j的lis不存在,nums[i]比长度为j-1的lis末尾元素大 if nums[i] > memo[i-1][j-1]: memo[i][j] = nums[i] # case 4: 长度为j的lis不存在,nums[i]比长度为j-1的lis末尾元素小/等 break for i in range(n, -1, -1): if memo[-1][i] != -1: return i if __name__ == '__main__': # 要求输入的都是大于0的正整数(可优化至支持任意整数) nums = [2, 9, 3, 6, 5, 1, 7] print(longest_increasing_subsequence(nums)) ================================================ FILE: python/42_dynamic_programming/min_edit_dist.py ================================================ """ Author: Wenru Dong """ def levenshtein_dp(s: str, t: str) -> int: m, n = len(s), len(t) table = [[0] * (n) for _ in range(m)] for i in range(n): if s[0] == t[i]: table[0][i] = i - 0 elif i != 0: table[0][i] = table[0][i - 1] + 1 else: table[0][i] = 1 for i in range(m): if s[i] == t[0]: table[i][0] = i - 0 elif i != 0: table[i][0] = table[i - 1][0] + 1 else: table[i][0] = 1 for i in range(1, m): for j in range(1, n): table[i][j] = min(1 + table[i - 1][j], 1 + table[i][j - 1], int(s[i] != t[j]) + table[i - 1][j - 1]) print(table) return table[-1][-1] def common_substring_dp(s: str, t: str) -> int: m, n = len(s), len(t) table = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): table[i][j] = max(table[i - 1][j], table[i][j - 1], int(s[i - 1] == t[j - 1]) + table[i - 1][j - 1]) return table[-1][-1] if __name__ == "__main__": s = "mitcmu" t = "mtacnu" print(levenshtein_dp(s, t)) print(common_substring_dp(s, t)) s = "kitten" t = "sitting" print(levenshtein_dp(s, t)) print(common_substring_dp(s, t)) s = "flaw" t = "lawn" print(levenshtein_dp(s, t)) print(common_substring_dp(s, t)) ================================================ FILE: python/43_topological_sorting/topological_sorting.py ================================================ """ Author: Wenru Dong """ from collections import deque from itertools import filterfalse class Graph: def __init__(self, num_vertices: int): self._num_vertices = num_vertices self._adjacency = [[] for _ in range(num_vertices)] def add_edge(self, s: int, t: int) -> None: self._adjacency[s].append(t) def tsort_by_kahn(self) -> None: in_degree = [0] * self._num_vertices for v in range(self._num_vertices): if len(self._adjacency[v]): for neighbour in self._adjacency[v]: in_degree[neighbour] += 1 q = deque(filterfalse(lambda x: in_degree[x], range(self._num_vertices))) while q: v = q.popleft() print(f"{v} -> ", end="") for neighbour in self._adjacency[v]: in_degree[neighbour] -= 1 if not in_degree[neighbour]: q.append(neighbour) print("\b\b\b ") def tsort_by_dfs(self) -> None: inverse_adjacency = [[] for _ in range(self._num_vertices)] for v in range(self._num_vertices): if len(self._adjacency[v]): for neighbour in self._adjacency[v]: inverse_adjacency[neighbour].append(v) visited = [False] * self._num_vertices def dfs(vertex: int) -> None: if len(inverse_adjacency[vertex]): for v in inverse_adjacency[vertex]: if not visited[v]: visited[v] = True dfs(v) print(f"{vertex} -> ", end="") for v in range(self._num_vertices): if not visited[v]: visited[v] = True dfs(v) print("\b\b\b ") if __name__ == "__main__": dag = Graph(4) dag.add_edge(1, 0) dag.add_edge(2, 1) dag.add_edge(1, 3) dag.tsort_by_kahn() dag.tsort_by_dfs() ================================================ FILE: python/44_shortest_path/dijkstra.py ================================================ #!/usr/bin/python # -*- coding: UTF-8 -*- from typing import List, Generator import heapq class Graph: def __init__(self, vertex_count: int) -> None: self.adj = [[] for _ in range(vertex_count)] def add_edge(self, s: int, t: int, w: int) -> None: edge = Edge(s, t, w) self.adj[s].append(edge) def __len__(self) -> int: return len(self.adj) class Vertex: def __init__(self, v: int, dist: int) -> None: self.id = v self.dist = dist def __gt__(self, other) -> bool: return self.dist > other.dist def __repr__(self) -> str: return str((self.id, self.dist)) class Edge: def __init__(self, source: int, target: int, weight: int) -> None: self.s = source self.t = target self.w = weight class VertexPriorityQueue: def __init__(self) -> None: self.vertices = [] def get(self) -> Vertex: return heapq.heappop(self.vertices) def put(self, v: Vertex) -> None: self.vertices.append(v) self.update_priority() def empty(self) -> bool: return len(self.vertices) == 0 def update_priority(self) -> None: heapq.heapify(self.vertices) def __repr__(self) -> str: return str(self.vertices) def dijkstra(g: Graph, s: int, t: int) -> int: size = len(g) pq = VertexPriorityQueue() # 节点队列 in_queue = [False] * size # 已入队标记 vertices = [ # 需要随时更新离s的最短距离的节点列表 Vertex(v, float('inf')) for v in range(size) ] predecessor = [-1] * size # 先驱 vertices[s].dist = 0 pq.put(vertices[s]) in_queue[s] = True while not pq.empty(): v = pq.get() if v.id == t: break for edge in g.adj[v.id]: if v.dist + edge.w < vertices[edge.t].dist: # 当修改了pq中的元素的优先级后: # 1. 有入队操作:触发了pq的堆化,此后出队可以取到优先级最高的顶点 # 2. 无入队操作:此后出队取到的顶点可能不是优先级最高的,会有bug # 为确保正确,需要手动更新一次 vertices[edge.t].dist = v.dist + edge.w predecessor[edge.t] = v.id pq.update_priority() # 更新堆结构 if not in_queue[edge.t]: pq.put(vertices[edge.t]) in_queue[edge.t] = True for n in print_path(s, t, predecessor): if n == t: print(t) else: print(n, end=' -> ') return vertices[t].dist def print_path(s: int, t: int, p: List[int]) -> Generator[int, None, None]: if t == s: yield s else: yield from print_path(s, p[t], p) yield t if __name__ == '__main__': g = Graph(6) g.add_edge(0, 1, 10) g.add_edge(0, 4, 15) g.add_edge(1, 2, 15) g.add_edge(1, 3, 2) g.add_edge(2, 5, 5) g.add_edge(3, 2, 1) g.add_edge(3, 5, 12) g.add_edge(4, 5, 10) print(dijkstra(g, 0, 5)) # 下面这个用例可以暴露更新队列元素优先级的问题 # g = Graph(4) # g.add_edge(0, 1, 18) # g.add_edge(0, 2, 3) # g.add_edge(2, 1, 1) # g.add_edge(1, 3, 5) # g.add_edge(2, 3, 8) # g.add_edge(0, 3, 15) # print(dijkstra(g, 0, 3)) ================================================ FILE: python/44_shortest_path/shortest_path.py ================================================ """ Dijkstra algorithm Author: Wenru Dong """ from dataclasses import dataclass from queue import PriorityQueue @dataclass class Edge: start_id: int end_id: int weight: int @dataclass(order=True) class Vertex: distance_to_start = float("inf") vertex_id: int class Graph: def __init__(self, num_vertices: int): self._num_vertices = num_vertices self._adjacency = [[] for _ in range(num_vertices)] def add_edge(self, from_vertex: int, to_vertex: int, weight: int) -> None: self._adjacency[from_vertex].append(Edge(from_vertex, to_vertex, weight)) def dijkstra(self, from_vertex: int, to_vertex: int) -> None: vertices = [Vertex(i) for i in range(self._num_vertices)] vertices[from_vertex].distance_to_start = 0 visited = [False] * self._num_vertices predecessor = [-1] * self._num_vertices q = PriorityQueue() q.put(vertices[from_vertex]) visited[from_vertex] = True while not q.empty(): min_vertex = q.get() if min_vertex.vertex_id == to_vertex: break for edge in self._adjacency[min_vertex.vertex_id]: next_vertex = vertices[edge.end_id] if min_vertex.distance_to_start + edge.weight < next_vertex.distance_to_start: next_vertex.distance_to_start = min_vertex.distance_to_start + edge.weight predecessor[next_vertex.vertex_id] = min_vertex.vertex_id if not visited[next_vertex.vertex_id]: q.put(next_vertex) visited[next_vertex.vertex_id] = True path = lambda x: path(predecessor[x]) + [str(x)] if from_vertex != x else [str(from_vertex)] print("->".join(path(to_vertex))) if __name__ == "__main__": graph = Graph(6) graph.add_edge(0, 1, 10) graph.add_edge(0, 4, 15) graph.add_edge(1, 2, 15) graph.add_edge(1, 3, 2) graph.add_edge(2, 5, 5) graph.add_edge(3, 2, 1) graph.add_edge(3, 5, 12) graph.add_edge(4, 5, 10) graph.dijkstra(0, 5) ================================================ FILE: python/45_bitmap/bitmap.py ================================================ """ Author: Wenru Dong """ from typing import Optional class Bitmap: def __init__(self, num_bits: int): self._num_bits = num_bits self._bytes = bytearray(num_bits // 8 + 1) def setbit(self, k: int) -> None: if k > self._num_bits or k < 1: return self._bytes[k // 8] |= (1 << k % 8) def getbit(self, k: int) -> Optional[bool]: if k > self._num_bits or k < 1: return return self._bytes[k // 8] & (1 << k % 8) != 0 if __name__ == "__main__": bitmap = Bitmap(10) bitmap.setbit(1) bitmap.setbit(3) bitmap.setbit(6) bitmap.setbit(7) bitmap.setbit(8) for i in range(1, 11): print(bitmap.getbit(i)) ================================================ FILE: python/array.py ================================================ # 1.数组的插入、删除、按照下标随机访问操作; # 2.数组中的数据类型是Int # # Author:Lee class Array(): def __init__(self): '''数组类初始化方法.''' self.__data = [] # 数据存储List def find(self, index): '''数组的查找方法. 参数: index:将要查找的数据的下标 返回: 如果查找成功,则返回找到的数据 如果查找失败,则返回False ''' if index > len(self.__data) or index < 0: return False else: return self.__data[index] def delete(self, index): '''数组的删除方法. 参数: index:将要删除的数据的下标 返回: 如果删除成功,则返回True 如果删除失败,则返回False ''' if index > len(self.__data) or index < 0: return False else: self.__data.pop(index) return True def insert(self, index, value): '''数组插入数据操作. 参数: index:将要插入的下标 value:将要插入的数据 返回: 如果插入成功,则返回True 如果插入失败,则返回False ''' if index > len(self.__data) or index < 0: return False else: self.__data.insert(index, value) return True def insertToTail(self, value): '''直接在数组尾部插入数据. 参数: value:将要插入的数据 ''' self.__data.append(value) def printAll(self): '''打印当前数组所有数据''' print(self.__data) ================================================ FILE: rust/05_array/main.rs ================================================ struct NewArray { array: Vec, count: i32, } impl NewArray { fn new(capacity: usize) -> NewArray { NewArray { array: Vec::with_capacity(capacity), count: 0 } } fn find(&self, index: usize) -> i32 { if index > self.count as usize { return -1; } self.array[index] } fn insert(&mut self, index: usize, value: i32) -> bool { let array_count = self.count as usize; if index > array_count || array_count == self.array.capacity() { return false; } if index == array_count { self.array.push(value); } else { let tmp_arr = self.array.clone(); self.array = Vec::with_capacity(self.array.capacity()); for i in 0..index { self.array.push(tmp_arr[i]); } self.array.push(value); for i in index..array_count { self.array.push(tmp_arr[i]); } } self.count += 1; true } fn remove(&mut self, index: usize) -> i32 { if index >= self.count as usize { return -1; } let result = self.array[index]; let tmp_arr = self.array.clone(); self.array = Vec::with_capacity(self.array.capacity()); for i in 0..index { self.array.push(tmp_arr[i]); } for i in index+1..self.count as usize { self.array.push(tmp_arr[i]); } self.count -=1; result } } fn main() { let mut new_array = NewArray::new(10); assert_eq!(new_array.insert(0, 3), true); assert_eq!(new_array.insert(1, 2), true); assert_eq!(new_array.insert(2, 8), true); assert_eq!(new_array.insert(0, 9), true); assert_eq!(new_array.insert(5, 7), false); assert_eq!(new_array.insert(4, 5), true); assert_eq!(new_array.find(3), 8); assert_eq!(new_array.find(12), -1); assert_eq!(new_array.remove(1), 3); assert_eq!(new_array.remove(9), -1); } ================================================ FILE: rust/07_linkedlist/linked_list_cycle.rs ================================================ use super::util::linked_list::{ListNode, to_list}; pub fn has_cycle(head: Option>) -> bool { let mut fast_p = &head; let mut slow_p = &head; while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() { slow_p = &slow_p.as_ref().unwrap().next; fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next; if slow_p == fast_p { return true; } } false } fn main() { println!("{:?}", has_cycle(to_list(vec![1, 2, 3, 4, 5]))); } ================================================ FILE: rust/07_linkedlist/merge_two_sorted_lists.rs ================================================ use super::util::linked_list::{ListNode, to_list}; pub fn merge_two_lists(l1: Option>, l2: Option>) -> Option> { match (l1, l2) { (Some(node1), None) => Some(node1), (None, Some(node2)) => Some(node2), (Some(mut node1), Some(mut node2)) => { if node1.val < node2.val { let n = node1.next.take(); node1.next = Solution::merge_two_lists(n, Some(node2)); Some(node1) } else { let n = node2.next.take(); node2.next = Solution::merge_two_lists(Some(node1), n); Some(node2) } }, _ => None, } } fn main() { println!("{:?}", merge_two_lists(to_list(vec![1, 3, 4]), to_list(vec![1, 2, 4]))); } ================================================ FILE: rust/07_linkedlist/middle_of_the_linked_list.rs ================================================ use super::util::linked_list::{ListNode, to_list}; pub fn middle_node(head: Option>) -> Option> { let mut fast_p = &head; let mut slow_p = &head; while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() { slow_p = &slow_p.as_ref().unwrap().next; fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next; } slow_p.clone() } fn main() { println!("{:?}", middle_node(to_list(vec![1, 3, 4]))); } ================================================ FILE: rust/07_linkedlist/remove_nth_node_from_end_of_list.rs ================================================ use super::util::linked_list::{ListNode, to_list}; pub fn remove_nth_from_end(head: Option>, n: i32) -> Option> { let mut dummy = Some(Box::new(ListNode { val: 0, next: head })); let mut cur = &mut dummy; let mut length = 0; while let Some(_node) = cur.as_mut() { cur = &mut cur.as_mut().unwrap().next; if let Some(_inner_node) = cur { length += 1; } } let mut new_cur = dummy.as_mut(); let idx = length - n; for _ in 0..idx { new_cur = new_cur.unwrap().next.as_mut(); } let next = new_cur.as_mut().unwrap().next.as_mut().unwrap().next.take(); new_cur.as_mut().unwrap().next = next; dummy.unwrap().next } fn main() { println!("{:?}", remove_nth_from_end(to_list(vec![1, 3, 4]))); } ================================================ FILE: rust/07_linkedlist/reverse_linked_list.rs ================================================ use super::util::linked_list::{ListNode, to_list}; pub fn reverse_list(head: Option>) -> Option> { let mut prev = None; let mut curr = head; while let Some(mut boxed_node) = curr.take() { let next = boxed_node.next.take(); boxed_node.next = prev.take(); prev = Some(boxed_node); curr = next; } prev } fn main() { println!("{:?}", reverse_list(to_list(vec![1, 2, 3, 4, 5]))); } ================================================ FILE: rust/07_linkedlist/util/linked_list.rs ================================================ // Definition for singly-linked list. #[derive(PartialEq, Eq, Clone, Debug)] pub struct ListNode { pub val: i32, pub next: Option> } impl ListNode { #[inline] fn new(val: i32) -> Self { ListNode { next: None, val } } } pub fn to_list(vec: Vec) -> Option> { let mut current = None; for &v in vec.iter().rev() { let mut node = ListNode::new(v); node.next = current; current = Some(Box::new(node)); } current } #[macro_export] macro_rules! linked { ($($e:expr),*) => {to_list(vec![$($e.to_owned()), *])}; ($($e:expr,)*) => {to_list(vec![$($e.to_owned()), *])}; } ================================================ FILE: rust/08_stack/simple_browser.rs ================================================ mod stack_based_on_linked_list; use stack_based_on_linked_list::{LinkedListStack}; #[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] struct Browser { forward_stack: LinkedListStack, back_stack: LinkedListStack, current_page: String, } impl Browser { fn new() -> Self { Default::default() } fn open(&mut self, url: &String) { if !self.current_page.is_empty() { self.back_stack.push(self.current_page.clone()); self.forward_stack.clear(); } self.show_url(&url, "Open".to_string()); } fn go_back(&mut self) -> String { if self.can_go_back() { self.forward_stack.push(self.current_page.clone()); let back_url = self.back_stack.pop(); self.show_url(&back_url, "Back".to_string()); return back_url; } println!("Can not go back, there is no page behind."); "-1".to_string() } fn go_forward(&mut self) -> String { if self.can_go_forward() { self.back_stack.push(self.current_page.clone()); let forward_url = self.forward_stack.pop(); self.show_url(&forward_url, "Forward".to_string()); return forward_url; } println!("Can not go forward, there is no page behind."); "-1".to_string() } fn can_go_back(&self) -> bool { !self.back_stack.is_empty() } fn can_go_forward(&self) -> bool { !self.forward_stack.is_empty() } fn show_url(&mut self, url: &String, prefix: String) { self.current_page = url.to_string(); println!("{:?} page == {:?}", prefix, url); } fn check_current_page(&self) { println!("current page == {:?}", self.current_page); } } fn main() { let mut browser = Browser::new(); browser.open(&"http://www.baidu.com".to_string()); browser.open(&"http://news.baidu.com/".to_string()); browser.open(&"http://news.baidu.com/ent".to_string()); browser.go_back(); browser.go_back(); browser.go_forward(); browser.open(&"http://www.qq.com".to_string()); browser.go_forward(); browser.go_back(); browser.go_forward(); browser.go_back(); browser.go_back(); browser.go_back(); browser.go_back(); browser.check_current_page(); } ================================================ FILE: rust/08_stack/stack_based_on_array.rs ================================================ #[derive(Debug)] struct ArrayStack { data: Vec, top: i32, } impl ArrayStack { fn new() -> Self { ArrayStack { data: Vec::with_capacity(32), top: -1 } } fn push(&mut self, x: i32) { self.top += 1; if self.top == self.data.capacity() as i32 { let tmp_arr = self.data.clone(); self.data = Vec::with_capacity(self.data.capacity() * 2); for d in tmp_arr.into_iter() { self.data.push(d); } } self.data.push(x); } fn pop(&mut self) { if self.is_empty() { return; } self.top -= 1; self.data.pop(); } fn top(&self) -> i32 { if self.is_empty() { return -1; } *self.data.last().unwrap() } fn is_empty(&self) -> bool { if self.top < 0 { true } else { false } } } fn main() { let mut stack = ArrayStack::new(); stack.push(-2); stack.push(0); stack.push(-3); stack.pop(); println!("{:?}", stack.top()); } ================================================ FILE: rust/08_stack/stack_based_on_linked_list.rs ================================================ #[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] pub struct ListNode { val: String, next: Option>, } #[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] pub struct LinkedListStack { node: Option>, } impl ListNode { fn new(val: String) -> Self { ListNode { val: val, next: None } } } impl LinkedListStack { pub fn new() -> Self { Default::default() } pub fn push(&mut self, x: String) { let mut n = ListNode::new(x); n.next = self.node.clone(); self.node = Some(Box::new(n)); } pub fn pop(&mut self) -> String { if self.is_empty() { return "-1".to_string(); } let val = self.node.as_ref().unwrap().val.clone(); self.node = self.node.as_mut().unwrap().next.take(); val.to_string() } pub fn print_all(&mut self) { let mut list = String::from(""); while let Some(n) = self.node.as_mut() { list.push_str(&(n.val).to_string()); list.push_str("-->"); self.node = n.next.take(); } println!("{:?}", list); } pub fn clear(&mut self) { self.node = None; } pub fn is_empty(&self) -> bool { if self.node.is_none() { true } else { false } } } // // fn main() { // let mut stack = LinkedListStack::new(); // stack.push("https://www.baidu.com".to_string()); // stack.push("https://www.google.com".to_string()); // stack.pop(); // stack.push("https://twitter.com".to_string()); // stack.print_all(); // } ================================================ FILE: rust/09_queue/array_queue.rs ================================================ #[derive(Debug)] struct ArrayQueue { queue: Vec, head: i32, tail: i32, } impl ArrayQueue { fn new(n: usize) -> Self { ArrayQueue { queue: Vec::with_capacity(n), head: 0, tail: 0, } } fn enqueue(&mut self, num: i32) -> bool { let c = self.queue.capacity() as i32; // queue is full if self.head == 0 && self.tail == c { return false; } if self.tail == c { for i in 0..(self.tail-self.head) as usize { self.queue[i] = self.queue[self.head as usize + i]; } self.tail -= self.head; self.head = 0; self.queue[self.tail as usize] = num; } else { self.queue.push(num); } self.tail += 1; true } fn dequeue(&mut self) -> i32 { if self.head == self.tail { return -1; } let shift = self.queue[self.head as usize]; self.head += 1; shift } fn print_all(&self) { let mut s = String::from(""); for i in self.head..self.tail { s.push(self.queue[i as usize] as u8 as char); s.push_str("->"); } println!("{:?}", s); } } fn main() { let mut queue = ArrayQueue::new(3); queue.enqueue(2); queue.enqueue(2); queue.enqueue(2); queue.enqueue(2); queue.dequeue(); queue.dequeue(); queue.enqueue(4); queue.dequeue(); queue.print_all(); } ================================================ FILE: rust/09_queue/circle_queue.rs ================================================ #[derive(Debug)] struct CircleQueue { queue: Vec, head: i32, tail: i32, n: i32, } impl CircleQueue { fn new(n: i32) -> Self { CircleQueue { queue: vec![-1; n as usize], head: 0, tail: 0, n: n, } } fn enqueue(&mut self, num: i32) -> bool { if (self.tail + 1) % self.n == self.head { return false; } self.queue[self.tail as usize] = num; self.tail = (self.tail + 1) % self.n; true } fn dequeue(&mut self) -> i32 { if self.head == self.tail { return -1; } let shift = self.queue[self.head as usize]; self.head = (self.head + 1) % self.n; shift } fn print_all(&self) { let mut s = String::from(""); for i in self.head..self.tail { s.push(self.queue[i as usize] as u8 as char); s.push_str("->"); } println!("{:?}", s); } } fn main() { let mut queue = CircleQueue::new(10); queue.enqueue(2); queue.enqueue(2); queue.enqueue(2); queue.enqueue(2); queue.dequeue(); queue.dequeue(); queue.enqueue(4); queue.dequeue(); queue.print_all(); } ================================================ FILE: rust/09_queue/linked_list_queue.rs ================================================ #![feature(box_into_raw_non_null)] use std::ptr::NonNull; #[derive(Debug)] pub struct LinkedListQueue { head: Option>, tail: Option>, } pub struct Node { next: Option>, element: i32, } impl Node { fn new(element: i32) -> Self { Node { next: None, element, } } fn into_element(self: Box) -> i32 { self.element } } impl LinkedListQueue { pub fn new() -> Self { LinkedListQueue { head: None, tail: None, } } pub fn dequeue(&mut self) -> i32 { self.head.map(|node| unsafe { let node = Box::from_raw(node.as_ptr()); self.head = node.next; node }).map(Node::into_element).unwrap() } pub fn enqueue(&mut self, elt: i32) { let mut node = Box::new(Node::new(elt)); unsafe { node.next = None; let node = Some(Box::into_raw_non_null(node)); match self.tail { None => self.head = node, Some(tail) => (*tail.as_ptr()).next = node, } self.tail = node; } } } fn main() { let mut m = LinkedListQueue::new(); m.enqueue(4); m.enqueue(4); m.enqueue(4); m.dequeue(); m.dequeue(); println!("{:?}", m); } ================================================ FILE: rust/11_sorts/bubble_sort.rs ================================================ /// 冒泡排序 /// 时间复杂度:O(n2),原地排序算法, 稳定排序算法 // 冒泡排序 fn bubble_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return vec![]; } let n = nums.len(); for i in 0..n { // 提前退出标志 let mut swap = false; for j in 0..n-i-1 { if nums[j] > nums[j+1] { // 此次冒泡有数据交换 swap = true; let tmp = nums[j]; nums[j] = nums[j+1]; nums[j+1] = tmp; } } // 若没有数据交换,提前退出 if !swap { break; } } nums } fn main() { let nums = vec![4, 5, 6, 1, 2, 3]; println!("{:?}", bubble_sort(nums)); } ================================================ FILE: rust/11_sorts/insertion_sort.rs ================================================ /// 插入排序 /// 时间复杂度:O(n2),原地排序算法, 稳定排序算法 // 插入排序 fn insertion_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return vec![]; } for i in 1..nums.len() { let value = nums[i]; let mut j = (i - 1) as i32; // 查找要插入的位置并移动数据 while j >= 0 { if nums[j as usize] > value { nums[(j+1) as usize] = nums[j as usize]; } else { break; } j -= 1; } nums[(j+1) as usize] = value; } nums } fn main() { let nums = vec![4, 5, 6, 1, 2, 3]; println!("{:?}", insert_on_sort(nums)); } ================================================ FILE: rust/11_sorts/selection_sort.rs ================================================ /// 选择排序 /// 时间复杂度:O(n2),原地排序算法, 不稳定排序算法 // 选择排序 fn selection_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return vec![]; } for i in 0..nums.len() { let mut mini_index = i; // 查找最小 index for j in i+1..nums.len() { if nums[j] < nums[mini_index] { mini_index = j; } } // 交换数据 let tmp = nums[i]; nums[i] = nums[mini_index]; nums[mini_index] = tmp; } nums } fn main() { let nums = vec![4, 5, 6, 1, 2, 3]; println!("{:?}", selection_sort(nums)); } ================================================ FILE: rust/12_sorts/kth_largest.rs ================================================ pub fn kth_largest(mut nums: Vec, k: i32) -> Option { if nums.is_empty() || k >= nums.len() as i32 { return None; } let end = nums.len() - 1; let k = k as usize; // 分区点 let mut pivot = partition(&mut nums, 0, end); while pivot + 1 != k { if k > pivot + 1 { pivot = partition(&mut nums, pivot + 1, end); } else { pivot = partition(&mut nums, 0, pivot - 1); } } Some(nums[pivot]) } fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { let pivot = nums[end]; let mut i = start; for j in start..end { if nums[j] >= pivot { // if nums[j] <= pivot then swap, search kth smallest swap(nums, i, j); i += 1; } } swap(nums, i, end); i } fn swap(nums: &mut Vec, i: usize, j: usize) { if i == j { return; } let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } fn main() { let nums = vec![8, 10, 2, 3, 6,1, 5]; println!("{:?}", kth_largest(nums, 3)); } ================================================ FILE: rust/12_sorts/merge_sort.rs ================================================ /// 归并排序 /// 时间复杂度 O(nlogn), 空间复杂度:O(n), 稳定排序 // 归并排序 pub fn merge_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return nums; } let n = nums.len() - 1; merge_sort_internally(&mut nums, 0, n); nums } fn merge_sort_internally(nums: &mut Vec, start: usize, end: usize) { if start >= end { return; } let middle = start + (end - start) / 2; merge_sort_internally(nums, start, middle); merge_sort_internally(nums, middle+1, end); // merge two array merge(nums, start, middle, end); } fn merge(nums: &mut Vec, start: usize, middle: usize, end: usize) { let mut i = start; let mut j = middle + 1; let mut tmp = vec![]; while i <= middle && j <= end { if nums[i] <= nums[j] { tmp.push(nums[i]); i += 1; } else { tmp.push(nums[j]); j += 1; } } let mut s = i; let mut r = middle; if j <= end { s = j; r = end; } while s <= r { tmp.push(nums[s]); s += 1; } for i in 0..=(end-start) { nums[start+i] = tmp[i]; } } fn main() { let nums = vec![8, 10, 2, 3, 6,1, 5]; println!("{:?}", merge_sort(nums)); } ================================================ FILE: rust/12_sorts/quick_sort.rs ================================================ /// 快排 /// 时间复杂度:O(nlogn), 空间复杂度: O(1), 不稳定排序 // 快排 pub fn quick_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return nums; } let n = nums.len() - 1; quick_sort_internally(&mut nums, 0, n); nums } fn quick_sort_internally(nums: &mut Vec, start: usize, end: usize) { if start >= end { return; } // 分区点 let pivot = partition(nums, start, end); if pivot != 0 { quick_sort_internally(nums, start, pivot-1); } quick_sort_internally(nums, pivot+1, end); } fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { let pivot = nums[end]; let mut i = start; for j in start..end { if nums[j] < pivot { swap(nums, i, j); i += 1; } } swap(nums, i, end); i } fn swap(nums: &mut Vec, i: usize, j: usize) { if i == j { return; } let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } fn main() { let nums = vec![8, 10, 2, 3, 6,1, 5]; println!("{:?}", quick_sort(nums)); } ================================================ FILE: rust/13_sorts/bucket_sort.rs ================================================ /// 桶排序 /// 时间复杂度:O(n), 稳定排序 // 桶排序 pub fn bucket_sort(mut nums: Vec, step: i32) -> Vec { let (mut min, mut max) = (nums[0], nums[0]); for i in 0..nums.len() { if min > nums[i] { min = nums[i]; } if max < nums[i] { max = nums[i]; } } // 设置需要的桶的数量 let bucket_num = (max - min) / step + 1; let mut bucket_list: Vec> = vec![vec![]; bucket_num as usize]; // 将 nums 数组中元素分别装入桶中 for i in 0..nums.len() { // 计算桶 index let index = (nums[i] - min) / step; bucket_list[index as usize].push(nums[i]); } let mut index = 0; for i in 0..bucket_num { let bucket = &bucket_list[i as usize]; // 对每个桶中元素使用快排进行排序 let new_bucket = quick_sort(bucket.to_vec()); // 将已经排序好的桶中元素拷贝到 nums 数组中 for num in new_bucket.into_iter() { nums[index as usize] = num; index += 1; } } nums } pub fn quick_sort(mut nums: Vec) -> Vec { if nums.is_empty() { return nums; } let n = nums.len() - 1; quick_sort_internally(&mut nums, 0, n); nums } fn quick_sort_internally(nums: &mut Vec, start: usize, end: usize) { if start >= end { return; } // 分区点 let pivot = partition(nums, start, end); if pivot != 0 { quick_sort_internally(nums, start, pivot-1); } quick_sort_internally(nums, pivot+1, end); } fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { let pivot = nums[end]; let mut i = start; for j in start..end { if nums[j] < pivot { swap(nums, i, j); i += 1; } } swap(nums, i, end); i } fn swap(nums: &mut Vec, i: usize, j: usize) { if i == j { return; } let tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } fn main() { let nums = vec![2, 5, 3, 0, 2, 3, 0, 3]; let m = bucket_sort(nums, 3); println!("{:?}", m); } ================================================ FILE: rust/13_sorts/counting_sort.rs ================================================ /// 计数排序 /// 时间复杂度:O(n), 稳定排序 // 计数排序 pub fn counting_sort(mut nums: Vec) -> Vec { if nums.len() <= 1 { return nums; } let nums_len = nums.len(); // 获取最大数 let mut max = nums[0]; let mut tmp = vec![0; nums_len]; for i in 1..nums_len { if max < nums[i] { max = nums[i]; } } // 申请一个长度为 max + 1 的新数组 let mut bucket = vec![0; (max+1) as usize]; for i in 0..nums_len { bucket[nums[i] as usize] += 1; } // 对 bucket 中元素进行累加,针对 nums 数组中每个元素,小于等于这个元素的个数 // 如,nums 数组中元素 3,小于等于 3 的个数为 7 个 for i in 1..bucket.len() { bucket[i] += bucket[i-1]; } // 排序 // 1. 申请一个与 nums 等长的数组,用于存储排序后的内容; // 2. 对数组 nums 从后向前迭代 // 1). 从 bucket 数组中取出下标为 nums 数组中当前元素的值,如 nums 中当前元素为3,则从 bucket // 中取出下标为 3 的元素(即:在 nums 数组中元素 3 的位置应该为 7,index 为 6) // 2). 将元素 3 存入临时数组,此时元素 3 的个数变为 6 个 // 3). 依次类推,直到将所有元素存入临时数组 tmp 中,完成排序 for i in (0..nums_len).rev() { let index = bucket[nums[i] as usize] - 1; tmp[index] = nums[i]; bucket[nums[i] as usize] -= 1; } for i in 0..tmp.len() { nums[i] = tmp[i]; } nums } fn main() { let nums = vec![2, 5, 3, 0, 2, 3, 0, 3]; let m = counting_sort(nums); println!("{:?}", m); } ================================================ FILE: rust/13_sorts/radix_sort.rs ================================================ /// 基数排序 /// 时间复杂度:O(n), 稳定排序 // 基数排序 pub fn radix_sort(mut nums: Vec) -> Vec { let max_bit = max_bit(&nums); // 申请一个长度为 10 的桶 let mut bucket = vec![0; 10]; let mut tmp = vec![0; nums.len()]; let mut radix = 1; let nums_len = nums.len(); // 迭代 max_bit 次 for _i in 0..max_bit { // 每次比较前先将桶清空 for j in 0..bucket.len() { bucket[j] = 0; } for j in 0..nums_len { let index = ((nums[j] / radix) % 10) as usize; bucket[index] += 1; } for j in 1..bucket.len() { bucket[j] += bucket[j-1]; } // 对 nums 进行排序 for j in (0..nums_len).rev() { let index = ((nums[j] / radix) % 10) as usize; tmp[(bucket[index]-1) as usize] = nums[j]; bucket[index] -= 1; } for j in 0..nums_len { nums[j] = tmp[j]; } radix *= 10; } nums } fn max_bit(nums: &Vec) -> i32 { let mut max = nums[0]; let mut bit = 1; // 预防数据库中元素全部是 0 的情况 for &num in nums.iter() { if num > max { max = num; } } while max >= 10 { max = max / 10; bit += 1; } bit } fn main() { // let nums = vec![1, 10, 100, 1000, 98, 67, 3, 28, 67, 888, 777]; let nums = vec![13111111111, 13299999999, 13311111111, 13133333333, 13922222222, 13722222222]; println!("{:?}", radix_sort(nums)); } ================================================ FILE: rust/13_sorts/sort_string.rs ================================================ fn sort_string(s: String) -> Vec> { let mut bucket: Vec> = vec![vec![]; 3]; for ch in s.as_bytes() { if ch as u8 >= 48 && ch as u8 <= 57 { bucket[0].push(ch); } else if ch as u8 >= 65 && ch as u8 <= 90 { bucket[1].push(ch); } else { bucket[2].push(ch); } } bucket } fn main() { let s = "DaFBCA789".to_string(); println!("{:?}", sort_string(s)); } ================================================ FILE: rust/15_binary_search/binary_search.rs ================================================ // 二分查找 pub fn binary_search(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } let mut low = 0; let mut high = nums.len() - 1; while low <= high { let mid = low + ((high - low) >> 1); if nums[mid] == value { return mid as i32; } if nums[mid] < value { low = mid + 1; } else { high = mid -1; } } -1 } // 二分查找递归 pub fn binary_search_recursion(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } _recursion(&nums, 0, nums.len()-1, value) } fn _recursion(nums: &Vec, low: usize, high: usize, value: i32) -> i32 { if low > high { return -1; } let mid = low + ((high - low) >> 1); if nums[mid] == value { return mid as i32; } if nums[mid] < value { return _recursion(nums, mid+1, high, value); } else { return _recursion(nums, low, mid-1, value); } } fn main() { let nums1 = vec![8,11,19,23,27,33,45,55,67,98]; let nums2 = vec![8,11,19,23,27,33,45,55,67,98]; println!("{:?}", binary_search(nums1, 23)); println!("{:?}", binary_search_recursion(nums2, 23)); } ================================================ FILE: rust/15_binary_search/sqrtx.rs ================================================ // leetcode: https://leetcode.com/problems/sqrtx/ pub fn my_sqrt(x: i32, precision: f32) -> f32 { if x == 0 || x == 1 { return x as f32; } let mut left = 0f32; let mut right = x as f32; let mut res = 0f32; while left <= right { let mid: f32 = (right - left) / 2.0 + left; if (right - left).abs() < precision { return mid; } if mid > x as f32 / mid { right = mid; } else { left = mid; } res = mid } res } fn main() { println!("{:?}", my_sqrt(8, 0.000001)); } ================================================ FILE: rust/16_binary_search/binary_search.rs ================================================ // 查找第一个给定值的元素 fn find_first_eq(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } let mut start = 0; let mut end = nums.len() - 1; while start <= end { let mid = start + ((end - start) >> 1); if nums[mid] <= value { if mid == 0 || (nums[mid] == value && nums[mid-1] != value) { return mid as i32; } start = mid + 1; } else { end = mid - 1; } } -1 } // 查找最后一个给定值的元素 fn find_last_eq(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } let mut start = 0; let mut end = nums.len() - 1; while start <= end { let mid = start + ((end - start) >> 1); if nums[mid] <= value { if mid == end || (nums[mid] == value && nums[mid+1] != value) { return mid as i32; } start = mid + 1; } else { end = mid - 1; } } -1 } // 查找第一个大于等于给定值的元素 fn find_first_ge(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } let mut start = 0; let mut end = nums.len() - 1; while start <= end { let mid = start + ((end - start) >> 1); if nums[mid] < value { start = mid + 1; } else { if mid == end || (nums[mid] >= value && nums[mid-1] < value) { return mid as i32; } end = mid - 1; } } -1 } // 查找最后一个小于等于给定值的元素 fn find_last_le(nums: Vec, value: i32) -> i32 { if nums.is_empty() { return -1; } let mut start = 0; let mut end = nums.len() - 1; while start <= end { let mid = start + ((end - start) >> 1); if nums[mid] <= value { if mid == 0 || (nums[mid] <= value && nums[mid+1] > value) { return mid as i32; } start = mid + 1; } else { end = mid - 1; } } -1 } fn main() { let nums1 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; let first_eq = find_first_eq(nums1, 8); println!("{:?}", first_eq); let nums2 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; let last_eq = find_last_eq(nums2, 8); println!("{:?}", last_eq); let nums3 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; let find_first_ge = find_first_ge(nums3, 5); println!("{:?}", find_first_ge); let nums4 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; let find_last_le = find_last_le(nums4, 17); println!("{:?}", find_last_le); } ================================================ FILE: rust/16_binary_search/search_in_rotated_sorted_array.rs ================================================ // leetcode 33 Search in Rotated Sorted Array (https://leetcode.com/problems/search-in-rotated-sorted-array/) pub fn search(nums: Vec, target: i32) -> i32 { if nums.is_empty() { return -1; } let mut low = 0; let mut high = nums.len() - 1; while low <= high { let mid = low + ((high - low) >> 1); if nums[mid] == target { return mid as i32; } // left is order if nums[low] <= nums[mid] { // target is in left array if nums[low] <= target && target <= nums[mid] { high = mid - 1; } else { low = mid + 1; } } else { if nums[mid] <= target && target <= nums[high] { low = mid + 1; } else { high = mid - 1; } } } -1 } fn main() { let nums = vec![4,5,6,7,0,1,2]; let n = search(nums, 0); println!("{:?}", n); } ================================================ FILE: rust/19_hash_table/hash_table.rs ================================================ #[derive(Debug,Default)] pub struct MyHashTable<'a> { table: Vec>, capacity: usize, } impl<'a> MyHashTable<'a> { fn new() -> MyHashTable<'a> { MyHashTable { table: vec![None; 16], capacity: 16, } } pub fn insert(&mut self, key: &'a str, value: &'a str) { let pos = self.hash(key) as usize; self.table[pos] = Some(value); } pub fn get(&self, key: &'a str) -> Option<&'a str> { let pos = self.hash(key) as usize; self.table[pos] } pub fn remove(&mut self, key: &'a str) -> Option<&'a str> { let pos = self.hash(key) as usize; let value = self.table[pos]; self.table[pos] = None; value } fn hash(&self, key: &'a str) -> i32 { let h = self.hash_code(key); (h ^ (h >> 16)) & (self.capacity as i32 - 1) } fn hash_code(&self, key: &'a str) -> i32 { let mut hash = 0; for ch in key.chars() { hash += 31 * hash + ch as i32; } hash as i32 } } fn main() { let mut hash_table = MyHashTable::new(); hash_table.insert("hello", "rust"); println!("{:?}", hash_table); hash_table.insert("hi", "C++"); println!("{:?}", hash_table); let m = hash_table.get("hello"); println!("{:?}", m); let n = hash_table.remove("hi"); println!("{:?}", n); println!("{:?}", hash_table); } ================================================ FILE: rust/23_binary_tree/inorder_traversal.rs ================================================ // leetcode: https://leetcode.com/problems/binary-tree-inorder-traversal/ use super::util::tree::{TreeNode, to_tree}; // 中序遍历(Recursive Approach) pub fn inorder_traversal(root: Option>>) -> Vec { let mut result: Vec = vec![]; if root.is_none() { return result; } _inorder(root, &mut result); result } fn _inorder(root: Option>>, result: &mut Vec) { match root { Some(node) => { _inorder(node.borrow().left.clone(), result); result.push(node.borrow().val); _inorder(node.borrow().right.clone(), result); }, None => { return; } } } // 中序遍历(Iterating method using Stack) pub fn inorder_traversal(root: Option>>) -> Vec { let mut result = vec![]; if root.is_none() { return result; } let mut stack: Vec>> = Vec::new(); let mut r = root.clone(); while r.is_some() || !stack.is_empty() { while let Some(node) = r { stack.push(node.clone()); r = node.borrow().left.clone(); } r = stack.pop(); if let Some(node) = r { result.push(node.borrow().val); r = node.borrow().right.clone(); } } result } ================================================ FILE: rust/23_binary_tree/level_order_traversal.rs ================================================ // leetcode https://leetcode.com/problems/binary-tree-level-order-traversal/ use super::util::tree::{TreeNode, to_tree}; use std::collections::VecDeque; pub fn level_order(root: Option>>) -> Vec> { let mut result: Vec> = vec![]; if root.is_none() { return result; } let mut deque: VecDeque>>> = VecDeque::new(); deque.push_back(root); while !deque.is_empty() { let mut current_level = vec![]; let mut added = false; let level_size = deque.len(); for i in 0..level_size { let n = deque.pop_front(); if let Some(Some(node)) = n { current_level.push(node.borrow().val); added = true; if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone()); } if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone()); } } } if !added { break; } result.push(current_level); } result } ================================================ FILE: rust/23_binary_tree/postorder_traversal.rs ================================================ // https://leetcode.com/problems/binary-tree-postorder-traversal/ use super::util::tree::{TreeNode, to_tree}; // 后续遍历(Recursive Approach) pub fn postorder_traversal(root: Option>>) -> Vec { let mut result: Vec = vec![]; if root.is_none() { return result; } _postorder(root, &mut result); result } fn _postorder(root: Option>>, result: &mut Vec) { match root { Some(node) => { _postorder(node.borrow().left.clone(), result); _postorder(node.borrow().right.clone(), result); result.push(node.borrow().val); }, None => { return; } } } // 后序遍历(Iterating method using Stack) pub fn postorder_traversal(root: Option>>) -> Vec { let mut result = vec![]; if root.is_none() { return result; } let mut stack1: Vec>>> = Vec::new(); let mut stack2: Vec>>> = Vec::new(); stack1.push(root); while let Some(Some(node)) = stack1.pop() { if node.borrow().left.is_some() { stack1.push(node.borrow().left.clone()); } if node.borrow().right.is_some() { stack1.push(node.borrow().right.clone()); } stack2.push(Some(node)); } while let Some(Some(node)) = stack2.pop() { result.push(node.borrow().val); } result } ================================================ FILE: rust/23_binary_tree/preorder_traversal.rs ================================================ //leetcode: https://leetcode.com/problems/binary-tree-preorder-traversal/ use super::util::tree::{TreeNode, to_tree}; // 前序遍历(Recursive Approach) pub fn preorder_traversal(root: Option>>) -> Vec { let mut result: Vec = vec![]; if root.is_none() { return result; } _preorder(root, &mut result); result } fn _preorder(root: Option>>, result: &mut Vec) { match root { Some(node) => { result.push(node.borrow().val); _preorder(node.borrow().left.clone(), result); _preorder(node.borrow().right.clone(), result); }, None => { return; } } } // 前序遍历(Iterating method using Stack) pub fn inorder_traversal(root: Option>>) -> Vec { let mut result = vec![]; if root.is_none() { return result; } let mut stack: Vec>> = Vec::new(); let mut r = root.clone(); while r.is_some() || !stack.is_empty() { while let Some(node) = r { result.push(node.borrow().val); stack.push(node.clone()); r = node.borrow().left.clone(); } r = stack.pop(); if let Some(node) = r { r = node.borrow().right.clone(); } } result } ================================================ FILE: rust/23_binary_tree/util/tree.rs ================================================ use std::rc::Rc; use std::cell::RefCell; #[derive(Debug, PartialEq, Eq)] pub struct TreeNode { pub val: i32, pub left: Option>>, pub right: Option>>, } impl TreeNode { #[inline] pub fn new(val: i32) -> Self { TreeNode { val, left: None, right: None } } } pub fn to_tree(vec: Vec>) -> Option>> { use std::collections::VecDeque; let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap())))); let mut queue = VecDeque::new(); queue.push_back(head.as_ref().unwrap().clone()); for children in vec[1..].chunks(2) { let parent = queue.pop_front().unwrap(); if let Some(v) = children[0] { parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v)))); queue.push_back(parent.borrow().left.as_ref().unwrap().clone()); } if children.len() > 1 { if let Some(v) = children[1] { parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v)))); queue.push_back(parent.borrow().right.as_ref().unwrap().clone()); } } } head } #[macro_export] macro_rules! tree { () => { None }; ($($e:expr),*) => { { let vec = vec![$(stringify!($e)), *]; let vec = vec.into_iter().map(|v| v.parse::().ok()).collect::>(); to_tree(vec) } }; ($($e:expr,)*) => {(tree![$($e),*])}; } ================================================ FILE: rust/24_binary_tree/insert_in_binary_tree.rs ================================================ // https://leetcode.com/problems/insert-into-a-binary-search-tree/ use super::util::tree::{TreeNode, to_tree}; pub fn insert_into_bst(root: Option>>, val: i32) -> Option>> { insert(&root, val); root } fn insert(node: &Option>>, val: i32) { if let Some(n) = node { let mut n = n.borrow_mut(); let target = if val > n.val { &mut n.right } else { &mut n.left }; if target.is_some() { return insert(target, val); } *target = Some(Rc::new(RefCell::new(TreeNode::new(val)))); } } ================================================ FILE: rust/24_binary_tree/max_depth_in_binary_tree.rs ================================================ // https://leetcode.com/problems/maximum-depth-of-binary-tree/ use super::util::tree::{TreeNode, to_tree}; // max depth BFS pub fn max_depth(root: Option>>) -> i32 { if root.is_none() { return 0; } let mut depth = 0; let mut deque: VecDeque>>> = VecDeque::new(); deque.push_back(root); while !deque.is_empty() { let level_size = deque.len(); let mut added = false; depth += 1; for _i in 0..level_size { added = true; if let Some(Some(node)) = deque.pop_front() { if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone());} if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone());} } } if !added { break; } } depth } // max depth DFS pub fn max_depth(root: Option>>) -> i32 { match root { Some(node) => { let left = Self::max_depth(node.borrow().left.clone()); let right = Self::max_depth(node.borrow().right.clone()); 1 + left.max(right) }, _ => 0, } } ================================================ FILE: rust/24_binary_tree/search_in_binary_tree.rs ================================================ // https://leetcode.com/problems/search-in-a-binary-search-tree/ use super::util::tree::{TreeNode, to_tree}; // Iterating method pub fn search_bst(root: Option>>, val: i32) -> Option>> { let mut r = root.clone(); while let Some(node) = r { if node.borrow().val == val { return Some(node); } if node.borrow().val > val { r = node.borrow().left.clone(); } else { r = node.borrow().right.clone(); } } None } // Recursive Approach pub fn search_bst(root: Option>>, val: i32) -> Option>> { if let Some(n) = &root { if n.borrow().val > val { return Self::search_bst(n.borrow().left.clone(), val); } if n.borrow().val < val { return Self::search_bst(n.borrow().right.clone(), val); } } root } ================================================ FILE: rust/24_binary_tree/util/tree.rs ================================================ use std::rc::Rc; use std::cell::RefCell; #[derive(Debug, PartialEq, Eq)] pub struct TreeNode { pub val: i32, pub left: Option>>, pub right: Option>>, } impl TreeNode { #[inline] pub fn new(val: i32) -> Self { TreeNode { val, left: None, right: None } } } pub fn to_tree(vec: Vec>) -> Option>> { use std::collections::VecDeque; let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap())))); let mut queue = VecDeque::new(); queue.push_back(head.as_ref().unwrap().clone()); for children in vec[1..].chunks(2) { let parent = queue.pop_front().unwrap(); if let Some(v) = children[0] { parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v)))); queue.push_back(parent.borrow().left.as_ref().unwrap().clone()); } if children.len() > 1 { if let Some(v) = children[1] { parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v)))); queue.push_back(parent.borrow().right.as_ref().unwrap().clone()); } } } head } #[macro_export] macro_rules! tree { () => { None }; ($($e:expr),*) => { { let vec = vec![$(stringify!($e)), *]; let vec = vec.into_iter().map(|v| v.parse::().ok()).collect::>(); to_tree(vec) } }; ($($e:expr,)*) => {(tree![$($e),*])}; } ================================================ FILE: rust/28_heap/build_heap.rs ================================================ // 建堆,自底向上堆化 pub fn build_heap_down_up(nums: &mut Vec) { for i in 1..nums.len() { heapify_down_up(nums, i); } } fn heapify_down_up(nums: &mut Vec, idx: usize) { let mut idx = idx; let mut parent_idx = idx - 1 >> 1; while nums[idx] > nums[parent_idx] { swap(nums, idx, parent_idx); idx = parent_idx; if idx == 0 { break; } parent_idx = idx - 1 >> 1; } } // 建堆,自上向下堆化 pub fn build_heap_up_down(nums: &mut Vec) { let nums_len = nums.len(); for i in (0..nums_len).rev() { heapify_up_down(nums, i, nums_len); } } fn heapify_up_down(nums: &mut Vec, idx: usize, nums_len: usize) { let mut idx = idx; loop { let mut max_pos = idx; if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; } if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; } if max_pos == idx { break; } swap(nums, idx, max_pos); idx = max_pos; } } fn swap(nums: &mut Vec, idx: usize, parent_idx: usize) { let tmp = nums[parent_idx]; nums[parent_idx] = nums[idx]; nums[idx] = tmp; } fn main() { let mut nums = vec![1, 4, 5, 7, 8, 13, 16, 19, 20]; build_heap_down_up(&mut nums); println!("{:?}", nums); let mut nums1 = vec![1, 4, 5, 7, 8, 13, 16, 19, 20]; build_heap_up_down(&mut nums1); println!("{:?}", nums1); } ================================================ FILE: rust/28_heap/heap.rs ================================================ #[derive(Debug)] struct Heap { data: Vec>, capacity: usize, count: i32, } impl Heap { pub fn new(capacity: usize) -> Self { Heap { data: vec![None; capacity], capacity: capacity, count: 0 } } pub fn insert(&mut self, x: i32) -> bool { // 堆已满 if self.capacity as i32 == self.count { return false; } self.data[self.count as usize] = Some(x); if self.count == 0 { self.count += 1; return true; } let mut idx = self.count as usize; // 子节点大于父节点,子节点与父节点交换 // 自底向上堆化 let mut parent_idx = ((idx - 1) >> 1) as usize; while parent_idx > 0 && self.data[idx] > self.data[parent_idx] { self.swap(idx, parent_idx); idx = parent_idx; parent_idx = ((idx - 1) >> 1) as usize; } self.count += 1; true } pub fn remove_max(&mut self) -> Option { // 堆已空 if self.count == 0 { return None; } let max_value = self.data[0]; // 将最后一个叶子节点移至堆顶 self.data[0] = self.data[(self.count - 1) as usize]; self.data[(self.count - 1) as usize] = None; self.heapify(); self.count -= 1; max_value } // 堆化,自上向下堆化 fn heapify(&mut self) { let mut idx = 0usize; loop { let mut max_pos = idx; if (2 * idx + 1) as i32 <= self.count && self.data[idx] < self.data[2 * idx + 1] { max_pos = 2 * idx + 1; } if (2 * idx + 2) as i32 <= self.count && self.data[max_pos] < self.data[2 * idx + 2] { max_pos = 2 * idx + 2; } if max_pos == idx { break; } self.swap(idx, max_pos); idx = max_pos; } } fn swap(&mut self, idx: usize, parent_idx: usize) { let tmp = self.data[parent_idx]; self.data[parent_idx] = self.data[idx]; self.data[idx] = tmp; } } fn main() { let mut h = Heap::new(16); h.insert(33); h.insert(27); h.insert(21); h.insert(16); h.insert(13); h.insert(15); h.insert(9); h.insert(5); h.insert(6); h.insert(7); h.insert(8); h.insert(1); h.insert(2); h.insert(22); println!("{:?}", h); h.remove_max(); println!("{:?}", h); h.remove_max(); println!("{:?}", h); h.remove_max(); println!("{:?}", h); } ================================================ FILE: rust/28_heap/sort_heap.rs ================================================ pub fn sort(nums: &mut Vec) { build_heap(nums); for i in (0..nums.len()).rev() { swap(nums, 0, i); heapify(nums, 0, i); } } fn build_heap(nums: &mut Vec) { let nums_len = nums.len(); for i in (0..nums_len).rev() { heapify(nums, i, nums_len); } } fn heapify(nums: &mut Vec, idx: usize, nums_len: usize) { let mut idx = idx; loop { let mut max_pos = idx; if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; } if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; } if max_pos == idx { break; } swap(nums, idx, max_pos); idx = max_pos; } } fn swap(nums: &mut Vec, idx: usize, parent_idx: usize) { let tmp = nums[parent_idx]; nums[parent_idx] = nums[idx]; nums[idx] = tmp; } fn main() { let mut nums = vec![9, 6, 3, 1, 5]; sort(&mut nums); println!("{:?}", nums); } ================================================ FILE: rust/29_heap/get_median.rs ================================================ use std::collections::BinaryHeap; // 动态数组取位数 // 对数组进行从小到大排序,数组下标为 n/2 的数据即为中位数 fn get_median(nums: &mut Vec, x: i32) -> i32 { let nums_len = nums.len(); let mid = nums_len >> 1; let mut max_heap = BinaryHeap::new(); let mut min_heap = BinaryHeap::new(); nums.sort(); // 将数组前半部分数据放入大顶堆 // 数组后半部分数据入入小顶堆 for i in 0..nums_len { if i < mid { max_heap.push(nums[i]); } else { min_heap.push(-nums[i]); } } nums.push(x); // 校验待插入数据 // 若此数据小于大顶堆中顶数据,则将此数据插入大顶堆 // 若此数据大于大顶堆中顶数据,将此数据插入小顶堆 if x <= *max_heap.peek().unwrap() { max_heap.push(x); } else { min_heap.push(-x); } // 平衡两个堆 // 大顶堆的数据个数一定小于等于小顶堆数据个数 // 小顶堆数据个数一定是等于或者比大顶堆数据个数多1个 // 不满足上述两个条件,即进行堆平衡 if max_heap.len() > min_heap.len() { min_heap.push(-max_heap.pop().unwrap()); } else if min_heap.len() - max_heap.len() >= 2 { max_heap.push(-min_heap.pop().unwrap()); } -*min_heap.peek().unwrap() } fn main() { let mut nums = vec![12, 45, 30, 77, 5, 6, 7, 8]; let m = get_median(&mut nums, 9); println!("{:?}", m); // 9 let n = get_median(&mut nums, 20); println!("{:?}", n); // 12 let h = get_median(&mut nums, 11); println!("{:?}", h); // 11 let i = get_median(&mut nums, 10); println!("{:?}", i); // 11 } ================================================ FILE: rust/29_heap/get_top_k.rs ================================================ use std::collections::BinaryHeap; // 动态数组取 top k 元素 fn get_top_k(nums: &mut Vec, k: i32, x: i32) -> Vec { let nums_len = nums.len() as i32; if nums_len <= k { return nums.clone(); } let mut heap = BinaryHeap::new(); // 先将数组的前k个数据放入堆中 for _i in 0..k { heap.push(-nums[k as usize]); } // 对数组中其它数据进行迭代,若数据大于堆顶元素,将堆顶元素移除,将此数据放入堆中 for i in k + 1..nums_len { if -nums[i as usize] < *heap.peek().unwrap() { heap.pop(); heap.push(-nums[i as usize]); } } // 向数组中插入新数据 nums.push(x); // 新插入的数据若大于堆顶元素,将堆顶元素移除,将此数据放入堆中 if -x < *heap.peek().unwrap() { heap.pop(); heap.push(-x); } // let m: Vec = heap.iter().map(|h| h * -1).collect(); // m heap.iter().map(|h| h * -1).collect::>() } fn main() { let mut nums = vec![4, 5, 7, 9, 10, 6, 11]; let m = get_top_k(&mut nums, 3, 23); println!("{:?}", m); } ================================================ FILE: rust/29_heap/merge_sorted_array.rs ================================================ use std::collections::BinaryHeap; fn merge_sorted_array(nums1: &mut Vec, nums2: &mut Vec, nums3: &mut Vec) -> Vec { let mut new_nums = vec![]; let mut heap = BinaryHeap::new(); // Rust heap 是大顶堆,将待入堆的数值取反后再入堆,堆顶即为最小值,即达到小顶堆效果 heap.push(-nums1[0]); heap.push(-nums2[0]); heap.push(-nums3[0]); while !nums1.is_empty() || !nums2.is_empty() || !nums3.is_empty() { if heap.is_empty() { break; } let num = -heap.pop().unwrap(); new_nums.push(num); if !nums1.is_empty() && num == nums1[0] { nums1.remove(0); if !nums1.is_empty() { heap.push(-nums1[0]); } } else if !nums2.is_empty() && num == nums2[0] { nums2.remove(0); if !nums2.is_empty() { heap.push(-nums2[0]); } } else if !nums3.is_empty() && num == nums2[0] { nums3.remove(0); if !nums3.is_empty() { heap.push(-nums3[0]); } } } new_nums } fn main() { let mut nums1 = vec![4, 5, 20, 90, 95, 100]; let mut nums2 = vec![1, 6, 7, 8, 11, 23, 67, 89]; let mut nums3 = vec![2, 5, 9, 30, 45]; let new_nums = merge_sorted_array(&mut nums1, &mut nums2, &mut nums3); println!("{:?}", new_nums); } ================================================ FILE: rust/31_graph/graph_search.rs ================================================ use std::collections::LinkedList; use std::collections::VecDeque; // 无向图 #[derive(Debug)] struct Graph { v: i32, linked_vec: Vec>, // 邻接表 } impl Graph { fn new(v: i32) -> Self { Graph { v: v, linked_vec: vec![LinkedList::new(); v as usize], } } // 无向图的每条边存两次 fn add_edge(&mut self, s: i32, t: i32) { self.linked_vec[s as usize].push_back(t); self.linked_vec[t as usize].push_back(s); } fn bfs(&self, s: i32, t: i32) { if s == t { return; } let mut prev = vec![-1; self.v as usize]; let mut visited = vec![false; self.v as usize]; let mut queue = VecDeque::new(); visited[s as usize] = true; queue.push_back(s); while !queue.is_empty() { let w = queue.pop_front().unwrap(); for item in self.linked_vec[w as usize].iter() { if visited[*item as usize] { continue; } prev[*item as usize] = w; if *item == t { self.draw(&prev, s, t); return; } visited[*item as usize] = true; queue.push_back(*item); } } } fn dfs(&self, s: i32, t: i32) { let mut found = false; let mut prev = vec![-1; self.v as usize]; let mut visited = vec![false; self.v as usize]; self.recur_dfs(s, t, &mut visited, &mut prev, &mut found); self.draw(&prev, s, t); } fn recur_dfs(&self, s: i32, t: i32, visited: &mut Vec, prev: &mut Vec, found: &mut bool) { if *found == true { return; } visited[s as usize] = true; if s == t { *found = true; return; } for item in self.linked_vec[s as usize].iter() { if visited[*item as usize] { continue; } prev[*item as usize] = s; self.recur_dfs(*item, t, visited, prev, found); } } // 递归打印路径 fn draw(&self, prev: &Vec, s: i32, t: i32) { if prev[t as usize] != -1 && s != t { self.draw(prev, s, prev[t as usize]); } println!("{} ->", t); } } fn main() { let mut graph = Graph::new(8); graph.add_edge(0, 1); graph.add_edge(0, 3); graph.add_edge(1, 2); graph.add_edge(1, 4); graph.add_edge(2, 5); graph.add_edge(3, 4); graph.add_edge(4, 5); graph.add_edge(4, 6); graph.add_edge(5, 7); graph.add_edge(6, 7); // Graph { v: 8, linked_vec: [[1, 3], [0, 2, 4], [1, 5], [0, 4], [1, 3, 5, 6], [2, 4, 7], [4, 7], [5, 6]] } println!("{:?}", graph); graph.bfs(0, 7); println!("bfs============="); graph.bfs(1, 3); println!("bfs============="); graph.dfs(0, 7); println!("dfs============="); graph.dfs(1, 3); println!("dfs============="); } ================================================ FILE: rust/32_string/bf_rk.rs ================================================ use std::collections::HashMap; fn bf(primary: &str, pattern: &str) -> i32 { if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; } let primary_chars: Vec = primary.chars().collect(); let pattern_chars: Vec = pattern.chars().collect(); for i in 0..(primary.len() - pattern.len() + 1) { if pattern_chars == primary_chars[i..i + pattern.len()].to_vec() { return i as i32; } } -1 } // 通过哈希算法对主串中的 n-m+1 个子串分别求哈希值, // 逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配 fn rk(primary: &str, pattern: &str) -> i32 { if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; } let primary_chars: Vec = primary.chars().collect(); let pattern_chars: Vec = pattern.chars().collect(); let base: i128 = 26; let m = pattern.len(); let n = primary.len(); let mut pow_vec = vec![]; let mut hash = HashMap::new(); // 存储 26 的 n 次方到数组中,方便后面调用 for i in 0..m { pow_vec.push(base.pow(i as u32)); } // 计算子串的 hash 值 let mut p_value = 0; for i in 0..m { p_value += (pattern_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i]; } // 计算主串的 n-m+1 个子串的 hash 值 for i in 0..(n - m + 1) { // 计算主串中 index 为 0 的子串的 hash 值 let mut value = 0; if i == 0 { for i in 0..m { value += (primary_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i]; } } else { // 计算 index 为 i 的子串的 hash 值 // 计算公式: hash[i] = (hash[i-1] - 26^(m-1) * (primary_chars[i-1] - 'a')) * 26 + (26^0 * (primary_chars[i+m-1] - 'a')) value = (hash[&((i-1) as i32)] - base.pow((m-1) as u32) * (primary_chars[i-1] as i128 - 'a' as i128)) * base + ((primary_chars[i+m-1]) as i128 - 'a' as i128); } // hash 值相等,比较两个串内容是否相等,避免 hash 碰撞 if value == p_value && pattern_chars == primary_chars[i..i+m].to_vec() { return i as i32; } hash.insert(i as i32, value); } -1 } fn main() { let primary = "thequickbrownfoxjumpsoverthelazydog"; let pattern = "jump"; let result = bf(primary, pattern); println!("{}", result); // 16 let result2 = rk(primary, pattern); println!("{:?}", result2); // 16 } ================================================ FILE: rust/33_string/bm.rs ================================================ // 生成模式串散列表 fn generate_bc(pattern: &str) -> Vec { let mut bc: Vec = vec![-1; 256]; let pattern_chars: Vec = pattern.chars().collect(); for (index, item) in pattern_chars.iter().enumerate() { bc[(*item as u8) as usize] = index as i32; } bc } // 计算 suffix 数组与 prefix 数组 fn generate_gs(pattern: &str) -> (Vec, Vec) { let m = pattern.len(); let mut suffix: Vec = vec![-1; m]; let mut prefix: Vec = vec![false; m]; let pattern_chars: Vec = pattern.chars().collect(); for i in 0..m-1 { let mut j = i as i32; let mut k = 0; while j >= 0 && pattern_chars[j as usize] == pattern_chars[m-k-1] { j -= 1; k += 1; suffix[k] = j + 1; } if j == -1 { prefix[k] = true; } } (suffix, prefix) } fn move_by_gs(bad_char_start_index: usize, pattern_len: usize, suffix: &Vec, prefix: &Vec) -> i32 { // 好后缀长度 let k = pattern_len - bad_char_start_index - 1; // 完全匹配 if suffix[k] != -1 { return (bad_char_start_index + 1 - suffix[k] as usize) as i32; } for i in pattern_len+2..bad_char_start_index { if prefix[pattern_len-i] { return i as i32; } } // 没有匹配 pattern_len as i32 } fn bm_search(primary: &str, pattern: &str) -> i32 { if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return 0; } let primary_chars: Vec = primary.chars().collect(); let pattern_chars: Vec = pattern.chars().collect(); let bc = generate_bc(pattern); let (suffix, prefix) = generate_gs(pattern); let n = primary.len(); let m = pattern.len(); let mut i = 0; while i <= n - m { let mut j = (m-1) as i32; while j >=0 { if primary_chars[i+j as usize] != pattern_chars[j as usize] { break; } j -= 1 } if j < 0 { return i as i32; } let step_for_bc = j as i32 - bc[(primary_chars[i+j as usize] as u8) as usize]; let mut step_for_gs = 0; if j < (m-1) as i32 { step_for_gs = move_by_gs(j as usize, m, &suffix, &prefix); } i = (i as i32 + step_for_bc.max(step_for_gs)) as usize; } -1 } fn main() { let primary = "abcacabcbcabcabc"; let pattern = "cabcab"; let m = bm_search(primary, pattern); println!("{:?}", m); } ================================================ FILE: rust/34_string/kmp.rs ================================================ fn kmp_search(primary: &str, pattern: &str) -> Vec { if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return vec![0]; } let primary_chars: Vec = primary.chars().collect(); let pattern_chars: Vec = pattern.chars().collect(); let max_match_lengths = get_failure_function(pattern); let mut count = 0; let m = pattern.len(); let mut positions = vec![]; for i in 0..primary.len() { while count > 0 && pattern_chars[count as usize] != primary_chars[i] { count = max_match_lengths[(count-1) as usize]; } if pattern_chars[count as usize] == primary_chars[i] { count += 1; } if count as usize == m { positions.push((i - m + 1) as i32); count = max_match_lengths[(count-1) as usize]; } } positions } fn get_failure_function(pattern: &str) -> Vec { let m = pattern.len(); let mut max_match_lengths: Vec = vec![0; m]; let mut max_length: i32 = 0; let pattern_chars: Vec = pattern.chars().collect(); for i in 1..m { while max_length > 0 && pattern_chars[max_length as usize] != pattern_chars[i] { max_length = max_match_lengths[(max_length-1) as usize]; } if pattern_chars[i] == pattern_chars[max_length as usize] { max_length += 1; } max_match_lengths[i] = max_length; } max_match_lengths } fn main() { let primary1 = "abbaabbaaba"; let pattern1 = "abbaaba"; println!("{:?}", kmp_search(primary1, pattern1)); // 4 let primary = "abc abcdab abcdabcdabde"; let pattern = "bcdabd"; println!("{:?}", kmp_search(primary, pattern)); // 16 } ================================================ FILE: rust/35_trie/trie.rs ================================================ // [leetcode 208](https://leetcode.com/problems/implement-trie-prefix-tree/) #[derive(Default, Debug)] struct Trie { is_ending: bool, nodes: [Option>; 26], } impl Trie { fn new() -> Self { Default::default() } fn insert(&mut self, word: &str) { let mut curr = self; for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) { curr = curr.nodes[i].get_or_insert_with(|| Box::new(Trie::new())); } curr.is_ending = true; } fn find(&self, word: &str) -> bool { let mut curr = self; for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) { match curr.nodes[i].as_ref() { Some(node) => { curr = node; }, None => { return false; }, } } curr.is_ending } } fn main() { let mut m = Trie::new(); m.insert("hello"); m.insert("she"); println!("{:?}", m); let r = m.search("hello"); println!("{}", r); // true } ================================================ FILE: rust/38_divide_and_conquer/merge_sort_count.rs ================================================ fn merge_sort_count(mut nums: Vec) -> i32 { let mut count = 0; let n = nums.len() - 1; merge_sort(&mut nums, 0, n, &mut count); count } fn merge_sort(nums: &mut Vec, low: usize, high: usize, count: &mut i32) { if low >= high { return; } let middle = low + ((high - low) >> 1); merge_sort(nums, low, middle, count); merge_sort(nums, middle+1, high, count); merge(nums, low, middle, high, count); } fn merge(nums: &mut Vec, low: usize, middle: usize, high: usize, count: &mut i32) { let mut i = low; let mut j = middle + 1; let mut tmp = vec![]; while i <= middle && j <= high { if nums[i] <= nums[j] { tmp.push(nums[i]); i += 1; } else { // count += &(middle - i + 1); *count += (middle - i + 1) as i32; tmp.push(nums[j]); j += 1; } } while i <= middle { tmp.push(nums[i]); i += 1; } while j <= high { tmp.push(nums[j]); j += 1; } for i in 0..=(high-low) { nums[low+1] = tmp[i]; } } fn main() { let nums = vec![1, 5, 6, 2, 3, 4]; let m = merge_sort_count(nums); println!("{:?}", m); } ================================================ FILE: rust/39_back_tracking/bag.rs ================================================ use std::collections::HashMap; fn solve_bag(items: Vec, capacity: i32) -> HashMap> { let pick_idx = 0; let current_weight = 0; let mut picks = vec![-1; items.len()]; let mut max_values = vec![-1; items.len()]; let mut result = HashMap::new(); bag(pick_idx, current_weight, &items, capacity, &mut picks, &mut max_values, &mut result,); result } fn bag(pick_idx: i32, current_weight: i32, items: &Vec, capacity: i32, picks: &mut Vec, max_values: &mut Vec, result: &mut HashMap>) { if current_weight == capacity || pick_idx == items.len() as i32 { if get_value(items, &picks) > get_value(items, max_values) { for i in 0..picks.len() { max_values[i] = picks[i]; } result.insert(get_value(items, max_values), picks.to_vec()); } return; } // 选 if current_weight + items[pick_idx as usize] <= capacity { picks[pick_idx as usize] = 1; bag(pick_idx + 1, current_weight + items[pick_idx as usize], items, capacity, picks, max_values, result); } // 不选 picks[pick_idx as usize] = 0; bag(pick_idx + 1, current_weight, items, capacity, picks, max_values, result); } fn get_value(items: &Vec, picks: &Vec) -> i32 { let mut result = 0; for i in 0..picks.len() { if picks[i] == 1 { result += items[i]; } } result } fn main() { let items = vec![2, 2, 4, 6, 3]; let m = solve_bag(items, 10); println!("{:?}", m); // {10: [1, 1, 0, 1, 0], 8: [1, 1, 1, 0, 0]} } ================================================ FILE: rust/39_back_tracking/bag_exec.rs ================================================ use std::collections::HashMap; fn solve_bag(items: Vec<(i32, i32)>, capacity: i32) -> HashMap> { let pick_idx = 0; let current_weight = 0; let mut picks = vec![-1; items.len()]; let mut max_values = vec![-1; items.len()]; let mut result = HashMap::new(); bag(pick_idx, current_weight, &items, capacity, &mut picks, &mut max_values, &mut result,); result } fn bag(pick_idx: i32, current_weight: i32, items: &Vec<(i32, i32)>, capacity: i32, picks: &mut Vec, max_values: &mut Vec, result: &mut HashMap>) { if current_weight == capacity || pick_idx == items.len() as i32 { if get_value(items, &picks) > get_value(items, max_values) { for i in 0..picks.len() { max_values[i] = picks[i]; } result.insert(get_value(items, max_values), picks.to_vec()); } return; } // 选 let item_weight = items[pick_idx as usize].0; if current_weight + item_weight <= capacity { picks[pick_idx as usize] = 1; bag(pick_idx + 1, current_weight + item_weight, items, capacity, picks, max_values, result); } // 不选 picks[pick_idx as usize] = 0; bag(pick_idx + 1, current_weight, items, capacity, picks, max_values, result); } fn get_value(items: &Vec<(i32, i32)>, picks: &Vec) -> i32 { let mut result = 0; for i in 0..picks.len() { if picks[i] == 1 { result += items[i].1; } } result } fn main() { // [(weight, value)...] let items = vec![(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)]; let m = solve_bag(items, 10); println!("{:?}", m); // {13: [1, 1, 1, 1, 0], 21: [1, 1, 1, 0, 1]} } ================================================ FILE: rust/39_back_tracking/n_queens.rs ================================================ # leetcode 51 [N-queens])https://leetcode.com/problems/n-queens/) pub fn solve_n_queens(n: i32) -> Vec> { let mut board = vec![vec!['.'; n as usize]; n as usize]; let mut solution = vec![]; schedule_queens(&mut board, &mut solution, n as usize, 0); solution } fn schedule_queens(board: &mut Vec>, solution: &mut Vec>, len: usize, row: usize) { for col in 0..len { if !collision(&board, len, row, col) { board[row][col] = 'Q'; if row == len - 1 { solution.push(board.iter().map(|vec| vec.iter().collect()).collect()); } else { schedule_queens(board, solution, len, row+1); } board[row][col] = '.'; } } } #[inline(always)] fn collision(board: &Vec>, len: usize, row: usize, col: usize) -> bool { for i in 0..row { if board[i][col] == 'Q' { return true } } let (mut i, mut j) = (row as i32 - 1, col as i32 - 1); while i >= 0 && j >= 0 { if board[i as usize][j as usize] == 'Q' { return true } i -= 1; j -= 1; } let (mut i, mut j) = (row as i32 - 1, col as i32 + 1); while i >= 0 && j < len as i32 { if board[i as usize][j as usize] == 'Q' { return true} i -= 1; j += 1; } false } fn main() { let m = solve_n_queens(8); println!("{:?}", m); } ================================================ FILE: rust/39_back_tracking/regex.rs ================================================ fn regex_match(text: &str, regex: &str) -> bool { let mut matched = false; let text_chars = text.chars().collect(); let regex_chars = regex.chars().collect(); rematch(&text_chars, ®ex_chars, 0, 0, &mut matched); matched } fn rematch(text_chars: &Vec, regex_chars: &Vec, t_idx: usize, r_idx: usize, matched: &mut bool) { // 已经匹配好了,直接返回; if *matched { return; } // 正则串已经全部匹配 if r_idx >= regex_chars.len() { *matched = true; return; } // 主串已经匹配完,但是正则串还没有全部进行匹配 if t_idx >= text_chars.len() && r_idx < regex_chars.len() { *matched = false; return; } // * 匹配1个或多个字符,递归进行匹配 if regex_chars[r_idx] == '*' { for i in t_idx..text_chars.len() { rematch(text_chars, regex_chars, i+1, t_idx+1, matched); } // ? 匹配0个或1个字符,分两种情况进行匹配 } else if regex_chars[r_idx] == '?' { rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched); rematch(text_chars, regex_chars, t_idx, r_idx+1, matched); // 非特殊字符,精确匹配 } else if regex_chars[r_idx] == text_chars[t_idx] { rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched); } } fn main() { let text = "abcdsadfkjlekjoiwjiojieeecd"; let regex = "ab*eee?d"; let m = regex_match(text, regex); println!("{}", m); } ================================================ FILE: rust/40_dynamic_programming/bag.rs ================================================ fn knapsack(items: Vec, capacity: i32) -> i32 { let mut states = vec![vec![false; (capacity + 1) as usize]; items.len()]; let mut result = vec![]; states[0][0] = true; if items[0] <= capacity { states[0][items[0] as usize] = true; } for i in 1..items.len() { for j in 0..=capacity as usize { if states[i-1][j] { states[i][j] = true; } } for j in 0..=(capacity - items[i]) as usize { if states[i-1][j] { states[i][j + items[i] as usize] = true; } } } let mut idx = capacity; while idx <= capacity { if states[items.len()-1][idx as usize] { break; } idx += 1; } for i in (1..items.len()).rev() { if idx - items[i] >= 0 && states[i-1][(idx-items[i]) as usize] { idx -= items[i]; result.push(items[i]); } } if idx != 0 { result.push(items[0]); } println!("{:?}", result); for i in (0..=capacity as usize).rev() { if states[items.len()-1][i] { return i as i32; } } 0 } fn main() { let items = vec![2, 2, 4, 6, 3]; let capacity = 9; let m = knapsack(items, capacity); println!("{}", m); } ================================================ FILE: rust/40_dynamic_programming/knapsack.rs ================================================ fn knapsack(items: Vec<(i32, i32)>, capacity: i32) -> i32 { let mut states = vec![-1; (capacity + 1) as usize]; let mut result = vec![]; states[0] = 0; if items[0].0 <= capacity { states[items[0].0 as usize] = items[0].1; } for i in 1..items.len() { for j in 0..=(capacity - items[i].0) as usize { if states[j] >= 0 { let value = states[j] + items[i].1; if value > states[j+items[i].0 as usize] { states[j+items[i].0 as usize] = value; result.push(items[i].0); } } } } let mut max_value = -1; for i in (0..=capacity as usize).rev() { if states[i] >= max_value { max_value = states[i]; } } max_value } fn main() { let items = vec![(2, 3), (2, 4), (4, 8), (6, 9), (3, 6)]; let capacity = 9; let m = knapsack(items, capacity); println!("{}", m); } ================================================ FILE: rust/40_dynamic_programming/triangle.rs ================================================ # leetcode [minimum_total](https://leetcode.com/problems/triangle/) pub fn minimum_total(mut triangle: Vec>) -> i32 { if triangle.len() == 0 { return 0; } for i in (0..triangle.len() - 1).rev() { for j in 0..triangle[i].len() { triangle[i][j] = triangle[i+1][j].min(triangle[i+1][j+1]) + triangle[i][j]; } } triangle[0][0] } fn main() { let triangle = vec![ vec![2], vec![3, 4], vec![6, 5, 7], vec![4, 1, 8, 3], ]; println!("{:?}", minimum_total(triangle)); } ================================================ FILE: rust/41_dynamic_programming/coin_change.rs ================================================ fn coin_change(coins: Vec, amount: i32) -> i32 { let mut dp = vec![amount+1; (amount+1) as usize]; dp[0] = 0; for i in 1..=amount as usize { for &coin in coins.iter() { if i as i32 >= coin { dp[i] = dp[i].min(dp[i-coin as usize] + 1); } } } let last = *dp.last().unwrap(); if last > amount { -1 } else { last } } fn main() { let coins = vec![1, 3, 5]; let m = coin_change(coins, 9); println!("{}", m); // 3 } ================================================ FILE: rust/41_dynamic_programming/min_dis_path.rs ================================================ fn min_dis_path(matrix: Vec>) -> i32 { let m_len = matrix.len(); if m_len == 0 { return 0; } let mut states = vec![vec![0; m_len]; m_len]; let mut sum = 0; // 初始化第一行数据 for j in 0..m_len { sum += matrix[0][j]; states[0][j] = sum; } sum = 0; // 初始化第一列数据 for i in 0..m_len { sum += matrix[i][0]; states[i][0] = sum; } for i in 1..m_len { for j in 1..m_len { states[i][j] = matrix[i][j] + states[i-1][j].min(states[i][j-1]) } } states[m_len-1][m_len-1] } fn main() { let matrix = vec![ vec![1, 3, 5, 9], vec![2, 1, 3, 4], vec![5, 2, 6, 7], vec![6, 8, 4, 3], ]; let m = min_dis_path(matrix); println!("{}", m); } ================================================ FILE: rust/42_dynamic_programming/edit_distance.rs ================================================ // leetcode 72 [edit_distance](https://leetcode.com/problems/edit-distance/) fn edit_distance(word1: &str, word2: &str) -> i32 { let word1_chars: Vec = word1.chars().collect(); let word2_chars: Vec = word2.chars().collect(); let m = word1.len(); let n = word2.len(); let mut dp = vec![vec![0; m+1]; n+1]; // 初始化第一行 for i in 0..=m { dp[i][0] = i; } // 初始化第一列 for j in 0..=n { dp[0][j] = j; } for i in 1..=m { for j in 1..=n { let mut step = 0; if word1_chars[i-1] != word2_chars[j-1] { step = 1; } dp[i][j] = (dp[i][j-1] + 1).min(dp[i-1][j] + 1).min(dp[i-1][j-1] + step); } } dp[m][n] as i32 } fn main() { let word1 = "mitcmu"; let word2 = "mtacnu"; let m = edit_distance(word1, word2); println!("{:?}", m); } ================================================ FILE: rust/42_dynamic_programming/longest_increasing_subsequence.rs ================================================ // leetcode 300 [longest_increasing_subsequence](https://leetcode.com/problems/longest-increasing-subsequence) fn longest_increasing_subsequence(nums: Vec) -> i32 { if nums.len() <= 1 { return nums.len() as i32; } let mut dp = vec![1; nums.len()]; let mut max_list = 1; for i in 0..nums.len() { for j in 0..i { if nums[i] > nums[j] { dp[i] = dp[i].max(dp[j]+1); } } max_list = max_list.max(dp[i]); } max_list } fn main() { let nums = vec![2, 9, 3, 6, 5, 1, 7]; let m = longest_increasing_subsequence(nums); println!("{:?}", m); } ================================================ FILE: scala/.gitignore ================================================ target/ ================================================ FILE: scala/build.sbt ================================================ lazy val root = (project in file(".")) .settings( name := "algo-scala", version := "1.0", scalaVersion := "2.12.8", libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" ) ================================================ FILE: scala/project/build.properties ================================================ sbt.version=1.2.1 ================================================ FILE: scala/src/main/scala/ch05_array/ArrayDemo.scala ================================================ package ch05_array class ArrayDemo(capacity: Int) { var data: Array[Char] = new Array[Char](capacity) var length: Int = 0 def find(index: Int): Char = { if (index < 0 || index > length) { return 0.toChar } data(index) } def insert(index: Int, value: Char): Boolean = { if (length == capacity) { return false } if (index < 0 || index >= capacity) { return false } for (i <- length until index by -1) { data(i) = data(i - 1) } data(index) = value length += 1 true } def delete(index: Int): Char = { if (length == 0) { throw new IllegalStateException("array is empty") } if (index >= length) { throw new IllegalStateException("index out of range, current data length is " + length) } val result = data(index) for (i <- index until length-1) { data(i) = data(i + 1) } length -= 1 result } def print: String = { data.subSequence(0, length).toString } } ================================================ FILE: scala/src/main/scala/ch06_linkedlist/SinglyLinkedList.scala ================================================ package ch06_linkedlist import scala.util.control.Breaks._ // the model class for the linked list class Node(var data: Int, var next: Option[Node]) class SinglyLinkedList(var headOpt: Option[Node]) { //define constructor without param def this() = this(None) def findByValue(value: Int): Option[Node] = { headOpt.flatMap(head => { var node = head while (!node.data.equals(value) && node.next.isDefined) { node = node.next.get } if (node.data.equals(value)) { return Some(node) } else { //arrive the end of the chain return None } }) } def insertToHead(value: Int): Unit = { val newNode = new Node(value, None) insertToHead(newNode) } def insertToHead(newNode: Node): Unit = { headOpt match { case None => //it's an empty linked list, make new node as head headOpt = Some(newNode) case Some(head) => newNode.next = Some(head) headOpt = Some(newNode) } } def insertTail(value: Int): Unit = { val newNode = new Node(value, None) insertTail(newNode) } def insertTail(newNode: Node): Unit = { headOpt match { case None => //it's an empty linked list, make new node as head headOpt = Some(newNode) case Some(head) => //need to start to find from head to current tail var node = head while (node.next.isDefined) { node = node.next.get } //now node is the tail as node.next is None //add new tail node.next = Some(newNode) } } def insertAfter(existNode: Node, value: Int): Unit = { val newNode = new Node(value, None) insertAfter(existNode, newNode) } def insertAfter(existNode: Node, newNode: Node): Unit = { existNode.next match { case None => //exist node is tail newNode.next = None existNode.next = Some(newNode) case Some(next) => newNode.next = Some(next) existNode.next = Some(newNode) } } def insertBefore(existNode: Node, value: Int): Unit = { val newNode = new Node(value, None) insertBefore(existNode, newNode) } def insertBefore(existNode: Node, newNode: Node): Unit = { headOpt match { case None => throw new IllegalStateException("head node should not be None") case Some(head) => if (existNode.equals(head)) { insertToHead(newNode) } var node = head while (node.next.isDefined && !node.next.get.equals(existNode)) { node = node.next.get } if (node.next.isEmpty) { throw new IllegalArgumentException("existNode " + existNode + " does not exist in this chain") } newNode.next = node.next node.next = Some(newNode) } } def deleteByNode(node: Node): Unit = { headOpt.map(head => { if (head.equals(node)) { //deleting head headOpt = node.next } else { var p: Node = head while (p.next.isDefined && !p.next.get.equals(node)) { p = p.next.get } if (p.next.isEmpty) { throw new IllegalArgumentException("could not find given node") } p.next = node.next } }) } //inverse the link from given node to head def inverseLink(node: Node): Node = { if (headOpt.isEmpty) { throw new IllegalArgumentException("list is empty") } var pre: Option[Node] = None var next: Option[Node] = None var current: Option[Node] = headOpt while (current.isDefined && !current.get.equals(node)) { next = current.get.next current.get.next = pre pre = current current = next } current.get.next = pre current.get } def isPalindrome(): Boolean = { headOpt match { case None => false case Some(head) => var p: Node = head var q: Node = head if (p.next.isEmpty) { //we only got 1 element in the chain return true } //start to find middle of the chain while (q.next.isDefined && q.next.get.next.isDefined) { p = p.next.get q = q.next.get.next.get } var leftLink: Option[Node] = None var rightLink: Option[Node] = None q.next match { case None => //p is in the middle of an odd numbers of chain rightLink = p.next leftLink = inverseLink(p).next case Some(_) => //p and p.next is in the middle of the even numbers of chain rightLink = p.next leftLink = Some(inverseLink(p)) } compareLinkedNodes(leftLink, rightLink) } } def compareLinkedNodes(leftLink: Option[Node], rightLink: Option[Node]): Boolean = { var left = leftLink var right = rightLink breakable { while (left.isDefined && right.isDefined) { if (!left.get.data.equals(right.get.data)) { break } left = left.get.next right = right.get.next } } //make sure we have loop until the end of the chain left.isEmpty && right.isEmpty } def mkString(): String = { headOpt.map(head => { var node = head val result = new StringBuilder while (node.next.isDefined) { result.append(node.data) node = node.next.get } result.append(node.data) result.mkString }).getOrElse("") } } ================================================ FILE: scala/src/main/scala/ch07_linkedlist/LinkedListAlgo.scala ================================================ package ch07_linkedlist import ch06_linkedlist.Node object LinkedListAlgo { //reverse a linked list def reverse(head: Node): Node = { if (head.next.isEmpty) { //single node of linked list return head } var prev: Option[Node] = None var current: Option[Node] = Some(head) var next: Option[Node] = None while (current.get.next.isDefined) { next = current.get.next current.get.next = prev prev = current current = next } //for the tail of the node, make it point to previous node to complete the reverse current.get.next = prev current.get } /** * * @param head * @return Some(Node) a node in a circle or None */ def checkCircle(head: Node): Option[Node] = { var fast = head var slow = head while (fast.next.isDefined && fast.next.get.next.isDefined) { fast = fast.next.get.next.get slow = slow.next.get if (fast.equals(slow)) { return Some(slow) } } None } /** * calculate the length of the circle * * @param node - some node in the circle * @return circle length */ def calculateCircleLength(node: Node): Int = { var length = 1 var cursor = node.next.get while (cursor != node) { length += 1 cursor = cursor.next.get } length } def findCircleEntrance(head: Node): Option[Node] = { checkCircle(head).map(node => { val length = calculateCircleLength(node) var fast = head var slow = head //fast move length steps for (i <- 0 until length) { fast = fast.next.get } while (slow != fast) { fast = fast.next.get slow = slow.next.get } slow }) } //assuming nodeA and nodeB are all sorted list in ascending order def mergeSortedList(nodeA: Option[Node], nodeB: Option[Node]): Option[Node] = { if (nodeA.isEmpty && nodeB.isEmpty) { return None } if (nodeA.isEmpty && nodeB.isDefined) { return nodeB } if (nodeA.isDefined && nodeB.isEmpty) { return nodeA } //now we have both nodeA and nodeB defined var head: Option[Node] = None var leftCursor = nodeA var rightCursor = nodeB //calculate head and we are sure both leftCursor and rightCursor has data if (leftCursor.get.data < rightCursor.get.data) { head = leftCursor leftCursor = leftCursor.get.next } else { head = rightCursor rightCursor = rightCursor.get.next } var mergedCursor: Option[Node] = head while (leftCursor.isDefined && rightCursor.isDefined) { if (leftCursor.get.data < rightCursor.get.data) { mergedCursor.get.next = leftCursor leftCursor = leftCursor.get.next } else { mergedCursor.get.next = rightCursor rightCursor = rightCursor.get.next } mergedCursor = mergedCursor.get.next } //we have loop over at least one chain //we just put the other chain in to the merged chain if (leftCursor.isDefined) { mergedCursor.get.next = leftCursor } else { mergedCursor.get.next = rightCursor } head } def deleteLastKthNode(headOpt: Option[Node], k: Int): Option[Node] = { require(k > 0, "k must greater than 0") headOpt match { case None => None case Some(head) => var index = 0 var slow = headOpt var fast = headOpt while (fast.get.next.isDefined && index < k) { //move fast cursor to k fast = fast.get.next index += 1 } if (fast.get.next.isEmpty && index + 1 == k) { //deleting the head element return head.next } require(index.equals(k), "given linked list should contains at least k elements ") while (fast.get.next.isDefined) { fast = fast.get.next slow = slow.get.next } //fast cursor is in the end of the chain //slow is the previous pos of k element //do the operation slow.get.next = slow.get.next.get.next } headOpt } //form all the chain value as string def mkStringForChain(node: Node): String = { val result = new StringBuilder var p = node while (p.next.isDefined) { result.append(p.data) p = p.next.get } result.append(p.data) result.mkString } } ================================================ FILE: scala/src/main/scala/ch08_stack/BrowserDemo.scala ================================================ package ch08_stack class BrowserDemo(var currentPageOpt: Option[String], val backStack: StackDemo[String], val forwardStack: StackDemo[String]) { def this() = this(None, new StackDemo[String], new StackDemo[String]) def open(page: String) = { currentPageOpt.foreach(backStack.push) forwardStack.clear() currentPageOpt = Some(page) } def canGoBack(): Boolean = backStack.size > 0 def goBack(): Unit = { backStack.pop().foreach(page => { forwardStack.push(currentPageOpt.get) currentPageOpt = Some(page.data) }) } def canGoForward(): Boolean = forwardStack.size > 0 def goForward(): Unit = { forwardStack.pop().foreach(page => { backStack.push(currentPageOpt.get) currentPageOpt = Some(page.data) }) } } ================================================ FILE: scala/src/main/scala/ch08_stack/StackDemo.scala ================================================ package ch08_stack class Node[T](var data: T, var next: Option[Node[T]]) class StackDemo[T] { var headOpt: Option[Node[T]] = None var size = 0 def clear(): Unit = { headOpt = None size = 0 } def push(data: T) = { val newHead = new Node(data, headOpt) headOpt = Some(newHead) size += 1 } def pop(): Option[Node[T]] = { headOpt match { case None => None case Some(head) => headOpt = head.next size -= 1 Some(head) } } } ================================================ FILE: scala/src/main/scala/ch09_queue/ArrayQueue.scala ================================================ package ch09_queue import scala.reflect.ClassTag class ArrayQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] { var items: Array[T] = new Array[T](capacity) var head = 0 var tail = 0 override def enqueue(data: T): Unit = { require(tail < capacity, "queue is full") items(tail) = data tail += 1 size += 1 } override def dequeue(): Option[T] = { if (head < tail) { val result = Some(items(head)) head += 1 size -= 1 result } else { None } } } ================================================ FILE: scala/src/main/scala/ch09_queue/CircularQueue.scala ================================================ package ch09_queue import scala.reflect.ClassTag class CircularQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] { var items: Array[T] = new Array[T](capacity) var head = 0 var tail = 0 override def enqueue(data: T): Unit = { require((tail + 1) % capacity != head, "queue is full") items(tail) = data tail = (tail + 1) % capacity size += 1 } override def dequeue(): Option[T] = { if (head == tail) { None } else { size -= 1 val result = Some(items(head)) head = (head + 1) % capacity result } } } ================================================ FILE: scala/src/main/scala/ch09_queue/DemoQueue.scala ================================================ package ch09_queue trait DemoQueue[T] { var size = 0 def enqueue(data: T): Unit def dequeue(): Option[T] } ================================================ FILE: scala/src/main/scala/ch09_queue/DynamicArrayQueue.scala ================================================ package ch09_queue import scala.reflect.ClassTag class DynamicArrayQueue[T: ClassTag](val capacity: Int) extends ArrayQueue[T](capacity) { override def enqueue(data: T): Unit = { if (tail == capacity) { //tail is the end of require(head > 0, "queue is full") for (i <- Range(head, tail)) { items(i - head) = items(head) } tail = tail - head head = 0 } super.enqueue(data) } } ================================================ FILE: scala/src/main/scala/ch09_queue/LinkedListQueue.scala ================================================ package ch09_queue class Node[T](var data: T, var next: Option[Node[T]]) class LinkListQueue[T]() extends DemoQueue[T] { var headOpt: Option[Node[T]] = None var tailOpt: Option[Node[T]] = None override def enqueue(data: T): Unit = { val node = new Node(data, None) size += 1 headOpt match { case None => headOpt = Some(node) case Some(_) => } tailOpt match { case None => tailOpt = Some(node) case Some(tail) => tail.next = Some(node) tailOpt = Some(node) } } override def dequeue(): Option[T] = { headOpt.map(head => { size -= 1 headOpt = head.next if (headOpt.isEmpty) { //head is empty reach the end of the queue tailOpt = None } head.data }) } } ================================================ FILE: scala/src/main/scala/ch10_recursive/RecursiveDemo.scala ================================================ package ch10_recursive import scala.collection.mutable object RecursiveDemo { def calculateStepWays(steps: Int): Int = { //use knownResults to avoid duplicated computing val knownResults = mutable.HashMap.empty[Int, Int] steps match { case 1 => 1 case 2 => 2 case _ => knownResults.get(steps) match { case Some(result) => result case None => { val result = calculateStepWays(steps - 1) + calculateStepWays(steps - 2) knownResults.put(steps, result) result } } } } } ================================================ FILE: scala/src/main/scala/ch11_sorts/Sorts.scala ================================================ package ch11_sorts import scala.util.control.Breaks.{break, breakable} /** * 冒泡排序、插入排序、选择排序 * * Author: yangchuz */ object Sorts { def bubbleSort(items: Array[Int]): Array[Int] = { val length = items.length breakable { for (i <- Range(0, length)) { var exit = true for (j <- Range(0, length - i - 1)) { if (items(j + 1) < items(j)) { val temp = items(j + 1) items(j + 1) = items(j) items(j) = temp exit = false } } if (exit) { break } } } items } def insertSort(items: Array[Int]): Array[Int] = { val length = items.length for (i <- Range(1, length)) { val value = items(i) var j = i - 1 breakable { while (j >= 0) { if (items(j) > value) { items(j + 1) = items(j) } else { break } j -= 1 } } items(j + 1) = value } items } def selectionSort(items: Array[Int]): Array[Int] = { val length = items.length for (i <- Range(0, length)) { var minIndex = i for (j <- Range(i + 1, length)) { if (items(j) < items(minIndex)) { minIndex = j } } //put the min value to the front val temp = items(i) items(i) = items(minIndex) items(minIndex) = temp } items } } ================================================ FILE: scala/src/main/scala/ch12_sorts/MergeSort.scala ================================================ package ch12_sorts object MergeSort { def mergeSort(items: Array[Int]): Array[Int] = { _mergeSort(items, 0, items.length - 1) items } private[this] def _mergeSort(items: Array[Int], p: Int, r: Int): Unit = { if (p >= r) { return } val q = p + (r - p) / 2 _mergeSort(items, p, q) _mergeSort(items, q + 1, r) _merge(items, p, q, r) } private[this] def _merge(items: Array[Int], p: Int, q: Int, r: Int): Unit = { //start of first half var i = p //start of second half var j = q + 1 var k = 0 //temp array to hold the data val tempArray = new Array[Int](r - p + 1) while (i <= q && j <= r) { if (items(i) <= items(j)) { tempArray(k) = items(i) i += 1 } else { tempArray(k) = items(j) j += 1 } k += 1 } var start = i var end = q if (j <= r) { start = j end = r } for (n <- start to end) { tempArray(k) = items(n) k += 1 } //copy tempArray back to items for (n <- 0 to r - p) { items(p + n) = tempArray(n) } } } ================================================ FILE: scala/src/main/scala/ch12_sorts/QuickSort.scala ================================================ package ch12_sorts object QuickSort { //find the K th smallest element int the array def findKthElement(items: Array[Int], k: Int): Int = { _findKthElement(items, k, 0, items.length - 1) } private[this] def _findKthElement(items: Array[Int], k: Int, p: Int, r: Int): Int = { val q = _partition(items, p, r) if (k == q + 1) { items(q) } else if (k < q + 1) { _findKthElement(items, k, p, q - 1) } else { _findKthElement(items, k, q + 1, r) } } def quickSort(items: Array[Int]): Array[Int] = { _quickSort(items, 0, items.length - 1) items } private[this] def _quickSort(items: Array[Int], p: Int, r: Int): Unit = { if (p >= r) { return } val q = _partition(items, p, r) _quickSort(items, p, q - 1) _quickSort(items, q + 1, r) } private[this] def _partition(items: Array[Int], p: Int, r: Int): Int = { val pivot = items(r) var i = p for (j <- Range(p, r)) { if (items(j) < pivot) { val temp = items(i) items(i) = items(j) items(j) = temp i += 1 } } val temp = items(i) items(i) = items(r) items(r) = temp i } } ================================================ FILE: scala/src/main/scala/ch15_bsearch/BSearch.scala ================================================ package ch15_bsearch import scala.math.abs object BSearch { def search(items: Array[Int], target: Int): Int = { var low = 0 var high = items.length - 1 while (low <= high) { val mid = low + (high - low) / 2 if (items(mid) == target) { return mid } else if (items(mid) > target) { high = mid - 1 } else { low = mid + 1 } } -1 } def sqrt(x: Double, precision: Double): Double = { require(precision > 0, "precision must > 0") require(x > 0, "input value for sqrt must > 0") var low = 0.0 var high = x val actualPrecision = precision / 10 if (x > 0 && x < 1) { low = x high = 1 } while (high - low > actualPrecision) { val mid = low + (high - low) / 2 if (abs(mid * mid - x) < actualPrecision) { //find it return mid } else if (mid * mid > x) { high = mid } else { low = mid } } throw new IllegalStateException("could not determine the sqrt value for " + x) } } ================================================ FILE: scala/src/main/scala/ch15_bsearch/BSearchRecursive.scala ================================================ package ch15_bsearch object BSearchRecursive { def search(items: Array[Int], target: Int): Int = { _search(items, target, 0, items.length - 1) } private[this] def _search(items: Array[Int], target: Int, low: Int, high: Int): Int = { if (low > high) { return -1 } val mid = low + (high - low) / 2 if (items(mid) == target) { mid } else if (items(mid) > target) { _search(items, target, low, mid - 1) } else { _search(items, target, mid + 1, high) } } } ================================================ FILE: scala/src/main/scala/ch16_bsearch/BSearch.scala ================================================ package ch16_bsearch object BSearch { //find the first index of given value //-1 if not found def findFirstValue(items: Array[Int], target: Int): Int = { require(items.length > 0, "given array is empty") var low = 0 var high = items.length - 1 while (low <= high) { val mid = low + (high - low) / 2 if (items(mid) > target) { high = mid - 1 } else if (items(mid) < target) { low = mid + 1 } else { //find the value in the array if (mid == 0 || items(mid - 1) != target) { return mid } else { high = mid - 1 } } } -1 } def findLastValue(items: Array[Int], target: Int): Int = { var low = 0 var high = items.length - 1 while (low <= high) { val mid = low + (high - low) / 2 if (items(mid) > target) { high = mid - 1 } else if (items(mid) < target) { low = mid + 1 } else { //find the target value if (mid == items.length - 1 || items(mid + 1) != target) { return mid } else { low = mid + 1 } } } -1 } def findFirstGreaterThan(items: Array[Int], target: Int): Int = { var low = 0 var high = items.length while (low <= high) { val mid = low + (high - low) / 2 if (items(mid) >= target) { //find the range if (mid == 0 || items(mid - 1) < target) { return mid } else { high = mid - 1 } } else { low = mid + 1 } } -1 } def findLastSmallerThan(items: Array[Int], target: Int): Int = { var low = 0 var high = items.length - 1 while (low <= high) { var mid = low + (high - low) / 2 if (items(mid) <= target) { //find the range if (mid == items.length - 1 || items(mid + 1) > target) { return mid } else { low = mid + 1 } } else { high = mid - 1 } } -1 } } ================================================ FILE: scala/src/main/scala/ch17_skip_list/SkipList.scala ================================================ package ch17_skip_list import scala.util.Random class Node(var data: Int, var forwards: Array[Node], var maxLevel: Int) class SkipList(var head: Node, var skipListLevel: Int) { def this() = this(new Node(-1, new Array[Node](16), 0), 1) val MAX_LEVEL = 16 val random = new Random() def find(value: Int): Option[Node] = { var p = head for (i <- skipListLevel - 1 to 0 by -1) { while (p.forwards(i) != null && p.forwards(i).data < value) { p = p.forwards(i) } } if (p.forwards(0) != null && p.forwards(0).data == value) { Some(p.forwards(0)) } else { None } } def insert(value: Int): Unit = { //init the new node val level = randomLevel() val newNode = new Node(value, new Array[Node](level), level) //use updtes array to record all nodes in all level before the inserted node val updates: Array[Node] = new Array[Node](level) var p = head for (i <- level - 1 to 0 by -1) { while (p.forwards(i) != null && p.forwards(i).data < value) { p = p.forwards(i) } updates(i) = p } for (i <- Range(0, level)) { newNode.forwards(i) = updates(i).forwards(i) updates(i).forwards(i) = newNode } if (level > skipListLevel) { skipListLevel = level } } def delete(value: Int): Unit = { var p = head val updates: Array[Node] = new Array[Node](skipListLevel) //try to locate the given node with the value for (i <- skipListLevel - 1 to 0 by -1) { while (p.forwards(i) != null && p.forwards(i).data < value) { p = p.forwards(i) } updates(i) = p } if (p.forwards(0) != null && p.forwards(0).data == value) { //find the value node, start to delete the node from the skip list for (i <- skipListLevel - 1 to 0 by -1) { if (updates(i).forwards(i) != null && updates(i).forwards(i).data == value) { updates(i).forwards(i) = updates(i).forwards(i).forwards(i) } } } } def randomLevel(): Int = { var level = 1 for (i <- Range(1, MAX_LEVEL)) { if (random.nextInt() % 2 == 1) { level += 1 } } level } def mkString(): String = { val builder = new StringBuilder var p = head while (p.forwards(0) != null) { p = p.forwards(0) builder.append(p.data) } builder.mkString } } ================================================ FILE: scala/src/main/scala/ch20_linked_hash_map/LRUCache.scala ================================================ package ch20_linked_hash_map class Node[K, V](var key: Option[K], var data: Option[V], var prev: Option[Node[K, V]], var next: Option[Node[K, V]], var hNext: Option[Node[K, V]]) { def this(key: Option[K], data: Option[V]) = this(key, data, None, None, None) } /** * LRU cache - https://leetcode.com/problems/lru-cache/ see unit test from LRUCacheTest * * @author email2liyang@gmail.com */ class LRUCache[K, V](var head: Node[K, V], var tail: Node[K, V], var table: Array[Node[K, V]], capacity: Int = 1000, var elementCount: Int = 0) { head.next = Some(tail) tail.prev = Some(head) def this(capacity: Int) = this(new Node(None, None), new Node(None, None), new Array[Node[K, V]](capacity), capacity) def get(key: K): Option[V] = { val index = indexFor(key.hashCode()) var hNode = table(index) if (hNode == null) { None } else { while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) { hNode = hNode.hNext.get } if (hNode.key.get.equals(key)) { //move this to the end of the linked list moveHNodeToTail(hNode) hNode.data } else { None } } } //put data into the linked hash map //1: check if the data exist in the linked list //2: if it's not exist , append it in the linked list //3: if it's exist in the list, move it to the tail of the linked list //4: return old value if it's exist def put(key: K, value: V): Option[V] = { if (elementCount == capacity) { deleteLRUElement() } val node = new Node(Some(key), Some(value)) val index = indexFor(key.hashCode()) var hNode = table(index) var result: Option[V] = None if (hNode == null) { //if it's not exist , append it in the linked list node.prev = tail.prev node.next = Some(tail) tail.prev.get.next = Some(node) tail.prev = Some(node) table(index) = node elementCount += 1 } else { //we find a key conflict in the hash table //start to loop the hNode to match the key while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) { hNode = hNode.hNext.get } if (hNode.key.get.equals(key)) { //find the old data from the hash table result = hNode.data hNode.data = Some(value) //move the node to the tail of the linked list moveHNodeToTail(hNode) //hNext pointer stay untouched } else { //could not find the old data //put the new node into the tail of the linked list node.prev = tail.prev node.next = Some(tail) tail.prev.get.next = Some(node) tail.prev = Some(node) //put it the tail of the hash table's list //iterator to the end of hNode while (hNode.hNext.isDefined) { hNode = hNode.hNext.get } hNode.hNext = Some(node) elementCount += 1 } } result } private[this] def moveHNodeToTail(hNode: Node[K, V]) = { hNode.prev.get.next = hNode.next hNode.next.get.prev = hNode.prev hNode.prev = tail.prev hNode.next = Some(tail) tail.prev.get.next = Some(hNode) tail.prev = Some(hNode) } private[this] def deleteLRUElement(): Unit = { //cache is full, start to delete element from the head val node = head.next.get //delete it from head node.next.get.prev = Some(head) head.next = node.next //deal with hNext in the table val index = indexFor(node.key.get.hashCode()) var hNode = table(index) //deal with first element in the hash table if (hNode.key.get.equals(node.key.get)) { hNode.hNext match { case Some(n) => table(index) = n case None => table(index) = null } } else { //deal with not first element in the hash table var hNodePrev = hNode hNode = hNode.next.get while (!hNode.key.get.equals(node.key.get)) { hNode = hNode.next.get hNodePrev = hNodePrev.next.get } //now hNodePrev is the previous hNode in the hashtable //remove the hNode hNodePrev.next = hNode.next hNode.next match { case Some(n) => n.prev = Some(hNodePrev) case None => } } elementCount -= 1 } private[this] def indexFor(hash: Int): Int = { hash % table.length } } ================================================ FILE: scala/src/main/scala/ch23_binary_tree/BinaryTree.scala ================================================ package ch23_binary_tree import scala.collection.mutable class Node[T](var data: T, var left: Option[Node[T]], var right: Option[Node[T]]) class BinaryTree[T] { def preOrder(root: Option[Node[T]]): String = { val result = new StringBuilder if (root.isDefined) { result.append(root.map(_.data.toString).get) result.append(preOrder(root.get.left)) result.append(preOrder(root.get.right)) } result.mkString } def inOrder(root: Option[Node[T]]): String = { val result = new StringBuilder if (root.isDefined) { result.append(inOrder(root.get.left)) result.append(root.map(_.data.toString).get) result.append(inOrder(root.get.right)) } result.mkString } def postOrder(root: Option[Node[T]]): String = { val result = new StringBuilder if (root.isDefined) { result.append(postOrder(root.get.left)) result.append(postOrder(root.get.right)) result.append(root.map(_.data.toString).get) } result.mkString } def levelOrder(root: Option[Node[T]]): String = { val result = new StringBuilder val queue = new mutable.Queue[Node[T]]() if (root.isDefined) { queue += root.get while (!queue.isEmpty) { val node = queue.dequeue() result.append(node.data.toString) if (node.left.isDefined) { queue += node.left.get } if (node.right.isDefined) { queue += node.right.get } } } result.mkString } } ================================================ FILE: scala/src/main/scala/ch24_binary_search_tree/BinarySearchTree.scala ================================================ package ch24_binary_search_tree import ch23_binary_tree.{BinaryTree, Node} import scala.util.control.Breaks._ class BinarySearchTree(var root: Option[Node[Int]]) extends BinaryTree[Int] { def insert(data: Int): Node[Int] = { val dataNode = new Node(data, None, None) root match { case None => root = Some(dataNode) case Some(_) => { var p = root breakable { while (p.isDefined) { if (data > p.get.data) { p.get.right match { case None => { p.get.right = Some(dataNode) break } case Some(_) => p = p.get.right } } if (data < p.get.data) { p.get.left match { case None => { p.get.left = Some(dataNode) break } case Some(_) => p = p.get.left } } } } } } dataNode } def find(data: Int): Option[Node[Int]] = { var p = root breakable { while (p.isDefined) { if (data > p.get.data) { p = p.get.right } else if (data < p.get.data) { p = p.get.left } else { //find the value break } } } p } def delete(data: Int): Unit = { //there are 3 scenarios //1: data is leaf node //2: data has one child node //3: data has two child nodes, we need to find out the smallest node from right branch var p = root var pp: Option[Node[Int]] = None //parent node of deleted node //find matching node to delete breakable { while (p.isDefined) { if (data > p.get.data) { pp = p p = p.get.right } else if (data < p.get.data) { pp = p p = p.get.left } else { //find the value break } } } if (p.isEmpty) { //find nothing return } //now we find the node to delete //scenario 3 if (p.get.left.isDefined && p.get.right.isDefined) { //need to find out the smallest node in right branch var minPP = p var minP = p.get.right while (minP.get.left.isDefined) { minPP = minP minP = minP.get.left } //assign the smallest value in the right branch to the node to be deleted p.get.data = minP.get.data //now problems becomes delete the minP in the tree //minP must not have any left child node //minP may or may not have right child node //it will fall back to scenario 1 or 2 p = minP pp = minPP } //child is the child of p var child: Option[Node[Int]] = None if (p.get.left.isDefined) { child = p.get.left } else if (p.get.right.isDefined) { child = p.get.right } //starting the node deletion pp match { case None => root = child case Some(parentNode) => { if (parentNode.left == p) { parentNode.left = child } else if (parentNode.right == p) { parentNode.right = child } } } } def height(): Int = { _height(root) } private[this] def _height(nodeOpt: Option[Node[Int]]): Int = { nodeOpt match { case None => 0 case Some(node) => { if (node.left.isEmpty && node.right.isEmpty) { 1 } else { scala.math.max(_height(node.left), _height(node.right)) + 1 } } } } } ================================================ FILE: scala/src/main/scala/ch28_heap/Heap.scala ================================================ package ch28_heap import scala.util.control.Breaks._ class Heap(val capacity: Int, var elementCount: Int = 0) { def this(arrayParam: Array[Int], bottomUp: Boolean) = { this(arrayParam.length + 1) if (bottomUp) { arrayParam.foreach(this.insert) } else { //copy data into array of heap for (i <- arrayParam.indices) { array(i + 1) = arrayParam(i) elementCount = arrayParam.length } for (i <- elementCount / 2 + 1 to 1 by -1) { heapifyTopDown(i, elementCount - 1) } } } require(capacity > 0, "capacity should be > 0") val array: Array[Int] = new Array[Int](capacity) def insert(data: Int): Unit = { if (elementCount == capacity - 1) { throw new IllegalStateException("heap full") } elementCount += 1 array(elementCount) = data //heapify bottom up //compare the element with it's parent node i/2 until parent node > child node //this will make sure the root element of the tree is the biggest value var i = elementCount while (i / 2 > 0 && array(i) > array(i / 2)) { val temp = array(i) array(i) = array(i / 2) array(i / 2) = temp i = i / 2 } } def removeMax(): Int = { require(elementCount > 0, "heap is empty") val result = array(1) array(1) = array(elementCount) elementCount -= 1 heapifyTopDown(1, elementCount) result } //heapify from top to bottom //start from the top to compare with it's child nodes //swap if child node > parent node //stop at child node <= parent node private[this] def heapifyTopDown(startIndex: Int, stopIndex: Int) = { var pointer = startIndex breakable { while (true) { var maxPos = pointer if (pointer * 2 <= stopIndex && array(pointer * 2) > array(maxPos)) { maxPos = pointer * 2 } if (pointer * 2 <= stopIndex && array(pointer * 2 + 1) > array(maxPos)) { maxPos = pointer * 2 + 1 } if (maxPos == pointer) { break } //swap the parent and child val temp = array(pointer) array(pointer) = array(maxPos) array(maxPos) = temp //start a new round pointer = maxPos } } } } object Heap { def heapSort(array: Array[Int]): Array[Int] = { val result = new Array[Int](array.length) val heap = new Heap(array, true) for (i <- result.length - 1 to 0 by -1) { result(i) = heap.removeMax() } result } } ================================================ FILE: scala/src/main/scala/ch29_heap_solutions/FileMerger.scala ================================================ package ch29_heap_solutions import java.io.{BufferedWriter, File, FileWriter} import scala.collection.mutable import scala.io.Source import scala.util.control.Breaks._ object FileMerger { /** * each given file has sorted String as content, we need to merge them together * * @param smallFiles - small files with sorted content * @return merged file */ def mergeFiles(smallFiles: List[File]): File = { //init output file val output = File.createTempFile("merged-file", ".txt") val writer = new BufferedWriter(new FileWriter(output)) //init small top heap val priorityQueue = new mutable.PriorityQueue[(Char, Source)]()(Ordering.by((_: (Char, Source))._1).reverse) val sources = smallFiles.toArray.map(smallFile => Source.fromFile(smallFile)) //init fill the priority queue from each file sources.foreach(source => priorityQueue.enqueue((source.next(), source))) breakable { while (true) { val next = priorityQueue.dequeue() val output: Char = next._1 val source = next._2 writer.append(output) if (source.hasNext) { priorityQueue.enqueue((source.next(), source)) } //determine the end of merge if (sources.forall(!_.hasNext) && priorityQueue.isEmpty) { break } } } writer.close() output } } ================================================ FILE: scala/src/main/scala/ch29_heap_solutions/MiddleNumberKeeper.scala ================================================ package ch29_heap_solutions import scala.collection.mutable class MiddleNumberKeeper(val percent: Double) { def this() = this(0.5) val bigTop = new mutable.PriorityQueue[Int]() val smallTop = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse) def put(num: Int): Unit = { if (smallTop.nonEmpty && num >= smallTop.head) { smallTop += num adjustHeap() return } //for any other scenario, we just put the item to bitTop then adjustHeap bigTop += num adjustHeap() } def get(): Option[Int] = { bigTop.headOption } private[this] def adjustHeap(): Unit = { val totalLength = smallTop.length + bigTop.length //deal with bigTop while (bigTop.length.doubleValue() / totalLength - percent > 0.0001) { //move item from bigTop to smallTop smallTop += bigTop.dequeue() } //deal with smallTop while (smallTop.length.doubleValue() / totalLength - (1.0D - percent) > 0.0001) { bigTop += smallTop.dequeue() } } } ================================================ FILE: scala/src/main/scala/ch29_heap_solutions/TopKItemsKeeper.scala ================================================ package ch29_heap_solutions import scala.collection.mutable /** * keep the top k items in the the class */ class TopKItemsKeeper(val itemsToKeepCount: Int) { //have a smallest value top heap val queue = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse) def put(item: Int): Unit = { if (queue.length < itemsToKeepCount) { queue += item return } //queue already have the k items if (item.compareTo(queue.head) > 0) { queue.dequeue() queue += item } } def get(): List[Int] = { queue.clone().dequeueAll } } ================================================ FILE: scala/src/main/scala/ch31_graph/Graph.scala ================================================ package ch31_graph import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.util.control.Breaks._ class Graph(vertex: Array[String]) { require(vertex.nonEmpty, "nonEmpty vertex required") val adjacency = new Array[mutable.MutableList[String]](vertex.length) for (i <- Range(0, vertex.length)) { adjacency(i) = new mutable.MutableList[String]() } def addEdge(startNode: String, endNode: String): Unit = { adjacency(vertex.indexOf(startNode)) += endNode adjacency(vertex.indexOf(endNode)) += startNode } def getEdges(node: String): Array[String] = { adjacency(vertex.indexOf(node)).toArray } def breathFirstSearch(startNode: String, destNode: String): Option[Array[String]] = { var result: Option[Array[String]] = None val queue = new mutable.Queue[String]() val visited = new mutable.HashSet[String]() val explored = new ArrayBuffer[String]() //put starting node into the queue queue += startNode breakable { while (queue.nonEmpty) { val node = queue.dequeue() if (!visited.contains(node)) { explored += node visited += node if (node.equals(destNode)) { result = Some(explored.toArray) break } queue ++= adjacency(vertex.indexOf(node)) } } } result } def depthFirstSearch(startNode: String, destNode: String): Option[Array[String]] = { var found = false val visited = new mutable.HashSet[String]() val explored = new ArrayBuffer[String]() def _dfs(start: String): Unit = { if (found) { return } if (!visited.contains(start)) { visited += start explored += start val destsForStart: mutable.MutableList[String] = adjacency(vertex.indexOf(start)) breakable { for (i <- destsForStart.indices) { val node = destsForStart(i) if (node.equals(destNode)) { found = true if (!explored.contains(node)) { explored += node } break() } _dfs(node) } } } } _dfs(startNode) if (found) { Some(explored.toArray) } else { None } } } ================================================ FILE: scala/src/main/scala/ch32_matching/BruteForce.scala ================================================ package ch32_matching import scala.util.control.Breaks._ object BruteForce { def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = { require(main != null, "main array required") require(sub != null, "sub array required") require(main.length >= sub.length, "sub array should be small than main array") var result = -1 breakable { for (i <- 0 until (main.length - sub.length)) { if (main.slice(i, i + sub.length) sameElements sub) { result = i break } } } result } } ================================================ FILE: scala/src/main/scala/ch32_matching/RabinKarp.scala ================================================ package ch32_matching import scala.util.control.Breaks.{break, breakable} object RabinKarp { def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = { require(main != null, "main array required") require(sub != null, "sub array required") require(main.length >= sub.length, "sub array should be small than main array") val baseNums: Array[Long] = new Array[Long](sub.length) for (i <- sub.indices) { baseNums(i) = scala.math.pow(128, i).longValue() } val subHash = hash(sub, baseNums) var result = -1 breakable { for (i <- 0 until (main.length - sub.length)) { if (hash(main.slice(i, i + sub.length), baseNums).equals(subHash) && main.slice(i, i + sub.length).sameElements(sub)) { result = i break } } } result } def hash(array: Array[Char], baseNums: Array[Long]): Long = { var hash = 0L for (i <- array.indices) { hash += array(i).toInt * baseNums(array.length - 1 - i) } hash } } ================================================ FILE: scala/src/main/scala/ch35_tire_tree/TrieTree.scala ================================================ package ch35_tire_tree import scala.collection.mutable.ArrayBuffer import scala.util.control.Breaks._ class TrieNode(var data: Char, var children: Array[TrieNode], var isEndNode: Boolean = false) { def this(data: Char) = this(data, new Array[TrieNode](26)) } class TrieTree { val root = new TrieNode('/') //root node without any data def insert(text: Array[Char]): Unit = { var p = root for (i <- text.indices) { val index = text(i) - 'a' if (p.children(index) == null) { val node = new TrieNode(text(i)) p.children(index) = node } p = p.children(index) } //now p is pointing the leaf node p.isEndNode = true } def find(text: Array[Char]): Boolean = { var p = root breakable { for (i <- text.indices) { val index = text(i) - 'a' if (p.children(index) == null) { break } p = p.children(index) } } p.isEndNode } def prefix(prefix: Array[Char]): Boolean = { var p = root var level = -1 breakable { for (i <- prefix.indices) { val index = prefix(i) - 'a' if (p.children(index) == null) { break } p = p.children(index) level = i } } //should not reach leaf node and should have at least 1 matching level !p.isEndNode && level > -1 } def suggestion(text: Array[Char]): Option[Array[String]] = { var p = root var level = -1 breakable { for (i <- text.indices) { val index = text(i) - 'a' if (p.children(index) == null) { break } p = p.children(index) level = i } } //have prefix matching if (!p.isEndNode && level > -1) { //now the problem becomes print all the children from p Some(_children(p).map(str => "%s%s".format(text.slice(0, text.length - 1).mkString(""), str)).toArray) } else { None } } def _children(p: TrieNode): ArrayBuffer[String] = { if (p.isEndNode) { return ArrayBuffer(p.data.toString) } //p is not leaf node val arrayBuffer = new ArrayBuffer[String]() for (i <- p.children.indices) { if (p.children(i) != null) { _children(p.children(i)).foreach(subStr => arrayBuffer.append("%s%s".format(p.data.toString, subStr)) ) } } arrayBuffer } } ================================================ FILE: scala/src/main/scala/ch39_back_tracking/BagWeight.scala ================================================ package ch39_back_tracking class BagWeight(maxBagItemCount: Int, maxBagWeight: Int) { def calculateMaxWeight(items: Array[Int]): Int = { var maxWeight = 0 def _calcMaxWeight(itemIndex: Int, currentWeight: Int): Unit = { if (currentWeight == maxBagWeight || itemIndex == items.length) { if (currentWeight > maxWeight) { maxWeight = currentWeight } } else { //check next item _calcMaxWeight(itemIndex + 1, currentWeight) if (currentWeight + items(itemIndex) <= maxBagWeight) { _calcMaxWeight(itemIndex + 1, currentWeight + items(itemIndex)) } } } _calcMaxWeight(0, 0) maxWeight } } ================================================ FILE: scala/src/main/scala/ch39_back_tracking/EightQueens.scala ================================================ package ch39_back_tracking import scala.util.control.Breaks._ class EightQueens { //use array index to identify the row,the value of the row to identify the column val result = new Array[Int](8) var count = 0 def calc8Queues(row: Int): Unit = { if (row == 8) { //everything is done print8Queens() return } for (column <- Range(0, 8)) { if (isOkOnColumn(row, column)) { result(row) = column //place the column value into the array calc8Queues(row + 1) //calculate next row } } } def isOkOnColumn(row: Int, column: Int): Boolean = { var ok = true var leftUp = column - 1 var rightUp = column + 1 breakable { //will compare all the rows above current row for (i <- row - 1 to 0 by -1) { //check current column if (result(i) == column) { ok = false break } //check left up if (leftUp >= 0) { if (result(i) == leftUp) { ok = false break } } //check right up if (rightUp < 8) { if (result(i) == rightUp) { ok = false break } } //move leftUp and rightUp leftUp -= 1 rightUp += 1 } } ok } def print8Queens(): Unit = { count +=1 for (row <- Range(0, 8)) { for (column <- Range(0, 8)) { if (result(row) == column) { print("Q ") } else { print("* ") } } //new line for next row println("") } println(count+"==============") } } ================================================ FILE: scala/src/main/scala/ch39_back_tracking/NQueens.scala ================================================ package ch39_back_tracking import scala.util.control.Breaks.{break, breakable} class NQueens(numberOfQueens:Int) { //use array index to identify the row,the value of the row to identify the column val result = new Array[Int](numberOfQueens) var count = 0 def calcNQueues(row: Int): Unit = { if (row == numberOfQueens) { //everything is done printNQueens() return } for (column <- Range(0, numberOfQueens)) { if (isOkOnColumn(row, column)) { result(row) = column //place the column value into the array calcNQueues(row + 1) //calculate next row } } } def isOkOnColumn(row: Int, column: Int): Boolean = { var ok = true var leftUp = column - 1 var rightUp = column + 1 breakable { //will compare all the rows above current row for (i <- row - 1 to 0 by -1) { //check current column if (result(i) == column) { ok = false break } //check left up if (leftUp >= 0) { if (result(i) == leftUp) { ok = false break } } //check right up if (rightUp < numberOfQueens) { if (result(i) == rightUp) { ok = false break } } //move leftUp and rightUp leftUp -= 1 rightUp += 1 } } ok } def printNQueens(): Unit = { count +=1 for (row <- Range(0, numberOfQueens)) { for (column <- Range(0, numberOfQueens)) { if (result(row) == column) { print("Q ") } else { print("* ") } } //new line for next row println("") } println(count+"==============") } } ================================================ FILE: scala/src/main/scala/ch39_back_tracking/Sudoku.scala ================================================ package ch39_back_tracking import scala.util.control.Breaks._ class Sudoku { def resolve(grid: Array[Array[Int]]): Unit = { printGrid(grid) println("") if (resolve(grid, 0, 0)) { printGrid(grid) } else { println("no result") printGrid(grid) } } private[this] def resolve(grid: Array[Array[Int]], row: Int, column: Int): Boolean = { if (row == 8 && column == 9) { //find the result return true } if (column == 9) { //move to next line return resolve(grid, row + 1, 0) } if (grid(row)(column) != 0) { //given number, resolve next one return resolve(grid, row, column + 1) } //start the real resolve for (num <- 1 to 9) { if (isOk(grid, row, column, num)) { grid(row)(column) = num if (resolve(grid, row, column + 1)) { return true } } } //do not find anything, reset given row and column grid(row)(column) = 0 false } def isOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = { isRowOk(grid, row, num) && isColumnOk(grid, column, num) && isSmallBoxOk(grid, row, column, num) } def isRowOk(grid: Array[Array[Int]], row: Int, num: Int): Boolean = { var isOk = true breakable { for (column <- Range(0, 9)) { if (grid(row)(column) == num) { isOk = false break } } } isOk } def isColumnOk(grid: Array[Array[Int]], column: Int, num: Int): Boolean = { var isOk = true breakable { for (row <- Range(0, 9)) { if (grid(row)(column) == num) { isOk = false break } } } isOk } def isSmallBoxOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = { val rowOffSet = (row / 3) * 3 val columnOffSet = (column / 3) * 3 var isOk = true breakable { for (i <- Range(0, 3)) { for (j <- Range(0, 3)) { if (grid(i + rowOffSet)(j + columnOffSet) == num) { isOk = false break } } } } isOk } def printGrid(grid: Array[Array[Int]]): Unit = { for (i <- Range(0, 9)) { if (i % 3 == 0) { println("-------------------------") } for (j <- Range(0, 9)) { if (j % 3 == 0) { print("| ") } print(grid(i)(j) + " ") } println("| ") } println("-------------------------") } } ================================================ FILE: scala/src/main/scala/ch43_topology_sort/GraphTopology.scala ================================================ package ch43_topology_sort import scala.collection.mutable import scala.collection.mutable.ArrayBuffer class GraphTopology(vertex: Int) { //define the graph val adjacency = new Array[mutable.MutableList[Int]](vertex) for (i <- Range(0, vertex)) { adjacency(i) = new mutable.MutableList[Int]() } def addEdge(startIndex: Int, targetIndex: Int) = { adjacency(startIndex) += targetIndex } def topologySortByKahn(): Array[Int] = { val seq = new mutable.ArrayBuffer[Int]() //inDegrees contains all the inDegree for a given node val inDegrees = new Array[Int](vertex) for (i <- Range(0, vertex)) { for (j <- adjacency(i).indices) { val index = adjacency(i).get(j).get inDegrees(index) += 1 } } val queue = new mutable.Queue[Int]() for (i <- inDegrees.indices) { if (inDegrees(i) == 0) { // means there is no inDegree for this node, // this could be the starting point of the dependency graph queue += i } } //start to navigating the graph from the starting point while (queue.nonEmpty) { val index = queue.dequeue() //push to the result seq += index for (i <- adjacency(index).indices) { val inDegreeIndex = adjacency(index).get(i).get inDegrees(inDegreeIndex) -= 1 if (inDegrees(inDegreeIndex) == 0) { queue += inDegreeIndex } } } seq.toArray } def topologySortByDFS(): Array[Int] = { val inverseAdj = new Array[mutable.MutableList[Int]](vertex) for (i <- Range(0, vertex)) { inverseAdj(i) = new mutable.MutableList[Int]() } //build the inverse adj for (i <- Range(0, vertex)) { for (j <- adjacency(i).indices) { val index = adjacency(i).get(j).get inverseAdj(index) += i } } val visited = new Array[Boolean](vertex) val seq = new ArrayBuffer[Int]() for (i <- Range(0, vertex)) { if (!visited(i)) { visited(i) = true //call dfs seq ++= dfs(i, inverseAdj, visited) } } seq.toArray } def dfs(index: Int, inverseAdj: Array[mutable.MutableList[Int]], visited: Array[Boolean]): ArrayBuffer[Int] = { val seq = new ArrayBuffer[Int]() for (i <- inverseAdj(index).indices) { val sourceIndex = inverseAdj(index).get(i).get if (!visited(sourceIndex)) { visited(sourceIndex) = true seq ++= dfs(sourceIndex, inverseAdj, visited) } } seq += index seq } } ================================================ FILE: scala/src/test/scala/ch05_array/ArrayDemoSpec.scala ================================================ package ch05_array import org.scalatest.{FlatSpec, Matchers} class ArrayDemoSpec extends FlatSpec with Matchers { behavior of "ArrayDemoTest" it should "find value after insert" in { val demo = new ArrayDemo(10) assert(demo.insert(0, 'a')) assert(demo.insert(1, 'b')) assert(demo.insert(2, 'c')) assert(demo.insert(3, 'd')) assert(demo.insert(4, 'e')) demo.print should equal("abcde") assert(demo.insert(2, 'x')) demo.print should equal("abxcde") } it should "not insert value if capacity is full " in { val demo = new ArrayDemo(10) for (i <- Range(0, 10)) { demo.insert(i, (i + 97).toChar) } assert(!demo.insert(1, 'a')) } it should "not insert if index is negative" in { val demo = new ArrayDemo(10) assert(!demo.insert(-1, 'a')) } it should "not insert if index is exceed capacity" in { val demo = new ArrayDemo(10) assert(!demo.insert(10, 'a')) assert(!demo.insert(11, 'a')) } it should "not find after delete" in { val demo = new ArrayDemo(10) assert(demo.insert(0, 'a')) assert(demo.insert(1, 'b')) assert(demo.insert(2, 'c')) assert(demo.insert(3, 'd')) assert(demo.insert(4, 'e')) demo.print should equal("abcde") assert(demo.insert(2, 'x')) demo.print should equal("abxcde") demo.delete(2) should equal('x') demo.find(2) should not equal ('x') demo.print should equal("abcde") } it should "not delete for empty array" in { val demo = new ArrayDemo(10) assertThrows[IllegalStateException] { demo.delete(2) should equal('x') } } } ================================================ FILE: scala/src/test/scala/ch06_linkedlist/NodeTest.scala ================================================ package ch06_linkedlist import org.scalatest.{FlatSpec, Matchers} class NodeTest extends FlatSpec with Matchers { behavior of "NodeTest" it should "create node with constructor" in { val node1 = new Node(1, None) val node2 = new Node(2, Some(node1)) val node3 = new Node(2, Some(node2)) node1.next should be(None) node1.next = Some(node3) node1.next should equal(Some(node3)) } } ================================================ FILE: scala/src/test/scala/ch06_linkedlist/SinglyLinkedListTest.scala ================================================ package ch06_linkedlist import org.scalatest.{FlatSpec, Matchers} class SinglyLinkedListTest extends FlatSpec with Matchers { behavior of "SinglyLinkedListTest" it should "insertToHead for new values" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertToHead(0) list.insertToHead(1) list.insertToHead(2) list.insertToHead(3) list.mkString() should equal("3210") } it should "insertTail for new values" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(2) list.insertTail(3) list.mkString() should equal("0123") } it should "find by value" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(2) list.insertTail(3) list.mkString() should equal("0123") val node1 = list.findByValue(1).get node1.next.get.data should equal(2) } it should "insert after node and can find it back" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(3) list.insertTail(4) val node1 = list.findByValue(1).get list.insertAfter(node1, 2) list.mkString() should equal("01234") } it should "insert before node and can find it back" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(3) list.insertTail(4) val node3 = list.findByValue(3).get list.insertBefore(node3, 2) list.mkString() should equal("01234") } it should "delete desired node" in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(2) list.insertTail(3) val node1 = list.findByValue(1).get list.deleteByNode(node1) list.mkString() should equal("023") } it should "delete head node " in { val list: SinglyLinkedList = new SinglyLinkedList() list.insertTail(0) list.insertTail(1) list.insertTail(2) list.insertTail(3) val node0 = list.findByValue(0).get list.deleteByNode(node0) list.mkString() should equal("123") } it should "inverseLink to head node" in { val list: SinglyLinkedList = new SinglyLinkedList() for (i <- 0 to 10) { list.insertTail(i) } val node8 = list.findByValue(8) val inverseNode = list.inverseLink(node8.get) var node = inverseNode val result = new StringBuilder while (node.next.nonEmpty) { result.append(node.data) node = node.next.get } result.append(node.data) result.mkString should equal("876543210") } it should "be isPalindrome for 0123210" in { val list: SinglyLinkedList = new SinglyLinkedList() for (i <- 0 to 3) { list.insertTail(i) } for (i <- 2 to 0 by -1) { list.insertTail(i) } list.mkString() should equal("0123210") assert(list.isPalindrome()) } it should "be isPalindrome for 01233210" in { val list: SinglyLinkedList = new SinglyLinkedList() for (i <- 0 to 3) { list.insertTail(i) } for (i <- 3 to 0 by -1) { list.insertTail(i) } list.mkString() should equal("01233210") assert(list.isPalindrome()) } it should "not be isPalindrome for 012332100" in { val list: SinglyLinkedList = new SinglyLinkedList() for (i <- 0 to 3) { list.insertTail(i) } for (i <- 3 to 0 by -1) { list.insertTail(i) } list.insertTail(0) list.mkString() should equal("012332100") assert(!list.isPalindrome()) } it should "be isPalindrome for large numbers" in { val list: SinglyLinkedList = new SinglyLinkedList() val num = 5000 for (i <- 0 to num) { list.insertTail(i) } for (i <- num to 0 by -1) { list.insertTail(i) } assert(list.isPalindrome()) } } ================================================ FILE: scala/src/test/scala/ch07_linkedlist/LinkedListAlgoTest.scala ================================================ package ch07_linkedlist import ch06_linkedlist.{Node, SinglyLinkedList} import org.scalatest.{FlatSpec, Matchers} class LinkedListAlgoTest extends FlatSpec with Matchers { behavior of "LinkedListAlgoTest" it should "reverse a node for long linked list" in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) val reverseNode = LinkedListAlgo.reverse(list.headOpt.get) LinkedListAlgo.mkStringForChain(reverseNode) should equal((9 to 0 by -1).toArray.mkString("")) } it should "reverse a node for linked list with 1 node" in { val list = new SinglyLinkedList() list.insertTail(100) LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal("100") val reverseNode = LinkedListAlgo.reverse(list.headOpt.get) LinkedListAlgo.mkStringForChain(reverseNode) should equal("100") } it should "check circle for circled linked list" in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->3 it's a circle //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) node6.next = Some(node3) assert(LinkedListAlgo.checkCircle(node1).isDefined) } it should "check circle for none circled linked list" in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->null it's not a circle //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) assert(LinkedListAlgo.checkCircle(node1).isEmpty) } it should "calculate circle length" in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->3 it's a circle //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) node6.next = Some(node3) val node = LinkedListAlgo.checkCircle(node1).get val length = LinkedListAlgo.calculateCircleLength(node) length should equal(4) } it should "find entrance of the circle" in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->3 it's a circle //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) node6.next = Some(node3) val node = LinkedListAlgo.findCircleEntrance(node1) node.get.data should equal(3) } it should "merge 2 sorted list into 1" in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->null //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) val nodeA = new Node(2, None) val nodeB = new Node(4, None) val nodeC = new Node(6, None) val nodeD = new Node(8, None) val nodeE = new Node(10, None) //2->4->6->8->10->null //nodeA is the head nodeA.next = Some(nodeB) nodeB.next = Some(nodeC) nodeC.next = Some(nodeD) nodeD.next = Some(nodeE) val node = LinkedListAlgo.mergeSortedList(Some(node1), Some(nodeA)) assert(node.isDefined) LinkedListAlgo.mkStringForChain(node.get) should equal("122344566810") } it should "merge empty list into exist list " in { val node1 = new Node(1, None) val node2 = new Node(2, None) val node3 = new Node(3, None) val node4 = new Node(4, None) val node5 = new Node(5, None) val node6 = new Node(6, None) //1->2->3->4->5->6->null //node1 is the head node1.next = Some(node2) node2.next = Some(node3) node3.next = Some(node4) node4.next = Some(node5) node5.next = Some(node6) val nodeA = LinkedListAlgo.mergeSortedList(Some(node1), None) assert(nodeA.isDefined) LinkedListAlgo.mkStringForChain(nodeA.get) should equal("123456") val nodeB = LinkedListAlgo.mergeSortedList(None, Some(node1)) assert(nodeB.isDefined) LinkedListAlgo.mkStringForChain(nodeB.get) should equal("123456") } it should "merge 2 empty lists " in { val node = LinkedListAlgo.mergeSortedList(None, None) assert(node.isEmpty) } it should "delete last 2nd element " in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 2) assert(node.isDefined) LinkedListAlgo.mkStringForChain(node.get) should equal("012345679") } it should "delete last element " in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1) assert(node.isDefined) LinkedListAlgo.mkStringForChain(node.get) should equal("012345678") } it should "delete first element " in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 10) assert(node.isDefined) LinkedListAlgo.mkStringForChain(node.get) should equal("123456789") } it should "delete firs only element " in { val list = new SinglyLinkedList() list.insertTail(0) val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1) assert(node.isEmpty) } it should "throw exception if k < 0 " in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } assertThrows[IllegalArgumentException] { LinkedListAlgo.deleteLastKthNode(list.headOpt, -1) } } it should "throw exception if k greater than list length " in { val list = new SinglyLinkedList() for (i <- 0 to 9) { list.insertTail(i) } assertThrows[IllegalArgumentException] { LinkedListAlgo.deleteLastKthNode(list.headOpt, 15) } } } ================================================ FILE: scala/src/test/scala/ch08_stack/BrowserDemoTest.scala ================================================ package ch08_stack import org.scalatest.{FlatSpec, Matchers} class BrowserDemoTest extends FlatSpec with Matchers { behavior of "BrowserDemoTest" it should "canGoBack" in { val browser = new BrowserDemo() assert(!browser.canGoBack()) browser.open("a") assert(!browser.canGoBack()) browser.open("b") assert(browser.canGoBack()) browser.currentPageOpt.get should equal("b") } it should "goBack" in { val browser = new BrowserDemo() browser.open("a") browser.open("b") browser.currentPageOpt.get should equal("b") assert(browser.canGoBack()) browser.goBack() assert(!browser.canGoBack()) browser.currentPageOpt.get should equal("a") } it should "canGoForward" in { val browser = new BrowserDemo() browser.open("a") browser.open("b") browser.currentPageOpt.get should equal("b") assert(browser.canGoBack()) browser.goBack() assert(!browser.canGoBack()) browser.currentPageOpt.get should equal("a") assert(browser.canGoForward()) } it should "goForward" in { val browser = new BrowserDemo() browser.open("a") browser.open("b") browser.currentPageOpt.get should equal("b") assert(browser.canGoBack()) browser.goBack() assert(!browser.canGoBack()) browser.currentPageOpt.get should equal("a") assert(browser.canGoForward()) browser.goForward() browser.currentPageOpt.get should equal("b") } it should "open new page to clear forward stack" in { val browser = new BrowserDemo() browser.open("a") browser.open("b") browser.currentPageOpt.get should equal("b") assert(browser.canGoBack()) browser.goBack() assert(!browser.canGoBack()) browser.currentPageOpt.get should equal("a") assert(browser.canGoForward()) browser.open("c") assert(!browser.canGoForward()) browser.goBack() browser.currentPageOpt.get should equal("a") } } ================================================ FILE: scala/src/test/scala/ch08_stack/StackDemoTest.scala ================================================ package ch08_stack import org.scalatest.{FlatSpec, Matchers} class StackDemoTest extends FlatSpec with Matchers { behavior of "StackDemoTest" it should "push/pop should be FILO" in { val stack = new StackDemo[String] val num = 100 for (i <- 1 to num) { stack.push(i.toString) } for (i <- num to 1 by -1) { stack.pop().get.data should equal(i.toString) } } it should "pop should also work for empty stack" in { val stack = new StackDemo[Int] val num = 100 for (i <- num to 1 by -1) { assert(stack.pop().isEmpty) } } } ================================================ FILE: scala/src/test/scala/ch09_queue/ArrayQueueTest.scala ================================================ package ch09_queue class ArrayQueueTest extends DemoQueueTest { override def getInstance() = new ArrayQueue[Int](15) } ================================================ FILE: scala/src/test/scala/ch09_queue/CircularQueueTest.scala ================================================ package ch09_queue class CircularQueueTest extends DemoQueueTest { override def getInstance(): DemoQueue[Int] = new CircularQueue[Int](15) } ================================================ FILE: scala/src/test/scala/ch09_queue/DemoQueueTest.scala ================================================ package ch09_queue import org.scalatest.{FlatSpec, Matchers} abstract class DemoQueueTest extends FlatSpec with Matchers { def getInstance(): DemoQueue[Int] behavior of "test" it should "dequeue nothing for empty queue" in { val queue = getInstance() assert(queue.dequeue().isEmpty) } it should "enqueue/dequeue should be FIFO" in { val queue = getInstance() for (i <- Range(0, 10)) { queue.enqueue(i) } queue.size should equal(10) for (i <- Range(0, 10)) { queue.dequeue().get should equal(i) } } } ================================================ FILE: scala/src/test/scala/ch09_queue/DynamicArrayQueueTest.scala ================================================ package ch09_queue class DynamicArrayQueueTest extends DemoQueueTest { override def getInstance(): DemoQueue[Int] = new DynamicArrayQueue[Int](15) it should "copy data when tail reach the end of the queue" in { val queue = getInstance() for (i <- Range(0, 15)) { queue.enqueue(i) } queue.size should equal(15) queue.dequeue().get should equal(0) //enqueue another one queue.enqueue(30) queue.size should equal(15) } } ================================================ FILE: scala/src/test/scala/ch09_queue/LinkedListQueueTest.scala ================================================ package ch09_queue class LinkedListQueueTest extends DemoQueueTest { override def getInstance() = new LinkListQueue[Int] } ================================================ FILE: scala/src/test/scala/ch10_recursive/RecursiveDemoTest.scala ================================================ package ch10_recursive import org.scalatest.{FlatSpec, Matchers} class RecursiveDemoTest extends FlatSpec with Matchers { behavior of "RecursiveDemoTest" it should "calculateStepWays" in { RecursiveDemo.calculateStepWays(1) should equal(1) RecursiveDemo.calculateStepWays(2) should equal(2) RecursiveDemo.calculateStepWays(3) should equal(3) RecursiveDemo.calculateStepWays(4) should equal(5) RecursiveDemo.calculateStepWays(5) should equal(8) } } ================================================ FILE: scala/src/test/scala/ch11_sorts/SortsTest.scala ================================================ package ch11_sorts import ch12_sorts.{MergeSort, QuickSort} import ch28_heap.Heap import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class SortsTest extends FlatSpec with Matchers { behavior of "SortsTest in ch11" it should "bubbleSort int arrays" in { var array = Array(4, 5, 6, 3, 2, 1) array = Sorts.bubbleSort(array) array.mkString("") should equal("123456") array = Array(4) array = Sorts.bubbleSort(array) array.mkString("") should equal("4") } it should "insertSort int arrays" in { var array = Array(4, 5, 6, 1, 3, 2) array = Sorts.insertSort(array) array.mkString("") should equal("123456") array = Array(4) array = Sorts.insertSort(array) array.mkString("") should equal("4") } it should "selectionSort int arrays" in { var array = Array(4, 5, 6, 1, 3, 2) array = Sorts.insertSort(array) array.mkString("") should equal("123456") array = Array(4) array = Sorts.insertSort(array) array.mkString("") should equal("4") } it should "compare the sort algo" in { val length = 50000 val array = new Array[Int](length) val rnd = new Random() for (i <- Range(0, length)) { array(i) = rnd.nextInt() } timed("bubbleSort", Sorts.bubbleSort, array.clone()) timed("insertSort", Sorts.insertSort, array.clone()) timed("selectionSort", Sorts.selectionSort, array.clone()) timed("mergeSort", MergeSort.mergeSort, array.clone()) timed("quickSort", QuickSort.quickSort, array.clone()) timed("heapSort", Heap.heapSort, array.clone()) } def reportElapsed(name: String, time: Long): Unit = println(name + " takes in " + time + "ms") def timed(name: String, f: (Array[Int]) => Unit, array: Array[Int]): Unit = { val start = System.currentTimeMillis() try f(array) finally reportElapsed(name, System.currentTimeMillis - start) } } ================================================ FILE: scala/src/test/scala/ch12_sorts/MergeSortTest.scala ================================================ package ch12_sorts import org.scalatest.{FlatSpec, Matchers} class MergeSortTest extends FlatSpec with Matchers { behavior of "SortsTest in ch12" it should "mergeSort int arrays" in { var array = Array(4, 5, 6, 3, 2, 1) array = MergeSort.mergeSort(array) array.mkString("") should equal("123456") array = Array(4) array = MergeSort.mergeSort(array) array.mkString("") should equal("4") array = Array(4, 2) array = MergeSort.mergeSort(array) array.mkString("") should equal("24") } } ================================================ FILE: scala/src/test/scala/ch12_sorts/QuickSortTest.scala ================================================ package ch12_sorts import org.scalatest.{FlatSpec, Matchers} class QuickSortTest extends FlatSpec with Matchers { behavior of "QuickSortTest" it should "quickSort" in { var array = Array(4, 5, 6, 3, 2, 1) array = QuickSort.quickSort(array) array.mkString("") should equal("123456") array = Array(4) array = QuickSort.quickSort(array) array.mkString("") should equal("4") array = Array(4, 2) array = QuickSort.quickSort(array) array.mkString("") should equal("24") } it should "find the Kth element in the array" in { val array = Array(4, 2, 5, 12, 3) QuickSort.findKthElement(array, 3) should equal(4) QuickSort.findKthElement(array, 5) should equal(12) QuickSort.findKthElement(array, 1) should equal(2) } } ================================================ FILE: scala/src/test/scala/ch15_bsearch/BSearchRecursiveTest.scala ================================================ package ch15_bsearch import ch12_sorts.QuickSort import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class BSearchRecursiveTest extends FlatSpec with Matchers { behavior of "BSearchRecursiveTest" it should "search with exist value" in { val length = 50000 val array = new Array[Int](length) val rnd = new Random() for (i <- Range(0, length)) { array(i) = rnd.nextInt() } val target = array(2698) BSearchRecursive.search(QuickSort.quickSort(array), target) should be > -1 } } ================================================ FILE: scala/src/test/scala/ch15_bsearch/BSearchTest.scala ================================================ package ch15_bsearch import ch12_sorts.QuickSort import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class BSearchTest extends FlatSpec with Matchers { behavior of "BSearchTest" it should "search with exist value" in { val length = 50000 val array = new Array[Int](length) val rnd = new Random() for (i <- Range(0, length)) { array(i) = rnd.nextInt() } val target = array(2698) BSearch.search(QuickSort.quickSort(array), target) should be > -1 } it should "calculate sqrt value -1 " in { val x = 4 val precision = 0.000001 BSearch.sqrt(x, precision) should equal(2.0) } it should "calculate sqrt value -2 " in { val x = 0.04 val precision = 0.000001 BSearch.sqrt(x, precision) should equal(0.2 +- precision) } } ================================================ FILE: scala/src/test/scala/ch16_bsearch/BSearchTest.scala ================================================ package ch16_bsearch import org.scalatest.{FlatSpec, Matchers} class BSearchTest extends FlatSpec with Matchers { behavior of "BSearchTest" it should "findFirstValue" in { val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) BSearch.findFirstValue(items, 8) should equal(5) } it should "findLastValue" in { val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) BSearch.findLastValue(items, 8) should equal(7) } it should "findFirstGreaterThan" in { val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) BSearch.findFirstGreaterThan(items, 2) should equal(1) BSearch.findFirstGreaterThan(items, 8) should equal(5) } it should "findLastSmallerThan" in { val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) BSearch.findLastSmallerThan(items, 2) should equal(0) BSearch.findLastSmallerThan(items, 8) should equal(7) } } ================================================ FILE: scala/src/test/scala/ch17_skip_list/SkipListTest.scala ================================================ package ch17_skip_list import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class SkipListTest extends FlatSpec with Matchers { behavior of "SkipListTest" it should "insert skip list" in { val list = new SkipList() for (i <- Range(0, 10)) { list.insert(i) } list.mkString() should equal("0123456789") } it should "delete skip list" in { val list = new SkipList() for (i <- Range(0, 10)) { list.insert(i) } list.delete(5) list.mkString() should equal("012346789") } it should "find value in skip list" in { val list = new SkipList() val length = 5000 val array = new Array[Int](length) val rnd = new Random() for (i <- Range(0, length)) { array(i) = rnd.nextInt(length) list.insert(array(i)) } assert(list.find(array(rnd.nextInt(length - 1))).isDefined) assert(list.find(array(rnd.nextInt(length - 1)) + length + 1).isEmpty) } } ================================================ FILE: scala/src/test/scala/ch20_linked_hash_map/LRUCacheTest.scala ================================================ package ch20_linked_hash_map import org.scalatest.{FlatSpec, Matchers} class LRUCacheTest extends FlatSpec with Matchers { behavior of "LRUCacheTest" it should "put data and get back" in { val cache = new LRUCache[Int, Int](2) cache.put(1, 1) cache.put(2, 2) cache.get(1) should equal(Some(1)) // returns 1 cache.put(3, 3) // evicts key 2 cache.get(2) should equal(None) //should not find cache.put(4, 4) // evicts key 1 cache.get(1) should equal(None) //should not find cache.get(3) should equal(Some(3)) // returns 3 cache.get(4) should equal(Some(4)) // returns 4 } } ================================================ FILE: scala/src/test/scala/ch23_binary_tree/BinaryTreeTest.scala ================================================ package ch23_binary_tree import org.scalatest.{BeforeAndAfterEach, FlatSpec, Matchers} class BinaryTreeTest extends FlatSpec with BeforeAndAfterEach with Matchers { /* * A * / \ * B C * / \ / \ * D E F G * */ var root: Option[Node[String]] = None val tree = new BinaryTree[String] override def beforeEach() { val D = new Node[String]("D", None, None) val E = new Node[String]("E", None, None) val B = new Node[String]("B", Some(D), Some(E)) val F = new Node[String]("F", None, None) val G = new Node[String]("G", None, None) val C = new Node[String]("C", Some(F), Some(G)) val A = new Node[String]("A", Some(B), Some(C)) root = Some(A) } override protected def afterEach(): Unit = { root = None } behavior of "BinaryTreeTest" it should "postOrder" in { tree.postOrder(root) should equal("DEBFGCA") } it should "inOrder" in { tree.inOrder(root) should equal("DBEAFCG") } it should "preOrder" in { tree.preOrder(root) should equal("ABDECFG") } it should "levelOrder" in { tree.levelOrder(root) should equal("ABCDEFG") } } ================================================ FILE: scala/src/test/scala/ch24_binary_search_tree/BinarySearchTreeTest.scala ================================================ package ch24_binary_search_tree import org.scalatest.{FlatSpec, Matchers} class BinarySearchTreeTest extends FlatSpec with Matchers { /* * 33 * / \ * 17 50 * / \ / \ * 13 18 34 58 * \ \ / \ * 16 25 51 66 * / \ \ * 19 27 55 * */ behavior of "BinarySearchTreeTest" it should "insert" in { val tree = new BinarySearchTree(None) val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) nums.foreach(tree.insert) tree.inOrder(tree.root) should equal(nums.sorted.mkString("")) tree.preOrder(tree.root) should equal("3317131618251927503458515566") tree.postOrder(tree.root) should equal("1613192725181734555166585033") tree.levelOrder(tree.root) should equal("3317501318345816255166192755") } it should "find " in { val tree = new BinarySearchTree(None) val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) nums.foreach(tree.insert) nums.foreach(num => { assert(tree.find(num).isDefined) tree.find(num).get.data should equal(num) }) assert(tree.find(100).isEmpty) } it should "delete" in { val tree = new BinarySearchTree(None) val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) nums.foreach(tree.insert) tree.delete(13) tree.inOrder(tree.root) should equal(nums.sorted.tail.mkString("")) tree.delete(18) tree.inOrder(tree.root) should equal("1617" + nums.sorted.slice(4, nums.size).mkString("")) tree.delete(66) tree.inOrder(tree.root) should equal("1617" + nums.sorted.slice(4, nums.size - 1).mkString("")) } it should "calc height of a tree -1" in { val tree = new BinarySearchTree(None) val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) nums.foreach(tree.insert) tree.height() should equal(5) } it should "calc height of a tree -2" in { val tree = new BinarySearchTree(None) val nums = Array(33, 17, 50, 13, 18, 34, 88).sorted nums.foreach(tree.insert) tree.height() should equal(7) } it should "calc height of a tree -3" in { val tree = new BinarySearchTree(None) val nums = Array(33).sorted nums.foreach(tree.insert) tree.height() should equal(1) } } ================================================ FILE: scala/src/test/scala/ch28_heap/HeapTest.scala ================================================ package ch28_heap import org.scalatest.{FlatSpec, Matchers} class HeapTest extends FlatSpec with Matchers { behavior of "HeapTest" it should "insert and removeMax" in { val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) val heap = new Heap(nums.length + 1) nums.foreach(heap.insert) heap.removeMax() should equal(33) heap.removeMax() should equal(27) } it should "build heap from array - bottom up" in { val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) val heap = new Heap(nums, true) heap.removeMax() should equal(33) heap.removeMax() should equal(27) } it should "build heap from array - top down" in { val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) val heap = new Heap(nums, false) heap.removeMax() should equal(33) heap.removeMax() should equal(27) } it should "sort array" in { val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) val result = Heap.heapSort(nums) result.mkString("") should equal(nums.sorted.mkString("")) } } ================================================ FILE: scala/src/test/scala/ch29_heap_solutions/FileMergerTest.scala ================================================ package ch29_heap_solutions import java.io.{BufferedWriter, File, FileWriter} import org.scalatest.{FlatSpec, Matchers} import scala.io.Source import scala.util.Random class FileMergerTest extends FlatSpec with Matchers { behavior of "FileMergerTest" it should "mergeFiles" in { val num = 10 val contentCount = 10 val random = Random.alphanumeric val files = new Array[File](num) for (i <- Range(0, num)) { val file = File.createTempFile(i + "-small", ".txt") files(i) = file val writer = new BufferedWriter(new FileWriter(file)) val content = random.take((i + 1) * contentCount).toArray.slice(i * contentCount, (i + 1) * contentCount) writer.write(content.sorted) writer.flush() writer.close() } println("small files below") files.foreach(printFile) val mergedFile = FileMerger.mergeFiles(files.toList) val raw = Source.fromFile(mergedFile).toArray raw should equal(raw.sorted) raw.length should equal(num * contentCount) println("") println("merged file below") printFile(mergedFile) //clean up files.foreach(_.delete()) mergedFile.delete() } def printFile(file: File): Unit = { val source = Source.fromFile(file) source.getLines().foreach(println) } } ================================================ FILE: scala/src/test/scala/ch29_heap_solutions/MiddleNumberKeeperTest.scala ================================================ package ch29_heap_solutions import org.scalatest.{FlatSpec, Matchers} class MiddleNumberKeeperTest extends FlatSpec with Matchers { behavior of "MiddleNumberKeeperTest" it should "get middle of the array" in { val numKeeper = new MiddleNumberKeeper() for (i <- Range(0, 10)) { numKeeper.put(i) } numKeeper.get().get should equal(4) } it should "get 90% position of the array" in { val numKeeper = new MiddleNumberKeeper(0.9) for (i <- Range(0, 9)) { numKeeper.put(i) } numKeeper.put(9) numKeeper.get().get should equal(8) } } ================================================ FILE: scala/src/test/scala/ch29_heap_solutions/TopKItemsKeeperTest.scala ================================================ package ch29_heap_solutions import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class TopKItemsKeeperTest extends FlatSpec with Matchers { behavior of "TopKItemsKeeperTest" it should "put and get top K from the keeper" in { val length = 50 val k = 5 val topKItemsKeeper = new TopKItemsKeeper(k) val nums = new Array[Int](length) for (i <- Range(0, length)) { nums(i) = Random.nextInt } nums.foreach(topKItemsKeeper.put) val ordering = scala.math.Ordering.Int.reverse topKItemsKeeper.get().toArray.sorted(ordering) should equal(nums.sorted(ordering).slice(0, k)) } } ================================================ FILE: scala/src/test/scala/ch31_graph/GraphTest.scala ================================================ package ch31_graph import org.scalatest.{FlatSpec, Matchers} class GraphTest extends FlatSpec with Matchers { /* 0 - 1 - 2 | | | 3 - 4 - 5 | | 6 - 7 */ behavior of "GraphTest" def initGraph: Graph = { val num = 8 val vertex = new Array[String](num) for (i <- Range(0, num)) { vertex(i) = i.toString } val graph = new Graph(vertex) graph.addEdge("0", "1") graph.addEdge("1", "2") graph.addEdge("0", "3") graph.addEdge("1", "4") graph.addEdge("2", "5") graph.addEdge("3", "4") graph.addEdge("4", "5") graph.addEdge("4", "6") graph.addEdge("5", "7") graph.addEdge("6", "7") graph } it should "construct the graph" in { val graph: Graph = initGraph graph.getEdges("0").sorted should equal(Array("1", "3")) graph.getEdges("1").sorted should equal(Array("0", "2", "4")) graph.getEdges("2").sorted should equal(Array("1", "5")) graph.getEdges("3").sorted should equal(Array("0", "4")) graph.getEdges("4").sorted should equal(Array("1", "3", "5", "6")) graph.getEdges("5").sorted should equal(Array("2", "4", "7")) graph.getEdges("6").sorted should equal(Array("4", "7")) graph.getEdges("7").sorted should equal(Array("5", "6")) } it should "do breath first search in graph" in { val graph: Graph = initGraph graph.breathFirstSearch("0", "4").get should equal(Array("0", "1", "3", "2", "4")) graph.breathFirstSearch("1", "5").get should equal(Array("1", "0", "2", "4", "3", "5")) assert(graph.breathFirstSearch("1", "8").isEmpty) } it should "do depth first search in graph" in { val graph: Graph = initGraph graph.depthFirstSearch("0", "4").get should equal(Array("0", "1", "2", "5", "4")) graph.depthFirstSearch("1", "5").get should equal(Array("1", "0", "3", "4", "5")) assert(graph.depthFirstSearch("1", "8").isEmpty) } } ================================================ FILE: scala/src/test/scala/ch32_matching/BruteForceTest.scala ================================================ package ch32_matching import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class BruteForceTest extends FlatSpec with Matchers { behavior of "BruteForceTest" it should "find firstIndexOf a sub string" in { val random = Random.alphanumeric val main = random.take(1000).toArray val index = Random.nextInt(950) val sub = random.take(1000).toArray.slice(index, index + 50) BruteForce.firstIndexOf(main, sub) should equal(index) } } ================================================ FILE: scala/src/test/scala/ch32_matching/RabinKarpTest.scala ================================================ package ch32_matching import org.scalatest.{FlatSpec, Matchers} import scala.util.Random class RabinKarpTest extends FlatSpec with Matchers { it should "find firstIndexOf a sub string" in { val random = Random.alphanumeric val main = random.take(1000).toArray val index = Random.nextInt(950) val sub = random.take(1000).toArray.slice(index, index + 50) RabinKarp.firstIndexOf(main, sub) should equal(index) } } ================================================ FILE: scala/src/test/scala/ch35_tire_tree/TrieTreeTest.scala ================================================ package ch35_tire_tree import org.scalatest.{FlatSpec, Matchers} class TrieTreeTest extends FlatSpec with Matchers { behavior of "TrieTreeTest" it should "insert data into the tree and find it back" in { val texts = Array("how", "hi", "her", "hello", "so", "see") val tree = new TrieTree() for (i <- texts.indices) { tree.insert(texts(i).toCharArray) } for (i <- texts.indices) { assert(tree.find(texts(i).toCharArray)) } } it should "find prefix" in { val texts = Array("how", "hi", "her", "hello", "so", "see") val tree = new TrieTree() for (i <- texts.indices) { tree.insert(texts(i).toCharArray) } for (i <- texts.indices) { assert(tree.prefix(texts(i).toCharArray.slice(0, 1))) } assert(!tree.prefix("howlo".toCharArray)) assert(!tree.prefix("aaa".toCharArray)) } it should "give suggestion" in { val texts = Array("how", "hi", "her", "hello", "so", "see") val tree = new TrieTree() for (i <- texts.indices) { tree.insert(texts(i).toCharArray) } tree.suggestion("he".toCharArray).get should contain("her") tree.suggestion("he".toCharArray).get should contain("hello") tree.suggestion("he".toCharArray).get.size should equal(2) } } ================================================ FILE: scala/src/test/scala/ch39_back_tracking/BagWeightTest.scala ================================================ package ch39_back_tracking import org.scalatest.FlatSpec class BagWeightTest extends FlatSpec { behavior of "BagWeightTest" it should "calculateMaxWeight" in { val bagWeight = new BagWeight(5,9) val maxWeight = bagWeight.calculateMaxWeight(Array(1,2,3,5,6)) println(maxWeight) } } ================================================ FILE: scala/src/test/scala/ch39_back_tracking/EightQueensTest.scala ================================================ package ch39_back_tracking import org.scalatest.FlatSpec class EightQueensTest extends FlatSpec { behavior of "EightQueensTest" it should "calc8Queues" in { val eightQueens = new EightQueens() eightQueens.calc8Queues(0) } } ================================================ FILE: scala/src/test/scala/ch39_back_tracking/NQueensTest.scala ================================================ package ch39_back_tracking import org.scalatest.FlatSpec class NQueensTest extends FlatSpec { behavior of "NQueensTest" it should "calc8Queues" in { val eightQueens = new NQueens(8) eightQueens.calcNQueues(0) } it should "calc4Queues" in { val eightQueens = new NQueens(4) eightQueens.calcNQueues(0) } } ================================================ FILE: scala/src/test/scala/ch39_back_tracking/SudokuTest.scala ================================================ package ch39_back_tracking import org.scalatest.FlatSpec class SudokuTest extends FlatSpec { behavior of "SudokuTest" it should "resolve - 1" in { val grid = Array( Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0) ) val sudoku = new Sudoku() sudoku.resolve(grid) } it should "resolve - 2" in { val grid = Array( Array(0, 0, 7, 2, 5, 6, 4, 0 ,0), Array(4, 0, 0, 0, 0, 0, 0, 0, 5), Array(0, 1, 0, 0, 3, 9, 0, 6, 0), Array(0, 0, 0, 5, 0, 8, 0, 0, 0), Array(0, 0, 8, 0, 6, 0, 2, 0, 0), Array(0, 9, 0, 1, 0, 7, 0, 0, 0), Array(0, 3, 0, 0, 7, 0, 0, 9, 0), Array(2, 0, 0, 0, 0, 0, 0, 0, 4), Array(0, 0, 6, 3, 1, 2, 7, 0, 8) ) val sudoku = new Sudoku() sudoku.resolve(grid) } it should "resolve - 3" in { val grid = Array( Array(3, 8, 0, 0, 0, 0, 0, 0, 1), Array(0, 0, 6, 4, 0, 0, 7, 8, 5), Array(0, 0, 9, 0, 2, 0, 3, 0, 0), Array(0, 6, 0, 0, 9, 0, 0, 0, 0), Array(8, 0, 0, 3, 0, 2, 0, 0, 9), Array(0, 0, 0, 0, 4, 0, 0, 7, 0), Array(0, 0, 1, 0, 7, 0, 5, 0, 0), Array(4, 9, 5, 0, 0, 6, 1, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 9, 2) ) val sudoku = new Sudoku() sudoku.resolve(grid) } it should "print grid" in { val grid = Array( Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0) ) val sudoku = new Sudoku() sudoku.printGrid(grid) } } ================================================ FILE: scala/src/test/scala/ch43_topology_sort/GraphTopologyTest.scala ================================================ package ch43_topology_sort import org.scalatest.{FlatSpec, Matchers} class GraphTopologyTest extends FlatSpec with Matchers { behavior of "GraphTopologyTest" /* a -> b | | \|/ \|/ c -> d -> e */ it should "topologySortByKahn - 1" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 1) graphTopology.addEdge(0, 2) graphTopology.addEdge(1, 3) graphTopology.addEdge(2, 3) graphTopology.addEdge(3, 4) val seq = graphTopology.topologySortByKahn() seq.map(nodes(_)).mkString(",") should equal("a,b,c,d,e") } /* a -> d --- | | \|/ \|/ e -> c */ it should "topologySortByKahn - 2" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 3) graphTopology.addEdge(3, 4) graphTopology.addEdge(3, 2) graphTopology.addEdge(4, 2) val seq = graphTopology.topologySortByKahn() seq.map(nodes(_)).mkString(",") should equal("a,b,d,e,c") } /* a -> d <- b | /|\ \|/ | e -> c */ it should "topologySortByKahn - 3" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 3) graphTopology.addEdge(3, 4) graphTopology.addEdge(4, 2) graphTopology.addEdge(2, 1) graphTopology.addEdge(1, 3) val seq = graphTopology.topologySortByKahn() seq.map(nodes(_)).mkString(",") should equal("a") } /* a -> b | | \|/ \|/ c -> d -> e */ it should "topologySortByDFS - 1" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 1) graphTopology.addEdge(0, 2) graphTopology.addEdge(1, 3) graphTopology.addEdge(2, 3) graphTopology.addEdge(3, 4) val seq = graphTopology.topologySortByDFS() seq.map(nodes(_)).mkString(",") should equal("a,b,c,d,e") } /* a -> d --- | | \|/ \|/ e -> c */ it should "topologySortByDFS - 2" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 3) graphTopology.addEdge(3, 4) graphTopology.addEdge(3, 2) graphTopology.addEdge(4, 2) val seq = graphTopology.topologySortByDFS() seq.map(nodes(_)).mkString(",") should equal("a,b,d,e,c") } /* a -> d <- b | /|\ \|/ | e -> c */ it should "topologySortByDFS - 3" in { val nodes = Array("a", "b", "c", "d", "e") val graphTopology = new GraphTopology(nodes.length) graphTopology.addEdge(0, 3) graphTopology.addEdge(3, 4) graphTopology.addEdge(4, 2) graphTopology.addEdge(2, 1) graphTopology.addEdge(1, 3) val seq = graphTopology.topologySortByKahn() seq.map(nodes(_)).mkString(",") should equal("a") } } ================================================ FILE: swift/05_array/MyArray.swift ================================================ // // Created by Jiandan on 2018/10/10. // Copyright (c) 2018 Jiandan. All rights reserved. // import Foundation // Swift 泛型,此数组支持不同数据类型 public struct MyArray { private var data: [Element] private var capacity = 0 // 数组长度 private var count = 0 // 已保存的数据个数 /// 构造方法 /// - parameter defaultElement: 默认元素,用来占位 /// - parameter capacity: 数组长度 init(defaultElement: Element, capacity: Int) { data = [Element](repeating: defaultElement, count: capacity) self.capacity = capacity } // 根据 index,查找元素 func find(at index: Int) -> Element? { // index 必须在 [0, count) guard index >= 0, index < count else { return nil } return data[index] } // 根据 index,删除元素 mutating func delete(at index: Int) -> Bool { // index 必须在 [0, count) guard index >= 0, index < count else { return false } // [index, count - 1) 从 index 开始,元素分别向前移动一位 for i in index ..< count - 1 { data[i] = data[i+1] } count -= 1 return true } // 根据 index 插入元素 mutating func insert(value: Element, at index: Int) -> Bool { // index 必须在 [0, count) guard index >= 0, index < count, count < capacity else { return false } // count - 1 ~ index for i in (index ... count - 1).reversed() { data[i + 1] = data[i] } data[index] = value count += 1 return true } // 添加元素 mutating func add(value: Element) -> Bool { guard count < capacity else { return false } data[count] = value count += 1 return true } func printAll() { print("\(data)") } } ================================================ FILE: swift/06_linkedlist/SinglyLinkedList.swift ================================================ // // 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 } } } ================================================ FILE: swift/07_linkedlist/LinkedListAlgo.swift ================================================ // // 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 } ================================================ FILE: swift/08_stack/Browser.swift ================================================ // // 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()!) } } ================================================ FILE: swift/08_stack/BrowserDemo.swift ================================================ // // 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()!) } } ================================================ FILE: swift/08_stack/Stack.swift ================================================ // // 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? } ================================================ FILE: swift/08_stack/StackBasedOnLinkedList.swift ================================================ // // 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 var cur = head.next while cur != nil { count += 1 cur = cur?.next } 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 } } ================================================ FILE: swift/09_queue/ArrayQueue.swift ================================================ // // Created by Jiandan on 2018/10/11. // Copyright (c) 2018 Jiandan. All rights reserved. // import Foundation /// 用数组实现的队列 struct ArrayQueue: Queue { /// 数组 private var items: [Element] /// 数组最大长度 private var capacity = 0 /// 队头下标 private var head = 0 /// 队尾下标 private var tail = 0 /// 构造方法 /// - parameter defaultElement: 默认元素 /// - parameter capacity: 数组长度 init(defaultElement: Element, capacity: Int) { self.capacity = capacity items = [Element](repeating: defaultElement, count: capacity) } // MARK: Protocol: Queue var isEmpty: Bool { return head == tail } var size: Int { return tail - head } var peek: Element? { return isEmpty ? nil : items[head] } // 没有数据搬移的实现,即实现了一个有界序列 // mutating func enqueue(newElement: Element) -> Bool { // // 整个队列都占满了 // if tail == capacity { // return false // } // // items[tail] = newElement // tail += 1 // return true // } // 有数据搬移的实现,即实现了一个无界序列 mutating func enqueue(newElement: Element) -> Bool { // 如果 tail == capacity 表示队列末尾没有空间了 if tail == capacity { // 整个队列都占满了 if head == 0 { return false } // 数据搬移 for i in head ..< tail { items[i - head] = items[i] } // 搬移完之后重新更新 head 和 tail tail -= head head = 0 } items[tail] = newElement tail += 1 return true } mutating func dequeue() -> Element? { if isEmpty { return nil } let item = items[head] head += 1 return item } } /// 使用2个数组实现无界队列,用到 Swift 中 Array 较多的方法 /// 来源:《iOS 面试之道》(故胤道长,唐巧) struct ArrayQueue2: Queue { /// 输入数组,主要负责入队 var inArray = [Element]() /// 输出数组,主要负责出队 var outArray = [Element]() var isEmpty: Bool { return inArray.isEmpty && outArray.isEmpty } var size: Int { return inArray.count + outArray.count } // 当 outArray 为空时,返回 inArray 首个元素,否则返回 outArray 末尾元素 var peek: Element? { return outArray.isEmpty ? inArray.first : outArray.last } mutating func enqueue(newElement: Element) -> Bool { // inArray 添加元素 inArray.append(newElement) return true } mutating func dequeue() -> Element? { if outArray.isEmpty { // 将 inArray 倒序存入 outArray 中 outArray = inArray.reversed() // 清空 inArray inArray.removeAll() } // 弹出 outArray 最后一个元素 return outArray.popLast() } } ================================================ FILE: swift/09_queue/CircularQueue.swift ================================================ // // Created by Jiandan on 2018/10/11. // Copyright (c) 2018 Jiandan. All rights reserved. // import Foundation /// 循环队列 struct CircularQueue: Queue { /// 数组 private var items: [Element] /// 数组最大长度 private var capacity = 0 /// 队头下标 private var head = 0 /// 队尾下标 private var tail = 0 /// 构造方法 /// - parameter defaultElement: 默认元素 /// - parameter capacity: 数组长度 init(defaultElement: Element, capacity: Int) { self.capacity = capacity items = [Element](repeating: defaultElement, count: capacity) } // MARK: Protocol: Queue var isEmpty: Bool { return head == tail } var size: Int { if tail >= head { return tail - head } else { return (tail + 1) + (capacity - head) } } var peek: Element? { return isEmpty ? nil : items[head] } mutating func enqueue(newElement: Element) -> Bool { // 整个队列都占满了 if (tail + 1) % capacity == head { return false } items[tail] = newElement tail = (tail + 1) % capacity return true } mutating func dequeue() -> Element? { if isEmpty { return nil } let item = items[head] head = (head + 1) % capacity return item } } ================================================ FILE: swift/09_queue/Queue.swift ================================================ // // Created by Jiandan on 2018/10/11. // Copyright (c) 2018 Jiandan. All rights reserved. // import Foundation protocol Queue { /// 持有的数据类型 associatedtype Element /// 是否为空 var isEmpty: Bool { get } /// 队列大小 var size: Int { get } /// 返回队列头部元素 var peek: Element? { get } /// 入队 mutating func enqueue(newElement: Element) -> Bool /// 出队 mutating func dequeue() -> Element? } ================================================ FILE: swift/09_queue/QueueBasedOnLinkedList.swift ================================================ // // Created by Jiandan on 2018/10/11. // Copyright (c) 2018 Jiandan. All rights reserved. // import Foundation struct QueueBasedOnLinkedList: Queue { /// 队首 var head: Node? /// 队尾 var tail: Node? // MARK: Protocol: Queue var isEmpty: Bool { return head == nil } var size: Int { if isEmpty { return 0 } var count = 1 // head 本身算一个 while head?.next != nil { count += 1 } return count } var peek: Element? { return head?.value } mutating func enqueue(newElement: Element) -> Bool { if isEmpty { // 空队列 let node = Node(value: newElement) head = node tail = node } else { tail!.next = Node(value: newElement) tail = tail!.next } return true } mutating func dequeue() -> Element? { if isEmpty { return nil } let node = head head = head!.next return node?.value } } ================================================ FILE: swift/11_sorts/Sorts.swift ================================================ import Foundation /// 冒泡排序 /// /// - Parameter elements: 数组 /// - Returns: 返回值 public func bubbleSort(_ elements: [T]) ->[T] where T: Comparable { var array = elements guard array.count > 1 else { return array } for i in 0.. array[j+1] { array.swapAt(j+1, j) // 此次冒泡有数据交换 flag = true } } if (!flag) { break } } return array } /// 插入排序 /// /// - Parameter elements: 数组 /// - Returns: 返回值 public func insertionSort(_ elements: [T]) -> [T] where T: Comparable { var array = elements guard array.count > 1 else { return array } for i in 1.. value { array[p+1] = array[p] } else { break } } array[j+1] = value } return array } /// 选择排序 /// /// - Parameter elements: 数组 /// - Returns: 返回值 public func selectionSort(_ elements: [T]) -> [T] where T: Comparable { var array = elements guard array.count > 1 else { return array } for i in 0..(_ a: inout T) where T.Element: Comparable { quickSort(&a, from: a.startIndex, to: a.index(before: a.endIndex)) } fileprivate func quickSort(_ a: inout T, from low: T.Index, to high: T.Index) where T.Element: Comparable { if low >= high { return } let m = partition(&a, from: low, to: high) quickSort(&a, from: low, to: a.index(before: m)) quickSort(&a, from: a.index(after: m), to: high) } fileprivate func partition(_ a: inout T, from low: T.Index, to high: T.Index) -> T.Index where T.Element: Comparable { let pivot = a[low] var j = low var i = a.index(after: low) while i <= high { if a[i] < pivot { a.formIndex(after: &j) a.swapAt(i, j) } a.formIndex(after: &i) } a.swapAt(low, j) return j } ================================================ FILE: swift/12_sorts/SortsTests.swift ================================================ // // SortsTests.swift // SortsTests // // Created by Wenru Dong on 2018/10/14. // Copyright © 2018年 Wenru Dong. All rights reserved. // import XCTest class SortsTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testMergeSort() { var a1 = [1, 1, 1, 1] mergeSort(&a1) XCTAssertEqual(a1, [1, 1, 1, 1]) var a2 = [4, 3, 2, 1] mergeSort(&a2) XCTAssertEqual(a2, [1, 2, 3, 4]) var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0] mergeSort(&a3) XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9]) } func testQuickSort() { var a1 = [1, 1, 1, 1] quickSort(&a1) XCTAssertEqual(a1, [1, 1, 1, 1]) var a2 = [4, 3, 2, 1] quickSort(&a2) XCTAssertEqual(a2, [1, 2, 3, 4]) var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0] quickSort(&a3) XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9]) } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: swift/12_sorts/mergeSort.swift ================================================ // // sorts.swift // algo // // Created by Wenru Dong on 2018/10/14. // Copyright © 2018年 Wenru Dong. All rights reserved. // import Foundation public func mergeSort(_ a: inout T) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { mergeSort(&a, from: a.startIndex, to: a.index(before: a.endIndex)) } fileprivate func mergeSort(_ a: inout T, from low: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { if low >= high { return } let dist = a.distance(from: low, to: high) let mid = a.index(low, offsetBy: dist/2) mergeSort(&a, from: low, to: mid) mergeSort(&a, from: a.index(mid, offsetBy: 1), to: high) merge(&a, from: low, through: mid, to: high) } fileprivate func merge(_ a: inout T, from low: T.Index, through mid: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { var i = low var j = a.index(mid, offsetBy: 1) var tmp = Array() tmp.reserveCapacity(a.distance(from: low, to: high) + 1) while i <= mid && j <= high { if a[i] <= a[j] { tmp.append(a[i]) a.formIndex(after: &i) } else { tmp.append(a[j]) a.formIndex(after: &j) } } var start = i var end = mid if j <= high { start = j end = high } tmp.append(contentsOf: a[start...end]) var current = low for element in tmp { a[current] = element a.formIndex(after: ¤t) } } ================================================ FILE: swift/14_sorts/CountingSort.swift ================================================ // // CountingSort.swift // algo // // Created by Wenru Dong on 2018/10/18. // Copyright © 2018年 Wenru Dong. All rights reserved. // import Foundation // 假设数组中储存的都是非负整数 public func countingSort(_ a: inout [Int]) { if a.count <= 1 { return } var counts = Array(repeating: 0, count: a.max()! + 1) for num in a { counts[num] += 1 } for i in 1.. { private cacheMap: Map> private readonly limit: number private head: LinkedListNode | null = null private end: LinkedListNode | null = null constructor(limit: number) { if (limit <= 0) throw new Error('limit of cache must > 0') this.cacheMap = new Map() this.limit = limit } public get(key: K): V | null { const node = this.cacheMap.get(key) if (!node) return null this.refreshNode(node) return node.value } public put(key: K, value: V) { const node = this.cacheMap.get(key) // 原缓存不存在则加入到队尾 if (!node) { // 大于规定的size则删除最不常用的 if (this.cacheMap.size >= this.limit) { const oldKey = this.removeNode(this.head!) this.cacheMap.delete(oldKey) } // 在队尾添加 const newNode = new LinkedListNode(key, value) this.addNode(newNode) this.cacheMap.set(key, newNode) } else { node.value = value this.refreshNode(node) } } private refreshNode(node: LinkedListNode) { if (node === this.end) return this.removeNode(node) this.addNode(node) } private removeNode(node: LinkedListNode): K { if (node === this.end) { this.end = this.end.prev } else if (node === this.head) { this.head = this.head.next } else { // 这个由于排除了首尾节点 node.prev!.next = node.next node.next!.prev = node.prev } return node.key } /** * 这里向尾部追加节点 * @param node */ private addNode(node: LinkedListNode) { if (this.end) { this.end.next = node node.prev = this.end } this.end = node if (this.head === null) { this.head = node } // 消除之前的节点的下一个引用对象,防止无限循环 node.next = null } } class LinkedListNode { key: K value: V next: LinkedListNode | null prev: LinkedListNode | null constructor( key: K, value: V, next: LinkedListNode | null = null, prev: LinkedListNode | null = null ) { this.key = key this.value = value this.next = next this.prev = prev } } const cache = new LRUCache(3) cache.put('lv','xzw') cache.put('lv2','xzw2') cache.put('lv3','xzw3') cache.put('lv4','xzw4') cache.put('lv5','xzw5') console.log(cache) ================================================ FILE: typescript/06_linkedlist/LinkedList.ts ================================================ /** * 双向链表,更加常用设计也更加复杂一些 * 需要更多的存储空间和操作复杂度 */ import List from './List' class LinkedList implements List { size: number = 0 private head: LinkedListNode | null = null private last: LinkedListNode | null = null findByIndex(index: number): LinkedListNode | null { let p = this.head let pos = 0 while (p && pos !== index) { p = p.next pos++ } return p } findByValue(value: T): LinkedListNode | null { let p = this.head while (p && p.item !== value) { p = p.next } return p } insertToHead(value: T): void { let p = this.head const newNode = new LinkedListNode(value) // 没有元素的时候需要初始化头节点和尾节点 if (!p) { this.last = this.head = newNode } else { p.prev = newNode newNode.next = p this.head = newNode } this.size++ } /** * 在指定的index后面插入节点 * @param value 节点的值 * @param index 指定的位置 */ insertToIndex(value: T, index: number): void { let p = this.head let pos = 0 const newNode = new LinkedListNode(value) while (p !== null && pos !== index) { p = p.next pos++ } if (p === null) return newNode.next = p.next p.next = newNode newNode.prev = p this.size++ } insertToTail(value: T): void { let p = this.last const newNode = new LinkedListNode(value) if (p === null) { this.head = this.last = newNode } else { p.next = newNode newNode.prev = p this.last = newNode } this.size++ } remove(value: T): boolean { let p = this.head while (p && p.item !== value) { p = p.next } if (!p) return false if (p.prev) { p.prev.next = p.next } else { this.head = p.next } if (p.next) { p.next.prev = p.prev } else { this.last = p.prev } this.size-- return true } toString(): string { let ret: string = '' let p = this.head while (p) { ret = `${ret} ${p.item} ` p = p.next } return ret } } class LinkedListNode { item: T next: LinkedListNode | null prev: LinkedListNode | null constructor( item: T, next: LinkedListNode | null = null, prev: LinkedListNode | null = null ) { this.item = item this.next = next this.prev = prev } } const linkedList = new LinkedList() linkedList.insertToHead('12') linkedList.insertToHead('haha') linkedList.insertToHead('www') linkedList.insertToTail('zxc') linkedList.insertToIndex('12ooo', 0) linkedList.remove('12oooo') console.log(linkedList.toString()) ================================================ FILE: typescript/06_linkedlist/List.ts ================================================ interface List { insertToHead(value: T): void findByValue(value: T): any findByIndex(index: number): any insertToIndex(value: T, index: number): void remove(value: T): boolean insertToHead(value: T): void insertToTail(value: T): void toString(): string } export default List ================================================ FILE: typescript/06_linkedlist/SingleLinkedList.ts ================================================ /** * 1)单链表的插入、删除、查找操作; * 2)链表支持任意类型数据 */ import List from './List' class SingleLinkedList implements List { // 哨兵头节点 private readonly head: SingleNode constructor() { this.head = new SingleNode(null) } public findByValue(value: T): SingleNode | null { let p = this.head while (p.next != null) { if (p.next.value === value) return p.next p = p.next } return p.next } public findByIndex(index: number): SingleNode | null { let p = this.head let pos = 0 while (p.next != null && pos !== index) { p = p.next pos++ } return p.next } /** * 向指定的位置插入节点 * @param value * @param index */ public insertToIndex(value: T, index: number): void { const newNode = new SingleNode(value) let p = this.head let pos = 0 while (p.next != null && pos !== index) { p = p.next pos++ } if (p.next == null) return newNode.next = p.next.next p.next.next = newNode } /** * 根据值去删除节点 * @param value 1 表示删除成功,0 表示删除失败 */ public remove(value: T): boolean { let p = this.head while (p.next != null) { if (p.next.value === value) break p = p.next } if (p.next === null) return false p.next = p.next.next return true } public insertToHead(value: T): void { const newNode = new SingleNode(value, null) this.insertNodeToHead(newNode) } public insertToTail(value: T): void { const newNode = new SingleNode(value, null) this.insertNodeToTail(newNode) } private insertNodeToHead(node: SingleNode): void { node.next = this.head.next this.head.next = node } public toString(): string { let ret: string = '' let p = this.head while (p.next != null) { ret = `${ret} ${p.next.value} ` p = p.next } return ret } /** * 单链表的尾插入比较费时 * @param newNode 插入的新节点 */ private insertNodeToTail(newNode: SingleNode): void { let p = this.head while (p.next != null) { p = p.next } p.next = newNode } } class SingleNode { public value: T public next: SingleNode | null constructor(value: T, next: SingleNode | null = null) { this.value = value this.next = next } } const singleLinkedList = new SingleLinkedList() singleLinkedList.insertToTail('god') singleLinkedList.insertToTail('my') // console.log(singleLinkedList.printLinkedList()) singleLinkedList.insertToIndex('haha', 1) singleLinkedList.remove('ha1') singleLinkedList.toString() ================================================ FILE: typescript/07_linkedlist/LinkedListAlog.ts ================================================ /** * 单链表的常见操作包括 * 链表反转 * 链表中环的检测 * 有序链表的合并 * 删除链表倒数第n个节点 * 链表中间节点 */ class LinkedListAlog { /** * 反转链表,依次将节点插入到头部 * @param list */ public static reverse(list: SingleNode): SingleNode | null { let currentNode: SingleNode | null = list let prevNode = null while (currentNode) { const nextNode: SingleNode | null = currentNode.next currentNode.next = prevNode prevNode = currentNode currentNode = nextNode } return prevNode } /** * 通过快慢指针来检测是否为一个环 * @param list */ public static checkCircle(list: SingleNode): boolean { if (!list) return false let fast: SingleNode | null = list.next let slow: SingleNode | null = list while (fast && fast.next) { fast = fast.next.next slow = slow!.next if (fast === slow) return true } return false } /** * 倒序删除节点 * @param list * @param index */ public static removeFromEnd(list: SingleNode, index: number): SingleNode | null { // 如果是个环,就没必要找了 if (this.checkCircle(list)) return list let newNode = this.reverse(list) let preNode = null let pos = 0 while (newNode && pos !== index) { newNode = newNode.next pos++ preNode = newNode } if (!newNode) return null if (preNode) { preNode.next = newNode.next } return this.reverse(newNode) } public static findMidNode(list: SingleNode): SingleNode | null { if (!list) return null let fast = list.next let slow = list while (fast && fast.next) { fast = fast.next.next slow = slow.next! } return slow } /** * 有序链表的合并,根据不同的值进行插入 * @param a * @param b */ public static mergeSortedLists(a: SingleNode, b: SingleNode): SingleNode | null { if (!a || !b) return a ? a : b let p: SingleNode | null = a let q: SingleNode | null = b // 新链表的头部指针 let newList: SingleNode | null = null if (p.value < q.value) { newList = p p = p.next } else { newList = q q = q.next } let currNode = newList while (p && q) { if (p.value < q.value) { currNode.next = p p = p.next } else { currNode.next = q q = q.next } currNode = currNode.next } if (p) { currNode.next = p } else { currNode.next = q } return newList } } class SingleNode { public value: T public next: SingleNode | null constructor(value: T, next: SingleNode | null = null) { this.value = value this.next = next } } const node1 = new SingleNode(1) node1.next = new SingleNode(3) node1.next.next = new SingleNode(5) const node2 = new SingleNode(2) node2.next = new SingleNode(7) node2.next.next = new SingleNode(8) node2.next.next.next = new SingleNode(10) console.log(LinkedListAlog.findMidNode(node1)) ================================================ FILE: typescript/08_stack/StackAndBrowser.ts ================================================ /** * 基于单向链表实现栈结构 */ export class Stack { private node: LinkedNode | null = null size: number = 0 public push(value: T) { if (!value) return const newNode = new LinkedNode(value) if (!this.node) { this.node = newNode } else { newNode.next = this.node this.node = newNode } this.size++ } public pop(): T | null { if (!this.node) { return null } const value = this.node.value this.node = this.node.next this.size-- return value } } /** * 单向链表 */ class LinkedNode { value: T next: LinkedNode | null constructor(value: T, next: LinkedNode | null = null) { this.value = value this.next = next } } /** * 使用双栈结构实现浏览器的前进后退 */ class Browser { // 存放后退的所有历史url private backStack: Stack // 存放前进的所有url private forwardStack: Stack private current: T constructor(current: T) { this.backStack = new Stack() this.forwardStack = new Stack() this.current = current } public back(): T | null { if (this.backStack.size > 0) { this.forwardStack.push(this.current) this.current = this.backStack.pop()! return this.getCurrentPage() } return null } public forward(): T | null { if (this.forwardStack.size > 0) { this.backStack.push(this.current) this.current = this.forwardStack.pop()! return this.getCurrentPage() } return null } /** * 在网页上点击一个链接 * @param value */ public linkUrl(value: T) { this.current && this.backStack.push(this.current) this.current = value } public getCurrentPage(): T { return this.current } } const browser = new Browser('www.baidu.com') browser.linkUrl('www.yuanzhoucehui.com') browser.linkUrl('www.github.com/jsrdxzw') // browser.back() // www.github.com/jsrdxzw console.log(browser.getCurrentPage()) browser.back() // www.yuanzhucehui.com console.log(browser.getCurrentPage()) browser.back() // www.baidu.com console.log(browser.getCurrentPage()) browser.back() // www.baidu.com console.log(browser.getCurrentPage()) browser.forward() // www.yuanzhucehui.com console.log(browser.getCurrentPage()) browser.forward() // www.github.com/jsrdxzw console.log(browser.getCurrentPage()) ================================================ FILE: typescript/09_queue/CircularQueue.ts ================================================ /** * 基于数组的循环队列 * 为了方便判断队列为空的情况,这里最好引入元素个数 */ class CircularQueue { private items: T[] = [] private readonly n: number = 0 private head: number = 0 private tail: number = 0 // 队列的实际元素大小 size: number = 0 constructor(capacity: number) { this.n = capacity } public enqueue(item: T): boolean { // 表示队列已经满了 if (this.size === this.n) return false this.items[this.tail] = item this.tail = (this.tail + 1) % this.n this.size++ return true } public dequeue(): T | null { if (!this.size) return null const item = this.items[this.head] this.head = (this.head + 1) % this.n this.size-- return item } } const circularQueue = new CircularQueue(3) circularQueue.enqueue(1) circularQueue.enqueue(2) circularQueue.enqueue(3) circularQueue.enqueue(4) const value = circularQueue.dequeue() const value1 = circularQueue.dequeue() const value2 = circularQueue.dequeue() const value3 = circularQueue.dequeue() // null console.log(value3) // 0 console.log(circularQueue.size) ================================================ FILE: typescript/09_queue/README.md ================================================ ### 队列 由于js语言天生就可以使用array数组来实现栈和队列等结构: ```javascript // 模拟数组队列 const queue = [] queue.push(1) // 向数组尾部添加数据 queue.shift() //数组头部去除数据,并返回 ``` 这里我们使用链表来实现队列结构 队列分为无限队列和循环队列 ================================================ FILE: typescript/09_queue/SimpleQueue.ts ================================================ /** * 使用链表实现简单队列 */ class SimpleQueue { private head: LinkedNode | null = null private tail: LinkedNode | null = null /** * 入队,插入队尾 * @param value */ public enqueue(value: T) { if (!this.tail) { this.head = this.tail = new LinkedNode(value) } else { const newNode = new LinkedNode(value) this.tail.next = newNode this.tail = newNode } } /** * 出队,在队头删除 */ public dequeue(): T | null { if (!this.head) return null const value = this.head.value this.head = this.head.next return value } public printAll(): string { let p = this.head let res = '' while (p) { res = `${res} ${p.value}` p = p.next } return res } } /** * 单向链表 */ class LinkedNode { value: T next: LinkedNode | null constructor(value: T, next: LinkedNode | null = null) { this.value = value this.next = next } } const queue = new SimpleQueue() queue.enqueue(1) queue.enqueue(2) queue.enqueue(3) queue.dequeue() queue.dequeue() console.log(queue.printAll()) ================================================ FILE: typescript/10_recursive/climbStairs.ts ================================================ /** * 递归求解爬楼梯问题 */ /** * 最开始版本的递归算法 * @param n */ function fn(n: number): any { if (n === 1) return 1 if (n === 2) return 2 return fn(n - 1) + fn(n - 2) } const res1 = fn(10) // 89 console.log(res1) /** * 使用depth结合js的闭包特性实现限制函数调用次数的功能 * @param depth 递归的深度 */ function fnWithDepth(depth: number) { return function fn(n: number): any { depth++ if (depth > 1000) throw new Error('function stack is too deep!') if (n === 1) return 1 if (n === 2) return 2 return fn(n - 1) + fn(n - 2) } } const res2 = fnWithDepth(3)(10) // 89 console.log(res2) /** * 通过map来存储已经计算过的值,避免递归重复计算 */ function fnWithMap() { const map = new Map() return function fn(n: number): any { if (n === 1) return 1 if (n === 2) return 2 if (map.has(n)) { return map.get(n) } const ret = fn(n - 1) + fn(n - 2) map.set(n, ret) return ret } } const res3 = fnWithMap()(10) console.log(res3) ================================================ FILE: typescript/11_sorts/simpleSort.ts ================================================ /** * 简单的排序,分为冒泡和插入排序 * 注意他们都是稳定的排序,并且是原地排序 * 一般情况下更推荐使用插入排序,因为它所需要的操作更少 * 这里使用简单工厂创建我们的排序算法 */ /** * 排序的枚举类型 */ enum SortType { BubbleSort, InsertSort } interface SortAlgo { sort(array: number[]): void } class BubbleSort implements SortAlgo { sort(array: number[]) { for (let i = 0; i < array.length; i++) { let flag = false for (let j = 0; j < array.length; j++) { if (array[j] > array[j + 1]) { const temp = array[j] array[j] = array[j + 1] array[j + 1] = temp flag = true } } if (!flag) { break } } } } class InsertSort implements SortAlgo { sort(array: number[]) { for (let i = 1; i < array.length; i++) { let j = i - 1 const temp = array[i] for (; j >= 0; j--) { if (array[j] > array[j + 1]) { array[j + 1] = array[j] } else { // 这个说明之前的已经排好了,没必要继续比较 break } } array[j + 1] = temp } } } class SortFactory { static getSortAlgo(type: SortType): SortAlgo { switch (type) { case SortType.BubbleSort: return new BubbleSort() case SortType.InsertSort: return new InsertSort() default: throw new Error('unknown sort algorithm type') } } } const insertSort = SortFactory.getSortAlgo(SortType.InsertSort) const test1 = [1, 0, 2, 4, 8, 5, 10] insertSort.sort(test1) console.log(test1) ================================================ FILE: typescript/12_sorts/KthNum.ts ================================================ /** * O(n)的时间复杂度内求无序数组的第K大元素 * 如[4,2,5,12,3]的第3大元素就是4 * 这里也是使用了分治和分区的思想 */ class KthNum { getKthNum(array: number[], k: number): number { const length = array.length if (k > length) return -1 // q+1对应的元素一定比q和之前的元素大,q+1就是第q+1大元素 // 注意返回的q是数组下标,所以我们要加1才能表示第k个元素 let q = this.partition(array, 0, array.length - 1) while (q + 1 !== k) { if (q + 1 > k) { q = this.partition(array, 0, q - 1) } else { q = this.partition(array, q + 1, length - 1) } } return array[q] } /** * 这里和快速排序一样 * @param array 数组的一部分 * @param p 开始坐标 * @param r 结束坐标 */ private partition(array: number[], p: number, r: number) { const pivot = array[p] let index = p + 1 for (let i = index; i <= r; i++) { if (array[i] < pivot) { this.swap(array, index, i) index++ } } this.swap(array, p, index - 1) return index - 1 } private swap(array: number[], p: number, q: number) { const temp = array[p] array[p] = array[q] array[q] = temp } } const testFindSortNum = [4, 2, 5, 12, 3] const kthNum = new KthNum() const num = kthNum.getKthNum(testFindSortNum, 3) console.log(num) ================================================ FILE: typescript/12_sorts/MergeSort.ts ================================================ /** * 归并排序 * 稳定排序,稳定的O(nlgn)时间复杂度 * O(n)的空间复杂度 * 在小规模数据排序中很常用 */ class MergeSort { public static mergeSort(array: number[]) { if (!array || !array.length) return const length = array.length this.mergeSortInternally(array, 0, length - 1) } static mergeSortInternally(array: number[], p: number, r: number) { if (p >= r) return // 严格按照中间值作切分点 // js中除法需要做取整操作,不然结果有可能是小数 const q = Math.floor(p + (r - p) / 2) this.mergeSortInternally(array, p, q) this.mergeSortInternally(array, q + 1, r) this.mergeArray(array, p, q, r) } private static mergeArray(a: number[], p: number, q: number, r: number) { let i = p let j = q + 1 let k = 0 // 定义一个临时数组来存放排序的值 const tmp: number[] = [] while (i <= q && j <= r) { if (a[i] <= a[j]) { tmp[k++] = a[i++] } else { tmp[k++] = a[j++] } } // 判断哪个子数组中有剩余的数据 let start = i let end = q if (j <= r) { start = j end = r } // 将剩余的数据拷贝到临时数组tmp while (start <= end) { tmp[k++] = a[start++] } // 将tmp中的数组拷贝回a[p...r] for (i = 0; i <= r - p; i++) { a[p + i] = tmp[i] } } } const test4 = [1, 3, 2, 3, 10, 9, 7, 6, 0, 12] MergeSort.mergeSort(test4) console.log(test4) ================================================ FILE: typescript/12_sorts/quickSort.ts ================================================ /** * 快速排序是不稳定的排序 * 原地排序,空间复杂度O(1),比归并排序使用更广泛 * 平均复杂度基本接近O(nlg(n)) */ export class QuickSort { static sort(array: number[]): void { this.sortInternally(array, 0, array.length - 1) } private static sortInternally(array: number[], p: number, r: number) { if (p >= r) return // 获取分界点 const q: number = this.partition(array, p, r) this.sortInternally(array, p, q - 1) this.sortInternally(array, q + 1, r) } private static partition(array: number[], p: number, r: number): number { /** * 参考值pivot,小于pivot的放在左边,大于pivot的在右边,最后再把分界点的值和它做交换 * 这样返回的index一定是值在中间的下标 */ const pivot = array[p] // 分界点 let index = p + 1 for (let i = index; i <= r; i++) { if (array[i] < pivot) { this.swap(array, index, i) // 找到了比标记值小的元素就移动分界点 index++ } } this.swap(array, p, index - 1) return index - 1 } private static swap(array: number[], p: number, q: number) { const temp = array[p] array[p] = array[q] array[q] = temp } } const testSort = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12] QuickSort.sort(testSort) console.log(testSort) ================================================ FILE: typescript/13_sorts/BucketSort.ts ================================================ /** * 桶排序对数据的要求比较高 * 首先要知道数据的范围 * 然后根据范围将数据分到小范围的桶中 * 每个桶采用快速排序 * 当桶的数量接近数据量大小的时候,时间复杂度为O(n) */ import { QuickSort } from '../12_sorts/quickSort' class BucketSort { static sort(array: number[], bucketSize: number = 5) { const length = array.length if (length === 0) return array // 首先要确定数据的范围 let min = array[0] let max = array[0] for (let i = 0; i < length; i++) { if (array[i] < min) { min = array[i] } else if (array[i] > max) { max = array[i] } } // 初始化桶,确定桶的数量 // 因为不能保证正好被整除,需要+1 存放剩余的元素 const bucketCount = Math.floor((max - min) / bucketSize) + 1 // 桶是个二维数组 const buckets = new Array(bucketCount) for (let i = 0; i < bucketCount; i++) { buckets[i] = [] } // 利用映射函数将数据分配到各个桶中 // 这个时间复杂度为O(n) for (let i = 0; i < length; i++) { buckets[Math.floor((array[i]-min) / bucketSize)].push(array[i]) } array.length = 0 // 返回数组 for (let i = 0; i < bucketCount; i++) { // 每个桶里根据具体情况排序,使用插入排序或者快速排序等等 QuickSort.sort(buckets[i]) for (let j = 0; j < buckets[i].length; j++) { array.push(buckets[i][j]); } } } } const bucketTest = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12] BucketSort.sort(bucketTest) console.log(bucketTest) ================================================ FILE: typescript/13_sorts/CountingSort.ts ================================================ /** * 计数排序 * 也是线性时间复杂度,和桶排序非常类似 * 适用于值范围较小的大数据排序 * 注意值范围需要不小于0,不然需要将数据预处理 * 并非原地排序 */ class CountingSort { static sort(array: number[]) { const length = array.length // 找到这个数组的最大值 let max = array[0] array.forEach((item) => { if (item > max) { max = item } }) // 初始化值范围数组 const countArray = new Array(max + 1).fill(0, 0, max + 1) // 先计算每个元素的出现个数 for (let i = 0; i < length; i++) { countArray[array[i]] = countArray[array[i]] + 1 } // 计算元素的累计出现个数 for (let i = 1; i <= max; i++) { countArray[i] = countArray[i - 1] + countArray[i] } // 接下来开始计数排序了 // 空间还是要申请 const sortedArray = [] // 倒序遍历能够达到稳定排序的作用 for (let i = length - 1; i >= 0; i--) { // -1是为了填补sortedArray在0的位置,因为countArray在0的位置中一定么有值 const index = countArray[array[i]] - 1 sortedArray[index] = array[i] countArray[array[i]]-- } for (let i = 0; i < length; i++) { array[i] = sortedArray[i] } } } const testSort2 = [1, 3, 2, 3, 10, 9, 7, 6, 0] CountingSort.sort(testSort2) console.log(testSort2) ================================================ FILE: typescript/14_binarysearch/BinarySearch.ts ================================================ /** * 二分查找适合于连续内存的数组查找 * 并且是已经排好序的数组 * 时间复杂度只有log(n) */ class BinarySearch { static bSearch(array: number[], target: number) { if (!array || array.length === 0) return -1 const length = array.length let low = 0 let high = length - 1 while (low <= high) { // 一定是整数,这边的移位运算优先级低于+,-运算符,需要加括号 const mid = low + ((high - low) >> 1) if (array[mid] === target) { return mid } else if (array[mid] > target) { high = mid - 1 } else { low = mid + 1 } } return -1 } } const testBinarySearch = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] console.log(BinarySearch.bSearch(testBinarySearch, 10)) ================================================ FILE: typescript/15_binarysearch/BinaryFind.ts ================================================ /** * 二分查找法在实际的场合使用不多 * 我们更多的是看到一些查找法的变种 */ class BinaryFind { /** * 在数组中查找第一个等于给定值的位置 * @param array 给定的要查找的范围数组 * @param target 要查找的目标值 * @return 目标在数组中的位置 */ static findFirstElement(array: number[], target: number): number { const length = array ? array.length : null if (!length || length === 0) return -1 let low = 0 let high = length - 1 while (low <= high) { const mid = low + ((high - low) >> 2) if (array[mid] > target) { high = mid - 1 } else if (array[mid] < target) { low = mid + 1 } else { if (mid === 0 || array[mid - 1] !== target) { return mid } else { high = mid - 1 } } } return -1 } /** * 在数组中查找最后一个等于给定值的位置 * @param array 给定的要查找的范围数组 * @param target 要查找的目标值 */ static findLastElement(array: number[], target: number): number { const length = array ? array.length : null if (!length || length === 0) return -1 let low = 0 let high = length - 1 while (low <= high) { const mid = low + ((high - low) >> 2) if (array[mid] > target) { high = mid - 1 } else if (array[mid] < target) { low = mid + 1 // 这里已经是找到相等的元素了 } else { if (mid === length - 1 || array[mid + 1] !== target) { return mid } else { low = mid + 1 } } } return -1 } /** * 在数组中查找第一个大于等于给定值的位置 * @param array 给定的要查找的范围数组 * @param target 要查找的目标值 */ static findFirstElementGreaterThanTarget(array: number[], target: number): number { const length = array ? array.length : null if (!length || length === 0) return -1 let low = 0 let high = length - 1 while (low <= high) { const mid = low + ((high - low) >> 2) if (array[mid] < target) { low = mid + 1 } else { if (mid === 0 || array[mid - 1] < target) { return mid } else { high = mid - 1 } } } return -1 } /** * 在数组中查找最后一个小于等于给定值的位置 * @param array 给定的要查找的范围数组 * @param target 要查找的目标值 */ static findLastElementLessThanTarget(array: number[], target: number): number { const length = array ? array.length : null if (!length || length === 0) return -1 let low = 0 let high = length - 1 while (low <= high) { const mid = low + ((high - low) >> 2) if (array[mid] > target) { high = mid - 1 } else { if (mid === length - 1 || array[mid + 1] > target) { return mid } else { low = mid + 1 } } } return -1 } } const binaryFindTest = [1, 3, 4, 4, 5, 6, 8, 8, 8, 11, 18] const target = BinaryFind.findLastElementLessThanTarget(binaryFindTest, -1) console.log(target) ================================================ FILE: typescript/17_skiplist/SkipList.ts ================================================ /** * 跳跃表是Redis使用的底层算法 * 在增删改查都有近似O(log n)的时间复杂度 * 哈希表虽然在不产生冲突的情况下是O(1)的时间复杂度 * 但是随着冲突的增多,所需要的扩容操作还是比较耗时的,综合起来不一定快于跳表 * 这两种结构可以互相补充 * 下面摘抄一段来自知乎的话 (https://juejin.im/post/57fa935b0e3dd90057c50fbc) * 比较跳表和哈希表,平衡树之间的区别 * skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。 * 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。 * 如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。 * 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。 * 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。 * 如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。 * 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。 * 从算法实现难度上来比较,skiplist比平衡树要简单。 */ export class SkipList { // head和tail始终指向最顶层的首位节点,通过链表能访问任何位置 private head: SkipListNode private tail: SkipListNode // 索引的层数,0表示最底层 private levelCount = 0 // 元素的个数 private size = 0 // private readonly MAX_LEVEL = 16 constructor() { this.head = new SkipListNode(SkipListNode.negInf, null) this.tail = new SkipListNode(SkipListNode.posInf, null) } public insert(key: number, value: T): void { let p: SkipListNode let q: SkipListNode let i: number = 0 // 先查找位置 p = this.findNode(key) // 如果跳跃表中的值已经存在了,直接赋值即可 if (p.key === key) { p.value = value return } // 没有该值,则进行插入操作,应该是插在p节点的右边. p -> q -> ? q = new SkipListNode(key, value) q.left = p q.right = p.right if (p.right) { p.right.left = q } p.right = q // 再使用随机数决定是否要向更高层攀升 while (Math.random() < 0.5) { // 如果新元素的级别已经达到跳跃表的最大高度,则新建空白层 if (i >= this.levelCount) { this.addEmptyLevel() } // 从p向左扫描含有高层节点的节点, 方便节点在每一层插入 while (!p.up) { p = p.left! } p = p.up // 新值对应的索引,这里不需要存value了,因为只需要最底层存value即可 const z = new SkipListNode(key, null) z.left = p z.right = p.right if (p.right) { p.right.left = z } p.right = z z.down = q q.up = z q = z i = i + 1 } this.size++ } public get(key: number): T | null { const p = this.findNode(key) return p.key === key ? p.value : null } public remove(key: number) { let p: SkipListNode | undefined = this.findNode(key) if (p.key !== key) return while (p != null) { p.left!.right = p.right p.right!.left = p.left p = p.up } } private addEmptyLevel() { const p1: SkipListNode = new SkipListNode(SkipListNode.negInf, null) const p2: SkipListNode = new SkipListNode(SkipListNode.posInf, null) p1.right = p2 p1.down = this.head p2.left = p1 p2.down = this.tail this.head.up = p1 this.tail.up = p2 this.head = p1 this.tail = p2 this.levelCount++ } private findNode(key: number): SkipListNode { const { head } = this let p = head while (true) { // 从左向右查找,直到右节点的key值大于要查找的key值 while (p.right && p.right.key !== SkipListNode.posInf && p.right.key <= key) { p = p.right } // 如果有更低层的节点,则向低层移动 if (p.down) { p = p.down } else { break } } // 这里返回的p的key值,是小于等于要找的key值的 return p } } export class SkipListNode { key: number value: T | null up?: SkipListNode down?: SkipListNode left?: SkipListNode right?: SkipListNode constructor(key: number, value: T | null) { this.key = key this.value = value } // 最小的数,无限接近于0,用于表示左标兵 static negInf: number = Number.MIN_VALUE // 最大的数,用于表示右标兵 static posInf: number = Number.MAX_VALUE } const testSkipList = new SkipList() testSkipList.insert(12, 'qwe') testSkipList.insert(3, 'mmm') console.log(testSkipList.get(3)) ================================================ FILE: typescript/24_treesearch/TreeSearch.ts ================================================ /** * 二叉树的增删查操作 */ export class TreeSearch { private root?: Node public find(data: number) { let p = this.root while (p != null) { if (data < p.data) { p = p.left } else if (data > p.data) { p = p.right } else { return p } } return p } public insert(data: number) { if (this.root == null) { this.root = new Node(data) return } let p = this.root while (p != null) { if (data < p.data) { if (p.left == null) { p.left = new Node(data) return } else { p = p.left } } else { if (data > p.data) { if (p.right == null) { p.right = new Node(data) return } else { p = p.right } } } } } /** * 删除的时候分三种情况 * @param data */ public delete(data: number) { // 先把对应的元素找出来,并且为了删除,也得记录父节点的内存地址 // 包含了null或者undefined两种情况 if (this.root == null) return let p: Node | undefined = this.root let pp = this.root while (p != null && p.data !== data) { pp = p data > p.data ? p = p.right : p = p.left } // 没有找到要删除的元素 if (p == null) return // 要删除的元素有两个子节点,需要找到右子树的最小节点,然后赋值 if (p.left != null && p.right != null) { // 右子树的最小节点 let minP = p.right // 右子树的最小节点的父节点 let minPP = p while (minP.left != null) { minPP = minP minP = minP.left } p.data = minP.data p = minP pp = minPP } // 有一个子节点或者没有子节点 let child if (p.left != null) { child = p.left } else if (p.right != null) { child = p.right } else { child = null } if (pp == null) { this.root = undefined } else if (pp.left === p) { pp.left = child! } else { pp.right = child! } } public printAllData(): void { this.printAll(this.root) } private printAll(node?: Node): void { if (node == null) return console.log(node.data) this.printAll(node.left) this.printAll(node.right) } } export class Node { data: number left?: Node right?: Node constructor(data: number) { this.data = data } } const treeSearch = new TreeSearch() treeSearch.insert(1) treeSearch.insert(4) treeSearch.insert(3) treeSearch.insert(2) treeSearch.insert(5) treeSearch.insert(0) treeSearch.delete(4) treeSearch.printAllData()