├── grokking-algorithms-illustrated-programmers-curious ├── 10 │ ├── test.py │ ├── draw.py │ ├── recommendation_system.py │ └── distance.py ├── 06 │ ├── .gitignore │ ├── graphLibOfPython │ │ ├── networkx_demo2.py │ │ ├── igraph_demo1.py │ │ └── networkx_demo1.py │ ├── graph │ │ ├── DirectedGraph.py │ │ ├── WeightedGraph.py │ │ └── graph.py │ ├── bfs │ │ ├── simpleQueue.py │ │ ├── bfs-search.py │ │ └── bfs-traverse.py │ ├── graphStore │ │ ├── graph_matrix.py │ │ └── graph_list.py │ └── others │ │ ├── SocialNetwork.py │ │ └── graph_with_d3 │ │ ├── d3-demo6.html │ │ ├── d3-demo1.html │ │ ├── d3-demo2.html │ │ └── d3-relations.html ├── 07 │ ├── README.md │ ├── dijkstra.py │ ├── dijkstra2.py │ └── d3-demo.html ├── 03 │ ├── fibonacci.py │ ├── fibonacci_loop.py │ └── fibonacci_tail.py ├── 05 │ ├── demo.py │ ├── UserDatabase.py │ ├── ChainingHashTable.py │ ├── RabinKarp.py │ └── LinearProbeHashTable.py ├── 09 │ ├── fibonacci.py │ ├── fibonacci_bottom_up.py │ ├── fibonacci_memo.py │ ├── coinChange.py │ ├── longestCommonSubsequence_without_path_1D.py │ ├── knapsack_1D.py │ ├── canPartition.py │ ├── bio.py │ ├── fractional_knapsack.py │ ├── knapsack.py │ ├── knapsack_transposed.py │ ├── coinChange_2D.py │ ├── longestCommonSubsequence_with_path.py │ ├── longestCommonSubsequence_with_path_1D.py │ └── dp-table │ │ ├── coin-DP-table.html │ │ └── lcs-DP-table.html ├── 08 │ ├── sort_by_end_time.py │ ├── knapsack_greedy.py │ ├── sort_by_start_time.py │ ├── graph │ │ └── graph.py │ ├── travle.py │ ├── simple_way_solve_set_covering_problem.py │ ├── knapsack_dp.py │ ├── classroom_scheduling.py │ └── greedy_algorithm_solve_set_covering_problem.py ├── 02 │ ├── selection_sort_arr.py │ └── selection_sort_linked_list.py ├── 04 │ ├── quick_sort.py │ ├── merge_sort.py │ └── quick_sort_loop.py ├── 01 │ └── binary_search.py └── README.md ├── Algorithms-4th-Edition ├── src │ └── main │ │ ├── resources │ │ ├── symbol_table │ │ │ └── tinyST.txt │ │ ├── analysis_of_algorithms │ │ │ └── 8ints.txt │ │ └── minimum_spanning_trees │ │ │ └── tinyEWG.txt │ │ └── java │ │ ├── analysis_of_algorithms │ │ ├── FibonacciRecursive.java │ │ ├── InfiniteRecursion.java │ │ ├── BirthdayProblem.java │ │ ├── ThreeSumFinalModelFitting.java │ │ ├── ThreeSumFast.java │ │ ├── FibonacciLoop.java │ │ ├── ThreeSumFinal.java │ │ ├── DatabaseQueryComparison.java │ │ ├── ThreeSum.java │ │ ├── LinearRegression.java │ │ ├── MemoryAnalysis.java │ │ ├── MatrixPower.java │ │ ├── SortComparison.java │ │ ├── MatrixExponentiation.java │ │ ├── SearchComparison.java │ │ └── ThreeSumFastModelFitting.java │ │ ├── symbol_table │ │ ├── tools │ │ │ ├── TestVisualAccumulator.java │ │ │ ├── VisualAccumulator.java │ │ │ └── TestAccumulator.java │ │ ├── OrderedSymbolTable.java │ │ └── search_st │ │ │ ├── TestSequentialSearchST.java │ │ │ ├── FrequencyCounter.java │ │ │ ├── FCounterBinarySearchST.java │ │ │ └── SequentialSearchST.java │ │ ├── priority_queues │ │ ├── MaxPQ.java │ │ ├── TopM.java │ │ ├── PriorityQueueExample.java │ │ ├── PriorityQueue.java │ │ ├── UnorderedArrayPQ.java │ │ ├── OrderedArrayPQ.java │ │ ├── PerformanceTest2.java │ │ ├── OrderedLinkedListPQ.java │ │ ├── UnorderedArrayPQImpl.java │ │ ├── UnorderedLinkedListPQ.java │ │ └── BinaryHeapPQ.java │ │ ├── heap_sort │ │ ├── HeapSortUsingPriorityQueue.java │ │ ├── HeapTopDown.java │ │ ├── HeapBottomUp.java │ │ ├── HeapSortStandard.java │ │ ├── TestDataGenerator.java │ │ ├── HeapSortPerformanceTest.java │ │ ├── HeapSortStringPerformanceTest.java │ │ ├── HeapSortFloyd.java │ │ └── HeapSortStandardCount.java │ │ ├── substr_search │ │ └── BoyerMoore.java │ │ └── minimum_spanning_trees │ │ ├── KruskalMST.java │ │ └── LazyPrimMST.java ├── runConfigurations │ ├── BinarySearchST.run.xml │ ├── FrequencyCounter.run.xml │ ├── ThreeSum-16Kints.run.xml │ ├── ThreeSum-1Kints.run.xml │ ├── ThreeSum-2Kints.run.xml │ ├── ThreeSum-32Kints.run.xml │ ├── ThreeSum-4Kints.run.xml │ ├── ThreeSum-8Kints.run.xml │ ├── ThreeSumFinal-${n}k.run.xml │ ├── balanced_binary_tree.AVLTree.run.xml │ ├── BirthdayProblem.run.xml │ ├── TestVisualAccumulator.run.xml │ ├── AVLTreeST.run.xml │ ├── PrimMST.run.xml │ ├── ThreeSumFast-${n}k.run.xml │ ├── LazyPrimMST.run.xml │ ├── SequentialSearchST.run.xml │ └── TestSequentialSearchST.run.xml ├── scripts │ ├── calculate_sinking_sum.py │ └── analysis_of_algorithms │ │ ├── plot_math_model.py │ │ ├── ThreeSumFinal_model_fitting.py │ │ ├── plot_math_model2.py │ │ ├── ThreeSumFast_model_fitting.py │ │ ├── power_law_model_estimator.py │ │ └── plot_math_model3.py └── pom.xml ├── README.md └── .gitignore /grokking-algorithms-illustrated-programmers-curious/06/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/resources/symbol_table/tinyST.txt: -------------------------------------------------------------------------------- 1 | S E A R C H E X A M P L E 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 代码仓库 2 | 3 | * [算法图解](./grokking-algorithms-illustrated-programmers-curious/) 4 | * [算法-第四版](./Algorithms-4th-Edition/) 5 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/resources/analysis_of_algorithms/8ints.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 30 3 | -30 4 | -20 5 | -10 6 | 40 7 | 0 8 | 10 9 | 15 10 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/07/README.md: -------------------------------------------------------------------------------- 1 | ### 1. 代码说明 2 | 3 | * dijkstra.py : 只找出每个节点到起点的最短距离 4 | * dijkstra2.py : 不仅找出最短距离,也可以找出最短路径 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | generate_code_script.html 3 | 4 | ### IntelliJ IDEA ### 5 | .idea 6 | *.iws 7 | *.iml 8 | *.ipr 9 | target/ 10 | 11 | out/ 12 | 13 | ### VS Code ### 14 | .vscode/ 15 | 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/03/fibonacci.py: -------------------------------------------------------------------------------- 1 | # 采用递归的方式求第N个斐波那契数 2 | def fibonacci(n): 3 | if n <= 1: 4 | return n 5 | else: 6 | return fibonacci(n-1) + fibonacci(n-2) 7 | 8 | # 测试代码 9 | n = 10 10 | print("斐波那契数列的第", n, "个数是:", fibonacci(n)) 11 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graphLibOfPython/networkx_demo2.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import matplotlib.pyplot as plt 3 | 4 | # 创建一个含有10个节点的完全图 5 | G = nx.complete_graph(10) 6 | 7 | # 绘制图 8 | nx.draw(G, with_labels=True) 9 | 10 | # 显示图形 11 | plt.show() 12 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/resources/minimum_spanning_trees/tinyEWG.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 16 3 | 4 5 0.35 4 | 4 7 0.37 5 | 5 7 0.28 6 | 0 7 0.16 7 | 1 5 0.32 8 | 0 4 0.38 9 | 2 3 0.17 10 | 1 7 0.19 11 | 0 2 0.26 12 | 1 2 0.36 13 | 1 3 0.29 14 | 2 7 0.34 15 | 6 2 0.40 16 | 3 6 0.52 17 | 6 0 0.58 18 | 6 4 0.93 19 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/05/demo.py: -------------------------------------------------------------------------------- 1 | # 创建散列表 2 | hash_table = {} 3 | 4 | # 向散列表插入键值对 5 | hash_table['apple'] = 1 6 | hash_table['banana'] = 2 7 | hash_table['orange'] = 3 8 | 9 | # 获取键对应的值 10 | print(hash_table['apple']) # 输出: 1 11 | print(hash_table['banana']) # 输出: 2 12 | print(hash_table['orange']) # 输出: 3 13 | 14 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graphLibOfPython/igraph_demo1.py: -------------------------------------------------------------------------------- 1 | 2 | from igraph import Graph, summary 3 | 4 | # 创建一个含有10000个顶点,50000条边的随机图 5 | g = Graph.Erdos_Renyi(n=10000, m=50000) 6 | 7 | # 打印图的基本信息 8 | summary(g) 9 | 10 | # 打印图的直径 11 | print("Diameter of the graph: ", g.diameter()) 12 | 13 | # 计算并打印图的聚类系数 14 | print("Clustering coefficient: ", g.transitivity_undirected()) 15 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/fibonacci.py: -------------------------------------------------------------------------------- 1 | def fibonacci_recursive(N): 2 | """ 3 | 使用递归的方法求解第N个斐波那契数 4 | :param N: 指定的斐波那契数的位置 5 | :return: 第N个斐波那契数 6 | """ 7 | if N <= 0: 8 | return 0 9 | elif N == 1: 10 | return 1 11 | else: 12 | return fibonacci_recursive(N - 1) + fibonacci_recursive(N - 2) 13 | 14 | # 求解第10个斐波那契数 15 | fibonacci_recursive(10) 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graphLibOfPython/networkx_demo1.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import matplotlib.pyplot as plt 3 | 4 | # 创建一个空的无向图 5 | G = nx.Graph() 6 | 7 | # 添加节点,从1到6 8 | G.add_nodes_from(range(1, 7)) 9 | 10 | # 添加边 11 | G.add_edge(1, 2) 12 | G.add_edge(1, 3) 13 | G.add_edge(2, 4) 14 | G.add_edge(2, 5) 15 | G.add_edge(3, 6) 16 | 17 | # 绘制图 18 | nx.draw(G, with_labels=True) 19 | 20 | # 显示图形 21 | plt.show() 22 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/10/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sklearn.neighbors import NearestNeighbors 3 | 4 | 5 | knn = NearestNeighbors(n_neighbors=3, metric='cosine', algorithm='brute') 6 | 7 | data = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] 8 | 9 | # 使用数据拟合对象 10 | knn.fit(data) 11 | 12 | # 查询数据点 [4,5] 的最近邻 13 | distances, indices = knn.kneighbors([[4, 5]]) 14 | 15 | print("Distances:", distances) 16 | print("Indices:", indices) -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/fibonacci_bottom_up.py: -------------------------------------------------------------------------------- 1 | # 定义自下而上(表格方法)的函数 2 | def fibonacci_bottom_up(N): 3 | # 用 0 初始化 DP 数组,并设置 F(0) 和 F(1) 的基本情况 4 | dp = [0] * (N + 1) 5 | dp[1] = 1 6 | 7 | # 从 F(2) 开始,自下而上构建解决方案 8 | for i in range(2, N + 1): 9 | dp[i] = dp[i - 1] + dp[i - 2] 10 | 11 | # 返回第 N 个斐波那契数 12 | return dp[N] 13 | 14 | # 测试函数 15 | N = 1000 16 | print(f"第 {N} 个斐波那契数(自下而上):{fibonacci_bottom_up(N)}") 17 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/03/fibonacci_loop.py: -------------------------------------------------------------------------------- 1 | # 采用循环的方式求第N个斐波那契数 2 | def fibonacci_loop(n): 3 | if n <= 0: 4 | return 0 5 | elif n == 1: 6 | return 1 7 | else: 8 | a, b = 0, 1 9 | for i in range(2, n+1): 10 | c = a + b 11 | a = b 12 | b = c 13 | return b 14 | 15 | 16 | # 测试代码 17 | n = 10 18 | print("斐波那契数列的第", n, "个数是:", fibonacci_loop(n)) 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/FibonacciRecursive.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | public class FibonacciRecursive { 4 | public static int fibonacci(int n) { 5 | if (n <= 1) return n; 6 | return fibonacci(n - 1) + fibonacci(n - 2); 7 | } 8 | 9 | public static void main(String[] args) { 10 | // 测试斐波那契数列的前10项 11 | for (int i = 0; i < 10; i++) { 12 | System.out.println("F(" + i + ") = " + fibonacci(i)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/fibonacci_memo.py: -------------------------------------------------------------------------------- 1 | def fibonacci_memo(n, memo={}): 2 | """ 3 | 备忘录法求第N个斐波那契数 (记忆化递归) 4 | """ 5 | if n in memo: 6 | return memo[n] 7 | 8 | if n <= 1: 9 | return n 10 | else: 11 | result = fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo) 12 | memo[n] = result # 存储问题结果 13 | return result 14 | 15 | 16 | # 测试函数 17 | 18 | # 栈溢出情况 19 | # N = 1000 20 | 21 | # 正常情况 22 | N = 10 23 | print(f"第 {N} 个斐波那契数(自上而下):{fibonacci_memo(N)}") 24 | 25 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/BinarySearchST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/FrequencyCounter.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-16Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-1Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-2Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-32Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-4Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSum-8Kints.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSumFinal-${n}k.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/calculate_sinking_sum.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def calculate_sinking_sum(N): 4 | log_N = int(math.log2(N)) 5 | total_sinking = 0 6 | for i in range(log_N): 7 | nodes = N / (2 ** (i + 2)) # 第 i 层的节点数 8 | sinking_per_node = i + 1 # 每个节点的下沉次数 9 | total_sinking += nodes * sinking_per_node 10 | return total_sinking 11 | 12 | # 计算 N = 1000 和 N = 10000 的情况 13 | sinking_sum_1000 = calculate_sinking_sum(1000) 14 | sinking_sum_10000 = calculate_sinking_sum(10000) 15 | 16 | print(sinking_sum_1000, sinking_sum_10000) 17 | 18 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/sort_by_end_time.py: -------------------------------------------------------------------------------- 1 | # 定义活动列表,每个活动由开始时间和结束时间组成 2 | activities = [(1, 10), (2, 3), (4, 5), (6, 7), (8, 9)] 3 | 4 | # 按照结束时间排序 5 | activities.sort(key = lambda x:x[1]) 6 | 7 | # 初始化当前活动的结束时间为0 8 | current_time = 0 9 | # 初始化计数器 10 | count = 0 11 | 12 | # 遍历活动列表 13 | for activity in activities: 14 | # 如果活动的开始时间大于等于当前时间,说明可以进行这个活动 15 | if activity[0] >= current_time: 16 | # 更新当前时间为活动的结束时间 17 | current_time = activity[1] 18 | # 计数器加1 19 | count += 1 20 | 21 | print("按照结束时间排序,可以进行的最多活动数为:", count) 22 | 23 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/knapsack_greedy.py: -------------------------------------------------------------------------------- 1 | # 定义物品列表,每个物品由重量和价值组成 2 | items = [(10, 60), (20, 100), (30, 120)] 3 | # 背包的容量 4 | capacity = 50 5 | 6 | # 计算每个物品的价值重量比,并按照价值重量比从高到低排序 7 | items.sort(key=lambda x: x[1]/x[0], reverse=True) 8 | 9 | # 初始化背包中物品的总价值 10 | value = 0 11 | 12 | # 依次选择价值重量比最高的物品 13 | for item in items: 14 | if capacity >= item[0]: 15 | # 如果背包能装下物品,就把物品放入背包 16 | capacity -= item[0] 17 | value += item[1] 18 | else: 19 | # 如果背包装不下物品,就停止选择 20 | break 21 | 22 | print("贪心策略得到的背包中物品的总价值为:", value) 23 | 24 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/sort_by_start_time.py: -------------------------------------------------------------------------------- 1 | # 定义活动列表,每个活动由开始时间和结束时间组成 2 | activities = [(1, 10), (2, 3), (4, 5), (6, 7), (8, 9)] 3 | 4 | # 按照开始时间排序 5 | activities.sort(key = lambda x:x[0]) 6 | 7 | # 初始化当前活动的结束时间为0 8 | current_time = 0 9 | # 初始化计数器 10 | count = 0 11 | 12 | # 遍历活动列表 13 | for activity in activities: 14 | # 如果活动的开始时间大于等于当前时间,说明可以进行这个活动 15 | if activity[0] >= current_time: 16 | # 更新当前时间为活动的结束时间 17 | current_time = activity[1] 18 | # 计数器加1 19 | count += 1 20 | 21 | print("按照开始时间排序,可以进行的最多活动数为:", count) 22 | 23 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/tools/TestVisualAccumulator.java: -------------------------------------------------------------------------------- 1 | package symbol_table.tools; 2 | 3 | import edu.princeton.cs.algs4.StdOut; 4 | import edu.princeton.cs.algs4.StdRandom; 5 | 6 | /** 7 | * java TestVisualAccumulator 2000 8 | */ 9 | 10 | public class TestVisualAccumulator { 11 | public static void main(String[] args) { 12 | int T = Integer.parseInt(args[0]); 13 | VisualAccumulator a = new VisualAccumulator(T, 1.0); 14 | for (int t = 0; t < T; t++) { 15 | a.addDataValue(StdRandom.random()); 16 | } 17 | // Mean (2000 values): 0.4972 18 | StdOut.println(a); 19 | } 20 | } -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/03/fibonacci_tail.py: -------------------------------------------------------------------------------- 1 | # 注意:这里只是采用尾递归优化形式! 2 | # Python 并没有优化尾递归,这意味着即使你写出了尾递归形式的函数, 3 | # Python解释器也不会自动将其转换为循环以节省栈空间。 4 | # 这是因为Python的设计哲学和实现方式不将尾递归优化视为优先事项 5 | 6 | def fibonacci_tail(n, a=0, b=1): 7 | # 当 n 等于 0 时,返回 a(此时的斐波那契数列第0项) 8 | if n == 0: 9 | return a 10 | # 当 n 等于 1 时,返回 b(此时的斐波那契数列第1项) 11 | elif n == 1: 12 | return b 13 | # 对于其他情况,执行尾递归 14 | else: 15 | # 函数调用自身,n 减少 1,并更新 a 和 b 的值(a = b, b = a + b) 16 | return fibonacci_tail(n-1, b, a+b) 17 | 18 | # 计算斐波那契数列的第10项 19 | result = fibonacci_tail(10) 20 | print(result) 21 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/coinChange.py: -------------------------------------------------------------------------------- 1 | def coinChange(coins, amount): 2 | """ 3 | 使用动态规划解决硬币找零问题 4 | :param coins: 可用的硬币面额列表 5 | :param amount: 需要找的总金额 6 | :return: 所需的最少硬币数量 7 | """ 8 | # dp[i]表示金额为i时所需的最少硬币数量 9 | dp = [float('inf')] * (amount + 1) 10 | dp[0] = 0 # 初始条件,金额为0时所需硬币数量为0 11 | 12 | for coin in coins: 13 | for i in range(coin, amount + 1): 14 | dp[i] = min(dp[i], dp[i - coin] + 1) 15 | 16 | return dp[amount] if dp[amount] != float('inf') else -1 17 | 18 | coins = [1, 2, 5] 19 | amount = 11 20 | coinChange(coins, amount) 21 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/plot_math_model.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | # 定义模型参数 5 | a = 1.318221890610448E-9 6 | b = 1.085747925662513E-6 7 | c = 0.004204166666666693 8 | 9 | # 生成N的值。这里我们以1000为步长生成了一系列的N 10 | N = np.arange(1000, 35000, 1000) 11 | 12 | # 根据模型计算y的值 13 | y = a * N**2 + b * N + c 14 | 15 | # 绘制图形 16 | plt.figure(figsize=(10,6)) 17 | plt.plot(N, y, '-o', label='y = {:.2e} * N^2 + {:.2e} * N + {:.2e}'.format(a, b, c)) 18 | plt.xlabel('N') 19 | plt.ylabel('y') 20 | plt.title('Plot of the Mathematical Model') 21 | plt.legend() 22 | plt.grid(True) 23 | plt.show() 24 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/graph/graph.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from scipy.special import factorial 4 | 5 | n = np.linspace(0, 10, 400) # 创建n的范围为0到10的400个点 6 | y1 = 2 ** n # 2的n次方 7 | y2 = factorial(n) # n的阶乘 8 | 9 | plt.plot(n, y1, label='2^n') # 绘制2的n次方曲线 10 | plt.plot(n, y2, label='n!') # 绘制n的阶乘曲线 11 | 12 | plt.title('Curves of 2^n and n!') # 标题 13 | plt.xlabel('n') # x轴标签 14 | plt.ylabel('Value') # y轴标签 15 | 16 | plt.yscale('log') # 由于n!的增长速度非常快,因此可以考虑使用对数尺度 17 | 18 | plt.legend() # 显示图例 19 | plt.grid(True) # 显示网格 20 | 21 | plt.show() # 显示图像 22 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/ThreeSumFinal_model_fitting.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.optimize import curve_fit 3 | 4 | # Given data points 5 | N_values = np.array([1e3, 2e3, 4e3, 8e3, 16e3, 32e3]) 6 | T_values = np.array([0.005, 0.012, 0.031, 0.098, 0.358, 1.389]) 7 | 8 | # Define the function for the model: T = aN^2 + bN + c 9 | def model(N, a, b, c): 10 | return a * N**2 + b * N + c 11 | 12 | # Use curve_fit to find the coefficients a, b, and c 13 | params, covariance = curve_fit(model, N_values, T_values) 14 | a, b, c = params 15 | a, b, c 16 | 17 | # RESULT 18 | # (1.3182218917850921e-09, 1.0857478792859185e-06, 0.004204166976515905) 19 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/02/selection_sort_arr.py: -------------------------------------------------------------------------------- 1 | def selection_sort(arr): 2 | """ 3 | 选择排序函数,用于对数组进行排序 4 | :param arr: 将要进行排序的数组 5 | :return: 排序后的数组 6 | """ 7 | # 遍历数组 8 | for i in range(len(arr)): 9 | # 初始化最小值索引为当前遍历位置 10 | min_idx = i 11 | # 遍历未排序部分的数组 12 | for j in range(i+1, len(arr)): 13 | # 查找最小值的索引 14 | if arr[min_idx] > arr[j]: 15 | min_idx = j 16 | # 交换当前遍历位置与最小值位置的元素,将最小值归位 17 | arr[i], arr[min_idx] = arr[min_idx], arr[i] 18 | return arr 19 | 20 | 21 | # 测试 22 | newArr = selection_sort([2,3,1,5,4,6,9,8,0]) 23 | print(newArr) 24 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/InfiniteRecursion.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | /** 4 | * 无限递归示例 5 | * Error: java.lang.StackOverflowError 6 | */ 7 | public class InfiniteRecursion { 8 | 9 | public static void main(String[] args) { 10 | try { 11 | recursiveFunction(0); 12 | } catch (StackOverflowError e) { 13 | // java.lang.StackOverflowError 14 | System.err.println(e); 15 | } 16 | } 17 | 18 | public static void recursiveFunction(int depth) { 19 | 20 | // 递归深度(我本机最大深度: 22164,你运行的结果取决于你的运行环境) 21 | System.out.println(depth); 22 | recursiveFunction(depth + 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/balanced_binary_tree.AVLTree.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/plot_math_model2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import math 4 | 5 | # 定义模型函数 6 | def model(N, a, b, c): 7 | return a * N**2 * np.log(N) + b * N * np.log(N) + c 8 | 9 | # 定义参数 10 | a = 1.961900952491261e-09 11 | b = 2.4380254620244043e-06 12 | c = -0.034854130547573775 13 | 14 | # 生成x数据 15 | x_data = np.linspace(1000, 32000, 400) 16 | # 计算y数据 17 | y_data = model(x_data, a, b, c) 18 | 19 | # 绘图 20 | plt.plot(x_data, y_data, label='Model') 21 | plt.scatter([1000, 2000, 4000, 8000, 16000, 32000], [0.015, 0.064, 0.296, 1.238, 5.228, 21.611], color='red', label='Data') 22 | plt.xlabel('N') 23 | plt.ylabel('Time (s)') 24 | plt.legend() 25 | plt.show() 26 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/BirthdayProblem.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/travle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | # 城市之间的距离矩阵 4 | # 这里为了简单,我们假设距离矩阵为随机生成的,实际应用中,距离应由实际的地理信息计算得出 5 | distance_matrix = np.random.rand(5, 5) 6 | 7 | # 城市列表 8 | cities = ["上海", "南京", "武汉", "重庆", "成都"] 9 | 10 | # 选择一个起始城市 11 | current_city = 0 12 | 13 | # 存储旅行路线 14 | route = [cities[current_city]] 15 | 16 | # 待访问城市集合 17 | to_visit = set(range(1, 5)) 18 | 19 | while to_visit: 20 | # 在待访问城市中选择距离当前城市最近的城市 21 | next_city = min(to_visit, key=lambda x: distance_matrix[current_city][x]) 22 | to_visit.remove(next_city) 23 | 24 | # 更新当前城市,并将其添加到旅行路线中 25 | current_city = next_city 26 | route.append(cities[current_city]) 27 | 28 | # 最后返回起始城市 29 | route.append(cities[0]) 30 | 31 | print("旅行路线:", route) 32 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/TestVisualAccumulator.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/AVLTreeST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/PrimMST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/ThreeSumFast-${n}k.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/10/draw.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from mpl_toolkits.mplot3d import Axes3D 3 | import numpy as np 4 | 5 | # 定义向量A和B 6 | A = np.array([2, 1, 8]) 7 | B = np.array([6, 10, 1]) 8 | 9 | # 创建一个3D绘图对象 10 | fig = plt.figure() 11 | ax = fig.add_subplot(111, projection='3d') 12 | 13 | # 绘制原点 14 | ax.scatter(0, 0, 0, color="k", s=100, label="Origin") 15 | 16 | # 绘制向量A 17 | ax.quiver(0, 0, 0, A[0], A[1], A[2], color='b', label='Vector A') 18 | 19 | # 绘制向量B 20 | ax.quiver(0, 0, 0, B[0], B[1], B[2], color='r', label='Vector B') 21 | 22 | # 设置坐标轴 23 | ax.set_xlim([0, 3]) 24 | ax.set_ylim([0, 3]) 25 | ax.set_zlim([0, 3]) 26 | ax.set_xlabel('X') 27 | ax.set_ylabel('Y') 28 | ax.set_zlabel('Z') 29 | 30 | # 添加图例 31 | ax.legend() 32 | 33 | # 展示图形 34 | plt.show() 35 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/LazyPrimMST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/SequentialSearchST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/runConfigurations/TestSequentialSearchST.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/longestCommonSubsequence_without_path_1D.py: -------------------------------------------------------------------------------- 1 | def lcs(X, Y): 2 | """ 3 | 使用一维DP数组解决LCS问题 4 | :param X: 字符串X 5 | :param Y: 字符串Y 6 | :return: LCS的长度 7 | """ 8 | m, n = len(X), len(Y) 9 | 10 | # 初始化一维DP数组,大小为字符串Y的长度+1 11 | dp = [0] * (n + 1) 12 | 13 | for i in range(1, m + 1): 14 | prev = 0 # 保存dp[j-1]的旧值,对应于二维DP数组中的dp[i-1][j-1] 15 | for j in range(1, n + 1): 16 | temp = dp[j] # 保存当前dp[j]的值,因为我们需要在下次迭代中使用它 17 | if X[i-1] == Y[j-1]: 18 | dp[j] = prev + 1 19 | else: 20 | dp[j] = max(dp[j], dp[j-1]) 21 | prev = temp # 更新prev的值为dp[j]的旧值 22 | 23 | return dp[n] 24 | 25 | # 测试 26 | X = "ABCBDAB" 27 | Y = "BDCAB" 28 | print(lcs(X, Y)) # 输出4,最长公共子序列为"BCAB" 29 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/knapsack_1D.py: -------------------------------------------------------------------------------- 1 | def knapsack_optimized(W, weights, values): 2 | """ 3 | 使用一维DP数组解决0-1背包问题 4 | :param W: 背包的总容量 5 | :param weights: 每个物品的重量列表 6 | :param values: 每个物品的价值列表 7 | :return: 背包能装的最大价值 8 | """ 9 | n = len(weights) # 获取物品的数量 10 | dp = [0] * (W + 1) # 初始化一维DP数组,大小为背包容量+1,所有位置初始化为0 11 | 12 | for i in range(n): 13 | # 从背包的容量开始递减,直到当前物品的重量。这是为了确保每个物品只被考虑一次 14 | for w in range(W, weights[i] - 1, -1): 15 | 16 | # 对于每个容量w,我们尝试放入物品i,并更新dp[w]的值。 17 | # dp[w]的新值是不放入物品i和放入物品i这两种选择中的最大值。 18 | dp[w] = max(dp[w], values[i] + dp[w - weights[i]]) 19 | 20 | return dp[W] 21 | 22 | 23 | weights = [2, 5, 7, 11, 13] 24 | values = [5000, 8000, 7000, 9000, 11000] 25 | W = 25 26 | 27 | print(knapsack_optimized(W, weights, values)) 28 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/MaxPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | import java.util.PriorityQueue; 4 | import java.util.Comparator; 5 | 6 | interface MaxPQ> { 7 | void insert(T value); 8 | T extractMax(); 9 | boolean isEmpty(); 10 | } 11 | 12 | class JavaPriorityQueueWrapper implements MaxPQ { 13 | private PriorityQueue pq; 14 | 15 | public JavaPriorityQueueWrapper() { 16 | this.pq = new PriorityQueue<>(Comparator.reverseOrder()); // 反转自然顺序以得到最大堆 17 | } 18 | 19 | @Override 20 | public void insert(Integer value) { 21 | pq.offer(value); 22 | } 23 | 24 | @Override 25 | public Integer extractMax() { 26 | return pq.poll(); 27 | } 28 | 29 | @Override 30 | public boolean isEmpty() { 31 | return pq.isEmpty(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/TopM.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | import edu.princeton.cs.algs4.*; 4 | 5 | public class TopM 6 | { 7 | public static void main(String[] args) 8 | { // Print the top M lines in the input stream. 9 | int M = Integer.parseInt(args[0]); 10 | MinPQ pq = new MinPQ(M+1); 11 | while (StdIn.hasNextLine()) 12 | { // Create an entry from the next line and put on the PQ. 13 | pq.insert(new Transaction(StdIn.readLine())); 14 | if (pq.size() > M) 15 | pq.delMin(); // Remove minimum if M+1 entries on the PQ. 16 | } // Top M entries are on the PQ. 17 | Stack stack = new Stack(); 18 | while (!pq.isEmpty()) stack.push(pq.delMin()); 19 | for (Transaction t : stack) StdOut.println(t); 20 | } } -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortUsingPriorityQueue.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | /** 4 | * 使用 java 提供的优先队列实现堆排序 5 | * 6 | * 注意: 7 | * 1. 并非原地排序,需要额外的空间 8 | * 2. 性能可能不如直接使用数组实现的堆排序高效 9 | */ 10 | 11 | import java.util.PriorityQueue; 12 | 13 | public class HeapSortUsingPriorityQueue { 14 | public static void sort(int[] arr) { 15 | PriorityQueue pq = new PriorityQueue<>(); 16 | 17 | // 将所有元素加入到优先队列 18 | for (int value : arr) { 19 | pq.offer(value); 20 | } 21 | 22 | // 依次取出元素,实现排序 23 | for (int i = 0; i < arr.length; i++) { 24 | arr[i] = pq.poll(); 25 | } 26 | } 27 | 28 | public static void main(String[] args) { 29 | int[] array = {12, 11, 13, 5, 6, 7}; 30 | sort(array); 31 | for (int value : array) { 32 | System.out.print(value + " "); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/canPartition.py: -------------------------------------------------------------------------------- 1 | def canPartition(nums): 2 | """ 3 | 判断是否可以将nums分割为两个和相等的子集 4 | :param nums: 整数列表 5 | :return: 布尔值,True表示可以分割,False表示不能 6 | """ 7 | total = sum(nums) 8 | # 如果总和是奇数,则不能将其分割为两个和相等的子集 9 | if total % 2 != 0: 10 | return False 11 | 12 | target = total // 2 13 | n = len(nums) 14 | # dp[i][j]表示使用前i个数字,是否可以得到和为j 15 | dp = [[False for _ in range(target + 1)] for _ in range(n + 1)] 16 | for i in range(n + 1): 17 | dp[i][0] = True 18 | 19 | for i in range(1, n + 1): 20 | for j in range(1, target + 1): 21 | # 不选择第i个数字 22 | dp[i][j] = dp[i-1][j] 23 | # 选择第i个数字,条件是j不小于nums[i-1] 24 | if j >= nums[i-1]: 25 | dp[i][j] = dp[i][j] or dp[i-1][j-nums[i-1]] 26 | 27 | return dp[n][target] 28 | 29 | nums = [1, 5, 11, 5] 30 | canPartition(nums) 31 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/bio.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from Bio.Align import PairwiseAligner 3 | 4 | def plot_alignment(align1, align2): 5 | """Visualize the sequence alignment.""" 6 | fig, ax = plt.subplots(figsize=(len(align1) * 0.5, 3)) 7 | ax.axis('off') 8 | for i, (a1, a2) in enumerate(zip(align1, align2)): 9 | if a1 == a2: 10 | ax.plot([i, i], [1, 2], color='k') 11 | ax.text(i, 1, a1, ha='center', va='center') 12 | ax.text(i, 2, a2, ha='center', va='center') 13 | ax.set_ylim(0.5, 2.5) 14 | plt.show() 15 | 16 | seq1 = "ATCGGATTAG" 17 | seq2 = "GATCTAGTA" 18 | 19 | aligner = PairwiseAligner() 20 | alignments = aligner.align(seq1, seq2) 21 | 22 | # For the sake of visualization, let's pick the first alignment 23 | alignment = alignments[0] 24 | align1, align2 = alignment[0], alignment[1] 25 | 26 | plot_alignment(align1, align2) 27 | 28 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/fractional_knapsack.py: -------------------------------------------------------------------------------- 1 | def fractional_knapsack(values, weights, capacity): 2 | """ 3 | 0/1 分数背包问题的贪心算法解决方案 4 | :param values: 物品的价值列表 5 | :param weights: 物品的重量列表 6 | :param capacity: 背包的容量 7 | :return: 背包中物品的最大价值 8 | """ 9 | # 计算每个物品的单位重量价值 10 | unit_values = [(v / w, w, v) for v, w in zip(values, weights)] 11 | 12 | # 根据单位重量价值对物品进行排序(降序) 13 | unit_values.sort(key=lambda x: x[0], reverse=True) 14 | 15 | total_value = 0 # 总价值 16 | for uv, w, v in unit_values: 17 | # 如果背包可以完全装下当前物品 18 | if capacity >= w: 19 | capacity -= w 20 | total_value += v 21 | # 如果背包只能装下当前物品的一部分 22 | else: 23 | total_value += uv * capacity 24 | break 25 | 26 | return total_value 27 | 28 | # 物品的价值 29 | values = [60, 100, 120] 30 | # 物品的重量 31 | weights = [10, 20, 30] 32 | # 背包的容量 33 | capacity = 50 34 | 35 | fractional_knapsack(values, weights, capacity) 36 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/05/UserDatabase.py: -------------------------------------------------------------------------------- 1 | class User: 2 | def __init__(self, id, name, email): 3 | self.id = id 4 | self.name = name 5 | self.email = email 6 | 7 | def __str__(self): 8 | return f"User(id={self.id}, name={self.name}, email={self.email})" 9 | 10 | 11 | class UserDatabase: 12 | def __init__(self): 13 | self.users = {} 14 | 15 | def add_user(self, user): 16 | self.users[user.id] = user 17 | 18 | def get_user(self, user_id): 19 | return self.users.get(user_id, None) 20 | 21 | 22 | # 创建一个用户数据库实例 23 | user_db = UserDatabase() 24 | 25 | # 添加用户 26 | user1 = User(1, "Alice", "alice@example.com") 27 | user2 = User(2, "Bob", "bob@example.com") 28 | user3 = User(3, "Charlie", "charlie@example.com") 29 | 30 | user_db.add_user(user1) 31 | user_db.add_user(user2) 32 | user_db.add_user(user3) 33 | 34 | # 根据用户ID检索用户 35 | user = user_db.get_user(2) 36 | print(user) # 输出: User(id=2, name=Bob, email=bob@example.com) 37 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/ThreeSumFast_model_fitting.py: -------------------------------------------------------------------------------- 1 | # ThreeSumFast 模型确定 2 | 3 | from scipy.optimize import curve_fit 4 | import numpy as np 5 | 6 | # Define the model function 7 | def model(N, a, b, c, d): 8 | return a * N**2 * np.log(N) + b * N * np.log(N) + c * N + d 9 | 10 | # Given data points 11 | N_values_2 = np.array([1000, 2000, 4000, 8000, 16000, 32000]) 12 | 13 | # 需要换成你的测试结果 14 | T_values_2 = np.array([0.015, 0.064, 0.296, 1.238, 5.118, 21.611]) 15 | 16 | # Fit the model to the data and find the optimal parameters 17 | params_opt_2, params_cov_2 = curve_fit(model, N_values_2, T_values_2, maxfev=5000) 18 | 19 | # Extract the parameters 20 | a_2_new, b_2_new, c_2_new, d_2_new = params_opt_2 21 | 22 | # Print the parameters 23 | print("a =", a_2_new) 24 | print("b =", b_2_new) 25 | print("c =", c_2_new) 26 | print("d =", d_2_new) 27 | 28 | # RESULT 29 | # a = 2.016018884377828e-09 30 | # b = -3.5589716787117615e-06 31 | # c = 4.378173843180173e-05 32 | # d = -0.024066987491874164 33 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/tools/VisualAccumulator.java: -------------------------------------------------------------------------------- 1 | package symbol_table.tools; 2 | 3 | import edu.princeton.cs.algs4.StdDraw; 4 | 5 | /** 6 | * an abstract data type for accumulating data values (visual version) 7 | */ 8 | 9 | public class VisualAccumulator { 10 | private double total; 11 | private int N; 12 | 13 | public VisualAccumulator(int trials, double max) { 14 | // 设置画布尺寸以适应长方形区域 15 | StdDraw.setCanvasSize(1024, (int)(1024 * (max / trials))); 16 | StdDraw.setXscale(0, trials); 17 | StdDraw.setYscale(0, max); 18 | StdDraw.setPenRadius(.005); 19 | } 20 | 21 | public void addDataValue(double val) { 22 | N++; 23 | total += val; 24 | StdDraw.setPenColor(StdDraw.DARK_GRAY); 25 | StdDraw.point(N, val); 26 | StdDraw.setPenColor(StdDraw.RED); 27 | StdDraw.point(N, total/N); 28 | } 29 | 30 | public double mean() { 31 | return total / N; 32 | } 33 | 34 | public String toString() { 35 | return "Mean (" + N + " values): " + String.format("%7.5f", mean()); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/BirthdayProblem.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import edu.princeton.cs.algs4.StdRandom; 4 | import edu.princeton.cs.algs4.StdOut; 5 | 6 | import java.util.HashSet; 7 | 8 | 9 | /** 10 | * 对应书中习题 1.4.44 Birthday problem. 11 | */ 12 | 13 | public class BirthdayProblem { 14 | 15 | public static void main(String[] args) { 16 | int N = Integer.parseInt(args[0]); // 从命令行读取N的值 17 | int count = 0; // 记录生成的随机数数量 18 | HashSet set = new HashSet<>(); // 记录已经生成的随机数 19 | 20 | while (true) { 21 | int num = StdRandom.uniform(N); // 生成0到N-1之间的随机整数 22 | count++; 23 | if (set.contains(num)) { // 如果该随机数已经存在,则跳出循环 24 | break; 25 | } 26 | set.add(num); // 将该随机数添加到集合中 27 | } 28 | 29 | StdOut.println("Generated numbers before first duplicate: " + count); 30 | StdOut.println("Theoretical value: " + Math.sqrt(Math.PI * N / 2)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/power_law_model_estimator.py: -------------------------------------------------------------------------------- 1 | import numpy as np # 导入NumPy库,用于数组操作 2 | from sklearn.linear_model import LinearRegression # 导入sklearn的线性回归模型 3 | import math # 导入数学库 4 | 5 | # 给定数据点 (可以换成自己的运行结果) 6 | # 数据第一列是数据规模(n),第二列是运行时间(T) 7 | data = np.array([ 8 | [1, 0.175], 9 | [2, 1.213], 10 | [4, 9.41], 11 | [8, 74.941] 12 | ]) 13 | 14 | # 提取数据规模(n)和运行时间(T) 15 | n = data[:, 0] 16 | T = data[:, 1] 17 | 18 | # 对数变换 19 | # 为了将幂指模型线性化,我们对n和T取对数 20 | log_n = np.log(n) 21 | log_T = np.log(T) 22 | 23 | # 调整数据形状以适应sklearn 24 | # sklearn要求输入为二维数组 25 | log_n = log_n.reshape(-1, 1) 26 | log_T = log_T.reshape(-1, 1) 27 | 28 | # 执行线性回归 29 | # 使用对数变换后的数据进行线性回归,以估算模型参数 30 | model = LinearRegression() 31 | model.fit(log_n, log_T) 32 | 33 | # 提取斜率(k)和截距(log(a)) 34 | # 斜率和截距用于构建幂指模型 35 | k = model.coef_[0][0] 36 | log_a = model.intercept_[0] 37 | 38 | # 计算a的值 39 | # 通过对截距取指数得到a的值 40 | a = math.exp(log_a) 41 | 42 | # 输出k和a 43 | print(k, a) 44 | 45 | # RESULT 46 | # 2.9182384662267324 0.16830073785100377 -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapTopDown.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Arrays; 4 | 5 | public class HeapTopDown { 6 | 7 | // 自顶向下建堆方法 8 | public static void heapifyTopDown(int[] arr) { 9 | for (int i = 1; i < arr.length; i++) { 10 | swim(arr, i); 11 | } 12 | } 13 | 14 | // 上浮操作 15 | private static void swim(int[] arr, int k) { 16 | while (k > 0 && arr[k] > arr[(k - 1) / 2]) { 17 | swap(arr, k, (k - 1) / 2); 18 | k = (k - 1) / 2; 19 | } 20 | } 21 | 22 | // 交换数组中的两个元素 23 | private static void swap(int[] arr, int i, int j) { 24 | int temp = arr[i]; 25 | arr[i] = arr[j]; 26 | arr[j] = temp; 27 | } 28 | 29 | // 测试用例 30 | public static void main(String[] args) { 31 | int[] arrTopDown = {19, 100, 1, 8, 3, 25, 4, 36, 17}; 32 | HeapTopDown.heapifyTopDown(arrTopDown); 33 | 34 | System.out.println("Heap built Top-Down: " + Arrays.toString(arrTopDown)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/tools/TestAccumulator.java: -------------------------------------------------------------------------------- 1 | package symbol_table.tools; 2 | 3 | import edu.princeton.cs.algs4.Accumulator; 4 | import edu.princeton.cs.algs4.StdIn; 5 | import edu.princeton.cs.algs4.StdOut; 6 | 7 | public class TestAccumulator { 8 | public static void main(String[] args) { 9 | Accumulator stats = new Accumulator(); 10 | 11 | while(!StdIn.isEmpty()) { 12 | double x = StdIn.readDouble(); 13 | stats.addDataValue(x); 14 | } 15 | 16 | /** 17 | * 计算: 平均值(mean)、方差(var)、标准差(stddev) 18 | * 输入: 85, 90, 95, 100, 105 19 | * 输出: mean = 95.0, stddev = 7.90569, var = 62.50000 20 | */ 21 | StdOut.printf("n = %d\n", new Object[]{stats.count()}); 22 | StdOut.printf("mean = %.5f\n", new Object[]{stats.mean()}); 23 | StdOut.printf("stddev = %.5f\n", new Object[]{stats.stddev()}); 24 | StdOut.printf("var = %.5f\n", new Object[]{stats.var()}); 25 | StdOut.println(stats); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/PriorityQueueExample.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | public class PriorityQueueExample { 6 | 7 | public static void main(String[] args) { 8 | // 创建一个优先队列 9 | PriorityQueue priorityQueue = new PriorityQueue<>(); 10 | 11 | // 向队列中添加元素 12 | priorityQueue.offer(10); 13 | priorityQueue.offer(4); 14 | priorityQueue.offer(15); 15 | priorityQueue.offer(7); 16 | priorityQueue.offer(3); 17 | 18 | // 输出队列中的最小元素,但不移除 19 | System.out.println("队列中的最小元素(不移除): " + priorityQueue.peek()); // 3 20 | 21 | // 移除并输出队列中的最小元素 22 | System.out.println("移除并返回队列中的最小元素: " + priorityQueue.poll()); // 3 23 | 24 | // 输出移除后队列中的最小元素 25 | System.out.println("再次查看队列中的最小元素: " + priorityQueue.peek()); // 4 26 | 27 | // 为了展示优先队列的特性,我们继续向队列中添加元素并查看最小值 28 | priorityQueue.offer(2); 29 | System.out.println("添加新元素后队列中的最小元素: " + priorityQueue.peek()); // 2 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/ThreeSumFinalModelFitting.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import org.apache.commons.math3.fitting.PolynomialCurveFitter; 4 | import org.apache.commons.math3.fitting.WeightedObservedPoints; 5 | 6 | public class ThreeSumFinalModelFitting { 7 | public static void main(String[] args) { 8 | 9 | // 这里换成你的测试数据 10 | double[][] data = { {1000, 0.005}, {2000,0.012}, {4000,0.031}, {8000,0.098}, {16000,0.358}, {32000,1.389} }; 11 | 12 | WeightedObservedPoints obs = new WeightedObservedPoints(); 13 | for (double[] datum : data) { 14 | obs.add(datum[0], datum[1]); 15 | } 16 | 17 | PolynomialCurveFitter fitter = PolynomialCurveFitter.create(2); // 2 表示二次多项式 18 | double[] coeff = fitter.fit(obs.toList()); 19 | 20 | System.out.println("y = " + coeff[2] + " * N^2 + " + coeff[1] + " * N + " + coeff[0]); 21 | 22 | // RESULT: y = 1.318221890610448E-9 * N^2 + 1.085747925662513E-6 * N + 0.004204166666666693 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graph/DirectedGraph.py: -------------------------------------------------------------------------------- 1 | 2 | ## 有向图 3 | 4 | class DirectedGraph: 5 | def __init__(self, vertices): 6 | # 初始化图,参数为顶点列表 7 | self.graph = {vertex: [] for vertex in vertices} 8 | 9 | def add_edge(self, src, dest): 10 | # 添加边,参数为源节点和目标节点 11 | # 注意因为是有向图,所以只需要在源节点的邻接表中添加目标节点 12 | if src in self.graph: 13 | self.graph[src].append(dest) 14 | else: 15 | raise ValueError("源节点不在图中") 16 | 17 | def display(self): 18 | # 输出图的内容 19 | for vertex, edges in self.graph.items(): 20 | print(vertex, "->", edges) 21 | 22 | # 测试用例 23 | vertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] 24 | 25 | graph = DirectedGraph(vertices) 26 | graph.add_edge('A', 'B') 27 | graph.add_edge('A', 'C') 28 | graph.add_edge('B', 'D') 29 | graph.add_edge('B', 'E') 30 | graph.add_edge('C', 'F') 31 | graph.add_edge('C', 'G') 32 | graph.display() 33 | # 输出: 34 | # A -> ['B', 'C'] 35 | # B -> ['D', 'E'] 36 | # C -> ['F', 'G'] 37 | # D -> [] 38 | # E -> [] 39 | # F -> [] 40 | # G -> [] 41 | 42 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xiuhong 8 | algorithms-4th-edition 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 11 13 | 11 14 | 15 | 16 | 17 | 18 | org.apache.commons 19 | commons-math3 20 | 3.6.1 21 | 22 | 23 | 24 | edu.princeton.cs 25 | algs4 26 | 1.0.4 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/04/quick_sort.py: -------------------------------------------------------------------------------- 1 | def partition(arr, low, high): 2 | # 选择第一个元素作为基准 3 | pivot = arr[low] 4 | # 挖坑的初始位置 5 | left = low 6 | right = high 7 | 8 | while left < right: 9 | # 从右边开始找小于基准的元素 10 | while left < right and arr[right] >= pivot: 11 | right -= 1 12 | # 将小于基准的元素填入左边的坑 13 | arr[left] = arr[right] 14 | 15 | # 从左边开始找大于基准的元素 16 | while left < right and arr[left] <= pivot: 17 | left += 1 18 | # 将大于基准的元素填入右边的坑 19 | arr[right] = arr[left] 20 | 21 | # 基准元素放入最后的坑 22 | arr[left] = pivot 23 | # 返回基准元素的索引 24 | return left 25 | 26 | def quicksort(arr, low, high): 27 | if low < high: 28 | # 找到分割点 29 | pivot_index = partition(arr, low, high) 30 | # 递归对分割点左侧部分进行快速排序 31 | quicksort(arr, low, pivot_index - 1) 32 | # 递归对分割点右侧部分进行快速排序 33 | quicksort(arr, pivot_index + 1, high) 34 | 35 | # 测试代码 36 | arr = [5, 2, 9, 1, 7, 6, 3, 8, 4] 37 | print("原始数组:", arr) 38 | quicksort(arr, 0, len(arr) - 1) 39 | print("排序后的数组:", arr) 40 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/10/recommendation_system.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sklearn.neighbors import NearestNeighbors 3 | 4 | # 模拟的数据:5部电影,5位用户 5 | # 每行代表一个用户对5部电影的评分(评分范围1-5,0表示未评分) 6 | ratings = np.array([ 7 | [5, 3, 0, 0, 2], 8 | [0, 4, 4, 3, 1], 9 | [2, 0, 5, 0, 0], 10 | [0, 0, 4, 5, 0], 11 | [3, 2, 0, 2, 3] 12 | ]) 13 | 14 | # 使用KNN找到与当前用户最相似的用户 15 | # 这里选择3个最近邻居 16 | 17 | knn = NearestNeighbors(n_neighbors=3, metric='cosine', algorithm='brute') 18 | 19 | knn.fit(ratings) 20 | 21 | # 假设当前用户的评分是:[3, 0, 5, 0, 3] 22 | current_user_rating = np.array([[3, 0, 5, 0, 3]]) 23 | distances, indices = knn.kneighbors(current_user_rating) 24 | 25 | # 计算平均评分,但要忽略评分为0的部分 26 | neighbors_ratings = ratings[indices][0] # 提取邻居的评分 27 | sum_ratings = np.sum(neighbors_ratings, axis=0) 28 | count_nonzero = np.count_nonzero(neighbors_ratings, axis=0) 29 | 30 | # 避免除以0 31 | mean_ratings = np.divide(sum_ratings, count_nonzero, out=np.zeros_like(sum_ratings, dtype=float), where=count_nonzero != 0) 32 | 33 | # 打印出2号和4号电影(索引1和3)的预测评分 34 | print(f"预测的2号电影评分为: {mean_ratings[1]}") 35 | print(f"预测的4号电影评分为: {mean_ratings[3]}") 36 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/scripts/analysis_of_algorithms/plot_math_model3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import math 4 | 5 | # 定义模型函数 6 | def model1(N, a, b, c): 7 | return a * N**2 + b * N + c 8 | 9 | def model2(N, a, b, c): 10 | return a * N**2 * np.log(N) + b * N * np.log(N) + c 11 | 12 | # 定义参数 13 | a1 = 1.318221890610448E-9 14 | b1 = 1.085747925662513E-6 15 | c1 = 0.004204166666666693 16 | 17 | a2 = 1.961900952491261e-09 18 | b2 = 2.4380254620244043e-06 19 | c2 = -0.034854130547573775 20 | 21 | # 生成x数据 22 | x_data = np.linspace(1000, 32000, 400) 23 | # 计算y数据 24 | y_data1 = model1(x_data, a1, b1, c1) 25 | y_data2 = model2(x_data, a2, b2, c2) 26 | 27 | # 绘图 28 | plt.plot(x_data, y_data1, label='Model 1') 29 | plt.scatter([1000, 2000, 4000, 8000, 16000, 32000], [0.005, 0.012, 0.031, 0.098, 0.358, 1.389], color='red', marker='o', label='Data 1') 30 | 31 | plt.plot(x_data, y_data2, label='Model 2') 32 | plt.scatter([1000, 2000, 4000, 8000, 16000, 32000], [0.015, 0.064, 0.296, 1.238, 5.228, 21.611], color='blue', marker='x', label='Data 2') 33 | 34 | plt.xlabel('N') 35 | plt.ylabel('Time (s)') 36 | plt.legend() 37 | plt.show() 38 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/simple_way_solve_set_covering_problem.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | 3 | # 定义全集,表示需要覆盖的长江经济带所有省份 4 | provinces_needed = set(["上海", "江苏", "浙江", "安徽", 5 | "江西", "湖南", "湖北", "重庆", 6 | "四川", "云南", "贵州"]) 7 | 8 | # 定义子集,表示每个广播台可以覆盖的省份 9 | stations = {} 10 | 11 | stations["一台"] = set(["上海", "江苏", "浙江"]) 12 | stations["二台"] = set(["安徽", "江西", "湖南"]) 13 | stations["三台"] = set(["湖北", "重庆", "四川"]) 14 | stations["四台"] = set(["云南", "贵州"]) 15 | stations["五台"] = set(["湖南", "湖北", "重庆"]) 16 | 17 | # 存储最终选择的广播台 18 | final_stations = set() 19 | # 存储最小广播台数量 20 | min_length = float('inf') 21 | 22 | # 遍历所有可能的组合 23 | for i in range(1, len(stations) + 1): 24 | for subset in combinations(stations, i): 25 | provinces_covered = set([province for station in subset for province in stations[station]]) 26 | # 如果这个组合可以覆盖所有的省份,且数量小于当前最小数量,则更新最小数量和最终选择的广播台 27 | if provinces_needed.issubset(provinces_covered) and len(subset) < min_length: 28 | min_length = len(subset) 29 | final_stations = subset 30 | 31 | print(final_stations) 32 | 33 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/ThreeSumFast.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.util.Arrays; 4 | 5 | import edu.princeton.cs.algs4.BinarySearch; 6 | import edu.princeton.cs.algs4.In; 7 | import edu.princeton.cs.algs4.StdOut; 8 | import edu.princeton.cs.algs4.Stopwatch; 9 | 10 | public class ThreeSumFast { 11 | 12 | // 不要实例化。 13 | private ThreeSumFast() { } 14 | 15 | 16 | /** 17 | * 第一次优化:时间复杂度 O(N^2logN) 18 | */ 19 | 20 | public static int count(int[] a) { 21 | Arrays.sort(a); 22 | int N = a.length; 23 | int cnt = 0; 24 | for (int i = 0; i < N; i++) 25 | for (int j = i + 1; j < N; j++) 26 | if (BinarySearch.rank(-a[i] - a[j], a) > j) 27 | cnt++; 28 | return cnt; 29 | } 30 | 31 | public static void main(String[] args) { 32 | In in = new In(args[0]); 33 | int[] a = in.readAllInts(); 34 | 35 | Stopwatch timer = new Stopwatch(); 36 | int count = count(a); 37 | StdOut.println("耗时 = " + timer.elapsedTime()); 38 | StdOut.println(count); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/knapsack.py: -------------------------------------------------------------------------------- 1 | def knapsack(W, weights, values, n): 2 | """ 3 | 解决0-1背包问题的动态规划函数 4 | :param W: 背包的总容量 5 | :param weights: 每个物品的重量列表 6 | :param values: 每个物品的价值列表 7 | :param n: 物品的数量 8 | :return: 背包能装的最大价值 9 | """ 10 | # 构造DP表,行数为物品数量+1,列数为背包容量+1 11 | K = [[0 for w in range(W + 1)] for i in range(n + 1)] 12 | # print(K) 13 | 14 | # 逐行填充DP表 15 | for i in range(n + 1): 16 | for w in range(W + 1): 17 | if i == 0 or w == 0: 18 | K[i][w] = 0 # 填充基础情况 19 | elif weights[i-1] <= w: 20 | # 当前物品可以装入背包时,考虑装入和不装入两种情况 21 | K[i][w] = max(values[i-1] + K[i-1][w-weights[i-1]], K[i-1][w]) 22 | else: 23 | # 当前物品不能装入背包时,只能选择不装入 24 | K[i][w] = K[i-1][w] 25 | 26 | # DP表的最后一个元素即为问题的最优解 27 | return K[n][W] 28 | 29 | # 物品的重量 30 | weights = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] 31 | # 物品的价值 32 | values = [60, 100, 120, 130, 150, 200, 220, 250, 300, 350] 33 | # 物品的数量 34 | n = len(values) 35 | # 背包的容量 36 | W = 250 37 | 38 | print(knapsack(W, weights, values, n)) # 输出背包能装的最大价值 39 | 40 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/FibonacciLoop.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | public class FibonacciLoop { 4 | 5 | /** 6 | * 使用直接迭代的方法计算斐波那契数 7 | * 8 | * 空间复杂度:O(1) - 因为我们只需要存储前两个斐波那契数 9 | * 10 | * @param n 要计算的斐波那契数的位置 11 | * @return 第n个斐波那契数 12 | */ 13 | public static int fibonacci(int n) { 14 | if (n <= 1) return n; // 当n为0或1时,直接返回n 15 | 16 | int prev2 = 0; // 存储n-2位置的斐波那契数 17 | int prev1 = 1; // 存储n-1位置的斐波那契数 18 | int result = 0; // 存储计算结果 19 | 20 | for (int i = 2; i <= n; i++) { 21 | result = prev1 + prev2; // 当前斐波那契数是前两个数的和 22 | prev2 = prev1; // 更新n-2位置的值 23 | prev1 = result; // 更新n-1位置的值 24 | } 25 | 26 | return result; 27 | } 28 | 29 | public static void main(String[] args) { 30 | // 测试用例 31 | System.out.println(fibonacci(0)); // 输出: 0 32 | System.out.println(fibonacci(1)); // 输出: 1 33 | System.out.println(fibonacci(5)); // 输出: 5 34 | System.out.println(fibonacci(10)); // 输出: 55 35 | System.out.println(fibonacci(50)); // 输出: 12586269025 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/04/merge_sort.py: -------------------------------------------------------------------------------- 1 | def merge_sort(arr): 2 | """ 3 | 归并排序函数,输入一个数组,返回排序后的新数组。 4 | """ 5 | if len(arr) <= 1: 6 | return arr 7 | 8 | # 将数组分为两部分,分别进行递归排序 9 | mid = len(arr) // 2 10 | left_arr = merge_sort(arr[:mid]) 11 | right_arr = merge_sort(arr[mid:]) 12 | 13 | # 合并两个有序子数组 14 | return merge(left_arr, right_arr) 15 | 16 | def merge(left, right): 17 | """ 18 | 合并两个有序数组的函数,输入两个有序数组,返回一个新的有序数组。 19 | """ 20 | result = [] 21 | i = j = 0 22 | while i < len(left) and j < len(right): 23 | if left[i] <= right[j]: 24 | result.append(left[i]) 25 | i += 1 26 | else: 27 | result.append(right[j]) 28 | j += 1 29 | result += left[i:] 30 | result += right[j:] 31 | return result 32 | 33 | 34 | # 测试用例1:常规情况 35 | test_arr1 = [8, 4, 6, 2, 1, 9, 7, 5, 3] 36 | print(merge_sort(test_arr1)) # 输出结果为 [1, 2, 3, 4, 5, 6, 7, 8, 9] 37 | 38 | # 测试用例2:特殊情况-空数组 39 | test_arr2 = [] 40 | print(merge_sort(test_arr2)) # 输出结果为 [] 41 | 42 | # 测试用例3:特殊情况-输入数组为空或只含有一个元素 43 | test_arr3 = [9] 44 | print(merge_sort(test_arr3)) # 输出结果为 [9] 45 | 46 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/knapsack_transposed.py: -------------------------------------------------------------------------------- 1 | def knapsack_transposed(W, weights, values, n): 2 | """ 3 | 解决0-1背包问题的动态规划函数(颠倒DP表的横纵轴) 4 | :param W: 背包的总容量 5 | :param weights: 每个物品的重量列表 6 | :param values: 每个物品的价值列表 7 | :param n: 物品的数量 8 | :return: 背包能装的最大价值 9 | """ 10 | # 构造DP表,行数为背包容量+1,列数为物品数量+1 11 | K = [[0 for i in range(n + 1)] for w in range(W + 1)] 12 | 13 | # 逐行填充DP表 14 | for w in range(W + 1): 15 | for i in range(n + 1): 16 | if i == 0 or w == 0: 17 | K[w][i] = 0 # 填充基础情况 18 | elif weights[i-1] <= w: 19 | # 当前物品可以装入背包时,考虑装入和不装入两种情况 20 | K[w][i] = max(values[i-1] + K[w-weights[i-1]][i-1], K[w][i-1]) 21 | else: 22 | # 当前物品不能装入背包时,只能选择不装入 23 | K[w][i] = K[w][i-1] 24 | 25 | # DP表的最后一个元素即为问题的最优解 26 | return K[W][n] 27 | 28 | # 物品的重量 29 | weights = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] 30 | # 物品的价值 31 | values = [60, 100, 120, 130, 150, 200, 220, 250, 300, 350] 32 | # 物品的数量 33 | n = len(values) 34 | # 背包的容量 35 | W = 250 36 | 37 | # 输出背包能装的最大价值 38 | print(knapsack_transposed(W, weights, values, n)) 39 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graph/WeightedGraph.py: -------------------------------------------------------------------------------- 1 | class WeightedGraph: 2 | def __init__(self, vertices): 3 | # 初始化图,参数为顶点列表 4 | self.graph = {vertex: {} for vertex in vertices} 5 | 6 | def add_edge(self, src, dest, weight): 7 | # 添加边,参数为源节点,目标节点和权重 8 | # 因为是无向图,所以需要在源节点和目标节点的邻接列表中都添加对方节点和对应的权重 9 | if src in self.graph and dest in self.graph: 10 | self.graph[src][dest] = weight 11 | self.graph[dest][src] = weight 12 | else: 13 | raise ValueError("源节点或目标节点不在图中") 14 | 15 | def display(self): 16 | # 输出图的内容 17 | for vertex, edges in self.graph.items(): 18 | print(vertex, "->", edges) 19 | 20 | # 测试用例 21 | vertices = ['A', 'B', 'C', 'D', 'E'] 22 | 23 | graph = WeightedGraph(vertices) 24 | graph.add_edge('A', 'B', 10) 25 | graph.add_edge('A', 'C', 20) 26 | graph.add_edge('B', 'D', 30) 27 | graph.add_edge('B', 'E', 40) 28 | graph.add_edge('C', 'D', 50) 29 | graph.add_edge('D', 'E', 60) 30 | graph.display() 31 | # 输出: 32 | # A -> {'B': 10, 'C': 20} 33 | # B -> {'A': 10, 'D': 30, 'E': 40} 34 | # C -> {'A': 20, 'D': 50} 35 | # D -> {'B': 30, 'C': 50, 'E': 60} 36 | # E -> {'B': 40, 'D': 60} 37 | 38 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/coinChange_2D.py: -------------------------------------------------------------------------------- 1 | def coinChange_2D(coins, amount): 2 | """ 3 | 使用二维DP表解决硬币找零问题,并记录选择的硬币 4 | :param coins: 可用的硬币面额列表 5 | :param amount: 需要找的总金额 6 | :return: 所需的最少硬币数量和选择的硬币列表 7 | """ 8 | n = len(coins) 9 | # 初始化DP表 10 | dp = [[float('inf') for _ in range(amount + 1)] for _ in range(n + 1)] 11 | for i in range(n + 1): 12 | dp[i][0] = 0 # 金额为0时所需硬币数量为0 13 | 14 | # 更新DP表 15 | for i in range(1, n + 1): 16 | for j in range(1, amount + 1): 17 | # 不选择第i个硬币 18 | dp[i][j] = dp[i-1][j] 19 | # 选择第i个硬币,条件是金额不小于硬币面额 20 | if j >= coins[i-1]: 21 | dp[i][j] = min(dp[i][j], dp[i][j-coins[i-1]] + 1) 22 | 23 | # 回溯找到选择的硬币 24 | coin_selection = [] 25 | j = amount 26 | for i in range(n, 0, -1): 27 | while j >= coins[i-1] and dp[i][j] == dp[i][j-coins[i-1]] + 1: 28 | coin_selection.append(coins[i-1]) 29 | j -= coins[i-1] 30 | 31 | if dp[n][amount] == float('inf'): 32 | return -1, [] 33 | return dp[n][amount], coin_selection 34 | 35 | coins = [3, 5, 7, 11] 36 | amount = 28 37 | 38 | coinChange_2D(coins, amount) 39 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/01/binary_search.py: -------------------------------------------------------------------------------- 1 | # 定义二分查找函数 2 | def binary_search(arr, target): 3 | """ 4 | 二分查找算法 5 | 6 | 参数: 7 | arr: 一个已排序的列表 8 | target: 要查找的目标值 9 | 10 | 返回: 11 | 目标值在列表中的索引,如果没有找到则返回-1 12 | """ 13 | # 初始化左指针和右指针 14 | left = 0 15 | right = len(arr) - 1 16 | 17 | # 当左指针小于或等于右指针时,执行循环 18 | while left <= right: 19 | # 计算中间索引 20 | mid = (left + right) // 2 21 | 22 | # 如果中间元素等于目标值,则返回中间索引 23 | if arr[mid] == target: 24 | return mid 25 | # 如果中间元素小于目标值,调整左指针到mid+1 26 | elif arr[mid] < target: 27 | left = mid + 1 28 | # 如果中间元素大于目标值,调整右指针到mid-1 29 | else: 30 | right = mid - 1 31 | 32 | # 如果没有找到目标值,返回-1 33 | return -1 34 | 35 | # 测试用例 36 | if __name__ == '__main__': 37 | # 已排序的列表 38 | test_arr = [1, 3, 5, 7, 9, 11, 13, 15, 17] 39 | 40 | # 测试查找存在的元素 41 | assert binary_search(test_arr, 7) == 3 42 | 43 | # 测试查找不存在的元素 44 | assert binary_search(test_arr, 8) == -1 45 | 46 | # 测试查找列表首元素 47 | assert binary_search(test_arr, 1) == 0 48 | 49 | # 测试查找列表尾元素 50 | assert binary_search(test_arr, 17) == 8 51 | 52 | print("所有测试用例通过!") 53 | 54 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapBottomUp.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Arrays; 4 | 5 | public class HeapBottomUp { 6 | 7 | // 自底向上建堆方法 8 | public static void heapifyBottomUp(int[] arr) { 9 | int n = arr.length; 10 | // 重点:从第一个非叶子节点开始,从右向左进行"下沉" 11 | for (int i = n / 2 - 1; i >= 0; i--) { 12 | sink(arr, n, i); 13 | } 14 | } 15 | 16 | // 下沉操作 17 | private static void sink(int[] arr, int n, int k) { 18 | while (2 * k + 1 < n) { 19 | int j = 2 * k + 1; 20 | if (j < n - 1 && arr[j] < arr[j + 1]) j++; 21 | if (arr[k] >= arr[j]) break; 22 | swap(arr, k, j); 23 | k = j; 24 | } 25 | } 26 | 27 | // 交换数组中的两个元素 28 | private static void swap(int[] arr, int i, int j) { 29 | int temp = arr[i]; 30 | arr[i] = arr[j]; 31 | arr[j] = temp; 32 | } 33 | 34 | public static void main(String[] args) { 35 | int[] arrTopDown = {19, 100, 1, 8, 3, 25, 4, 36, 17}; 36 | int[] arrBottomUp = arrTopDown.clone(); 37 | 38 | HeapBottomUp.heapifyBottomUp(arrBottomUp); 39 | 40 | System.out.println("Heap built Bottom-Up: " + Arrays.toString(arrBottomUp)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/knapsack_dp.py: -------------------------------------------------------------------------------- 1 | def knapsack(W, weights, values, n): 2 | """ 3 | 解决0-1背包问题的动态规划函数 4 | :param W: 背包的总容量 5 | :param weights: 每个物品的重量列表 6 | :param values: 每个物品的价值列表 7 | :param n: 物品的数量 8 | :return: 背包能装的最大价值 9 | """ 10 | # 构造DP表,行数为物品数量+1,列数为背包容量+1 11 | K = [[0 for w in range(W + 1)] for i in range(n + 1)] 12 | # print(K) 13 | 14 | # 逐行填充DP表 15 | for i in range(n + 1): 16 | for w in range(W + 1): 17 | if i == 0 or w == 0: 18 | K[i][w] = 0 # 填充基础情况 19 | elif weights[i-1] <= w: 20 | # 当前物品可以装入背包时,考虑装入和不装入两种情况 21 | K[i][w] = max(values[i-1] + K[i-1][w-weights[i-1]], K[i-1][w]) 22 | print(K) 23 | else: 24 | # 当前物品不能装入背包时,只能选择不装入 25 | K[i][w] = K[i-1][w] 26 | 27 | # DP表的最后一个元素即为问题的最优解 28 | return K[n][W] 29 | 30 | # 物品的重量 31 | # weights = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] 32 | weights = [2, 5, 7] 33 | # 物品的价值 34 | # values = [60, 100, 120, 130, 150, 200, 220, 250, 300, 350] 35 | values = [5000, 8000, 7000] 36 | # 物品的数量 37 | n = len(values) 38 | # 背包的容量 39 | # W = 250 40 | W = 23 41 | 42 | print(knapsack(W, weights, values, n)) # 输出背包能装的最大价值 43 | 44 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/PriorityQueue.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | public interface PriorityQueue { 4 | 5 | /** 6 | * 向优先队列中插入一个元素。 7 | * 8 | * @param item 要插入的元素 9 | */ 10 | void insert(T item); 11 | 12 | /** 13 | * 返回但不移除优先队列中的最大元素。 14 | * 15 | * @return 优先队列中的最大元素 16 | */ 17 | T max(); 18 | 19 | /** 20 | * 返回但不移除优先队列中的最小元素。 21 | * 22 | * @return 优先队列中的最小元素 23 | */ 24 | T min(); 25 | 26 | /** 27 | * 移除并返回优先队列中的最大元素。 28 | * 29 | * @return 优先队列中被移除的最大元素 30 | */ 31 | T extractMax(); 32 | 33 | /** 34 | * 移除并返回优先队列中的最小元素。 35 | * 36 | * @return 优先队列中被移除的最小元素 37 | */ 38 | T extractMin(); 39 | 40 | /** 41 | * 返回优先队列中的元素个数。 42 | * 43 | * @return 优先队列的大小 44 | */ 45 | int size(); 46 | 47 | /** 48 | * 判断优先队列是否为空。 49 | * 50 | * @return 如果优先队列为空,返回true;否则返回false。 51 | */ 52 | boolean isEmpty(); 53 | 54 | /** 55 | * 将当前的优先队列与另一个优先队列合并。 56 | * 57 | * @param other 另一个要合并的优先队列 58 | */ 59 | void merge(PriorityQueue other); 60 | 61 | /** 62 | * 清空优先队列的所有元素。 63 | */ 64 | void clear(); 65 | 66 | // 依据实际需要,还可以继续扩充其他方法。 67 | } 68 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/OrderedSymbolTable.java: -------------------------------------------------------------------------------- 1 | package symbol_table; 2 | 3 | public interface OrderedSymbolTable, Value> { 4 | 5 | void put(Key key, Value val); // 插入键值对,如果值为空则删除键 6 | 7 | Value get(Key key); // 获取键对应的值 8 | 9 | void delete(Key key); // 删除键(及其对应的值) 10 | 11 | boolean contains(Key key); // 检查表中是否含有该键 12 | 13 | boolean isEmpty(); // 检查表是否为空 14 | 15 | int size(); // 获取表中键值对的数量 16 | 17 | Key min(); // 获取表中最小的键 18 | 19 | Key max(); // 获取表中最大的键 20 | 21 | Key floor(Key key); // 获取小于等于指定键的最大键 22 | 23 | Key ceiling(Key key); // 获取大于等于指定键的最小键 24 | 25 | int rank(Key key); // 获取小于指定键的键的数量 26 | 27 | Key select(int k); // 获取排名为k的键 28 | 29 | Iterable keys(Key lo, Key hi); // 获取指定范围内的所有键,已排序 30 | 31 | // 默认方法实现 32 | default void deleteMin() { 33 | delete(min()); 34 | } 35 | 36 | default void deleteMax() { 37 | delete(max()); 38 | } 39 | 40 | default int size(Key lo, Key hi) { 41 | if (hi.compareTo(lo) < 0) return 0; 42 | else if (contains(hi)) return rank(hi) - rank(lo) + 1; 43 | else return rank(hi) - rank(lo); 44 | } 45 | 46 | default Iterable keys() { 47 | return keys(min(), max()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/classroom_scheduling.py: -------------------------------------------------------------------------------- 1 | courses = [ 2 | {'name': 'Algorithms', 'start': 9, 'end': 11 }, 3 | {'name': 'Operating Systems', 'start': 11, 'end': 13 }, 4 | {'name': 'Computer Architecture', 'start': 8, 'end': 10 }, 5 | {'name': 'Software Engineering', 'start': 10, 'end': 12 }, 6 | {'name': 'Database Systems', 'start': 13, 'end': 15 }, 7 | {'name': 'Artificial Intelligence', 'start': 15, 'end': 17 }, 8 | {'name': 'Networks', 'start': 14, 'end': 16 }, 9 | {'name': 'Cryptography', 'start': 16, 'end': 18 }, 10 | {'name': 'Machine Learning', 'start': 17, 'end': 19 }, 11 | {'name': 'Data Science', 'start': 12, 'end': 14 }, 12 | ] 13 | 14 | def schedule(courses): 15 | # 按照结束时间对课程排序 16 | sorted_courses = sorted(courses, key=lambda x: x['end']) 17 | # 初始化结果集 18 | result = [sorted_courses[0]] 19 | for i in range(1, len(sorted_courses)): 20 | # 如果课程的开始时间不冲突,就把它加入结果集 21 | if sorted_courses[i]['start'] >= result[-1]['end']: 22 | result.append(sorted_courses[i]) 23 | return result 24 | 25 | result = schedule(courses) 26 | for course in result: 27 | print(course['name'], course['start'], course['end']) -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/longestCommonSubsequence_with_path.py: -------------------------------------------------------------------------------- 1 | def longestCommonSubsequence_with_path(X, Y): 2 | """ 3 | 计算两个序列X和Y的最长公共子序列,并返回子序列 4 | :param X: 序列X 5 | :param Y: 序列Y 6 | :return: 最长公共子序列的长度和子序列 7 | """ 8 | m, n = len(X), len(Y) 9 | 10 | # 初始化dp表,记录每个子问题的最长公共子序列长度 11 | dp = [[0] * (n + 1) for _ in range(m + 1)] 12 | 13 | # 逐个填充dp表的值 14 | for i in range(1, m + 1): 15 | for j in range(1, n + 1): 16 | # 当前字符匹配 17 | if X[i - 1] == Y[j - 1]: 18 | dp[i][j] = dp[i - 1][j - 1] + 1 19 | else: 20 | # 当前字符不匹配,取上方或左方的较大值 21 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) 22 | 23 | # 回溯,构造最长公共子序列 24 | i, j = m, n 25 | lcs = [] 26 | while i > 0 and j > 0: 27 | # 当前字符匹配 28 | if X[i - 1] == Y[j - 1]: 29 | lcs.append(X[i - 1]) 30 | i -= 1 31 | j -= 1 32 | # 否则,移动到值较大的方向(上方或左方) 33 | elif dp[i - 1][j] > dp[i][j - 1]: 34 | i -= 1 35 | else: 36 | j -= 1 37 | lcs = lcs[::-1] # 反转,得到正确的序列 38 | 39 | return dp[m][n], ''.join(lcs) 40 | 41 | X = "ABCBDAB" 42 | Y = "BDCAB" 43 | 44 | longestCommonSubsequence_with_path(X, Y) 45 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/bfs/simpleQueue.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | def __init__(self): 3 | """ 4 | 初始化一个空队列 5 | """ 6 | self.items = [] 7 | 8 | def isEmpty(self): 9 | """ 10 | 检查队列是否为空 11 | 返回True,如果队列为空 12 | """ 13 | return self.items == [] 14 | 15 | def enqueue(self, item): 16 | """ 17 | 把一个元素添加到队尾 18 | """ 19 | self.items.append(item) 20 | 21 | def dequeue(self): 22 | """ 23 | 从队首移除一个元素 24 | 返回被移除的元素 25 | """ 26 | if self.isEmpty(): 27 | raise Exception("队列为空,不能执行出队操作") 28 | return self.items.pop(0) 29 | 30 | def size(self): 31 | """ 32 | 返回队列中元素的数量 33 | """ 34 | return len(self.items) 35 | 36 | # 创建一个队列对象 37 | q = Queue() 38 | 39 | # 检查新创建的队列是否为空 40 | print(q.isEmpty()) # 输出: True 41 | 42 | # 把一些元素加入到队列中 43 | q.enqueue(1) 44 | q.enqueue(2) 45 | q.enqueue(3) 46 | 47 | # 检查队列是否为空 48 | print(q.isEmpty()) # 输出: False 49 | 50 | # 查看队列的大小 51 | print(q.size()) # 输出: 3 52 | 53 | # 执行一次出队操作,并打印出队的元素 54 | print(q.dequeue()) # 输出: 1 55 | 56 | # 查看队列的大小 57 | print(q.size()) # 输出: 2 58 | 59 | # 执行一次出队操作,并打印出队的元素 60 | print(q.dequeue()) # 输出: 2 61 | 62 | # 查看队列的大小 63 | print(q.size()) # 输出: 1 64 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/08/greedy_algorithm_solve_set_covering_problem.py: -------------------------------------------------------------------------------- 1 | # 定义全集,表示需要覆盖的所有省份 2 | provinces_needed = set((["上海", "江苏", "浙江", "安徽", 3 | "江西", "湖南", "湖北", "重庆", 4 | "四川", "云南", "贵州"])) 5 | 6 | # 定义子集,表示每个广播台可以覆盖的省份 7 | stations = {} 8 | stations["一台"] = set(["上海", "江苏", "浙江"]) 9 | stations["二台"] = set(["安徽", "江西", "湖南"]) 10 | stations["三台"] = set(["湖北", "重庆", "四川"]) 11 | stations["四台"] = set(["云南", "贵州"]) 12 | stations["五台"] = set(["湖南", "湖北", "重庆"]) 13 | 14 | # 存储最终选择的广播台 15 | final_stations = set() 16 | 17 | # 使用贪心算法选择覆盖省份 18 | while provinces_needed: 19 | # 初始化最佳广播台和覆盖省份集合 20 | best_station = None 21 | states_covered = set() 22 | 23 | # 遍历所有广播台,选择覆盖未覆盖省份最多的广播台 24 | for station, provinces in stations.items(): 25 | covered = provinces_needed & provinces 26 | if len(covered) > len(states_covered): 27 | best_station = station 28 | states_covered = covered 29 | 30 | # 打印每次选择的广播台和覆盖的省份 31 | print("选择的广播台:", best_station) 32 | print("覆盖的省份:", states_covered) 33 | print("==============") 34 | 35 | # 将已覆盖的省份从未覆盖省份集合中移除 36 | provinces_needed -= states_covered 37 | # 将选中的广播台加入最终结果集 38 | final_stations.add(best_station) 39 | 40 | print("最终选择的广播台集合:", final_stations) 41 | 42 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/ThreeSumFinal.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.util.Arrays; 4 | import edu.princeton.cs.algs4.In; 5 | import edu.princeton.cs.algs4.StdOut; 6 | import edu.princeton.cs.algs4.Stopwatch; 7 | 8 | public class ThreeSumFinal { 9 | 10 | // 不要实例化。 11 | private ThreeSumFinal() { } 12 | 13 | 14 | /** 15 | * 第二次优化:采用双指针技术 16 | * 时间复杂度:O(N^2) 17 | */ 18 | public static long count(int[] a) { 19 | Arrays.sort(a); 20 | int N = a.length; 21 | long cnt = 0; 22 | for (int i = 0; i < N - 2; i++) { 23 | int start = i + 1; 24 | int end = N - 1; 25 | while (start < end) { 26 | if (a[i] + a[start] + a[end] == 0) { 27 | cnt++; 28 | start++; 29 | end--; 30 | } else if (a[i] + a[start] + a[end] < 0) { 31 | start++; 32 | } else { 33 | end--; 34 | } 35 | } 36 | } 37 | return cnt; 38 | } 39 | 40 | public static void main(String[] args) { 41 | In in = new In(args[0]); 42 | int[] a = in.readAllInts(); 43 | 44 | Stopwatch timer = new Stopwatch(); 45 | long count = count(a); 46 | StdOut.println("耗时 = " + timer.elapsedTime()); 47 | StdOut.println(count); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/bfs/bfs-search.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | def bfs(graph, start, end): 4 | """ 5 | 使用广度优先搜索寻找从start到end的最短路径。 6 | 返回一个列表,表示最短路径中的节点。 7 | """ 8 | queue = deque([[start]]) 9 | 10 | # 创建一个空集合,存储已经访问过的节点 11 | visited = set() 12 | 13 | while queue: 14 | path = queue.popleft() 15 | # 获取路径上最后一个节点 16 | vertex = path[-1] 17 | 18 | # 如果该节点是目标节点,返回当前路径(即为最短路径) 19 | if vertex == end: 20 | return path 21 | 22 | elif vertex not in visited: 23 | # 遍历该节点的所有邻居节点 24 | for neighbour in graph[vertex]: 25 | 26 | # 对于每一个邻居节点,都将其加到当前路径的尾部,形成一个新的路径 27 | new_path = list(path) 28 | new_path.append(neighbour) 29 | 30 | # 将新的路径添加到队列的右侧 31 | queue.append(new_path) 32 | 33 | # 将当前节点标记为已访问 34 | visited.add(vertex) 35 | 36 | # 用邻接列表表示图 37 | graph = { 38 | "悟空": ["唐僧", "铁扇公主", "牛魔王", "猪八戒"], 39 | "唐僧": ["悟空", "猪八戒"], 40 | "铁扇公主": ["悟空", "牛魔王", "红孩儿"], 41 | "牛魔王": ["悟空", "铁扇公主", "红孩儿"], 42 | "猪八戒": ["悟空", "唐僧"], 43 | "红孩儿": ["铁扇公主", "牛魔王", "小猪妖"], 44 | "小猪妖": ["红孩儿"], 45 | } 46 | 47 | # 测试 48 | path = bfs(graph, "悟空", "小猪妖") 49 | # 注意:最短路径可能不止一条,打印结果只是其中一条 50 | print(" -> ".join(path)) # 输出:悟空 -> 铁扇公主 -> 红孩儿 -> 小猪妖 51 | 52 | 53 | path2 = bfs(graph, "悟空", "悟空") 54 | print(" -> ".join(path2)) 55 | 56 | 57 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graphStore/graph_matrix.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from collections import deque 3 | 4 | def bfs(graph, start, end): 5 | """ 6 | 使用广度优先搜索寻找从start到end的最短路径。 7 | 返回一个列表,表示最短路径中的节点。 8 | """ 9 | queue = deque([[start]]) 10 | visited = set() 11 | 12 | while queue: 13 | path = queue.popleft() 14 | vertex = path[-1] 15 | if vertex == end: 16 | return path 17 | elif vertex not in visited: 18 | for i, val in enumerate(graph[vertex]): 19 | if val == 1: # 如果存在边 20 | new_path = list(path) 21 | new_path.append(i) 22 | queue.append(new_path) 23 | visited.add(vertex) 24 | 25 | 26 | 27 | # 用邻接矩阵表示图。我们需要按顺序将所有的节点映射到整数值。 28 | # 这里是顺序映射:悟空:0, 唐僧:1, 铁扇公主:2, 牛魔王:3, 猪八戒:4, 红孩儿:5, 小猪妖:6 29 | graph = np.array([ 30 | [0, 1, 1, 1, 1, 0, 0], # 悟空 31 | [1, 0, 0, 0, 1, 0, 0], # 唐僧 32 | [1, 0, 0, 1, 0, 1, 0], # 铁扇公主 33 | [1, 0, 1, 0, 0, 1, 0], # 牛魔王 34 | [1, 1, 0, 0, 0, 0, 0], # 猪八戒 35 | [0, 0, 1, 1, 0, 0, 1], # 红孩儿 36 | [0, 0, 0, 0, 0, 1, 0] # 小猪妖 37 | ]) 38 | 39 | 40 | 41 | # 测试 42 | path = bfs(graph, 0, 6) # 0代表"悟空",6代表"小猪妖" 43 | print(" -> ".join(map(str, path))) # 输出应该是 0 -> 2 -> 3 -> 5 -> 6 44 | 45 | # 在实际应用中,我们可能希望将节点的数字编号转回对应的实际名称,这可以通过简单的列表或字典映射实现 46 | nodes = ["悟空", "唐僧", "铁扇公主", "牛魔王", "猪八戒", "红孩儿", "小猪妖"] 47 | path_names = [nodes[i] for i in path] 48 | print(" -> ".join(path_names)) # 输出应该是 悟空 -> 铁扇公主 -> 牛魔王 -> 红孩儿 -> 小猪妖 49 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/07/dijkstra.py: -------------------------------------------------------------------------------- 1 | # 只计算最短路径,不保存最短路径 2 | 3 | import heapq 4 | 5 | def dijkstra(graph, start): 6 | # 创建一个字典,用于储存每个节点的最短距离,初始值设为无穷大 7 | shortest_distances = {node: float('infinity') for node in graph} 8 | # 将起点的最短距离设为0 9 | shortest_distances[start] = 0 10 | # 创建一个空的优先级队列,并将起点添加进去 11 | heap = [(0, start)] 12 | 13 | while heap: 14 | # 获取当前距离最短的节点 15 | current_distance, current_node = heapq.heappop(heap) 16 | 17 | # 如果当前节点的距离已经更新过,就跳过 18 | if current_distance > shortest_distances[current_node]: 19 | continue 20 | 21 | # 遍历当前节点的邻居节点 22 | for neighbor, distance in graph[current_node].items(): 23 | # 计算经过当前节点到达邻居节点的距离 24 | new_distance = current_distance + distance 25 | 26 | # 如果经过当前节点到达邻居节点的距离比之前计算的距离还要短,就更新最短距离 27 | if new_distance < shortest_distances[neighbor]: 28 | shortest_distances[neighbor] = new_distance 29 | heapq.heappush(heap, (new_distance, neighbor)) 30 | 31 | return shortest_distances 32 | 33 | # 定义图 34 | graph = { 35 | 0: {1: 30, 7: 52}, 36 | 1: {0: 30, 7: 35, 2: 60}, 37 | 2: {1: 60, 8: 10, 3: 74, 5: 130}, 38 | 3: {2: 74, 5: 55, 4: 108}, 39 | 4: {3: 108, 5: 50}, 40 | 5: {2: 130, 3: 55, 4: 50, 6: 110}, 41 | 6: {5: 110, 7: 57, 8: 10}, 42 | 7: {0: 52, 1: 35, 8: 48, 6: 57}, 43 | 8: {2: 10, 6: 10, 7: 48} 44 | } 45 | 46 | # 调用函数,计算从节点0到其他所有节点的最短路径 47 | shortest_distances = dijkstra(graph, 0) 48 | print(shortest_distances) 49 | 50 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/04/quick_sort_loop.py: -------------------------------------------------------------------------------- 1 | def quicksort(arr): 2 | """ 3 | 使用循环实现快速排序 4 | :param arr: 待排序的数组 5 | :return: 排序后的数组 6 | """ 7 | if len(arr) <= 1: # 如果数组为空或只有一个元素,直接返回 8 | return arr 9 | stack = [(0, len(arr) - 1)] # 用栈来记录需要排序的子数组的起始和终止索引 10 | while stack: 11 | start, end = stack.pop() # 取出一个需要排序的子数组 12 | if start >= end: # 如果子数组中只有一个元素,或者已经按照pivot分好区了,跳过这个子数组 13 | continue 14 | pivot_index = partition(arr, start, end) # 分区 15 | stack.append((start, pivot_index - 1)) # 将左半部分的子数组需要排序的区间入栈 16 | stack.append((pivot_index + 1, end)) # 将右半部分的子数组需要排序的区间入栈 17 | return arr 18 | 19 | 20 | def partition(arr, start, end): 21 | """ 22 | 对arr[start:end+1]进行分区,返回pivot的索引 23 | :param arr: 待分区的数组 24 | :param start: 子数组的起始索引 25 | :param end: 子数组的终止索引 26 | :return: pivot的索引 27 | """ 28 | pivot = arr[end] # 取最后一个元素作为pivot 29 | i = start - 1 # i指向小于pivot区间的末尾 30 | for j in range(start, end): # 遍历整个子数组(除了pivot) 31 | if arr[j] < pivot: # 如果当前元素小于pivot,就把它和小于pivot区间的下一个元素交换 32 | i += 1 33 | arr[i], arr[j] = arr[j], arr[i] 34 | arr[i + 1], arr[end] = arr[end], arr[i + 1] # 将pivot放到正确的位置上 35 | return i + 1 36 | 37 | 38 | # 测试用例 39 | assert quicksort([]) == [] # 空数组 40 | assert quicksort([1]) == [1] # 只有一个元素的数组 41 | assert quicksort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] # 逆序数组 42 | assert quicksort(list(range(10))) == list(range(10)) # 有序数组 43 | assert quicksort([4, 2, 2, 8, 3, 3, 1, 6, 7, 5]) == [1, 2, 2, 3, 3, 4, 5, 6, 7, 8] # 包含重复元素的数组 44 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/DatabaseQueryComparison.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | public class DatabaseQueryComparison { 4 | 5 | // 模拟大常数延迟的一次性查询 6 | public static void batchQuery(int dataSize) throws InterruptedException { 7 | int bigConstantDelay = 2000; // 假设2秒的网络延迟 8 | Thread.sleep(bigConstantDelay); // 模拟网络延迟 9 | for (int i = 0; i < dataSize; i++) { 10 | // 处理数据 11 | } 12 | } 13 | 14 | // 模拟小常数延迟的多次查询 15 | public static void multipleSmallQueries(int querySize, int queryTimes) throws InterruptedException { 16 | int smallConstantDelay = 100; // 假设0.1秒的网络延迟 17 | for (int i = 0; i < queryTimes; i++) { 18 | Thread.sleep(smallConstantDelay); // 模拟网络延迟 19 | for (int j = 0; j < querySize; j++) { 20 | // 处理数据 21 | } 22 | } 23 | } 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | int dataSize = 10000; 27 | int querySize = 1000; 28 | int queryTimes = 10; // 确保 querySize * queryTimes = dataSize 29 | 30 | long start = System.currentTimeMillis(); 31 | batchQuery(dataSize); 32 | long end = System.currentTimeMillis(); 33 | System.out.println("Batch Query Time: " + (end - start) + " milliseconds"); 34 | 35 | start = System.currentTimeMillis(); 36 | multipleSmallQueries(querySize, queryTimes); 37 | end = System.currentTimeMillis(); 38 | System.out.println("Multiple Small Queries Time: " + (end - start) + " milliseconds"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/ThreeSum.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import edu.princeton.cs.algs4.In; 4 | import edu.princeton.cs.algs4.StdOut; 5 | import edu.princeton.cs.algs4.Stopwatch; 6 | 7 | /** 8 | * {@code ThreeSum} 类提供了用于计数和打印整数数组中求和为0的三元组的静态方法 9 | * (忽略整数溢出)。 10 | * 这个实现使用了三重嵌套循环,时间复杂度与n^3成正比, 11 | * 其中n是整数的数量。 12 | * 有关额外文档,请参见 Section 1.4 13 | */ 14 | public class ThreeSum { 15 | 16 | // 不要实例化。 17 | private ThreeSum() { } 18 | 19 | /** 20 | * 返回满足{@code i < j < k}且{@code a[i] + a[j] + a[k] == 0}的三元组的数量。 21 | * 22 | * @param a 整数数组 23 | * @return 满足{@code i < j < k}且{@code a[i] + a[j] + a[k] == 0}的三元组的数量 24 | */ 25 | public static int count(int[] a) { 26 | int n = a.length; 27 | int count = 0; 28 | for (int i = 0; i < n; i++) { 29 | for (int j = i+1; j < n; j++) { 30 | for (int k = j+1; k < n; k++) { 31 | if (a[i] + a[j] + a[k] == 0) { 32 | count++; 33 | } 34 | } 35 | } 36 | } 37 | return count; 38 | } 39 | 40 | /** 41 | * 从文件中读取一系列整数,文件是作为命令行参数指定的; 42 | * 计算三个整数之和恰好为零的数量;打印出执行计算所需的时间。 43 | * 44 | * @param args 命令行参数 45 | */ 46 | public static void main(String[] args) { 47 | In in = new In(args[0]); 48 | int[] a = in.readAllInts(); 49 | 50 | Stopwatch timer = new Stopwatch(); 51 | int count = count(a); 52 | StdOut.println("耗时 = " + timer.elapsedTime()); 53 | StdOut.println(count); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graph/graph.py: -------------------------------------------------------------------------------- 1 | class Graph: 2 | def __init__(self): 3 | """ 4 | 初始化图对象,使用一个空字典。 5 | 字典中的每个键代表图中的一个顶点, 6 | 对应的值是这个顶点的邻居列表。 7 | """ 8 | self.graph_dict = {} 9 | 10 | def add_vertex(self, node): 11 | """ 12 | 如果顶点 "node" 不在字典中,向字典添加键 "node",值为空列表。 13 | 否则,不需要进行任何操作。 14 | """ 15 | if node not in self.graph_dict: 16 | self.graph_dict[node] = [] 17 | 18 | def add_edge(self, node1, node2): 19 | """ 20 | 要在图中添加一条边, 21 | 需要在每个顶点的邻居列表中添加另一个顶点。 22 | """ 23 | self.graph_dict[node1].append(node2) 24 | self.graph_dict[node2].append(node1) 25 | 26 | def show_edges(self): 27 | """ 28 | 此方法返回一个元组列表,每个元组代表图中的一条边。 29 | 元组的两个元素是这条边连接的两个顶点。 30 | """ 31 | edges = [] 32 | for node in self.graph_dict: 33 | for neighbour in self.graph_dict[node]: 34 | if {neighbour, node} not in edges: 35 | edges.append({node, neighbour}) 36 | return edges 37 | 38 | 39 | # 初始化图 40 | 41 | G = Graph() 42 | 43 | # 添加节点 44 | G.add_vertex("孙悟空") 45 | G.add_vertex("猪八戒") 46 | G.add_vertex("唐僧") 47 | G.add_vertex("牛魔王") 48 | G.add_vertex("铁扇公主") 49 | G.add_vertex("红孩儿") 50 | G.add_vertex("小妖怪") 51 | 52 | # 添加边 53 | G.add_edge("孙悟空", "猪八戒") 54 | G.add_edge("孙悟空", "唐僧") 55 | G.add_edge("孙悟空", "牛魔王") 56 | G.add_edge("孙悟空", "铁扇公主") 57 | G.add_edge("铁扇公主", "红孩儿") 58 | G.add_edge("牛魔王", "红孩儿") 59 | G.add_edge("牛魔王", "铁扇公主") 60 | G.add_edge("猪八戒", "唐僧") 61 | 62 | 63 | print(G.graph_dict) # 输出: {1: [2], 2: [1]} 64 | print(G.show_edges()) # 输出: [{1, 2}] 65 | 66 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/10/distance.py: -------------------------------------------------------------------------------- 1 | 2 | from sklearn.neighbors import NearestNeighbors 3 | from sklearn.decomposition import PCA 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | 8 | 9 | # 模拟的数据:5部电影,5位用户 10 | ratings = np.array([ 11 | [5, 3, 0, 0, 2], 12 | [0, 4, 4, 3, 1], 13 | [2, 0, 5, 0, 0], 14 | [0, 0, 4, 5, 0], 15 | [3, 2, 0, 2, 3] 16 | ]) 17 | 18 | # 使用KNN找到与当前用户最相似的用户 19 | # 这里我们设定为5个最近邻居来得到所有5位用户的距离 20 | knn = NearestNeighbors(n_neighbors=5, metric='cosine', algorithm='brute') 21 | 22 | 23 | knn.fit(ratings) 24 | 25 | # 假设当前用户的评分是:[3, 0, 5, 0, 3] 26 | current_user_rating = np.array([[3, 0, 5, 0, 3]]) 27 | distances, indices = knn.kneighbors(current_user_rating) 28 | 29 | # 打印距离 30 | for i, (index, distance) in enumerate(zip(indices[0], distances[0])): 31 | print(f"当前用户与用户{index}的距离为: {distance:.4f}") 32 | 33 | # 使用PCA将数据降为2维以便进行可视化 34 | pca = PCA(n_components=2) 35 | transformed_ratings = pca.fit_transform(ratings) 36 | transformed_current_user = pca.transform(current_user_rating) 37 | 38 | # 使用matplotlib绘制散点图 39 | plt.figure(figsize=(10, 6)) 40 | 41 | # 绘制所有用户 42 | plt.scatter(transformed_ratings[:, 0], transformed_ratings[:, 1], label='Other Users', s=100) 43 | 44 | # 绘制与当前用户最相似的用户 45 | for index in indices[0]: 46 | plt.scatter(transformed_ratings[index, 0], transformed_ratings[index, 1], label=f'User {index}', s=100) 47 | 48 | # 绘制当前用户 49 | plt.scatter(transformed_current_user[:, 0], transformed_current_user[:, 1], marker='*', color='red', s=200, label='Current User') 50 | 51 | plt.title('Visualization of User Similarities') 52 | plt.xlabel('Principal Component 1') 53 | plt.ylabel('Principal Component 2') 54 | plt.legend() 55 | plt.grid(True) 56 | plt.show() 57 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/05/ChainingHashTable.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, key, value): 3 | self.key = key 4 | self.value = value 5 | self.next = None 6 | 7 | 8 | class ChainingHashTable: 9 | def __init__(self, size): 10 | """ 11 | 初始化散列表的大小和数组 12 | """ 13 | self.size = size 14 | self.hash_table = [None] * size 15 | 16 | def _hash(self, key): 17 | """ 18 | 散列函数,将键转换为散列索引 19 | """ 20 | return key % self.size 21 | 22 | def insert(self, key, value): 23 | """ 24 | 向散列表中插入键值对 25 | """ 26 | index = self._hash(key) # 计算散列索引 27 | if self.hash_table[index] is None: 28 | # 若索引位置为空,则直接插入新节点 29 | self.hash_table[index] = Node(key, value) 30 | else: 31 | # 若索引位置不为空,则遍历链表找到合适位置插入新节点 32 | current = self.hash_table[index] 33 | while current.next: 34 | current = current.next 35 | current.next = Node(key, value) 36 | 37 | def search(self, key): 38 | """ 39 | 在散列表中查找给定键的值 40 | """ 41 | index = self._hash(key) # 计算散列索引 42 | current = self.hash_table[index] 43 | while current: 44 | if current.key == key: 45 | return current.value # 返回键对应的值 46 | current = current.next 47 | return None # 键不存在于散列表中 48 | 49 | # 创建一个大小为10的散列表实例 50 | hash_table = ChainingHashTable(10) 51 | 52 | # 插入键值对 53 | hash_table.insert(5, "Value 1") 54 | hash_table.insert(15, "Value 2") 55 | hash_table.insert(25, "Value 3") 56 | 57 | # 查找键的值 58 | print(hash_table.search(5)) # 输出: Value 1 59 | print(hash_table.search(15)) # 输出: Value 2 60 | print(hash_table.search(25)) # 输出: Value 3 61 | print(hash_table.search(35)) # 输出: None (键不存在) 62 | 63 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/LinearRegression.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import org.apache.commons.math3.linear.*; 4 | import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression; 5 | 6 | public class LinearRegression { 7 | 8 | public static void main(String[] args) { 9 | 10 | // 给定数据点【可以换成自己的数据】 11 | double[][] data = { 12 | {1, 0.175}, 13 | {2, 1.213}, 14 | {4, 9.41}, 15 | {8, 74.941} 16 | }; 17 | 18 | double[] log_n = new double[data.length]; 19 | double[] log_T = new double[data.length]; 20 | 21 | for (int i = 0; i < data.length; i++) { 22 | double n = data[i][0]; 23 | double T = data[i][1]; 24 | log_n[i] = Math.log(n); 25 | log_T[i] = Math.log(T); 26 | } 27 | 28 | // 构建设计矩阵 29 | RealMatrix designMatrix = new Array2DRowRealMatrix(data.length, 2); 30 | for (int i = 0; i < log_n.length; i++) { 31 | designMatrix.setEntry(i, 0, 1.0); // 第一列全是1,用于计算截距 32 | designMatrix.setEntry(i, 1, log_n[i]); // 第二列是log_n 33 | } 34 | 35 | // 执行线性回归 36 | OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression(); 37 | regression.setNoIntercept(true); 38 | regression.newSampleData(log_T, designMatrix.getData()); 39 | 40 | // 提取参数 41 | double[] parameters = regression.estimateRegressionParameters(); 42 | double log_a = parameters[0]; // 截距,即log(a) 43 | double k = parameters[1]; // 斜率,即k 44 | 45 | // 计算a的值 46 | double a = Math.exp(log_a); 47 | 48 | // 输出k和a 49 | System.out.println("k: " + k + ", a: " + a); 50 | // RESULT: k: 2.9182384662267347, a: 0.16830073785100344 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/others/SocialNetwork.py: -------------------------------------------------------------------------------- 1 | 2 | # 题目:假设你是一名软件工程师,负责开发一个新的社交媒体平台。 3 | # 你的用户可以添加朋友,形成一个大的社交网络。现在,你的任务是实现一个功能:给定一个用户,找出离这个用户最近的所有用户。 4 | 5 | # 具体来说,你需要实现以下两个功能: 6 | 7 | # 用户可以添加朋友。 8 | # 给定一个用户,你的程序能够返回离这个用户最近的所有用户。也就是说,你的程序应该能够返回这个用户的朋友,朋友的朋友,等等。 9 | 10 | 11 | from collections import deque 12 | 13 | class SocialNetwork: 14 | def __init__(self): 15 | """ 16 | 初始化一个空的社交网络。 17 | 我们使用字典来表示这个网络, 18 | 字典的每个键是一个用户,对应的值是这个用户的朋友列表。 19 | """ 20 | self.network = {} 21 | 22 | def add_friend(self, user, friend): 23 | """ 24 | 在社交网络中添加一个朋友关系。 25 | 这个方法会在两个用户的朋友列表中都添加对方。 26 | """ 27 | if user not in self.network: 28 | self.network[user] = [] 29 | if friend not in self.network: 30 | self.network[friend] = [] 31 | self.network[user].append(friend) 32 | self.network[friend].append(user) 33 | 34 | def find_all_friends(self, user): 35 | """ 36 | 找出离一个用户最近的所有用户。 37 | 这个方法使用广度优先搜索实现。 38 | """ 39 | visited = [] 40 | queue = deque([user]) 41 | 42 | while queue: 43 | user = queue.popleft() 44 | if user not in visited: 45 | visited.append(user) 46 | friends = self.network[user] 47 | unvisited_friends = list(set(friends) - set(visited)) 48 | queue.extend(unvisited_friends) 49 | 50 | # 去除用户自身 51 | visited.remove(user) 52 | 53 | return visited 54 | 55 | 56 | # 测试 SocialNetwork 类 57 | SN = SocialNetwork() 58 | SN.add_friend("Alice", "Bob") 59 | SN.add_friend("Alice", "Charlie") 60 | SN.add_friend("Bob", "David") 61 | SN.add_friend("Charlie", "Edward") 62 | print(SN.find_all_friends("Alice")) # 输出: ['Bob', 'Charlie', 'David', 'Edward'] 63 | 64 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/MemoryAnalysis.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | public class MemoryAnalysis { 4 | 5 | /** 6 | * 说明:Java中的对象和基本数据类型的内存使用情况受到多种因素的影响,如JVM实现、系统架构(32位 vs 64位) 7 | * 下面分析基于 JVM 64 位的情况 8 | */ 9 | 10 | public static void main(String[] args) { 11 | // 基本数据类型 (通常存储在栈内存) 12 | byte b = 10; // 1字节 13 | char c = 'A'; // 2字节 14 | short s = 123; // 2字节 15 | int i = 12345; // 4字节 16 | long l = 1234567890L; // 8字节 17 | float f = 1.23f; // 4字节 18 | double d = 1.23456; // 8字节 19 | boolean bool = true; // JVM依赖, 通常为1字节 20 | 21 | // 包装类型 (存储在堆内存) 22 | Byte bObj = b; // 16字节 (对象开销) 23 | Character cObj = c; // 16字节 (对象开销) 24 | Short sObj = s; // 16字节 (对象开销) 25 | Integer iObj = i; // 16字节 (对象开销) 26 | Long lObj = l; // 24字节 (对象开销 + long) 27 | Float fObj = f; // 16字节 (对象开销) 28 | Double dObj = d; // 24字节 (对象开销 + double) 29 | Boolean boolObj = bool; // 16字节 (对象开销) 30 | 31 | // 数组 (存储在堆内存) 32 | int[] arr = new int[10]; // 56字节 (16字节对象开销 + 4*10) 33 | 34 | // 简单数据结构 (例如String, 存储在堆内存) 35 | String str = "Hello"; // 三部分:对象开销, char数组, 哈希值。具体大小取决于字符串长度。 36 | 37 | // 链表 (存储在堆内存) 38 | Node node = new Node(5); // 对象大小取决于节点数量 39 | 40 | // 自定义的类 (存储在堆内存) 41 | CustomClass custom = new CustomClass(); // 大小取决于属性和父类 42 | } 43 | 44 | // 链表的节点定义 45 | static class Node { 46 | int value; 47 | Node next; 48 | public Node(int value) { 49 | this.value = value; 50 | this.next = null; 51 | } 52 | } 53 | 54 | // 自定义类定义 55 | static class CustomClass { 56 | int attribute1; 57 | double attribute2; 58 | char attribute3; 59 | // 更多属性... 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/search_st/TestSequentialSearchST.java: -------------------------------------------------------------------------------- 1 | package symbol_table.search_st; 2 | 3 | import edu.princeton.cs.algs4.ST; 4 | import edu.princeton.cs.algs4.StdOut; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.util.Scanner; 9 | 10 | public class TestSequentialSearchST { 11 | // 不允许实例化此类。 12 | private TestSequentialSearchST() { } 13 | 14 | /** 15 | * 打印出最频繁出现的长度超过阈值的单词,以及长度超过阈值的单词数量和不同单词数量。 16 | * @param args 命令行参数 17 | */ 18 | public static void main(String[] args) { 19 | 20 | int distinct = 0, words = 0; 21 | int minlen = Integer.parseInt(args[0]); 22 | String fileName = args[1]; // 文件路径作为第二个参数 23 | ST st = new ST(); 24 | 25 | // 从文件中读取数据 26 | try (Scanner scanner = new Scanner(new File(fileName))) { 27 | while (scanner.hasNext()) { // 检查是否有更多数据 28 | String key = scanner.next(); 29 | if (key.length() < minlen) continue; 30 | words++; 31 | if (st.contains(key)) { 32 | st.put(key, st.get(key) + 1); 33 | } 34 | else { 35 | st.put(key, 1); 36 | distinct++; 37 | } 38 | } 39 | } catch (FileNotFoundException e) { 40 | System.err.println("文件未找到: " + fileName); 41 | return; 42 | } 43 | 44 | // 寻找出现频率最高的单词 45 | String max = ""; 46 | st.put(max, 0); 47 | for (String word : st.keys()) { 48 | if (st.get(word) > st.get(max)) 49 | max = word; 50 | } 51 | 52 | StdOut.println(max + " " + st.get(max)); 53 | StdOut.println("distinct = " + distinct); 54 | StdOut.println("words = " + words); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/UnorderedArrayPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | /** 4 | * Desc: 无序数组实现 PQ 插入、提取最大值接口 5 | * 分析: 6 | * 1. 提取最大值操作的时间复杂度(O(n)) 7 | * 2. 插入操作的时间复杂度(O(1)) 8 | * 3. 这个简单的实现无法插入新元素,在实际应用中,可以通过动态扩展数组来解决这个问题 9 | */ 10 | 11 | public class UnorderedArrayPQ { 12 | 13 | private int[] arr; // 用于存储数据的数组 14 | private int n; // 数组中的实际元素个数 15 | 16 | // 初始化一个给定大小的优先队列 17 | public UnorderedArrayPQ(int capacity) { 18 | arr = new int[capacity]; 19 | n = 0; 20 | } 21 | 22 | // 插入操作 23 | public void insert(int value) { 24 | if (n == arr.length) { 25 | throw new RuntimeException("队列已满"); // 如果数组已满,抛出异常 26 | } 27 | arr[n++] = value; // 在数组的末尾插入元素 28 | } 29 | 30 | // 提取最大值操作 31 | public int extractMax() { 32 | if (isEmpty()) { 33 | throw new RuntimeException("队列为空"); // 如果数组为空,抛出异常 34 | } 35 | 36 | int maxIndex = 0; 37 | // 遍历数组,找到最大值的索引 38 | for (int i = 1; i < n; i++) { 39 | if (arr[i] > arr[maxIndex]) { 40 | maxIndex = i; 41 | } 42 | } 43 | 44 | int maxValue = arr[maxIndex]; 45 | 46 | // 将最大值后面的元素左移,覆盖最大值 47 | for (int i = maxIndex; i < n - 1; i++) { 48 | arr[i] = arr[i + 1]; 49 | } 50 | 51 | n--; // 减少数组中的实际元素个数 52 | return maxValue; 53 | } 54 | 55 | // 判断队列是否为空 56 | public boolean isEmpty() { 57 | return n == 0; 58 | } 59 | 60 | public static void main(String[] args) { 61 | // 测试代码 62 | UnorderedArrayPQ pq = new UnorderedArrayPQ(10); 63 | pq.insert(5); 64 | pq.insert(3); 65 | pq.insert(7); 66 | pq.insert(2); 67 | 68 | System.out.println(pq.extractMax()); // 输出7 69 | System.out.println(pq.extractMax()); // 输出5 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/05/RabinKarp.py: -------------------------------------------------------------------------------- 1 | class RabinKarp: 2 | def __init__(self, pattern): 3 | """ 4 | 初始化 RabinKarp 类。 5 | 6 | Args: 7 | pattern (str): 模式字符串。 8 | 9 | """ 10 | self.pattern = pattern 11 | self.pattern_hash = self.calculate_hash(pattern) 12 | 13 | def calculate_hash(self, text): 14 | """ 15 | 计算字符串的哈希值。 16 | 17 | Args: 18 | text (str): 输入的字符串。 19 | 20 | Returns: 21 | int: 字符串的哈希值。 22 | 23 | """ 24 | prime = 31 # 选择一个适当的质数作为散列计算的基数 25 | hash_value = 0 26 | for char in text: 27 | hash_value = (hash_value * prime + ord(char)) % 1000000007 # 使用简单的散列计算方式 28 | return hash_value 29 | 30 | def search(self, text): 31 | """ 32 | 在文本字符串中搜索模式字符串。 33 | 34 | Args: 35 | text (str): 输入的文本字符串。 36 | 37 | Returns: 38 | int: 如果模式字符串存在于文本字符串中,返回匹配的起始索引;否则返回 -1。 39 | 40 | """ 41 | n = len(self.pattern) 42 | m = len(text) 43 | pattern_hash = self.pattern_hash 44 | text_hash = self.calculate_hash(text[:n]) 45 | 46 | for i in range(m - n + 1): 47 | if pattern_hash == text_hash and self.pattern == text[i:i+n]: 48 | return i 49 | 50 | if i < m - n: 51 | # 通过滑动窗口更新文本子串的哈希值 52 | text_hash = (text_hash - ord(text[i]) * pow(31, n-1)) % 1000000007 53 | text_hash = (text_hash * 31 + ord(text[i+n])) % 1000000007 54 | text_hash = (text_hash + 1000000007) % 1000000007 55 | 56 | return -1 57 | 58 | 59 | # 使用示例 60 | text = "Hello, world! This is a test string." 61 | pattern = "test" 62 | rk = RabinKarp(pattern) 63 | result = rk.search(text) 64 | 65 | if result != -1: 66 | print(f"Pattern found at index {result}") 67 | else: 68 | print("Pattern not found") 69 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/MatrixPower.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class MatrixPower { 6 | 7 | private static class Matrix2x2 { 8 | BigInteger a, b, c, d; 9 | 10 | Matrix2x2(BigInteger a, BigInteger b, BigInteger c, BigInteger d) { 11 | this.a = a; 12 | this.b = b; 13 | this.c = c; 14 | this.d = d; 15 | } 16 | 17 | Matrix2x2 multiply(Matrix2x2 other) { 18 | return new Matrix2x2( 19 | this.a.multiply(other.a).add(this.b.multiply(other.c)), 20 | this.a.multiply(other.b).add(this.b.multiply(other.d)), 21 | this.c.multiply(other.a).add(this.d.multiply(other.c)), 22 | this.c.multiply(other.b).add(this.d.multiply(other.d)) 23 | ); 24 | } 25 | } 26 | 27 | public static Matrix2x2 matrixPower(Matrix2x2 base, int exp) { 28 | if (exp == 1) { 29 | return base; 30 | } 31 | 32 | Matrix2x2 halfPower = matrixPower(base, exp / 2); 33 | if (exp % 2 == 0) { 34 | return halfPower.multiply(halfPower); 35 | } else { 36 | return base.multiply(halfPower.multiply(halfPower)); 37 | } 38 | } 39 | 40 | public static void main(String[] args) { 41 | // 测试用例 42 | Matrix2x2 Q = new Matrix2x2(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE, BigInteger.ZERO); 43 | 44 | int testN = 5; 45 | Matrix2x2 result = matrixPower(Q, testN); 46 | System.out.println("Q^" + testN + ":"); 47 | System.out.println(result.a + " " + result.b); 48 | System.out.println(result.c + " " + result.d); 49 | 50 | testN = 99; 51 | result = matrixPower(Q, testN); 52 | System.out.println("\nQ^" + testN + ":"); 53 | System.out.println(result.a + " " + result.b); 54 | System.out.println(result.c + " " + result.d); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortStandard.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Arrays; 4 | 5 | public class HeapSortStandard { 6 | 7 | // 泛型堆排序函数 8 | public static > void heapSort(T[] arr) { 9 | int n = arr.length; 10 | 11 | // 构建大顶堆(未优化版本,从上到下) 12 | for (int i = n / 2 - 1; i >= 0; i--) { 13 | heapify(arr, n, i); 14 | } 15 | 16 | // 从堆顶取出最大的元素,放到数组末尾,并重新调整堆 17 | for (int i = n - 1; i >= 0; i--) { 18 | swap(arr, 0, i); 19 | heapify(arr, i, 0); 20 | } 21 | } 22 | 23 | // 递归: 使得以root为根的子树成为大顶堆 24 | private static > void heapify(T[] arr, int n, int root) { 25 | int largest = root; 26 | int left = 2 * root + 1; 27 | int right = 2 * root + 2; 28 | 29 | // 如果左子节点比根大 30 | if (left < n && arr[left].compareTo(arr[largest]) > 0) { 31 | largest = left; 32 | } 33 | 34 | // 如果右子节点比最大值还大 35 | if (right < n && arr[right].compareTo(arr[largest]) > 0) { 36 | largest = right; 37 | } 38 | 39 | // 如果最大值不是根 40 | if (largest != root) { 41 | swap(arr, root, largest); 42 | heapify(arr, n, largest); 43 | } 44 | } 45 | 46 | // 交换数组中的两个元素 47 | private static void swap(T[] arr, int i, int j) { 48 | T temp = arr[i]; 49 | arr[i] = arr[j]; 50 | arr[j] = temp; 51 | } 52 | 53 | public static void main(String[] args) { 54 | // 整数数组示例 55 | Integer[] intArr = {12, 11, 13, 5, 6, 7}; 56 | heapSort(intArr); 57 | System.out.println("Sorted integer array:"); 58 | System.out.println(Arrays.toString(intArr)); 59 | 60 | // 字符串数组示例 61 | String[] stringArr = {"banana", "apple", "cherry", "date"}; 62 | heapSort(stringArr); 63 | System.out.println("Sorted string array:"); 64 | System.out.println(Arrays.toString(stringArr)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/longestCommonSubsequence_with_path_1D.py: -------------------------------------------------------------------------------- 1 | def lcs(X, Y): 2 | m, n = len(X), len(Y) # 获取两个字符串的长度 3 | 4 | # 初始化一维DP数组,大小为字符串Y的长度+1,用于存储当前行的LCS长度 5 | dp = [0] * (n + 1) 6 | 7 | # 初始化一个方向数组,大小为m+1 x n+1 8 | # 用于记录我们如何到达每个DP值 9 | # 'diag'表示我们是从左上角的方向来的,'left'表示我们是从左边来的,'up'表示我们是从上面来的 10 | directions = [['' for _ in range(n + 1)] for _ in range(m + 1)] 11 | 12 | # 遍历字符串X 13 | for i in range(1, m + 1): 14 | prev = 0 # 保存前一列的dp[j]值,因为我们需要在下次迭代中使用它 15 | # 遍历字符串Y 16 | for j in range(1, n + 1): 17 | temp = dp[j] # 保存当前的dp[j]值,以便于下一次迭代使用 18 | # 如果X的当前字符和Y的当前字符相同 19 | if X[i-1] == Y[j-1]: 20 | dp[j] = prev + 1 # 更新dp[j]值,这时我们是从左上角方向来的 21 | directions[i][j] = 'diag' 22 | else: 23 | # 如果X的当前字符和Y的当前字符不同 24 | # 我们需要从dp[j](上方)和dp[j-1](左方)中选择一个较大的值 25 | if dp[j] < dp[j-1]: 26 | dp[j] = dp[j-1] # 更新dp[j]值,这时我们是从左方来的 27 | directions[i][j] = 'left' 28 | else: 29 | # 如果dp[j]已经是较大的值,我们不需要更新它,但是我们是从上方来的 30 | directions[i][j] = 'up' 31 | prev = temp # 更新prev值为当前列的旧值 32 | 33 | # 使用方向数组回溯LCS 34 | i, j = m, n # 从最后一个字符开始 35 | lcs_str = [] # 用于存储LCS的字符 36 | while i > 0 and j > 0: 37 | # 如果我们是从左上角方向来的,那么X[i-1]和Y[j-1]是LCS的一部分 38 | if directions[i][j] == 'diag': 39 | lcs_str.append(X[i-1]) 40 | i -= 1 41 | j -= 1 42 | # 如果我们是从左方来的,仅仅移动到Y的前一个字符 43 | elif directions[i][j] == 'left': 44 | j -= 1 45 | # 如果我们是从上方来的,仅仅移动到X的前一个字符 46 | else: 47 | i -= 1 48 | 49 | # 因为我们是从后向前回溯的,所以需要反转LCS字符串来得到正确的顺序 50 | lcs_str = lcs_str[::-1] 51 | return dp[n], ''.join(lcs_str) # 返回LCS的长度和LCS字符串 52 | 53 | # 测试 54 | X = "ABCBDAB" 55 | Y = "BDCAB" 56 | length, sequence = lcs(X, Y) 57 | print(length) # 输出4 58 | print(sequence) # 输出"BCAB" 59 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/TestDataGenerator.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Random; 4 | 5 | public class TestDataGenerator { 6 | 7 | // Generate a large random array 8 | public static int[] generateRandomArray(int size) { 9 | Random rand = new Random(); 10 | int[] array = new int[size]; 11 | for (int i = 0; i < size; i++) { 12 | array[i] = rand.nextInt(size); 13 | } 14 | return array; 15 | } 16 | 17 | // Generate a reverse sorted array 18 | public static int[] generateReverseSortedArray(int size) { 19 | int[] array = new int[size]; 20 | for (int i = 0; i < size; i++) { 21 | array[i] = size - i; 22 | } 23 | return array; 24 | } 25 | 26 | // Generate a partially sorted array 27 | public static int[] generatePartiallySortedArray(int size, int sortedFraction) { 28 | int[] array = generateRandomArray(size); 29 | java.util.Arrays.sort(array, 0, size / sortedFraction); 30 | return array; 31 | } 32 | 33 | // Generate a nearly sorted array 34 | public static int[] generateNearlySortedArray(int size, int perturbation) { 35 | int[] array = new int[size]; 36 | for (int i = 0; i < size; i++) { 37 | array[i] = i; 38 | } 39 | Random rand = new Random(); 40 | for (int i = 0; i < perturbation; i++) { 41 | int index1 = rand.nextInt(size); 42 | int index2 = rand.nextInt(size); 43 | int temp = array[index1]; 44 | array[index1] = array[index2]; 45 | array[index2] = temp; 46 | } 47 | return array; 48 | } 49 | 50 | // Generate an array with few unique elements 51 | public static int[] generateFewUniqueElementsArray(int size, int uniqueElements) { 52 | Random rand = new Random(); 53 | int[] array = new int[size]; 54 | for (int i = 0; i < size; i++) { 55 | array[i] = rand.nextInt(uniqueElements); 56 | } 57 | return array; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/graphStore/graph_list.py: -------------------------------------------------------------------------------- 1 | class Vertex: 2 | """图中的顶点""" 3 | def __init__(self, key): 4 | """初始化顶点的键和相邻顶点集""" 5 | self.id = key 6 | self.connected_to = {} 7 | 8 | def add_neighbor(self, neighbor, weight=0): 9 | """添加一个相邻的顶点以及它的权重""" 10 | self.connected_to[neighbor] = weight 11 | 12 | def get_connections(self): 13 | """返回所有相邻顶点的键""" 14 | return self.connected_to.keys() 15 | 16 | def get_id(self): 17 | """返回顶点的键""" 18 | return self.id 19 | 20 | def get_weight(self, neighbor): 21 | """返回一个相邻顶点的权重""" 22 | return self.connected_to[neighbor] 23 | 24 | 25 | class Graph: 26 | """图类,使用邻接表存储""" 27 | def __init__(self): 28 | """初始化图的顶点集""" 29 | self.vert_list = {} 30 | self.num_vertices = 0 31 | 32 | def add_vertex(self, key): 33 | """添加一个顶点到图中""" 34 | self.num_vertices += 1 35 | new_vertex = Vertex(key) 36 | self.vert_list[key] = new_vertex 37 | return new_vertex 38 | 39 | def get_vertex(self, key): 40 | """根据键获取一个顶点""" 41 | return self.vert_list.get(key) 42 | 43 | def add_edge(self, frm, to, cost=0): 44 | """添加一条边到图中""" 45 | if frm not in self.vert_list: 46 | self.add_vertex(frm) 47 | if to not in self.vert_list: 48 | self.add_vertex(to) 49 | self.vert_list[frm].add_neighbor(self.vert_list[to], cost) 50 | 51 | def get_vertices(self): 52 | """返回图中所有顶点的键""" 53 | return self.vert_list.keys() 54 | 55 | def __iter__(self): 56 | """迭代返回图中所有顶点""" 57 | return iter(self.vert_list.values()) 58 | 59 | 60 | 61 | ## 测试 62 | g = Graph() 63 | for i in range(6): 64 | g.add_vertex(i) 65 | 66 | g.add_edge(0, 1, 5) 67 | g.add_edge(0, 5, 2) 68 | g.add_edge(1, 2, 4) 69 | g.add_edge(2, 3, 9) 70 | g.add_edge(3, 4, 7) 71 | g.add_edge(3, 5, 3) 72 | g.add_edge(4, 0, 1) 73 | g.add_edge(5, 4, 8) 74 | g.add_edge(5, 2, 1) 75 | 76 | for v in g: 77 | for w in v.get_connections(): 78 | print("( %s , %s )" % (v.get_id(), w.get_id())) 79 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/search_st/FrequencyCounter.java: -------------------------------------------------------------------------------- 1 | package symbol_table.search_st; 2 | 3 | import edu.princeton.cs.algs4.StdOut; 4 | import symbol_table.tools.VisualAccumulator; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.util.Scanner; 9 | 10 | public class FrequencyCounter { 11 | 12 | // 不要实例化这个类。 13 | private FrequencyCounter() { 14 | } 15 | 16 | /** 17 | * 从文件输入流读取数据 18 | * 打印出长度超过阈值且出现频率最高的单词。 19 | * 同时打印出长度超过阈值的单词总数和不同单词的数量。 20 | * 21 | * @param args 命令行参数 22 | */ 23 | public static void main(String[] args) { 24 | int distinct = 0, words = 0; 25 | int minlen = Integer.parseInt(args[0]); // 最小单词长度作为第一个参数 26 | String fileName = args[1]; // 文件路径作为第二个参数 27 | SequentialSearchSTForVisualTest st = new SequentialSearchSTForVisualTest<>(); 28 | VisualAccumulator va = st.getVisualAccumulator(); 29 | 30 | // 从文件中读取数据 31 | try (Scanner scanner = new Scanner(new File(fileName))) { 32 | while (scanner.hasNext()) { 33 | String key = scanner.next(); 34 | if (key.length() < minlen) continue; // 忽略长度小于阈值的单词 35 | words++; 36 | if (st.contains(key)) { 37 | st.put(key, st.get(key) + 1); 38 | } else { 39 | st.put(key, 1); 40 | distinct++; 41 | } 42 | } 43 | } catch (FileNotFoundException e) { 44 | StdOut.println("文件未找到: " + fileName); 45 | return; 46 | } 47 | 48 | // 找到出现频率最高的单词 49 | String max = ""; 50 | st.put(max, 0); 51 | 52 | for (String word : st.keys()) { 53 | if (st.get(word) > st.get(max)) max = word; 54 | } 55 | 56 | StdOut.println(max + " " + st.get(max)); 57 | StdOut.println("不同的单词数量 = " + distinct); 58 | StdOut.println("总单词数量 = " + words); 59 | 60 | // 打印最后一个红点的值(平均值) 61 | if (va != null) { 62 | System.out.println("最后一个平均值: " + va.toString()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/search_st/FCounterBinarySearchST.java: -------------------------------------------------------------------------------- 1 | package symbol_table.search_st; 2 | 3 | import edu.princeton.cs.algs4.StdOut; 4 | import symbol_table.tools.VisualAccumulator; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.util.Scanner; 9 | 10 | public class FCounterBinarySearchST { 11 | 12 | // 不要实例化这个类。 13 | private FCounterBinarySearchST() { 14 | } 15 | 16 | /** 17 | * 从文件输入流读取数据 18 | * 打印出长度超过阈值且出现频率最高的单词。 19 | * 同时打印出长度超过阈值的单词总数和不同单词的数量。 20 | * 21 | * @param args 命令行参数 22 | */ 23 | public static void main(String[] args) { 24 | int distinct = 0, words = 0; 25 | int minlen = Integer.parseInt(args[0]); // 最小单词长度作为第一个参数 26 | String fileName = args[1]; // 文件路径作为第二个参数 27 | BinarySearchSTForVisualTest st = new BinarySearchSTForVisualTest<>(); 28 | VisualAccumulator va = st.getVisualAccumulator(); 29 | 30 | // 从文件中读取数据 31 | try (Scanner scanner = new Scanner(new File(fileName))) { 32 | while (scanner.hasNext()) { 33 | String key = scanner.next(); 34 | if (key.length() < minlen) continue; // 忽略长度小于阈值的单词 35 | words++; 36 | if (st.contains(key)) { 37 | st.put(key, st.get(key) + 1); 38 | } else { 39 | st.put(key, 1); 40 | distinct++; 41 | } 42 | } 43 | } catch (FileNotFoundException e) { 44 | StdOut.println("文件未找到: " + fileName); 45 | return; 46 | } 47 | 48 | // 找到出现频率最高的单词 49 | String max = ""; 50 | st.put(max, 0); 51 | 52 | for (String word : st.keys()) { 53 | if (st.get(word) > st.get(max)) max = word; 54 | } 55 | 56 | StdOut.println(max + " " + st.get(max)); 57 | StdOut.println("不同的单词数量 = " + distinct); 58 | StdOut.println("总单词数量 = " + words); 59 | 60 | // 打印最后一个红点的值(平均值) 61 | if (va != null) { 62 | System.out.println("最后一个平均值: " + va.toString()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortPerformanceTest.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Random; 4 | 5 | public class HeapSortPerformanceTest { 6 | 7 | public static void main(String[] args) { 8 | // 定义不同的测试规模 9 | int[] sizes = {100000, 200000, 400000, 800000, 1600000}; // 倍率实验 10 | 11 | // 对每个规模进行测试 12 | for (int size : sizes) { 13 | long totalTimeBeforeOptimization = 0; 14 | long totalTimeAfterOptimization = 0; 15 | 16 | // 运行5次实验并取平均值 17 | for (int i = 0; i < 5; i++) { 18 | Integer[] originalArray = generateRandomIntegerArray(size); 19 | 20 | // 测试优化前的堆排序 21 | Integer[] arrayBeforeOptimization = originalArray.clone(); 22 | long startTime = System.currentTimeMillis(); 23 | HeapSortStandard.heapSort(arrayBeforeOptimization); // 确保这里使用的是泛型版本 24 | long endTime = System.currentTimeMillis(); 25 | totalTimeBeforeOptimization += (endTime - startTime); 26 | 27 | // 测试优化后的堆排序 28 | Integer[] arrayAfterOptimization = originalArray.clone(); 29 | startTime = System.currentTimeMillis(); 30 | HeapSortFloyd.heapSort(arrayAfterOptimization); // 使用泛型版本 31 | endTime = System.currentTimeMillis(); 32 | totalTimeAfterOptimization += (endTime - startTime); 33 | } 34 | 35 | long averageTimeBeforeOptimization = totalTimeBeforeOptimization / 5; 36 | long averageTimeAfterOptimization = totalTimeAfterOptimization / 5; 37 | 38 | System.out.println("规模: " + size); 39 | System.out.println("优化前平均时间: " + averageTimeBeforeOptimization + "ms"); 40 | System.out.println("优化后平均时间: " + averageTimeAfterOptimization + "ms"); 41 | System.out.println(); 42 | } 43 | } 44 | 45 | // 生成随机整数数组(使用Integer而不是int) 46 | private static Integer[] generateRandomIntegerArray(int size) { 47 | Integer[] array = new Integer[size]; 48 | Random random = new Random(); 49 | for (int i = 0; i < size; i++) { 50 | array[i] = random.nextInt(size); 51 | } 52 | return array; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/OrderedArrayPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | public class OrderedArrayPQ> implements PriorityQueue { 4 | private T[] queue; // 用于存储队列元素的数组 5 | private int size; // 记录当前队列的大小 6 | 7 | @SuppressWarnings("unchecked") 8 | public OrderedArrayPQ(int capacity) { 9 | queue = (T[]) new Comparable[capacity]; // 初始化数组 10 | size = 0; // 初始大小为0 11 | } 12 | 13 | // 插入操作 14 | public void insert(T item) { 15 | if (size == queue.length) { 16 | throw new RuntimeException("Queue is full"); // 当队列满时抛出异常 17 | } 18 | int i = size - 1; 19 | // 找到新元素应该插入的位置,同时将较大的元素向后移动 20 | while (i >= 0 && queue[i].compareTo(item) < 0) { 21 | queue[i + 1] = queue[i]; 22 | i--; 23 | } 24 | queue[i + 1] = item; // 插入新元素 25 | size++; // 更新队列大小 26 | } 27 | 28 | 29 | // 删除并返回最大元素 30 | public T extractMax() { 31 | if (isEmpty()) { 32 | throw new RuntimeException("Queue is empty"); // 当队列为空时抛出异常 33 | } 34 | T maxItem = queue[--size]; // 获取最大元素 35 | queue[size] = null; // 避免对象游离,帮助垃圾收集器回收内存 36 | return maxItem; // 返回最大元素 37 | } 38 | 39 | // 判断队列是否为空 40 | public boolean isEmpty() { 41 | return size == 0; 42 | } 43 | 44 | // 获取队列的大小 45 | public int size() { 46 | return size; 47 | } 48 | 49 | // 以下接口未实现... 50 | @Override 51 | public T max() { return null; } 52 | 53 | @Override 54 | public T min() { return null; } 55 | 56 | @Override 57 | public T extractMin() { return null; } 58 | 59 | @Override 60 | public void merge(PriorityQueue other) { } 61 | 62 | @Override 63 | public void clear() { } 64 | 65 | 66 | // 测试代码 67 | public static void main(String[] args) { 68 | OrderedArrayPQ pq = new OrderedArrayPQ<>(10); 69 | pq.insert(10); // 插入元素 70 | pq.insert(20); // 插入元素 71 | pq.insert(5); // 插入元素 72 | System.out.println(pq.extractMax()); // 输出并删除最大元素:20 73 | System.out.println(pq.extractMax()); // 输出并删除最大元素:10 74 | System.out.println(pq.extractMax()); // 输出并删除最大元素:5 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/SortComparison.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.util.Arrays; 4 | import java.util.Random; 5 | 6 | public class SortComparison { 7 | 8 | // 冒泡排序 9 | public static void bubbleSort(int[] arr) { 10 | boolean swapped; 11 | do { 12 | swapped = false; 13 | for (int i = 1; i < arr.length; i++) { 14 | if (arr[i - 1] > arr[i]) { 15 | int temp = arr[i - 1]; 16 | arr[i - 1] = arr[i]; 17 | arr[i] = temp; 18 | swapped = true; 19 | } 20 | } 21 | } while (swapped); 22 | } 23 | 24 | // 快速排序 25 | public static void quickSort(int[] arr, int low, int high) { 26 | if (low < high) { 27 | int pi = partition(arr, low, high); 28 | 29 | quickSort(arr, low, pi); 30 | quickSort(arr, pi + 1, high); 31 | } 32 | } 33 | 34 | private static int partition(int[] arr, int low, int high) { 35 | int pivot = arr[low]; 36 | int i = low, j = high; 37 | while (true) { 38 | while (arr[i] < pivot) i++; 39 | while (arr[j] > pivot) j--; 40 | 41 | if (i < j) { 42 | int temp = arr[i]; 43 | arr[i] = arr[j]; 44 | arr[j] = temp; 45 | } else { 46 | return j; 47 | } 48 | } 49 | } 50 | 51 | public static void main(String[] args) { 52 | Random random = new Random(); 53 | int size = 10; // 较小的数组大小 54 | 55 | int[] arr1 = new int[size]; 56 | for (int i = 0; i < size; i++) { 57 | arr1[i] = random.nextInt(1000); // 生成随机数填充数组 58 | } 59 | 60 | int[] arr2 = Arrays.copyOf(arr1, size); 61 | 62 | long start = System.nanoTime(); 63 | bubbleSort(arr1); 64 | long end = System.nanoTime(); 65 | System.out.println("Bubble Sort Time: " + (end - start) + " nanoseconds"); 66 | 67 | start = System.nanoTime(); 68 | quickSort(arr2, 0, arr2.length - 1); 69 | end = System.nanoTime(); 70 | System.out.println("Quick Sort Time: " + (end - start) + " nanoseconds"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/PerformanceTest2.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 单独为 java 标准库实现的PQ 提供的性能测试代码 7 | * 结果证明:标准库实现的PQ比我们简单实现的二叉堆更快一些 8 | */ 9 | public class PerformanceTest2 { 10 | 11 | private static Random random = new Random(); 12 | 13 | public static void main(String[] args) { 14 | int testCount = 10; 15 | int[] sizes = {1000, 2000, 4000, 8000, 16000, 32000, 64000}; // 倍率实验 16 | 17 | 18 | System.out.println("Java标准库PriorityQueue性能测试:"); 19 | testPerformance(new JavaPriorityQueueWrapper(), testCount, sizes); 20 | /** 21 | * 本机 RESULT 22 | 对于大小 1000, 平均用时: 391,216 纳秒 < 1毫秒 23 | 对于大小 2000, 平均用时: 346,091 纳秒 < 1毫秒 24 | 对于大小 4000, 平均用时: 574,508 纳秒 < 1毫秒 25 | 对于大小 8000, 平均用时: 1,252,154 纳秒 = 1.25 毫秒 26 | 对于大小 16000, 平均用时: 2,980,183 纳秒 = 2.98 毫秒 27 | 对于大小 32000, 平均用时: 6,679,366 纳秒 = 6.68 毫秒 28 | 对于大小 64000, 平均用时: 15,562,404 纳秒 = 15 毫秒 29 | */ 30 | } 31 | 32 | public static void testPerformance(MaxPQ pq, int testCount, int[] sizes) { 33 | for (int size : sizes) { 34 | long totalTime = 0; 35 | for (int i = 0; i < testCount; i++) { 36 | totalTime += testPriorityQueue(pq, size); 37 | } 38 | long averageTime = totalTime / testCount; 39 | System.out.println("对于大小 " + size + ", 平均用时: " + averageTime + " 纳秒"); 40 | } 41 | } 42 | 43 | public static long testPriorityQueue(MaxPQ pq, int size) { 44 | // 插入元素 45 | for (int i = 0; i < size; i++) { 46 | pq.insert(random.nextInt(Integer.MAX_VALUE)); 47 | } 48 | 49 | long startTime = System.nanoTime(); 50 | 51 | // 删除一半的元素 52 | for (int i = 0; i < size / 2; i++) { 53 | pq.extractMax(); 54 | } 55 | 56 | // 重新插入元素 57 | for (int i = 0; i < size / 2; i++) { 58 | pq.insert(random.nextInt(Integer.MAX_VALUE)); 59 | } 60 | 61 | // 删除所有元素 62 | while (!pq.isEmpty()) { 63 | pq.extractMax(); 64 | } 65 | 66 | long endTime = System.nanoTime(); 67 | 68 | return endTime - startTime; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/02/selection_sort_linked_list.py: -------------------------------------------------------------------------------- 1 | # 定义链表节点类 2 | class Node: 3 | def __init__(self, data): 4 | self.data = data # 节点的数据 5 | self.next = None # 下一个节点的指针 6 | 7 | # 定义链表类 8 | class LinkedList: 9 | def __init__(self): 10 | self.head = None # 头结点 11 | 12 | # 在链表尾部插入节点 13 | def append(self, data): 14 | new_node = Node(data) 15 | 16 | # 如果链表为空,将新节点设为头结点 17 | if self.head == None: 18 | self.head = new_node 19 | return 20 | 21 | # 遍历链表到最后一个节点 22 | last_node = self.head 23 | while last_node.next: 24 | last_node = last_node.next 25 | 26 | # 将新节点插入到最后一个节点之后 27 | last_node.next = new_node 28 | 29 | # 打印链表中的所有数据值 30 | def display(self): 31 | current_node = self.head 32 | while current_node: 33 | print(current_node.data) 34 | current_node = current_node.next 35 | 36 | # 选择排序 37 | def selection_sort(self): 38 | # 如果链表为空,直接返回 39 | if self.head == None: 40 | return 41 | 42 | # 排序过程 43 | current_node = self.head 44 | while current_node.next: 45 | min_value = current_node.data # 默认当前节点为最小值 46 | min_node = current_node # 最小值节点 47 | search_node = current_node.next # 从当前节点的下一个节点开始搜索 48 | 49 | while search_node: 50 | # 如果搜索到的节点的数据值比最小值小,更新最小值和最小值节点 51 | if search_node.data < min_value: 52 | min_value = search_node.data 53 | min_node = search_node 54 | search_node = search_node.next # 继续搜索下一个节点 55 | 56 | # 交换当前节点和最小值节点的数据值 57 | temp = current_node.data 58 | current_node.data = min_node.data 59 | min_node.data = temp 60 | 61 | current_node = current_node.next # 排序下一个节点 62 | 63 | # 测试 64 | if __name__ == '__main__': 65 | linked_list = LinkedList() 66 | linked_list.append(6) 67 | linked_list.append(3) 68 | linked_list.append(8) 69 | linked_list.append(2) 70 | linked_list.append(1) 71 | 72 | print("排序前:") 73 | linked_list.display() 74 | 75 | linked_list.selection_sort() 76 | 77 | print("排序后:") 78 | linked_list.display() 79 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/07/dijkstra2.py: -------------------------------------------------------------------------------- 1 | # 引入heapq库,用于实现优先队列 2 | import heapq 3 | 4 | # 定义迪杰斯特拉函数,输入为图、起始节点和目标节点 5 | def dijkstra(graph, start, end): 6 | 7 | # 初始化一个字典来保存每个节点的最短距离和前一个节点,初始距离都设置为无穷大,前一个节点都设置为None 8 | shortest_distances = {node: (float('infinity'), None) for node in graph} 9 | 10 | # 起始节点的最短距离设为0,前一个节点为None 11 | shortest_distances[start] = (0, None) 12 | 13 | # 初始化一个优先级队列,将起始节点和其距离加入队列 14 | heap = [(0, start)] 15 | 16 | # 当队列不为空时进行循环 17 | while heap: 18 | 19 | # 使用heappop取出队列中最短距离的节点 20 | current_distance, current_node = heapq.heappop(heap) 21 | 22 | # 如果当前节点的距离已经被更新过(大于我们记录的最短距离),则跳过 23 | if current_distance > shortest_distances[current_node][0]: 24 | continue 25 | 26 | # 遍历当前节点的所有邻居节点 27 | for neighbor, distance in graph[current_node].items(): 28 | 29 | # 计算经过当前节点到达邻居节点的距离 30 | new_distance = current_distance + distance 31 | 32 | # 如果经过当前节点到达邻居节点的距离比已知的邻居节点的最短距离还要短, 33 | # 就更新邻居节点的最短距离和前一节点 34 | if new_distance < shortest_distances[neighbor][0]: 35 | shortest_distances[neighbor] = (new_distance, current_node) 36 | 37 | # 将新的距离和邻居节点加入优先级队列 38 | heapq.heappush(heap, (new_distance, neighbor)) 39 | 40 | # 到此为止,我们已经找到了所有节点的最短距离,下面根据前一节点信息找出最短路径 41 | path = [] # 初始化一个列表来保存路径 42 | current_node = end # 从目标节点开始 43 | while current_node is not None: # 如果当前节点不是None,说明还未到达起始节点 44 | path.append(current_node) # 将当前节点添加到路径中 45 | next_node = shortest_distances[current_node][1] # 获取当前节点的前一节点 46 | current_node = next_node # 将前一节点设为当前节点 47 | path = path[::-1] # 反转路径,使其从起始节点开始 48 | 49 | # 返回最短距离和最短路径 50 | return shortest_distances[end][0], path 51 | 52 | # 定义图 53 | graph = { 54 | 0: {1: 30, 7: 52}, 55 | 1: {0: 30, 7: 35, 2: 60}, 56 | 2: {1: 60, 8: 10, 3: 74, 5: 130}, 57 | 3: {2: 74, 5: 55, 4: 108}, 58 | 4: {3: 108, 5: 50}, 59 | 5: {2: 130, 3: 55, 4: 50, 6: 110}, 60 | 6: {5: 110, 7: 57, 8: 10}, 61 | 7: {0: 52, 1: 35, 8: 48, 6: 57}, 62 | 8: {2: 10, 6: 10, 7: 48} 63 | } 64 | 65 | # 调用函数,计算从节点0到节点4的最短路径 66 | shortest_distance, shortest_path = dijkstra(graph, 0, 4) 67 | print(f"节点0到节点4的最短距离为: {shortest_distance}") 68 | print(f"节点0到节点4的最短路径为: {shortest_path}") 69 | 70 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/bfs/bfs-traverse.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | class Graph: 4 | def __init__(self): 5 | """ 6 | 初始化图对象,使用一个空字典。 7 | 字典中的每个键代表图中的一个顶点, 8 | 对应的值是这个顶点的邻居列表。 9 | """ 10 | self.graph_dict = {} 11 | 12 | def add_vertex(self, node): 13 | """ 14 | 如果顶点 "node" 不在字典中,向字典添加键 "node",值为空列表。 15 | 否则,不需要进行任何操作。 16 | """ 17 | if node not in self.graph_dict: 18 | self.graph_dict[node] = [] 19 | 20 | def add_edge(self, node1, node2): 21 | """ 22 | 要在图中添加一条边, 23 | 需要在每个顶点的邻居列表中添加另一个顶点。 24 | """ 25 | self.graph_dict[node1].append(node2) 26 | self.graph_dict[node2].append(node1) 27 | 28 | def bfs(self, start): 29 | """ 30 | 这个方法实现了广度优先搜索算法。 31 | 它以 "start" 为起点进行搜索。 32 | 返回一个列表,表示从 "start" 开始的广度优先搜索的顶点访问顺序。 33 | """ 34 | visited = [] 35 | queue = deque([start]) 36 | 37 | while queue: 38 | vertex = queue.popleft() 39 | print(f"Queue after popping: {list(queue)}") 40 | if vertex not in visited: 41 | visited.append(vertex) 42 | neighbours = self.graph_dict[vertex] 43 | unvisited_neighbours = list(set(neighbours) - set(visited)) 44 | queue.extend(unvisited_neighbours) 45 | print(f"Queue after extending: {list(queue)}") 46 | 47 | return visited 48 | 49 | 50 | # 测试 Graph 类 51 | G = Graph() 52 | G.add_vertex("悟空") 53 | G.add_vertex("唐僧") 54 | G.add_vertex("铁扇公主") 55 | G.add_vertex("牛魔王") 56 | G.add_vertex("猪八戒") 57 | G.add_vertex("红孩儿") 58 | G.add_vertex("小猪妖") 59 | 60 | G.add_edge("悟空", "唐僧") 61 | G.add_edge("悟空", "铁扇公主") 62 | G.add_edge("悟空", "牛魔王") 63 | G.add_edge("悟空", "猪八戒") 64 | 65 | G.add_edge("唐僧", "悟空") 66 | G.add_edge("唐僧", "猪八戒") 67 | 68 | G.add_edge("铁扇公主", "悟空") 69 | G.add_edge("铁扇公主", "牛魔王") 70 | G.add_edge("铁扇公主", "红孩儿") 71 | 72 | G.add_edge("牛魔王", "悟空") 73 | G.add_edge("牛魔王", "铁扇公主") 74 | G.add_edge("牛魔王", "红孩儿") 75 | 76 | G.add_edge("猪八戒", "悟空") 77 | G.add_edge("猪八戒", "唐僧") 78 | 79 | G.add_edge("红孩儿", "铁扇公主") 80 | G.add_edge("红孩儿", "牛魔王") 81 | G.add_edge("红孩儿", "小猪妖") 82 | 83 | G.add_edge("小猪妖", "红孩儿") 84 | 85 | print(G.bfs("悟空")) # 输出: ['悟空', '唐僧', '牛魔王', '猪八戒', '铁扇公主', '红孩儿', '小猪妖'] 86 | 87 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/MatrixExponentiation.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * 空间复杂度分析,分以下两步骤: 7 | * 1. 矩阵存储:我们需要常量空间来存储两个2x2的矩阵,所以其空间复杂度为O(1)。 8 | * 2. 递归的深度:使用快速幂算法时,递归的最大深度与N的二进制长度成正比,即O(log N)。 9 | * 由于每一级递归中我们并没有保存额外的矩阵或其他大量数据(我们只是对矩阵进行乘法),所以这里的空间复杂度主要是由于递归调用栈的深度,也是O(log N)。 10 | * 综上: 使用矩阵乘法和快速幂方法求第N个斐波那契数的空间复杂度是O(log N)。 11 | */ 12 | 13 | 14 | public class MatrixExponentiation { 15 | 16 | public static void main(String[] args) { 17 | 18 | /** 19 | * 测试代码 20 | */ 21 | 22 | System.out.println(fibonacci(9)); // 输出34,第9个斐波那契数 23 | System.out.println(fibonacci(10)); // 输出55 24 | System.out.println(fibonacci(0)); // 输出0 25 | System.out.println(fibonacci(50)); // 输出 12586269025 26 | System.out.println(fibonacci(100)); // 输出 354224848179261915075 27 | System.out.println(fibonacci(10000)); // 输出第10000个斐波那契数 28 | } 29 | 30 | // 2x2矩阵乘法 31 | public static BigInteger[][] multiply(BigInteger[][] A, BigInteger[][] B) { 32 | BigInteger[][] result = new BigInteger[2][2]; 33 | 34 | for (int i = 0; i < 2; i++) { 35 | for (int j = 0; j < 2; j++) { 36 | result[i][j] = (A[i][0].multiply(B[0][j])).add(A[i][1].multiply(B[1][j])); 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | 43 | // 快速幂计算矩阵的n次方 44 | public static BigInteger[][] power(BigInteger[][] matrix, int n) { 45 | if (n == 1) { 46 | return matrix; 47 | } 48 | 49 | if (n % 2 == 0) { 50 | BigInteger[][] halfPower = power(matrix, n / 2); 51 | return multiply(halfPower, halfPower); 52 | } else { 53 | return multiply(matrix, power(matrix, n - 1)); 54 | } 55 | } 56 | 57 | // 使用矩阵指数法计算第n个斐波那契数 58 | public static BigInteger fibonacci(int n) { 59 | if (n <= 1) { 60 | return BigInteger.valueOf(n); 61 | } 62 | 63 | // 基本矩阵 64 | BigInteger[][] matrix = { 65 | {BigInteger.ONE, BigInteger.ONE}, 66 | {BigInteger.ONE, BigInteger.ZERO} 67 | }; 68 | 69 | // 计算矩阵的n-1次方 70 | matrix = power(matrix, n - 1); 71 | 72 | // 结果即为矩阵的[0][0]元素 73 | return matrix[0][0]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/dp-table/coin-DP-table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 2D DP Table Visualization for Coin Change 8 | 9 | 23 | 24 | 25 | 26 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/OrderedLinkedListPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | public class OrderedLinkedListPQ> implements PriorityQueue { 4 | 5 | // 定义链表节点 6 | private class Node { 7 | T item; 8 | Node next; 9 | 10 | Node(T item, Node next) { 11 | this.item = item; 12 | this.next = next; 13 | } 14 | } 15 | 16 | private Node first; // 链表的第一个节点 17 | private int size; // 优先队列的大小 18 | 19 | public OrderedLinkedListPQ() { 20 | first = null; 21 | size = 0; 22 | } 23 | 24 | @Override 25 | public void insert(T item) { 26 | if (first == null || item.compareTo(first.item) >= 0) { 27 | // 插入到链表的开始位置 28 | first = new Node(item, first); 29 | } else { 30 | // 找到正确的插入位置 31 | Node prev = first; 32 | Node current = first.next; 33 | while (current != null && item.compareTo(current.item) < 0) { 34 | prev = current; 35 | current = current.next; 36 | } 37 | prev.next = new Node(item, current); 38 | } 39 | size++; 40 | } 41 | 42 | @Override 43 | public T max() { 44 | if (isEmpty()) { 45 | throw new RuntimeException("Queue is empty"); 46 | } 47 | return first.item; 48 | } 49 | 50 | 51 | @Override 52 | public T extractMax() { 53 | if (isEmpty()) { 54 | throw new RuntimeException("Queue is empty"); 55 | } 56 | T maxItem = first.item; 57 | first = first.next; 58 | size--; 59 | return maxItem; 60 | } 61 | 62 | @Override 63 | public boolean isEmpty() { 64 | return size == 0; 65 | } 66 | 67 | @Override 68 | public int size() { return size; } 69 | 70 | // 以下接口未实现 71 | @Override public T extractMin() { return null; } 72 | 73 | @Override 74 | public T min() { return null; } 75 | 76 | @Override 77 | public void merge(PriorityQueue other) { } 78 | 79 | @Override 80 | public void clear() {} 81 | 82 | public static void main(String[] args) { 83 | OrderedLinkedListPQ pq = new OrderedLinkedListPQ<>(); 84 | pq.insert(10); 85 | pq.insert(20); 86 | pq.insert(5); 87 | System.out.println(pq.extractMax()); // 输出: 20 88 | System.out.println(pq.max()); // 输出: 10 89 | System.out.println(pq.extractMax()); // 输出: 10 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/SearchComparison.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashSet; 6 | import java.util.Random; 7 | 8 | public class SearchComparison { 9 | 10 | public static int linearSearch(int[] arr, int target) { 11 | for (int i = 0; i < arr.length; i++) { 12 | if (arr[i] == target) { 13 | return i; 14 | } 15 | } 16 | return -1; 17 | } 18 | 19 | public static int binarySearch(int[] arr, int target) { 20 | int left = 0; 21 | int right = arr.length - 1; 22 | 23 | while (left <= right) { 24 | int mid = left + (right - left) / 2; 25 | if (arr[mid] == target) { 26 | return mid; 27 | } else if (arr[mid] < target) { 28 | left = mid + 1; 29 | } else { 30 | right = mid - 1; 31 | } 32 | } 33 | return -1; 34 | } 35 | 36 | public static void main(String[] args) { 37 | int size = 10; // 小数组,线性搜索更快 38 | HashSet set = new HashSet<>(); 39 | Random rand = new Random(); 40 | 41 | // 生成独一无二的随机数 42 | while (set.size() < size) { 43 | set.add(rand.nextInt()); 44 | } 45 | 46 | // 转换为数组并排序 47 | ArrayList list = new ArrayList<>(set); 48 | Collections.sort(list); 49 | int[] arr = list.stream().mapToInt(i -> i).toArray(); 50 | 51 | // 选择一个随机的目标值 52 | int target = arr[rand.nextInt(arr.length)]; 53 | 54 | // 计算线性搜索的平均运行时间 55 | long totalTime = 0; 56 | int repeatTimes = 100; 57 | for (int i = 0; i < repeatTimes; i++) { 58 | long startTime = System.nanoTime(); 59 | linearSearch(arr, target); 60 | long endTime = System.nanoTime(); 61 | totalTime += (endTime - startTime); 62 | } 63 | System.out.println("Average Linear Search Time: " + (totalTime / repeatTimes) + " nanoseconds(纳秒)"); 64 | 65 | // 计算二分搜索的平均运行时间 66 | totalTime = 0; 67 | for (int i = 0; i < repeatTimes; i++) { 68 | long startTime = System.nanoTime(); 69 | binarySearch(arr, target); 70 | long endTime = System.nanoTime(); 71 | totalTime += (endTime - startTime); 72 | } 73 | System.out.println("Average Binary Search Time: " + (totalTime / repeatTimes) + " nanoseconds(纳秒)"); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/UnorderedArrayPQImpl.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | /** 4 | * Desc: 功能同 UnorderedArrayPQ,这里展示实际对接口编程的具体方法 5 | */ 6 | 7 | public class UnorderedArrayPQImpl> implements PriorityQueue { 8 | 9 | private T[] arr; // 用于存储数据的数组 10 | private int n; // 数组中的实际元素个数 11 | 12 | // 初始化一个给定大小的优先队列 13 | @SuppressWarnings("unchecked") 14 | public UnorderedArrayPQImpl(int capacity) { 15 | arr = (T[]) new Comparable[capacity]; 16 | n = 0; 17 | } 18 | 19 | @Override 20 | public void insert(T item) { 21 | if (n == arr.length) { 22 | throw new RuntimeException("队列已满"); // 如果数组已满,抛出异常 23 | } 24 | arr[n++] = item; // 在数组的末尾插入元素 25 | } 26 | 27 | @Override 28 | public T extractMax() { 29 | if (isEmpty()) { 30 | throw new RuntimeException("队列为空"); // 如果数组为空,抛出异常 31 | } 32 | 33 | int maxIndex = 0; 34 | // 遍历数组,找到最大值的索引 35 | for (int i = 1; i < n; i++) { 36 | if (arr[i].compareTo(arr[maxIndex]) > 0) { 37 | maxIndex = i; 38 | } 39 | } 40 | 41 | T maxValue = arr[maxIndex]; 42 | 43 | // 将最大值后面的元素左移,覆盖最大值 44 | for (int i = maxIndex; i < n - 1; i++) { 45 | arr[i] = arr[i + 1]; 46 | } 47 | 48 | n--; // 减少数组中的实际元素个数 49 | return maxValue; 50 | } 51 | 52 | @Override 53 | public T max() { 54 | if (isEmpty()) { 55 | throw new RuntimeException("队列为空"); // 如果数组为空,抛出异常 56 | } 57 | 58 | int maxIndex = 0; 59 | // 遍历数组,找到最大值的索引 60 | for (int i = 1; i < n; i++) { 61 | if (arr[i].compareTo(arr[maxIndex]) > 0) { 62 | maxIndex = i; 63 | } 64 | } 65 | 66 | return arr[maxIndex]; 67 | } 68 | 69 | @Override 70 | public int size() { return n; } 71 | 72 | @Override 73 | public boolean isEmpty() { return n == 0; } 74 | 75 | // 下面的方法都未实现,可以根据需要进行实现 76 | @Override 77 | public T extractMin() { return null; } 78 | 79 | @Override 80 | public T min() { return null; } 81 | 82 | @Override 83 | public void merge(PriorityQueue other) {} 84 | 85 | @Override 86 | public void clear() {} 87 | 88 | public static void main(String[] args) { 89 | UnorderedArrayPQImpl pq = new UnorderedArrayPQImpl<>(10); 90 | pq.insert(5); 91 | pq.insert(3); 92 | pq.insert(7); 93 | pq.insert(2); 94 | 95 | System.out.println(pq.extractMax()); // 输出7 96 | System.out.println(pq.extractMax()); // 输出5 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortStringPerformanceTest.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Random; 4 | 5 | public class HeapSortStringPerformanceTest { 6 | 7 | public static void main(String[] args) { 8 | // 定义不同的测试规模 9 | int[] sizes = {200000, 400000, 600000}; // 对于长字符串,规模应适当减小 10 | 11 | // 对每个规模进行测试 12 | for (int size : sizes) { 13 | long totalTimeBeforeOptimization = 0; 14 | long totalTimeAfterOptimization = 0; 15 | 16 | // 运行5次实验并取平均值 17 | for (int i = 0; i < 5; i++) { 18 | String[] originalArray = generateRandomStringArray(size, 500); // 生成长度为 N 的随机字符串 19 | 20 | // 测试优化后的堆排序 21 | String[] arrayAfterOptimization = originalArray.clone(); 22 | long startTime = System.currentTimeMillis(); 23 | HeapSortFloyd.heapSort(arrayAfterOptimization); 24 | long endTime = System.currentTimeMillis(); 25 | totalTimeAfterOptimization += (endTime - startTime); 26 | 27 | // 测试优化前的堆排序 28 | String[] arrayBeforeOptimization = originalArray.clone(); 29 | 30 | startTime = System.currentTimeMillis(); 31 | HeapSortStandard.heapSort(arrayBeforeOptimization); 32 | endTime = System.currentTimeMillis(); 33 | totalTimeBeforeOptimization += (endTime - startTime); 34 | } 35 | 36 | long averageTimeBeforeOptimization = totalTimeBeforeOptimization / 5; 37 | long averageTimeAfterOptimization = totalTimeAfterOptimization / 5; 38 | 39 | System.out.println("规模: " + size); 40 | System.out.println("优化前平均时间: " + averageTimeBeforeOptimization + "ms"); 41 | System.out.println("优化后平均时间: " + averageTimeAfterOptimization + "ms"); 42 | System.out.println(); 43 | } 44 | } 45 | 46 | // 生成随机字符串数组 47 | private static String[] generateRandomStringArray(int size, int stringLength) { 48 | String[] array = new String[size]; 49 | Random random = new Random(); 50 | for (int i = 0; i < size; i++) { 51 | array[i] = generateRandomString(stringLength, random); 52 | } 53 | return array; 54 | } 55 | 56 | // 生成指定长度的随机字符串 57 | private static String generateRandomString(int length, Random random) { 58 | String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 59 | StringBuilder sb = new StringBuilder(length); 60 | for (int i = 0; i < length; i++) { 61 | sb.append(characters.charAt(random.nextInt(characters.length()))); 62 | } 63 | return sb.toString(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/09/dp-table/lcs-DP-table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCS DP Table with D3.js 8 | 9 | 10 | 11 | 12 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/05/LinearProbeHashTable.py: -------------------------------------------------------------------------------- 1 | class LinearProbeHashTable: 2 | def __init__(self, size): 3 | """ 4 | 初始化散列表的大小和数组 5 | """ 6 | self.size = size 7 | self.threshold = int(size * 0.7) # 装填因子阈值 8 | self.count = 0 # 当前散列表中的元素个数 9 | self.hash_table = [None] * size 10 | 11 | def _hash(self, key): 12 | """ 13 | 散列函数,将键转换为散列索引 14 | """ 15 | return key % self.size 16 | 17 | def _next_index(self, index): 18 | """ 19 | 获取下一个索引位置,使用线性探测法 20 | """ 21 | return (index + 1) % self.size 22 | 23 | def _resize(self): 24 | """ 25 | 重新开辟地址空间,扩大散列表的大小并重新插入已有的键值对 26 | """ 27 | self.size *= 2 # 扩大散列表的大小为原来的两倍 28 | self.threshold = int(self.size * 0.5) # 更新装填因子阈值 29 | old_table = self.hash_table 30 | self.hash_table = [None] * self.size 31 | self.count = 0 32 | 33 | for item in old_table: 34 | if item is not None: 35 | key, value = item 36 | self.insert(key, value) # 重新插入已有的键值对 37 | 38 | def insert(self, key, value): 39 | """ 40 | 向散列表中插入键值对,当装填因子达到阈值时重新开辟地址空间 41 | """ 42 | if self.count >= self.threshold: 43 | self._resize() # 装填因子超过阈值,重新开辟地址空间 44 | 45 | index = self._hash(key) # 计算初始索引 46 | while self.hash_table[index] is not None: 47 | index = self._next_index(index) # 冲突发生,获取下一个索引位置 48 | self.hash_table[index] = (key, value) # 插入键值对 49 | self.count += 1 50 | 51 | def search(self, key): 52 | """ 53 | 在散列表中查找给定键的值 54 | """ 55 | index = self._hash(key) # 计算初始索引 56 | while self.hash_table[index] is not None: 57 | if self.hash_table[index][0] == key: 58 | return self.hash_table[index][1] # 返回键对应的值 59 | index = self._next_index(index) # 冲突发生,获取下一个索引位置 60 | return None # 键不存在于散列表中 61 | 62 | # 创建一个大小为10的散列表实例 63 | hash_table = LinearProbeHashTable(10) 64 | 65 | # 插入键值对 66 | hash_table.insert(5, "Value 1") 67 | hash_table.insert(15, "Value 2") 68 | hash_table.insert(25, "Value 3") 69 | 70 | # 查找键的值 71 | print(hash_table.search(5)) # 输出: Value 1 72 | print(hash_table.search(15)) # 输出: Value 2 73 | print(hash_table.search(25)) # 输出: Value 3 74 | print(hash_table.search(35)) # 输出: None (键不存在) 75 | 76 | 77 | # 创建一个大小为3的散列表实例 78 | hash_table2 = LinearProbeHashTable(3) 79 | 80 | # 插入键值对,使装填因子超过阈值 81 | hash_table2.insert(5, "Value 1") 82 | hash_table2.insert(15, "Value 2") 83 | hash_table2.insert(25, "Value 3") 84 | hash_table2.insert(35, "Value 4") 85 | 86 | # 查找键的值 87 | print(hash_table2.search(5)) # 输出: Value 1 88 | print(hash_table2.search(15)) # 输出: Value 2 89 | print(hash_table2.search(25)) # 输出: Value 3 90 | print(hash_table2.search(35)) # 输出: Value 4 91 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/UnorderedLinkedListPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | 4 | public class UnorderedLinkedListPQ> implements PriorityQueue { 5 | 6 | // 定义链表节点 7 | private class Node { 8 | T item; 9 | Node next; 10 | 11 | Node(T item, Node next) { 12 | this.item = item; 13 | this.next = next; 14 | } 15 | } 16 | 17 | private Node first; // 链表的第一个节点 18 | private int size; // 优先队列的大小 19 | 20 | public UnorderedLinkedListPQ() { 21 | first = null; 22 | size = 0; 23 | } 24 | 25 | @Override 26 | public void insert(T item) { 27 | Node newNode = new Node(item, first); 28 | first = newNode; 29 | size++; 30 | } 31 | 32 | @Override 33 | public T max() { 34 | if (isEmpty()) { 35 | throw new RuntimeException("Queue is empty"); 36 | } 37 | Node maxNode = first; 38 | Node current = first.next; 39 | while (current != null) { 40 | if (current.item.compareTo(maxNode.item) > 0) { 41 | maxNode = current; 42 | } 43 | current = current.next; 44 | } 45 | return maxNode.item; 46 | } 47 | 48 | @Override 49 | public T min() { 50 | return null; 51 | } 52 | 53 | @Override 54 | public T extractMax() { 55 | if (isEmpty()) { 56 | throw new RuntimeException("Queue is empty"); 57 | } 58 | 59 | Node maxPrev = null; 60 | Node maxNode = first; 61 | 62 | // 找到最大元素的节点和它的前一个节点 63 | Node prev = first; 64 | Node current = first.next; 65 | while (current != null) { 66 | if (current.item.compareTo(maxNode.item) > 0) { 67 | maxNode = current; 68 | maxPrev = prev; 69 | } 70 | prev = current; 71 | current = current.next; 72 | } 73 | 74 | // 如果最大节点是第一个节点 75 | if (maxNode == first) { 76 | first = first.next; 77 | } else { 78 | maxPrev.next = maxNode.next; 79 | } 80 | 81 | size--; 82 | return maxNode.item; 83 | } 84 | 85 | @Override 86 | public boolean isEmpty() { return size == 0; } 87 | 88 | // 下面接口未实现 89 | @Override 90 | public T extractMin() { return null; } 91 | 92 | @Override 93 | public int size() { return 0; } 94 | 95 | 96 | @Override 97 | public void merge(PriorityQueue other) {} 98 | 99 | @Override 100 | public void clear() {} 101 | 102 | public static void main(String[] args) { 103 | UnorderedLinkedListPQ pq = new UnorderedLinkedListPQ<>(); 104 | pq.insert(10); 105 | pq.insert(20); 106 | pq.insert(5); 107 | System.out.println(pq.extractMax()); // 输出: 20 108 | System.out.println(pq.max()); // 输出: 10 109 | System.out.println(pq.extractMax()); // 输出: 10 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/07/d3-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | D3 Graph 5 | 6 | 16 | 17 | 18 | 19 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/analysis_of_algorithms/ThreeSumFastModelFitting.java: -------------------------------------------------------------------------------- 1 | package analysis_of_algorithms; 2 | 3 | import org.apache.commons.math3.analysis.MultivariateMatrixFunction; 4 | import org.apache.commons.math3.analysis.MultivariateVectorFunction; 5 | import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder; 6 | import org.apache.commons.math3.fitting.leastsquares.LeastSquaresOptimizer; 7 | import org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer; 8 | import org.apache.commons.math3.linear.RealVector; 9 | import org.apache.commons.math3.linear.ArrayRealVector; 10 | 11 | public class ThreeSumFastModelFitting { 12 | 13 | public static void main(String[] args) { 14 | // 给定数据点 15 | double[] N_values = new double[]{1000, 2000, 4000, 8000, 16000, 32000}; 16 | double[] T_values = new double[]{0.015, 0.064, 0.296, 1.238, 5.118, 21.611}; 17 | 18 | // 定义模型函数 19 | MultivariateVectorFunction vectorFunction = (point) -> { 20 | double a = point[0]; 21 | double b = point[1]; 22 | double c = point[2]; 23 | double d = point[3]; 24 | double[] values = new double[N_values.length]; 25 | for (int i = 0; i < values.length; i++) { 26 | values[i] = a * N_values[i] * N_values[i] * Math.log(N_values[i]) 27 | + b * N_values[i] * Math.log(N_values[i]) 28 | + c * N_values[i] 29 | + d; 30 | } 31 | return values; 32 | }; 33 | 34 | // 导数(雅可比矩阵)的计算 35 | MultivariateMatrixFunction matrixFunction = (point) -> { 36 | double[][] jacobian = new double[N_values.length][4]; 37 | for (int i = 0; i < jacobian.length; i++) { 38 | jacobian[i][0] = N_values[i] * N_values[i] * Math.log(N_values[i]); 39 | jacobian[i][1] = N_values[i] * Math.log(N_values[i]); 40 | jacobian[i][2] = N_values[i]; 41 | jacobian[i][3] = 1; 42 | } 43 | return jacobian; 44 | }; 45 | 46 | // 构建最小二乘问题 47 | LeastSquaresBuilder builder = new LeastSquaresBuilder(); 48 | builder.model(vectorFunction, matrixFunction); 49 | builder.target(new ArrayRealVector(T_values, false)); 50 | builder.start(new double[]{0.0, 0.0, 0.0, 0.0}); 51 | builder.maxEvaluations(Integer.MAX_VALUE); // 设置最大评估次数 52 | builder.maxIterations(Integer.MAX_VALUE); // 设置最大迭代次数 53 | 54 | // 解决最小二乘问题 55 | LeastSquaresOptimizer optimizer = new LevenbergMarquardtOptimizer(); 56 | LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(builder.build()); 57 | 58 | // 获取并打印参数 59 | RealVector optimumPoint = optimum.getPoint(); 60 | System.out.println("a = " + optimumPoint.getEntry(0)); 61 | System.out.println("b = " + optimumPoint.getEntry(1)); 62 | System.out.println("c = " + optimumPoint.getEntry(2)); 63 | System.out.println("d = " + optimumPoint.getEntry(3)); 64 | 65 | // a = 2.0160189155176637E-9 66 | // b = -3.558980015271123E-6 67 | // c = 4.378181546967302E-5 68 | // d = -0.024067014663248048 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/others/graph_with_d3/d3-demo6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tree Diagram 6 | 7 | 24 | 25 | 26 | 27 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortFloyd.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Arrays; 4 | 5 | public class HeapSortFloyd { 6 | 7 | // 泛化的堆排序函数 8 | public static > void heapSort(T[] arr) { 9 | 10 | int n = arr.length; 11 | 12 | // 构建最大堆 13 | for (int i = n / 2 - 1; i >= 0; i--) { 14 | heapify(arr, n, i); 15 | } 16 | 17 | // 一个个从堆顶取出元素 18 | for (int i = n - 1; i > 0; i--) { 19 | // 将堆尾元素保存到临时变量 20 | T temp = arr[i]; 21 | 22 | // 将堆顶元素放到堆尾 23 | arr[i] = arr[0]; 24 | 25 | // 从堆顶开始调整堆 26 | siftDown(arr, i, 0, temp); 27 | } 28 | } 29 | 30 | // 泛化的调整堆的函数 31 | private static > void siftDown(T[] arr, int n, int i, T temp) { 32 | int parent = i; // 起始位置为当前“空”堆顶 33 | int child; 34 | 35 | while (true) { 36 | int leftChild = 2 * parent + 1; 37 | int rightChild = 2 * parent + 2; 38 | 39 | if (leftChild >= n) { 40 | break; // 没有子节点了 41 | } 42 | 43 | if (rightChild >= n) { 44 | child = leftChild; // 只有左子节点 45 | } else { 46 | child = arr[leftChild].compareTo(arr[rightChild]) > 0 ? leftChild : rightChild; 47 | } 48 | 49 | arr[parent] = arr[child]; 50 | parent = child; 51 | } 52 | 53 | // 将原先的堆尾元素放到调整后的位置 54 | arr[parent] = temp; 55 | 56 | // 可能的上浮操作 57 | while (parent > 0 && arr[parent].compareTo(arr[(parent - 1) / 2]) > 0) { 58 | T swap = arr[parent]; 59 | arr[parent] = arr[(parent - 1) / 2]; 60 | arr[(parent - 1) / 2] = swap; 61 | parent = (parent - 1) / 2; 62 | } 63 | } 64 | 65 | // 泛化的构建堆的函数 66 | private static > void heapify(T[] arr, int n, int i) { 67 | int largest = i; 68 | int l = 2 * i + 1; 69 | int r = 2 * i + 2; 70 | 71 | if (l < n && arr[l].compareTo(arr[largest]) > 0) { 72 | largest = l; 73 | } 74 | 75 | if (r < n && arr[r].compareTo(arr[largest]) > 0) { 76 | largest = r; 77 | } 78 | 79 | if (largest != i) { 80 | T swap = arr[i]; 81 | arr[i] = arr[largest]; 82 | arr[largest] = swap; 83 | 84 | heapify(arr, n, largest); 85 | } 86 | } 87 | // 测试代码(主函数) 88 | public static void main(String[] args) { 89 | // 整数数组示例 90 | Integer[] intArr = {12, 11, 13, 5, 6, 7}; 91 | heapSort(intArr); 92 | System.out.println("Sorted integer array (Floyd):"); 93 | System.out.println(Arrays.toString(intArr)); 94 | 95 | // 字符串数组示例 96 | String[] stringArr = {"banana", "apple", "cherry", "date"}; 97 | heapSort(stringArr); 98 | System.out.println("Sorted string array (Floyd):"); 99 | System.out.println(Arrays.toString(stringArr)); 100 | 101 | // 创建一个包含40个随机整数的数组 102 | Integer[] testArray = new Integer[40]; 103 | for (int i = 0; i < testArray.length; i++) { 104 | testArray[i] = (int) (Math.random() * 100); // 生成0到99之间的随机整数 105 | } 106 | heapSort(testArray); 107 | System.out.println("Sorted integer array (Floyd):"); 108 | System.out.println(Arrays.toString(testArray)); 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/priority_queues/BinaryHeapPQ.java: -------------------------------------------------------------------------------- 1 | package priority_queues; 2 | 3 | import java.util.Arrays; 4 | 5 | public class BinaryHeapPQ> implements PriorityQueue { 6 | 7 | private T[] heap; 8 | private int size; 9 | 10 | @SuppressWarnings("unchecked") 11 | public BinaryHeapPQ(int capacity) { 12 | heap = (T[]) new Comparable[capacity + 1]; 13 | size = 0; 14 | } 15 | 16 | @Override 17 | public void insert(T item) { 18 | if (size == heap.length - 1) { 19 | throw new RuntimeException("Queue is full"); 20 | } 21 | heap[++size] = item; 22 | swim(size); 23 | } 24 | 25 | @Override 26 | public T max() { 27 | if (isEmpty()) { 28 | throw new RuntimeException("Queue is empty"); 29 | } 30 | return heap[1]; 31 | } 32 | 33 | @Override 34 | public T extractMax() { 35 | if (isEmpty()) { 36 | throw new RuntimeException("Queue is empty"); 37 | } 38 | T maxItem = heap[1]; 39 | swap(1, size); 40 | heap[size--] = null; 41 | sink(1); 42 | return maxItem; 43 | } 44 | 45 | // Other implemented methods 46 | 47 | @Override 48 | public T min() { 49 | throw new UnsupportedOperationException("BinaryHeapPQ is a max-priority queue"); 50 | } 51 | 52 | @Override 53 | public T extractMin() { 54 | throw new UnsupportedOperationException("BinaryHeapPQ is a max-priority queue"); 55 | } 56 | 57 | @Override 58 | public int size() { 59 | return size; 60 | } 61 | 62 | @Override 63 | public boolean isEmpty() { 64 | return size == 0; 65 | } 66 | 67 | @Override 68 | public void merge(PriorityQueue other) { 69 | if (other instanceof BinaryHeapPQ) { 70 | BinaryHeapPQ otherHeap = (BinaryHeapPQ) other; 71 | for (int i = 1; i <= otherHeap.size; i++) { 72 | insert(otherHeap.heap[i]); 73 | } 74 | } else { 75 | throw new IllegalArgumentException("Unsupported type: " + other.getClass()); 76 | } 77 | } 78 | 79 | @Override 80 | public void clear() { 81 | Arrays.fill(heap, null); 82 | size = 0; 83 | } 84 | 85 | // Helper methods 86 | 87 | private void swim(int k) { 88 | while (k > 1 && less(k/2, k)) { 89 | swap(k/2, k); 90 | k = k/2; 91 | } 92 | } 93 | 94 | private void sink(int k) { 95 | while (2*k <= size) { 96 | int j = 2*k; 97 | if (j < size && less(j, j+1)) j++; 98 | if (!less(k, j)) break; 99 | swap(k, j); 100 | k = j; 101 | } 102 | } 103 | 104 | private boolean less(int i, int j) { 105 | return heap[i].compareTo(heap[j]) < 0; 106 | } 107 | 108 | private void swap(int i, int j) { 109 | T temp = heap[i]; 110 | heap[i] = heap[j]; 111 | heap[j] = temp; 112 | } 113 | 114 | // Test cases 115 | public static void main(String[] args) { 116 | BinaryHeapPQ pq = new BinaryHeapPQ<>(10); 117 | pq.insert(10); 118 | pq.insert(20); 119 | pq.insert(5); 120 | System.out.println(pq.extractMax()); // Output: 20 121 | System.out.println(pq.size()); // Output: 2 122 | 123 | BinaryHeapPQ pq2 = new BinaryHeapPQ<>(10); 124 | pq2.insert(15); 125 | pq.merge(pq2); 126 | System.out.println(pq.size()); // Output: 3 127 | 128 | pq.clear(); 129 | System.out.println(pq.isEmpty()); // Output: true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/substr_search/BoyerMoore.java: -------------------------------------------------------------------------------- 1 | package substr_search; 2 | 3 | import edu.princeton.cs.algs4.StdOut; 4 | 5 | public class BoyerMoore { 6 | private final int R; // 基数 7 | private int[] right; // 坏字符跳转数组 8 | 9 | private char[] pattern; // 将模式存储为字符数组 10 | private String pat; // 或作为字符串 11 | 12 | /** 13 | * 预处理模式字符串。 14 | * 15 | * @param pat 模式字符串 16 | */ 17 | public BoyerMoore(String pat) { 18 | this.R = 256; 19 | this.pat = pat; 20 | 21 | // c在模式中最右出现的位置 22 | right = new int[R]; 23 | for (int c = 0; c < R; c++) 24 | right[c] = -1; 25 | for (int j = 0; j < pat.length(); j++) 26 | right[pat.charAt(j)] = j; 27 | } 28 | 29 | /** 30 | * 预处理模式字符串。 31 | * 32 | * @param pattern 模式字符串 33 | * @param R 字母表大小 34 | */ 35 | public BoyerMoore(char[] pattern, int R) { 36 | this.R = R; 37 | this.pattern = new char[pattern.length]; 38 | for (int j = 0; j < pattern.length; j++) 39 | this.pattern[j] = pattern[j]; 40 | 41 | // c在模式中最右出现的位置 42 | right = new int[R]; 43 | for (int c = 0; c < R; c++) 44 | right[c] = -1; 45 | for (int j = 0; j < pattern.length; j++) 46 | right[pattern[j]] = j; 47 | } 48 | 49 | /** 50 | * 返回模式字符串在文本字符串中第一次出现的索引。 51 | * 52 | * @param txt 文本字符串 53 | * @return 模式字符串在文本字符串中第一次出现的索引; 54 | * 如果没有匹配,则为n 55 | */ 56 | public int search(String txt) { 57 | int m = pat.length(); 58 | int n = txt.length(); 59 | int skip; 60 | for (int i = 0; i <= n - m; i += skip) { 61 | skip = 0; 62 | for (int j = m-1; j >= 0; j--) { 63 | if (pat.charAt(j) != txt.charAt(i+j)) { 64 | skip = Math.max(1, j - right[txt.charAt(i+j)]); 65 | break; 66 | } 67 | } 68 | if (skip == 0) return i; // 找到 69 | } 70 | return n; // 未找到 71 | } 72 | 73 | 74 | /** 75 | * 返回模式字符串在文本字符串中第一次出现的索引。 76 | * 77 | * @param text 文本字符串 78 | * @return 模式字符串在文本字符串中第一次出现的索引; 79 | * 如果没有匹配,则为n 80 | */ 81 | public int search(char[] text) { 82 | int m = pattern.length; 83 | int n = text.length; 84 | int skip; 85 | for (int i = 0; i <= n - m; i += skip) { 86 | skip = 0; 87 | for (int j = m-1; j >= 0; j--) { 88 | if (pattern[j] != text[i+j]) { 89 | skip = Math.max(1, j - right[text[i+j]]); 90 | break; 91 | } 92 | } 93 | if (skip == 0) return i; // 找到 94 | } 95 | return n; // 未找到 96 | } 97 | 98 | 99 | /** 100 | * 作为命令行参数接受一个模式字符串和一个输入字符串; 101 | * 在文本字符串中搜索模式字符串;并打印 102 | * 模式字符串在文本字符串中的第一次出现。 103 | * 104 | * @param args 命令行参数 105 | */ 106 | public static void main(String[] args) { 107 | 108 | // 示例: 109 | String pat = "abracadabra"; 110 | String txt = "abacadabrabracabracadabrabrabracad"; 111 | 112 | char[] pattern = pat.toCharArray(); 113 | char[] text = txt.toCharArray(); 114 | 115 | BoyerMoore boyermoore1 = new BoyerMoore(pat); 116 | BoyerMoore boyermoore2 = new BoyerMoore(pattern, 256); 117 | int offset1 = boyermoore1.search(txt); 118 | int offset2 = boyermoore2.search(text); 119 | 120 | // 打印结果 121 | StdOut.println("text: " + txt); 122 | 123 | StdOut.print("pattern: "); 124 | for (int i = 0; i < offset1; i++) 125 | StdOut.print(" "); 126 | StdOut.println(pat); 127 | 128 | StdOut.print("pattern: "); 129 | for (int i = 0; i < offset2; i++) 130 | StdOut.print(" "); 131 | StdOut.println(pat); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/minimum_spanning_trees/KruskalMST.java: -------------------------------------------------------------------------------- 1 | package minimum_spanning_trees; 2 | 3 | import edu.princeton.cs.algs4.*; 4 | 5 | import java.util.Arrays; 6 | 7 | public class KruskalMST { 8 | private static final double FLOATING_POINT_EPSILON = 1.0E-12; // 浮点数精度误差阈值 9 | 10 | private double weight; // MST的权重 11 | private Queue mst = new Queue(); // MST中的边 12 | 13 | /** 14 | * 计算一个加权图的最小生成树(或森林)。 15 | * @param G 加权图 16 | */ 17 | public KruskalMST(EdgeWeightedGraph G) { 18 | 19 | // 创建边的数组,并按权重排序 20 | Edge[] edges = new Edge[G.E()]; 21 | int t = 0; 22 | for (Edge e: G.edges()) { 23 | edges[t++] = e; 24 | } 25 | Arrays.sort(edges); 26 | 27 | // 执行贪心算法 28 | UF uf = new UF(G.V()); 29 | for (int i = 0; i < G.E() && mst.size() < G.V() - 1; i++) { 30 | Edge e = edges[i]; 31 | int v = e.either(); 32 | int w = e.other(v); 33 | 34 | // v-w不会创建环 35 | if (uf.find(v) != uf.find(w)) { 36 | uf.union(v, w); // 合并v和w的分量 37 | mst.enqueue(e); // 将边e添加到mst 38 | weight += e.weight(); 39 | } 40 | } 41 | 42 | // 检查最优条件 43 | assert check(G); 44 | } 45 | 46 | /** 47 | * 返回最小生成树(或森林)中的边。 48 | * @return 作为边的可迭代对象的最小生成树(或森林)中的边 49 | */ 50 | public Iterable edges() { 51 | return mst; 52 | } 53 | 54 | /** 55 | * 返回最小生成树(或森林)中边的权重总和。 56 | * @return 最小生成树(或森林)中边的权重总和 57 | */ 58 | public double weight() { 59 | return weight; 60 | } 61 | 62 | // 检查最优条件(时间与E V lg* V成正比) 63 | private boolean check(EdgeWeightedGraph G) { 64 | // 检查边的总权重 65 | double total = 0.0; 66 | for (Edge e : edges()) { 67 | total += e.weight(); 68 | } 69 | if (Math.abs(total - weight()) > FLOATING_POINT_EPSILON) { 70 | System.err.printf("边的权重总和与weight()不相等:%f vs. %f\n", total, weight()); 71 | return false; 72 | } 73 | 74 | // 检查它是无环的 75 | UF uf = new UF(G.V()); 76 | for (Edge e : edges()) { 77 | int v = e.either(), w = e.other(v); 78 | if (uf.find(v) == uf.find(w)) { 79 | System.err.println("不是森林"); 80 | return false; 81 | } 82 | uf.union(v, w); 83 | } 84 | 85 | // 检查它是一个覆盖森林 86 | for (Edge e : G.edges()) { 87 | int v = e.either(), w = e.other(v); 88 | if (uf.find(v) != uf.find(w)) { 89 | System.err.println("不是覆盖森林"); 90 | return false; 91 | } 92 | } 93 | 94 | // 检查它是最小覆盖森林(割优化条件) 95 | for (Edge e : edges()) { 96 | 97 | // MST中除了e的所有边 98 | uf = new UF(G.V()); 99 | for (Edge f : mst) { 100 | int x = f.either(), y = f.other(x); 101 | if (f != e) uf.union(x, y); 102 | } 103 | 104 | // 检查e是横跨割中最小权重的边 105 | for (Edge f : G.edges()) { 106 | int x = f.either(), y = f.other(x); 107 | if (uf.find(x) != uf.find(y)) { 108 | if (f.weight() < e.weight()) { 109 | System.err.println("边 " + f + " 违反了割优化条件"); 110 | return false; 111 | } 112 | } 113 | } 114 | 115 | } 116 | 117 | return true; 118 | } 119 | 120 | /** 121 | * 对{@code KruskalMST}数据类型进行单元测试。 122 | * 123 | * @param args 命令行参数 124 | */ 125 | public static void main(String[] args) { 126 | In in = new In(args[0]); 127 | EdgeWeightedGraph G = new EdgeWeightedGraph(in); 128 | KruskalMST mst = new KruskalMST(G); 129 | for (Edge e : mst.edges()) { 130 | StdOut.println(e); 131 | } 132 | StdOut.printf("%.5f\n", mst.weight()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/others/graph_with_d3/d3-demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Force Directed Graph 6 | 7 | 24 | 25 | 26 | 27 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/others/graph_with_d3/d3-demo2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Force Directed Graph 5 | 6 | 22 | 23 | 24 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/heap_sort/HeapSortStandardCount.java: -------------------------------------------------------------------------------- 1 | package heap_sort; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Random; 6 | import java.util.Set; 7 | 8 | public class HeapSortStandardCount { 9 | // 用于计数的静态变量 10 | static int comparisonCount = 0; 11 | static int swapCount = 0; 12 | 13 | // 泛型堆排序函数 14 | public static > void heapSort(T[] arr) { 15 | int n = arr.length; 16 | comparisonCount = 0; 17 | swapCount = 0; 18 | 19 | // 构建大顶堆(未优化版本,从上到下) 20 | for (int i = n / 2 - 1; i >= 0; i--) { 21 | heapify(arr, n, i); 22 | } 23 | 24 | // 从堆顶取出最大的元素,放到数组末尾,并重新调整堆 25 | for (int i = n - 1; i >= 0; i--) { 26 | swap(arr, 0, i); 27 | heapify(arr, i, 0); 28 | } 29 | } 30 | 31 | // 递归: 使得以root为根的子树成为大顶堆 32 | private static > void heapify(T[] arr, int n, int root) { 33 | int largest = root; 34 | int left = 2 * root + 1; 35 | int right = 2 * root + 2; 36 | 37 | // 如果左子节点比根大 38 | if (left < n && arr[left].compareTo(arr[largest]) > 0) { 39 | largest = left; 40 | comparisonCount++; 41 | } 42 | 43 | // 如果右子节点比最大值还大 44 | if (right < n && arr[right].compareTo(arr[largest]) > 0) { 45 | largest = right; 46 | comparisonCount++; 47 | } 48 | 49 | // 如果最大值不是根 50 | if (largest != root) { 51 | swap(arr, root, largest); 52 | heapify(arr, n, largest); 53 | } 54 | } 55 | 56 | // 交换数组中的两个元素 57 | private static void swap(T[] arr, int i, int j) { 58 | T temp = arr[i]; 59 | arr[i] = arr[j]; 60 | arr[j] = temp; 61 | swapCount++; 62 | } 63 | 64 | // 生成随机不重复数组的方法 65 | private static Integer[] generateUniqueRandomArray(int size, int range) { 66 | Random random = new Random(); 67 | Set set = new HashSet<>(); 68 | while (set.size() < size) { 69 | set.add(random.nextInt(range)); 70 | } 71 | return set.toArray(new Integer[0]); 72 | } 73 | 74 | // 测试排序并打印结果的方法 75 | private static void testSort(Integer[] array, String sizeLabel) { 76 | heapSort(array); 77 | // 为简洁起见,不打印整个数组 78 | System.out.println("Sorted array size " + sizeLabel + ": [First 10 elements]: " + Arrays.toString(Arrays.copyOf(array, 10))); 79 | System.out.println("Comparisons: " + comparisonCount + ", Swaps: " + swapCount); 80 | } 81 | 82 | public static void main(String[] args) { 83 | 84 | /** 85 | * 本机运行结果: 86 | * 规模为 10^3: 比较次数 11828, 交换次数 9119 87 | * 规模为 10^4: 比较次数 167383, 交换次数 124517 88 | * 规模为 10^5: 比较次数 2177017, 交换次数 1581495 89 | * 规模为 10^6: 比较次数 26680099, 交换次数 19078774 90 | */ 91 | // 测试N=10^3大小的数组 92 | Integer[] array1K = generateUniqueRandomArray(1_000, 10_000); 93 | testSort(array1K, "10^3"); 94 | 95 | // 测试N=10^4大小的数组 96 | Integer[] array10K = generateUniqueRandomArray(10_000, 100_000); 97 | testSort(array10K, "10^4"); 98 | 99 | // 测试N=10^5大小的数组 100 | Integer[] array100K = generateUniqueRandomArray(100_000, 1_000_000); 101 | testSort(array100K, "10^5"); 102 | 103 | // 测试N=10^6大小的数组 104 | Integer[] array1M = generateUniqueRandomArray(1_000_000, 10_000_000); 105 | testSort(array1M, "10^6"); 106 | 107 | /** 108 | * 注意:下面是10^9大小的随机数组,你需要提前给程序分配足够多的堆内存,运行时间会比较漫长 !!! 109 | * 注意:下面是10^9大小的随机数组,你需要提前给程序分配足够多的堆内存,运行时间会比较漫长 !!! 110 | * 注意:下面是10^9大小的随机数组,你需要提前给程序分配足够多的堆内存,运行时间会比较漫长 !!! 111 | */ 112 | // final int SIZE = 1_000_000_000; // 10^9 113 | // // 生成大数组 114 | // Integer[] largeArray = new Integer[SIZE]; 115 | // Random random = new Random(); 116 | // for (int i = 0; i < SIZE; i++) { 117 | // largeArray[i] = random.nextInt(Integer.MAX_VALUE); 118 | // } 119 | // heapSort(largeArray); 120 | // // 打印一些排序后的数据进行验证(例如前10个元素) 121 | // System.out.println(Arrays.toString(Arrays.copyOf(largeArray, 10))); 122 | // System.out.println("Comparisons: " + comparisonCount + ", Swaps: " + swapCount); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/README.md: -------------------------------------------------------------------------------- 1 | ## 动画解析《算法图解》代码仓库 2 | 3 | 欢迎来到本 GitHub 仓库!这里包含了与我的 B站 视频系列“动画解析《算法图解》”相关的所有代码。每个章节都有对应的 Python 代码示例,帮助你更好地理解和掌握算法。 4 | 5 | ## **视频地址** 6 | - [bilibili](https://www.bilibili.com/video/BV1Aw411U7mf/?spm_id_from=333.999.0.0&vd_source=42e7dc7e5ebb14d27d2434b00ff63157) 7 | - [YouTube](https://www.youtube.com/watch?v=cSRP53XTAUg&list=PLQ7NyNO3w6Ad5M7Am0suoK4XFng6_KEa0) 8 | 9 | ## 目录 10 | 11 | 1. [第一章节 - 算法简介](#第一章节---二分查找) 12 | 2. [第二章节 - 选择排序](#第二章节---选择排序) 13 | 3. [第三章节 - 递归](#第三章节---递归) 14 | 4. [第四章节 - 快速排序](#第四章节---快速排序) 15 | 5. [第五章节 - 散列表](#第五章节---散列表) 16 | 6. [第六章节 - 广度优先搜索](#第六章节---广度优先搜索) 17 | 7. [第七章节 - 迪杰斯特拉算法](#第七章节---迪杰斯特拉算法) 18 | 8. [第八章节 - 贪心算法](#第八章节---贪心算法) 19 | 9. [第九章节 - 动态规划](#第九章节---动态规划) 20 | 9. [第十章节 - KNN](#第十章节---K-近邻算法) 21 | 22 | --- 23 | 24 | ### 第一章节 - 算法简介 25 | 26 | - **代码**: [二分查找](01/binary_search.py) 27 | 28 | --- 29 | 30 | ### 第二章节 - 选择排序 31 | 32 | - **代码**: 33 | - [选择排序-数组实现(不稳定)](02/selection_sort_arr.py) 34 | - [选择排序-链表实现(稳定)](02/selection_sort_linked_list.py) 35 | 36 | --- 37 | 38 | ### 第三章节 - 递归 39 | 40 | - **代码**: 41 | - [求第 N 个斐波那契数-递归形式](03/fibonacci.py) 42 | - [求第 N 个斐波那契数-循环形式](03/fibonacci_loop.py) 43 | - [求第 N 个斐波那契数-尾递归形式](03/fibonacci_tail.py) 44 | 45 | --- 46 | 47 | ### 第四章节 - 快速排序 48 | 49 | - **代码**: 50 | - [归并排序](04/merge_sort.py) 51 | - [快速排序-原地(in-place)排序法](04/quick_sort.py) 52 | - [快速排序-替代递归的循环方式](04/quick_sort_loop.py) 53 | 54 | --- 55 | 56 | ### 第五章节 - 散列表 57 | 58 | - **代码**: 59 | - [散列表-python自带](05/demo.py) 60 | - [散列表-(开散列)拉链法实现](05/ChaingHashTable.py) 61 | - [选择排序-(闭散列)线性探测法](05/LinearProbeHashTable.py) 62 | - [RabinKarp算法-模式匹配](05/RabinKarp.py) 63 | 64 | --- 65 | 66 | ### 第六章节 - 广度优先搜索 67 | 68 | - **图相关代码**: 69 | - [图的实现](06/graph/graph.py) 70 | - [有向图的实现](06/graph/DirectedGraph.py) 71 | - [权重图的实现](06/graph/WeightedGraph.py) 72 | - [图的存储-链表](06/graphStore/graph_list.py) 73 | - [图的存储-矩阵(二维数组)](06/graphStore/graph_matrix.py) 74 | 75 | - **队列相关代码**: 76 | - [简单的队列实现](06/bfs/simpleQueue.py) 77 | 78 | - **广度优先搜索(BFS)相关代码**: 79 | - [BFS-搜索](06/bfs/bfs-search.py) 80 | - [BFS-遍历](06/bfs/bfs-traverse.py) 81 | 82 | - **关于图的第三方库代码**: 83 | - [igraph-demo](06/graphLibOfPython/igraph_demo1.py) 84 | - [networkx-demo1](06/graphLibOfPython/networkx_demo1.py) 85 | - [networkx-demo2](06/graphLibOfPython/networkx_demo2.py) 86 | --- 87 | 88 | ### 第七章节 - 迪杰斯特拉算法 89 | 90 | - **代码**: 91 | - [Dijstra(simple): 只计算最短距离,不存储最短路径](07/dijkstra.py) 92 | - [Dijstra: 计算最短距离且保存最短路径](07/dijkstra2.py) 93 | 94 | --- 95 | 96 | ### 第八章节 - 贪心算法 97 | 98 | 99 | - **教室排课代码**: 100 | - [教室排课](08/classroom_scheduling.py) 101 | - [教室排课-按结束时间(正确)](08/sort_by_end_time.py) 102 | - [教室排课-按开始时间(错误)](08/sort_by_start_time.py) 103 | 104 | - **0-1背包问题代码**: 105 | - [背包问题-贪心算法实现](08/knapsack_greedy.py) 106 | - [背包问题-动态规划实现](08/knapsack_dp.py) 107 | 108 | - **集合覆盖问题代码**: 109 | - [集合覆盖问题-简单方式(慢,结果准确)](08/simple_way_solve_set_covering_problem.py) 110 | - [集合覆盖问题-贪心算法(快,结果近似,不保证完全正确)](08/greedy_algorithm_solve_set_covering_problem.py) 111 | 112 | - **旅行商(TSP)问题代码**: 113 | - [旅行商问题-贪心算法实现](08/travle.py) 114 | 115 | --- 116 | 117 | ### 第九章节 - 动态规划 118 | 119 | - **Fibonacci相关代码**: 120 | - [fibonacci-递归实现](09/fibonacci.py) 121 | - [fibonacci-备忘录法优化](09/fibonacci_memo.py) 122 | - [fibonacci-自底向上优化](09/fibonacci_bottom_up.py) 123 | 124 | - **背包问题相关代码**: 125 | - [0-1背包-二维DP表实现](09/knapsack.py) 126 | - [0-1背包-二维DP表(颠倒横纵轴)实现](09/knapsack_transposed.py) 127 | - [0-1背包-一维DP表实现](09/knapsack_1D.py) 128 | - [0-1背包(分割等和子集)](09/canPartition.py) 129 | 130 | - [完全背包(硬币找零)-二维DP表实现](09/coinChange_2D.py) 131 | - [完全背包(硬币找零)-一维DP表实现](09/coinChange.py) 132 | 133 | - [分数背包-实现](09/fractional_knapsack.py) 134 | 135 | - **最长公共子序列(LCS)相关代码**: 136 | - [LCS-二维DP表实现](09/longestCommonSubsequence_with_path.py) 137 | - [LCS-一维DP表实现](09/longestCommonSubsequence_with_path_1D.py) 138 | - [LCS-只判断是否存在最长公共子序列](09/longestCommonSubsequence_without_path_1D.py) 139 | 140 | --- 141 | 142 | ### 第十章节 - K-近邻算法 143 | 144 | - **电影推荐系统相关代码** 145 | - [电影推荐系统-计算距离](10/distance.py) 146 | - [电影推荐系统-预测评分](10/recommendation_system.py) 147 | 148 | 149 | ## 如何使用本仓库 150 | 151 | 1. 克隆本仓库到你的计算机。 152 | 2. 打开你感兴趣的章节文件夹,比如 `01`。 153 | 3. 查看对应的 Python 代码文件,如 `binary_search.py`。 154 | 4. 与视频内容进行对照,加深理解。 155 | 156 | ## 联系和反馈 157 | 158 | 如果你有任何问题或者建议,随时通过[我的 B站 频道](https://www.bilibili.com/video/BV1Aw411U7mf/?spm_id_from=333.788&vd_source=42e7dc7e5ebb14d27d2434b00ff63157)留言,或者在这里开一个 Issue。 159 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/minimum_spanning_trees/LazyPrimMST.java: -------------------------------------------------------------------------------- 1 | package minimum_spanning_trees; 2 | 3 | import edu.princeton.cs.algs4.*; 4 | 5 | public class LazyPrimMST { 6 | private static final double FLOATING_POINT_EPSILON = 1.0E-12; // 浮点数的精度范围 7 | 8 | private double weight; // MST的总权重 9 | private Queue mst; // MST中的边 10 | private boolean[] marked; // 如果v在树上,则marked[v] = true 11 | private MinPQ pq; // 具有一个端点在树中的边 12 | 13 | /** 14 | * 计算一个加权边图的最小生成树(或森林) 15 | * @param G 加权边图 16 | */ 17 | public LazyPrimMST(EdgeWeightedGraph G) { 18 | mst = new Queue(); 19 | pq = new MinPQ(); 20 | marked = new boolean[G.V()]; 21 | for (int v = 0; v < G.V(); v++) // 从所有顶点运行Prim算法 22 | if (!marked[v]) prim(G, v); // 获取一个最小生成森林 23 | 24 | // 检查最优条件 25 | assert check(G); 26 | } 27 | 28 | // 运行Prim算法 29 | private void prim(EdgeWeightedGraph G, int s) { 30 | scan(G, s); 31 | while (!pq.isEmpty()) { // 当mst有V-1条边时停止更好 32 | Edge e = pq.delMin(); // pq上的最小边 33 | int v = e.either(), w = e.other(v); // 两个端点 34 | assert marked[v] || marked[w]; 35 | if (marked[v] && marked[w]) continue; // 懒惰,v和w已经被扫描 36 | mst.enqueue(e); // 将e加入到MST 37 | weight += e.weight(); 38 | if (!marked[v]) scan(G, v); // v成为树的一部分 39 | if (!marked[w]) scan(G, w); // w成为树的一部分 40 | } 41 | } 42 | 43 | // 如果另一个端点还没有被扫描,则将所有与v相邻的边e加入到pq中 44 | private void scan(EdgeWeightedGraph G, int v) { 45 | assert !marked[v]; 46 | marked[v] = true; 47 | for (Edge e : G.adj(v)) 48 | if (!marked[e.other(v)]) pq.insert(e); // 如果边e的另一端点未被扫描,则插入pq 49 | } 50 | 51 | /** 52 | * 返回最小生成树(或森林)中的边。 53 | * @return 作为边的可迭代集合的最小生成树(或森林)中的边 54 | */ 55 | public Iterable edges() { 56 | return mst; 57 | } 58 | 59 | /** 60 | * 返回最小生成树(或森林)中边的权重总和。 61 | * @return 最小生成树(或森林)中边的权重总和 62 | */ 63 | public double weight() { 64 | return weight; 65 | } 66 | 67 | // 检查最优条件(时间与E V lg* V成正比) 68 | private boolean check(EdgeWeightedGraph G) { 69 | 70 | // 检查权重 71 | double totalWeight = 0.0; 72 | for (Edge e : edges()) { 73 | totalWeight += e.weight(); 74 | } 75 | if (Math.abs(totalWeight - weight()) > FLOATING_POINT_EPSILON) { 76 | System.err.printf("边的权重之和不等于weight(): %f vs. %f\n", totalWeight, weight()); 77 | return false; 78 | } 79 | 80 | // 检查它是无环的 81 | UF uf = new UF(G.V()); 82 | for (Edge e : edges()) { 83 | int v = e.either(), w = e.other(v); 84 | if (uf.find(v) == uf.find(w)) { 85 | System.err.println("不是森林"); 86 | return false; 87 | } 88 | uf.union(v, w); 89 | } 90 | 91 | // 检查它是一个覆盖森林 92 | for (Edge e : G.edges()) { 93 | int v = e.either(), w = e.other(v); 94 | if (uf.find(v) != uf.find(w)) { 95 | System.err.println("不是覆盖森林"); 96 | return false; 97 | } 98 | } 99 | 100 | // 检查它是最小覆盖森林(割优化条件) 101 | for (Edge e : edges()) { 102 | 103 | // MST中除了e的所有边 104 | uf = new UF(G.V()); 105 | for (Edge f : mst) { 106 | int x = f.either(), y = f.other(x); 107 | if (f != e) uf.union(x, y); 108 | } 109 | 110 | // 检查e是横跨割中最小权重的边 111 | for (Edge f : G.edges()) { 112 | int x = f.either(), y = f.other(x); 113 | if (uf.find(x) != uf.find(y)) { 114 | if (f.weight() < e.weight()) { 115 | System.err.println("边 " + f + " 违反了割优化条件"); 116 | return false; 117 | } 118 | } 119 | } 120 | } 121 | return true; 122 | } 123 | 124 | 125 | /** 126 | * 对{@code LazyPrimMST}数据类型进行单元测试。 127 | * 128 | * @param args 命令行参数 129 | */ 130 | public static void main(String[] args) { 131 | In in = new In(args[0]); 132 | EdgeWeightedGraph G = new EdgeWeightedGraph(in); 133 | LazyPrimMST mst = new LazyPrimMST(G); 134 | for (Edge e : mst.edges()) { 135 | StdOut.println(e); 136 | } 137 | StdOut.printf("%.5f\n", mst.weight()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Algorithms-4th-Edition/src/main/java/symbol_table/search_st/SequentialSearchST.java: -------------------------------------------------------------------------------- 1 | package symbol_table.search_st; 2 | 3 | import edu.princeton.cs.algs4.Queue; 4 | 5 | import java.io.File; 6 | import java.io.FileNotFoundException; 7 | import java.util.Scanner; 8 | 9 | public class SequentialSearchST { 10 | private int n; // 键值对的数量 11 | private Node first; // 键值对的链表 12 | 13 | // 辅助的链表数据类型 14 | private class Node { 15 | private Key key; 16 | private Value val; 17 | private Node next; 18 | 19 | public Node(Key key, Value val, Node next) { 20 | this.key = key; 21 | this.val = val; 22 | this.next = next; 23 | } 24 | } 25 | 26 | /** 27 | * 初始化一个空的符号表。 28 | */ 29 | public SequentialSearchST() { 30 | } 31 | 32 | /** 33 | * 返回符号表中的键值对数量。 34 | * 35 | * @return 符号表中的键值对数量 36 | */ 37 | public int size() { 38 | return n; 39 | } 40 | 41 | /** 42 | * 判断符号表是否为空。 43 | * 44 | * @return 如果符号表为空返回true,否则返回false 45 | */ 46 | public boolean isEmpty() { 47 | return size() == 0; 48 | } 49 | 50 | /** 51 | * 判断符号表是否包含指定的键。 52 | * 53 | * @param key 键 54 | * @return 如果符号表包含key则返回true,否则返回false 55 | * @throws IllegalArgumentException 如果key为null 56 | */ 57 | public boolean contains(Key key) { 58 | if (key == null) throw new IllegalArgumentException("键为null"); 59 | return get(key) != null; 60 | } 61 | 62 | /** 63 | * 根据键获取对应的值。 64 | * 65 | * @param key 键 66 | * @return 如果键存在,返回相应的值;如果不存在,返回null 67 | * @throws IllegalArgumentException 如果key为null 68 | */ 69 | public Value get(Key key) { 70 | if (key == null) throw new IllegalArgumentException("键为null"); 71 | for (Node x = first; x != null; x = x.next) { 72 | if (key.equals(x.key)) 73 | return x.val; 74 | } 75 | return null; 76 | } 77 | 78 | /** 79 | * 向符号表中插入或更新键值对。 80 | * 如果值为null,则删除键。 81 | * 82 | * @param key 键 83 | * @param val 值 84 | * @throws IllegalArgumentException 如果key为null 85 | */ 86 | public void put(Key key, Value val) { 87 | if (key == null) throw new IllegalArgumentException("键为null"); 88 | if (val == null) { 89 | delete(key); 90 | return; 91 | } 92 | 93 | for (Node x = first; x != null; x = x.next) { 94 | if (key.equals(x.key)) { 95 | x.val = val; 96 | return; 97 | } 98 | } 99 | first = new Node(key, val, first); 100 | n++; 101 | } 102 | 103 | /** 104 | * 从符号表中删除指定的键及其关联的值。 105 | * 106 | * @param key 键 107 | * @throws IllegalArgumentException 如果key为null 108 | */ 109 | public void delete(Key key) { 110 | if (key == null) throw new IllegalArgumentException("键为null"); 111 | first = delete(first, key); 112 | } 113 | 114 | // 从以x为首的链表中删除键 115 | // 注意:如果表很大,函数调用栈可能过大 116 | private Node delete(Node x, Key key) { 117 | if (x == null) return null; 118 | if (key.equals(x.key)) { 119 | n--; 120 | return x.next; 121 | } 122 | x.next = delete(x.next, key); 123 | return x; 124 | } 125 | 126 | /** 127 | * 返回符号表中所有键的Iterable集合。 128 | * 使用for-each遍历符号表的所有键。 129 | * 130 | * @return 符号表中的所有键 131 | */ 132 | public Iterable keys() { 133 | Queue queue = new Queue(); 134 | for (Node x = first; x != null; x = x.next) 135 | queue.enqueue(x.key); 136 | return queue; 137 | } 138 | 139 | /** 140 | * 对SequentialSearchST数据类型进行单元测试。 141 | * 142 | * @param args 命令行参数 143 | */ 144 | public static void main(String[] args) { 145 | SequentialSearchST st = new SequentialSearchST(); 146 | // 确保命令行参数中包含文件路径 147 | if (args.length < 1) { 148 | System.out.println("Usage: java SequentialSearchST "); 149 | return; 150 | } 151 | 152 | try { 153 | // 使用Scanner从文件中读取 154 | File file = new File(args[0]); 155 | Scanner scanner = new Scanner(file); 156 | int i = 0; 157 | while (scanner.hasNext()) { 158 | String key = scanner.next(); 159 | st.put(key, i++); 160 | } 161 | scanner.close(); 162 | } catch (FileNotFoundException e) { 163 | System.out.println("File not found: " + args[0]); 164 | return; 165 | } 166 | 167 | // 打印所有键及其对应的值 168 | for (String s : st.keys()) 169 | System.out.println(s + " " + st.get(s)); 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /grokking-algorithms-illustrated-programmers-curious/06/others/graph_with_d3/d3-relations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | 23 | 24 | 25 | 129 | 130 | 131 | --------------------------------------------------------------------------------