From f0b092fec42ddb33f785ef41e32b05824890b331 Mon Sep 17 00:00:00 2001 From: ZJKung <34116485+ZJKung@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:24:24 +0800 Subject: [PATCH] Add C# code for the chapter Heap and Graph (#324) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add : C# heap ,graph, fix type "sift"=>"shift" * chore: rename "shift" to "sift" * add: heap,graph C# sample code ,fix format * fix md format * fix md intend foramt * fix basic_operation_of_graph.md format * fix md format * fix md format * fix indentation format * chore: fix my_heap.cs test * fix: test and doc typo * fix bug for commit 5eae708 (#317) * Add Zig code blocks. * fix: resolve build error for commit 5eae708 (#318) * Unify the function naming of queue from `offer()` to `push()` * Update TypeScript codes. * Update binary_search_tree * Update graph operations. * Fix code indentation. * Update worst_best_time_complexity, leetcode_two_sum * Update avl_tree * copy zig codes of chapter_array_and_linkedlist and chapter_computatio… (#319) * copy zig codes of chapter_array_and_linkedlist and chapter_computational_complexity to markdown files * Update time_complexity.md --------- Co-authored-by: Yudong Jin * Fix Python code styles. Update hash_map. * chore: fix heap logic * Update graph_adjacency_matrix.cs * Update graph_adjacency_matrix.cs * Update my_heap.cs * fix: heap test * fix naming format * merge markdown * fix markdown format * Update graph_adjacency_list.cs * Update graph_adjacency_matrix.cs * Update PrintUtil.cs * Create Vertex.cs * Update heap.cs --------- Co-authored-by: zjkung1123 Co-authored-by: sjinzh <99076655+sjinzh@users.noreply.github.com> Co-authored-by: Yudong Jin Co-authored-by: nuomi1 --- .../chapter_graph/graph_adjacency_list.cs | 132 +++++++++++++ .../chapter_graph/graph_adjacency_matrix.cs | 149 ++++++++++++++ codes/csharp/chapter_heap/heap.cs | 70 +++++++ codes/csharp/chapter_heap/my_heap.cs | 183 ++++++++++++++++++ codes/csharp/include/PrintUtil.cs | 54 +++++- codes/csharp/include/Vertex.cs | 37 ++++ docs/chapter_heap/heap.md | 32 +++ 7 files changed, 652 insertions(+), 5 deletions(-) create mode 100644 codes/csharp/chapter_graph/graph_adjacency_list.cs create mode 100644 codes/csharp/chapter_graph/graph_adjacency_matrix.cs create mode 100644 codes/csharp/chapter_heap/heap.cs create mode 100644 codes/csharp/chapter_heap/my_heap.cs create mode 100644 codes/csharp/include/Vertex.cs diff --git a/codes/csharp/chapter_graph/graph_adjacency_list.cs b/codes/csharp/chapter_graph/graph_adjacency_list.cs new file mode 100644 index 00000000..c199bcf8 --- /dev/null +++ b/codes/csharp/chapter_graph/graph_adjacency_list.cs @@ -0,0 +1,132 @@ +/** + * File: graph_adjacency_list.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +using hello_algo.include; +using NUnit.Framework; + +namespace hello_algo.chapter_graph; + +/* 基于邻接表实现的无向图类 */ +class GraphAdjList +{ + // 邻接表 adjList 中的元素是 Vertex 对象 + Dictionary> adjList; + + /* 构造函数 */ + public GraphAdjList(Vertex[][] edges) + { + this.adjList = new Dictionary>(); + // 添加所有顶点和边 + foreach (Vertex[] edge in edges) + { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* 获取顶点数量 */ + public int size() + { + return adjList.Count; + } + + /* 添加边 */ + public void addEdge(Vertex vet1, Vertex vet2) + { + if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) + throw new InvalidOperationException(); + // 添加边 vet1 - vet2 + adjList[vet1].Add(vet2); + adjList[vet2].Add(vet1); + } + + /* 删除边 */ + public void removeEdge(Vertex vet1, Vertex vet2) + { + if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) + throw new InvalidOperationException(); + // 删除边 vet1 - vet2 + adjList[vet1].Remove(vet2); + adjList[vet2].Remove(vet1); + } + + /* 添加顶点 */ + public void addVertex(Vertex vet) + { + if (adjList.ContainsKey(vet)) + return; + // 在邻接表中添加一个新链表 + adjList.Add(vet, new List()); + } + + /* 删除顶点 */ + public void removeVertex(Vertex vet) + { + if (!adjList.ContainsKey(vet)) + throw new InvalidOperationException(); + // 在邻接表中删除顶点 vet 对应的链表 + adjList.Remove(vet); + // 遍历其它顶点的链表,删除所有包含 vet 的边 + foreach (List list in adjList.Values) + { + list.Remove(vet); + } + } + + /* 打印邻接表 */ + public void print() + { + Console.WriteLine("邻接表 ="); + foreach (KeyValuePair> entry in adjList) + { + List tmp = new List(); + foreach (Vertex vertex in entry.Value) + tmp.Add(vertex.Val); + Console.WriteLine(entry.Key.Val + ": [" + string.Join(", ", tmp) + "],"); + } + } +} + +public class graph_adjacency_list +{ + [Test] + public void Test() + { + /* 初始化无向图 */ + Vertex[] v = Vertex.valsToVets(new int[] { 1, 3, 2, 5, 4 }); + Vertex[][] edges = new Vertex[][] { new Vertex[] { v[0], v[1] }, new Vertex[] { v[0], v[3] }, + new Vertex[] { v[1], v[2] }, new Vertex[] { v[2], v[3] }, + new Vertex[] { v[2], v[4] }, new Vertex[] { v[3], v[4] } }; + GraphAdjList graph = new GraphAdjList(edges); + Console.WriteLine("\n初始化后,图为"); + graph.print(); + + /* 添加边 */ + // 顶点 1, 2 即 v[0], v[2] + graph.addEdge(v[0], v[2]); + Console.WriteLine("\n添加边 1-2 后,图为"); + graph.print(); + + /* 删除边 */ + // 顶点 1, 3 即 v[0], v[1] + graph.removeEdge(v[0], v[1]); + Console.WriteLine("\n删除边 1-3 后,图为"); + graph.print(); + + /* 添加顶点 */ + Vertex v5 = new Vertex(6); + graph.addVertex(v5); + Console.WriteLine("\n添加顶点 6 后,图为"); + graph.print(); + + /* 删除顶点 */ + // 顶点 3 即 v[1] + graph.removeVertex(v[1]); + Console.WriteLine("\n删除顶点 3 后,图为"); + graph.print(); + } +} diff --git a/codes/csharp/chapter_graph/graph_adjacency_matrix.cs b/codes/csharp/chapter_graph/graph_adjacency_matrix.cs new file mode 100644 index 00000000..cfbfa8bc --- /dev/null +++ b/codes/csharp/chapter_graph/graph_adjacency_matrix.cs @@ -0,0 +1,149 @@ +/** + * File: graph_adjacency_list.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +using hello_algo.include; +using NUnit.Framework; + +namespace hello_algo.chapter_graph; + +/* 基于邻接矩阵实现的无向图类 */ +class GraphAdjMat +{ + List vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + List> adjMat; // 邻接矩阵,行列索引对应“顶点索引” + + /* 构造函数 */ + public GraphAdjMat(int[] vertices, int[][] edges) + { + this.vertices = new List(); + this.adjMat = new List>(); + // 添加顶点 + foreach (int val in vertices) + { + addVertex(val); + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + foreach (int[] e in edges) + { + addEdge(e[0], e[1]); + } + } + + /* 获取顶点数量 */ + public int size() + { + return vertices.Count; + } + + /* 添加顶点 */ + public void addVertex(int val) + { + int n = size(); + // 向顶点列表中添加新顶点的值 + vertices.Add(val); + // 在邻接矩阵中添加一行 + List newRow = new List(n); + for (int j = 0; j < n; j++) + { + newRow.Add(0); + } + adjMat.Add(newRow); + // 在邻接矩阵中添加一列 + foreach (List row in adjMat) + { + row.Add(0); + } + } + + /* 删除顶点 */ + public void removeVertex(int index) + { + if (index >= size()) + throw new IndexOutOfRangeException(); + // 在顶点列表中移除索引 index 的顶点 + vertices.RemoveAt(index); + // 在邻接矩阵中删除索引 index 的行 + adjMat.RemoveAt(index); + // 在邻接矩阵中删除索引 index 的列 + foreach (List row in adjMat) + { + row.RemoveAt(index); + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + public void addEdge(int i, int j) + { + // 索引越界与相等处理 + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw new IndexOutOfRangeException(); + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + adjMat[i][j] = 1; + adjMat[j][i] = 1; + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + public void removeEdge(int i, int j) + { + // 索引越界与相等处理 + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw new IndexOutOfRangeException(); + adjMat[i][j] = 0; + adjMat[j][i] = 0; + } + + /* 打印邻接矩阵 */ + public void print() + { + Console.Write("顶点列表 = "); + PrintUtil.PrintList(vertices); + Console.WriteLine("邻接矩阵 ="); + PrintUtil.printMatrix(adjMat); + } +} + +public class graph_adjacency_matrix +{ + [Test] + public void Test() + { + /* 初始化无向图 */ + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + int[] vertices = { 1, 3, 2, 5, 4 }; + int[][] edges = new int[][] { new int[] { 0, 1 }, new int[] { 0, 3 }, + new int[] { 1, 2 }, new int[] { 2, 3 }, + new int[] { 2, 4 }, new int[] { 3, 4 } }; + GraphAdjMat graph = new GraphAdjMat(vertices, edges); + Console.WriteLine("\n初始化后,图为"); + graph.print(); + + /* 添加边 */ + // 顶点 1, 2 的索引分别为 0, 2 + graph.addEdge(0, 2); + Console.WriteLine("\n添加边 1-2 后,图为"); + graph.print(); + + /* 删除边 */ + // 顶点 1, 3 的索引分别为 0, 1 + graph.removeEdge(0, 1); + Console.WriteLine("\n删除边 1-3 后,图为"); + graph.print(); + + /* 添加顶点 */ + graph.addVertex(6); + Console.WriteLine("\n添加顶点 6 后,图为"); + graph.print(); + + /* 删除顶点 */ + // 顶点 3 的索引为 1 + graph.removeVertex(1); + Console.WriteLine("\n删除顶点 3 后,图为"); + graph.print(); + } +} diff --git a/codes/csharp/chapter_heap/heap.cs b/codes/csharp/chapter_heap/heap.cs new file mode 100644 index 00000000..214b6842 --- /dev/null +++ b/codes/csharp/chapter_heap/heap.cs @@ -0,0 +1,70 @@ +/** + * File: heap.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +using hello_algo.include; +using NUnit.Framework; + +namespace hello_algo.chapter_heap; + +public class heap +{ + public void testPush(PriorityQueue heap, int val) + { + heap.Enqueue(val, val); // 元素入堆 + Console.WriteLine($"\n元素 {val} 入堆后\n"); + PrintUtil.printHeap(heap); + } + + public void testPoll(PriorityQueue heap) + { + int val = heap.Dequeue(); // 堆顶元素出堆 + Console.WriteLine($"\n堆顶元素 {val} 出堆后\n"); + PrintUtil.printHeap(heap); + } + [Test] + public void Test() + { + /* 初始化堆 */ + // 初始化小顶堆 + PriorityQueue minHeap = new PriorityQueue(); + // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) + PriorityQueue maxHeap = new PriorityQueue(Comparer.Create((x, y) => y - x)); + Console.WriteLine("以下测试样例为大顶堆"); + + /* 元素入堆 */ + testPush(maxHeap, 1); + testPush(maxHeap, 3); + testPush(maxHeap, 2); + testPush(maxHeap, 5); + testPush(maxHeap, 4); + + /* 获取堆顶元素 */ + int peek = maxHeap.Peek(); + Console.WriteLine($"堆顶元素为 {peek}"); + + /* 堆顶元素出堆 */ + // 出堆元素会形成一个从大到小的序列 + testPoll(maxHeap); + testPoll(maxHeap); + testPoll(maxHeap); + testPoll(maxHeap); + testPoll(maxHeap); + + /* 获取堆大小 */ + int size = maxHeap.Count; + Console.WriteLine($"堆元素数量为 {size}"); + + /* 判断堆是否为空 */ + bool isEmpty = maxHeap.Count == 0; + Console.WriteLine($"堆是否为空 {isEmpty}"); + + /* 输入列表并建堆 */ + var list = new int[] { 1, 3, 2, 5, 4 }; + minHeap = new PriorityQueue(list.Select(x => (x, x))); + Console.WriteLine("输入列表并建立小顶堆后"); + PrintUtil.printHeap(minHeap); + } +} diff --git a/codes/csharp/chapter_heap/my_heap.cs b/codes/csharp/chapter_heap/my_heap.cs new file mode 100644 index 00000000..3a408bae --- /dev/null +++ b/codes/csharp/chapter_heap/my_heap.cs @@ -0,0 +1,183 @@ +/** + * File: my_heap.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +using hello_algo.include; +using NUnit.Framework; + +namespace hello_algo.chapter_heap; + +/* 大顶堆 */ +class MaxHeap +{ + // 使用列表而非数组,这样无需考虑扩容问题 + private readonly List maxHeap; + + /* 构造函数,建立空堆 */ + public MaxHeap() + { + maxHeap = new List(); + } + + /* 构造函数,根据输入列表建堆 */ + public MaxHeap(IEnumerable nums) + { + // 将列表元素原封不动添加进堆 + maxHeap = new List(nums); + // 堆化除叶结点以外的其他所有结点 + var size = parent(this.size() - 1); + for (int i = size; i >= 0; i--) + { + siftDown(i); + } + } + + /* 获取左子结点索引 */ + int left(int i) + { + return 2 * i + 1; + } + + /* 获取右子结点索引 */ + int right(int i) + { + return 2 * i + 2; + } + + /* 获取父结点索引 */ + int parent(int i) + { + return (i - 1) / 2; // 向下整除 + } + + /* 访问堆顶元素 */ + public int peek() + { + return maxHeap[0]; + } + + /* 元素入堆 */ + public void push(int val) + { + // 添加结点 + maxHeap.Add(val); + // 从底至顶堆化 + siftUp(size() - 1); + } + + /* 获取堆大小 */ + public int size() + { + return maxHeap.Count; + } + + /* 判断堆是否为空 */ + public bool isEmpty() + { + return size() == 0; + } + + /* 从结点 i 开始,从底至顶堆化 */ + void siftUp(int i) + { + while (true) + { + // 获取结点 i 的父结点 + int p = parent(i); + // 若“越过根结点”或“结点无需修复”,则结束堆化 + if (p < 0 || maxHeap[i] <= maxHeap[p]) + break; + // 交换两结点 + swap(i, p); + // 循环向上堆化 + i = p; + } + } + + /* 元素出堆 */ + public int poll() + { + // 判空处理 + if (isEmpty()) + throw new IndexOutOfRangeException(); + // 交换根结点与最右叶结点(即交换首元素与尾元素) + swap(0, size() - 1); + // 删除结点 + int val = maxHeap.Last(); + maxHeap.RemoveAt(size() - 1); + // 从顶至底堆化 + siftDown(0); + // 返回堆顶元素 + return val; + } + + /* 从结点 i 开始,从顶至底堆化 */ + void siftDown(int i) + { + while (true) + { + // 判断结点 i, l, r 中值最大的结点,记为 ma + int l = left(i), r = right(i), ma = i; + if (l < size() && maxHeap[l] > maxHeap[ma]) + ma = l; + if (r < size() && maxHeap[r] > maxHeap[ma]) + ma = r; + // 若“结点 i 最大”或“越过叶结点”,则结束堆化 + if (ma == i) break; + // 交换两结点 + swap(i, ma); + // 循环向下堆化 + i = ma; + } + } + + /* 交换元素 */ + void swap(int i, int p) + { + (maxHeap[i], maxHeap[p]) = (maxHeap[p], maxHeap[i]); + } + + /* 打印堆(二叉树) */ + public void print() + { + var queue = new Queue(maxHeap); + PrintUtil.printHeap(queue); + } +} + +public class my_heap +{ + [Test] + public void Test() + { + /* 初始化大顶堆 */ + MaxHeap maxHeap = new MaxHeap(new int[] { 9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2 }); + Console.WriteLine("\n输入列表并建堆后"); + maxHeap.print(); + + /* 获取堆顶元素 */ + int peek = maxHeap.peek(); + Console.WriteLine($"堆顶元素为 {peek}"); + + /* 元素入堆 */ + int val = 7; + maxHeap.push(val); + Console.WriteLine($"元素 {val} 入堆后"); + maxHeap.print(); + + /* 堆顶元素出堆 */ + peek = maxHeap.poll(); + Console.WriteLine($"堆顶元素 {peek} 出堆后"); + maxHeap.print(); + + /* 获取堆大小 */ + int size = maxHeap.size(); + Console.WriteLine($"堆元素数量为 {size}"); + + /* 判断堆是否为空 */ + bool isEmpty = maxHeap.isEmpty(); + Console.WriteLine($"堆是否为空 {isEmpty}"); + } +} diff --git a/codes/csharp/include/PrintUtil.cs b/codes/csharp/include/PrintUtil.cs index 3a8f6f35..f940206f 100644 --- a/codes/csharp/include/PrintUtil.cs +++ b/codes/csharp/include/PrintUtil.cs @@ -1,8 +1,8 @@ -/** - * File: PrintUtil.cs - * Created Time: 2022-12-23 - * Author: haptear (haptear@hotmail.com) - */ +/** +* File: PrintUtil.cs +* Created Time: 2022-12-23 +* Author: haptear (haptear@hotmail.com), krahets (krahets@163.com) +*/ namespace hello_algo.include; @@ -20,6 +20,15 @@ public class Trunk public class PrintUtil { + /** + * Print a list + * @param list + */ + public static void PrintList(List list) + { + Console.WriteLine("[" + string.Join(", ", list) + "]"); + } + /** * Print a linked list * @param head @@ -119,4 +128,39 @@ public class PrintUtil Console.WriteLine(kv.ToString() + " -> " + map[kv]?.ToString()); } } + + public static void printHeap(Queue queue) + { + Console.Write("堆的数组表示:"); + List list = queue.ToList(); + Console.WriteLine(string.Join(',', list)); + Console.WriteLine("堆的树状表示:"); + TreeNode tree = TreeNode.ArrToTree(list.Cast().ToArray()); + PrintTree(tree); + } + + public static void printHeap(PriorityQueue queue) + { + var newQueue = new PriorityQueue(queue.UnorderedItems, queue.Comparer); + Console.Write("堆的数组表示:"); + List list = new List(); + while (newQueue.TryDequeue(out int element, out int priority)) + { + list.Add(element); + } + Console.WriteLine("堆的树状表示:"); + Console.WriteLine(string.Join(',', list.ToList())); + TreeNode tree = TreeNode.ArrToTree(list.Cast().ToArray()); + PrintTree(tree); + } + + public static void printMatrix(List> matrix) + { + Console.WriteLine("["); + foreach (List row in matrix) + { + Console.WriteLine(" [" + string.Join(", ", row.Select(r => $"{r}")) + "],"); + } + Console.WriteLine("]"); + } } diff --git a/codes/csharp/include/Vertex.cs b/codes/csharp/include/Vertex.cs new file mode 100644 index 00000000..954c0e8b --- /dev/null +++ b/codes/csharp/include/Vertex.cs @@ -0,0 +1,37 @@ +/** + * File: graph_adjacency_list.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com), krahets (krahets@163.com) + */ + +/* 顶点类 */ +class Vertex +{ + public int Val { get; init; } + public Vertex(int val) + { + Val = val; + } + + /* 输入值列表 vals ,返回顶点列表 vets */ + public static Vertex[] valsToVets(int[] vals) + { + Vertex[] vets = new Vertex[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + vets[i] = new Vertex(vals[i]); + } + return vets; + } + + /* 输入顶点列表 vets ,返回值列表 vals */ + public static List vetsToVals(List vets) + { + List vals = new List(); + foreach (Vertex vet in vets) + { + vals.Add(vet.Val); + } + return vals; + } +} diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 20cf88a2..6246aa0e 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -226,7 +226,39 @@ comments: true === "C#" ```csharp title="heap.cs" + /* 初始化堆 */ + // 初始化小顶堆 + PriorityQueue minHeap = new PriorityQueue(); + // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) + PriorityQueue maxHeap = new PriorityQueue(Comparer.Create((x, y) => y - x)); + /* 元素入堆 */ + maxHeap.Enqueue(1, 1); + maxHeap.Enqueue(3, 3); + maxHeap.Enqueue(2, 2); + maxHeap.Enqueue(5, 5); + maxHeap.Enqueue(4, 4); + + /* 获取堆顶元素 */ + int peek = maxHeap.Peek();//5 + + + /* 堆顶元素出堆 */ + // 出堆元素会形成一个从大到小的序列 + peek = maxHeap.Dequeue(); // 5 + peek = maxHeap.Dequeue(); // 4 + peek = maxHeap.Dequeue(); // 3 + peek = maxHeap.Dequeue(); // 2 + peek = maxHeap.Dequeue(); // 1 + + /* 获取堆大小 */ + int size = maxHeap.Count; + + /* 判断堆是否为空 */ + bool isEmpty = maxHeap.Count == 0; + + /* 输入列表并建堆 */ + minHeap = new PriorityQueue(new List<(int, int)> { (1, 1), (3, 3), (2, 2), (5, 5), (4, 4), }); ``` === "Swift"