diff --git a/CodingInterviewsProgram.cs b/CodingInterviewsProgram.cs index 759ce6f..8ff57fb 100644 --- a/CodingInterviewsProgram.cs +++ b/CodingInterviewsProgram.cs @@ -25,7 +25,7 @@ public static void Test() // new FindKthToTail.Solution().Test(); // 链表中倒数第k个结点 // new ReverseList.Solution().Test(); // 反转链表 // new Merge.Solution().Test(); // 合并两个排序的链表 - new HasSubtree.Solution().Test(); // 树的子结构 + // new HasSubtree.Solution().Test(); // 树的子结构 // new Mirror.Solution().Test(); // 二叉树的镜像 // new PrintMatrix.Solution().Test(); // 顺时针打印矩阵 // new StackWithMin.Solution().Test(); // 包含min函数的栈 @@ -76,6 +76,8 @@ public static void Test() // new HasPath.Solution().Test(); // 矩阵中的路径 // new MovingCount.Solution().Test(); // 机器人的运动范围 // new CutRope.Solution().Test(); // 剪绳子 + // new PrintNumbers.Solution().Test(); // 打印从1到最大的n位数 + new DeleteNode.Solution().Test(); // 删除链表的节点 } } } diff --git a/CommonProgram.cs b/CommonProgram.cs index 7c1911d..178a72c 100644 --- a/CommonProgram.cs +++ b/CommonProgram.cs @@ -10,7 +10,10 @@ class CommonProgram public static void Test() { - new KMP.Solution().Test(); // KMP模式匹配算法 + // new KMP.Solution().Test(); // KMP模式匹配算法 + // new AStar.Solution().Test(); // A*寻路算法 + // new Huffman.Solution().Test(); // 哈夫曼算法 + new PointIn.Solution().Test(); // 判断点是否在某一区域内 } } } diff --git a/Leetcode/ConstructQuadTree.cs b/Leetcode/ConstructQuadTree.cs new file mode 100644 index 0000000..e160a4e --- /dev/null +++ b/Leetcode/ConstructQuadTree.cs @@ -0,0 +1,175 @@ +/* +题目名称: +建立四叉树 + +题目描述: +给你一个 n * n 矩阵 grid ,矩阵由若干 0 和 1 组成。请你用四叉树表示该矩阵 grid 。 +你需要返回能表示矩阵的 四叉树 的根结点。 +注意,当 isLeaf 为 False 时,你可以把 True 或者 False 赋值给节点,两种值都会被判题机制 接受 +我们可以按以下步骤为二维区域构建四叉树: +如果当前网格的值相同(即,全为 0 或者全为 1),将 isLeaf 设为 True ,将 val 设为网格相应的值,并将四个子节点都设为 Null 然后停止。 +如果当前网格的值不同,将 isLeaf 设为 False, 将 val 设为任意值,然后如下图所示,将当前网格划分为四个子网格。 +使用适当的子网格递归每个子节点。 + +示例: +输入:grid = [[0,1],[1,0]] +输出:[[0,1],[1,0],[1,1],[1,1],[1,0]] + +代码结构: +public class Solution { + public Node Construct(int[][] grid) { + + } +} + +题目链接: +https://leetcode-cn.com/problems/construct-quad-tree/ +*/ +using System; +using System.Collections.Generic; +namespace ConstructQuadTree { + + public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } + } + + class Solution { + + /// + /// 解法,递归 + /// 基本思路: + /// 首先判断矩阵中的所有值是否相等,如果相等,则为叶子节点,直接返回 + /// 否则 + /// 1. 提取矩阵的上左部分,重复上述步骤,计算上左节点 + /// 1. 提取矩阵的上右部分,重复上述步骤,计算上右节点 + /// 1. 提取矩阵的下左部分,重复上述步骤,计算下左节点 + /// 1. 提取矩阵的下右部分,重复上述步骤,计算上右节点 + /// + + public Node Construct(int[][] grid) { + return ConstructImple(grid, 0, grid[0].Length - 1, 0, grid.Length - 1); + } + + public Node ConstructImple(int[][] grid, int left, int right, int top, int bottom) { + if(left> right || top> bottom) return null; + if(IsLeaf(grid, left, right, top, bottom)) { + return new Node(grid[top][left] == 1 ? true : false, true); + } + int delta = (right - left) / 2; + Node node = new Node(true, false); + node.topLeft = ConstructImple(grid, left, left + delta, top, top + delta); + node.topRight = ConstructImple(grid, left + delta + 1, right, top, top + delta); + node.bottomLeft = ConstructImple(grid, left, left + delta, top + delta + 1, bottom); + node.bottomRight = ConstructImple(grid, left + delta + 1, right, top + delta + 1, bottom); + return node; + } + + public bool IsLeaf(int[][] grid, int left, int right, int top, int bottom) { + int ret = grid[top][left]; + for(int i = top; i <= bottom; i ++) { + for(int j = left; j <= right; j ++) { + if(grid[i][j] != ret) { + return false; + } + } + } + return true; + } + + public void Print(Node root) { + Console.Write("["); + Queue queue = new Queue(); + queue.Enqueue(root); + while(queue.Count> 0) { + Node node = queue.Dequeue(); + if(node == null) { + Console.Write("null"); + }else{ + Console.Write("[" + (node.isLeaf ? 1 : 0) + "," + (node.val ? 1 : 0) + "]"); + queue.Enqueue(node.topLeft); + queue.Enqueue(node.topRight); + queue.Enqueue(node.bottomLeft); + queue.Enqueue(node.bottomRight); + } + if(queue.Count> 0) + Console.Write(","); + } + Console.WriteLine("]"); + } + + public void Test() { + int[][] grid = new int[][]{ + new int[]{0, 1}, + new int[]{1, 0} + }; + + // grid = new int[][]{ + // new int[]{0} + // }; + + // grid = new int[][]{ + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // new int[]{1, 1, 1, 1, 1, 1, 1, 1}, + // new int[]{1, 1, 1, 1, 1, 1, 1, 1}, + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // new int[]{1, 1, 1, 1, 0, 0, 0, 0}, + // }; + + // grid = new int[][]{ + // new int[]{1, 1}, + // new int[]{1, 1} + // }; + + // grid = new int[][]{ + // new int[]{1, 1, 0, 0}, + // new int[]{1, 1, 0, 0}, + // new int[]{0, 0, 1, 1}, + // new int[]{0, 0, 1, 1} + // }; + + // grid = new int[][]{ + // new int[]{1, 1, 0, 0}, + // new int[]{0, 0, 1, 1}, + // new int[]{1, 1, 0, 0}, + // new int[]{0, 0, 1, 1} + // }; + + Print(Construct(grid)); + } + } +} diff --git a/Leetcode/RectangleOverlap.cs b/Leetcode/RectangleOverlap.cs new file mode 100644 index 0000000..0041da5 --- /dev/null +++ b/Leetcode/RectangleOverlap.cs @@ -0,0 +1,78 @@ +/* +题目名称: +矩形重叠 + +题目描述: +矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。矩形的上下边平行于 x 轴,左右边平行于 y 轴。 +如果相交的面积为正 ,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。 +给出两个矩形 rec1 和 rec2 。如果它们重叠,返回 true;否则,返回 false 。 + +示例1: +输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3] +输出:true + +示例2: +输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1] +输出:false + +示例3: +输入:rec1 = [0,0,1,1], rec2 = [2,2,3,3] +输出:false + +代码结构: +public class Solution { + public bool IsRectangleOverlap(int[] rec1, int[] rec2) { + + } +} + +题目链接: +https://leetcode-cn.com/problems/rectangle-overlap/ +*/ +using System; +using System.Collections.Generic; +namespace RectangleOverlap { + + class Solution { + + /// + /// 解法1 + /// 基本思路: + /// 反向思维,检查两个矩形的位置,如果这两个矩形不重叠,则矩形1可能在矩形的左侧,上侧,右侧,或者下侧 + /// 如果第一个矩形在第二个矩形的左侧且不重叠,则矩形1的右下角横坐标必须小于等于矩形2左上角的横坐标 + /// 同理,可判断上侧,右侧,以及下侧 + /// 如果这些条件两个矩形没有一个满足的,就说明两个矩形重叠 + /// + public bool IsRectangleOverlap(int[] rec1, int[] rec2) { + // left top right bottom + return !(rec1[2] <= rec2[0] || rec1[1]>= rec2[3] || rec1[0]>= rec2[2] || rec1[3] <= rec2[1] || + rec1[0] == rec1[2] || rec1[1] == rec1[3] || rec2[0] == rec2[2] || rec2[1] == rec2[3]); + } + + /// + /// 解法2 + /// 基本思路: + /// 如果两个矩形重叠,则两个矩形的水平边,投影到x轴以后的两条线段也一定重合 + /// 如果两个矩形重叠,则两个矩形的垂直边,投影到y轴以后的两条线段也一定重合 + /// 所以最终将问题转换为求解两条线段是否重合的问题 + /// + public bool IsRectangleOverlap2(int[] rec1, int[] rec2) { + return Math.Max(rec1[0], rec2[0]) < Math.Min(rec1[2], rec2[2]) && + Math.Max(rec1[1], rec2[1]) < Math.Min(rec1[3], rec2[3]); + } + + public void Test() { + int[] rec1 = new int[]{0, 0, 2, 2}; + int[] rec2 = new int[]{1, 1, 3, 3}; + + rec1 = new int[]{0, 0, 1, 1}; + rec2 = new int[]{1, 0, 2, 1}; + + // rec1 = new int[]{0, 0, 2, 2}; + // rec2 = new int[]{2, 2, 3, 3}; + + // Console.WriteLine(IsRectangleOverlap(rec1, rec2)); + Console.WriteLine(IsRectangleOverlap2(rec1, rec2)); + } + } +} diff --git a/Leetcode/RemoveDuplicates.cs b/Leetcode/RemoveDuplicates.cs index 0bb19da..5746b20 100644 --- a/Leetcode/RemoveDuplicates.cs +++ b/Leetcode/RemoveDuplicates.cs @@ -92,6 +92,7 @@ public void Print(int[] nums, int len){ for(int i = 0; i < len; i ++){ Console.Write(nums[i] + " "); } + Console.WriteLine(); } public void Test() { diff --git a/Leetcode/ReverseString.cs b/Leetcode/ReverseString.cs index 99cf6eb..8727a5f 100644 --- a/Leetcode/ReverseString.cs +++ b/Leetcode/ReverseString.cs @@ -69,6 +69,7 @@ public void Print(char[] s){ { Console.Write(c + " "); } + Console.WriteLine(); } public void Test() { diff --git a/Leetcode/SpiralOrder.cs b/Leetcode/SpiralOrder.cs index 73bcbe5..f74e1ca 100644 --- a/Leetcode/SpiralOrder.cs +++ b/Leetcode/SpiralOrder.cs @@ -48,6 +48,7 @@ public void Print(IList list){ { Console.Write(item + " "); } + Console.WriteLine(); } public void Test() { diff --git a/LeetcodeProgram.cs b/LeetcodeProgram.cs index c064bf6..763222e 100644 --- a/LeetcodeProgram.cs +++ b/LeetcodeProgram.cs @@ -25,10 +25,12 @@ public static void Test() // new ReverseWords.Solution().Test(); // 反转字符串中的单词 III // new ProductExceptSelf.Solution().Test(); // 除自身以外数组的乘积 // new ContainsDuplicate.Solution().Test(); // 存在重复元素 - new SpiralOrder.Solution().Test(); // 螺旋矩阵 + // new SpiralOrder.Solution().Test(); // 螺旋矩阵 + // new ConstructQuadTree.Solution().Test(); // 建立四叉树 // new AddStrings.Solution().Test(); // 字符串相加 + new RectangleOverlap.Solution().Test(); // 矩形重叠 } } } diff --git a/Program.cs b/Program.cs index da4c24c..41d663b 100644 --- a/Program.cs +++ b/Program.cs @@ -6,8 +6,8 @@ class Program { static void Main(string[] args) { - CodingInterviewsProgram.Test(); // 剑指offer - // LeetcodeProgram.Test(); // Leetcode + // CodingInterviewsProgram.Test(); // 剑指offer + LeetcodeProgram.Test(); // Leetcode // SortProgram.Test(); // 排序算法 // InterviewGuideProgram.Test(); // 程序员代码面试指南 // CommonProgram.Test(); // 常用算法 diff --git a/README.md b/README.md index e7dd72e..c7d90a3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ #### 目录 * [剑指offer](#剑指offer) * [排序算法](#排序算法) -* [常用算法](#常用算法) +* [经典算法](#经典算法) * [Leetcode](#Leetcode) * [程序员代码面试指南](#程序员代码面试指南) @@ -341,10 +341,12 @@ -## 常用算法 +## 经典算法 * [KMP模式匹配算法](%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/KMP.cs) -* A*寻路算法 +* [A*寻路算法](%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/AStar.cs) +* [哈夫曼算法](%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/Huffman.cs) +* [判断点是否在某一区域内](%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/PointIn.cs) ## Leetcode @@ -550,8 +552,10 @@ Nim 游戏 字符串相加 +建立四叉树 +矩形重叠 diff --git "a/345円211円221円346円214円207円offer/Convert.cs" "b/345円211円221円346円214円207円offer/Convert.cs" index a5ee377..83064f8 100644 --- "a/345円211円221円346円214円207円offer/Convert.cs" +++ "b/345円211円221円346円214円207円offer/Convert.cs" @@ -123,6 +123,7 @@ public void Print(TreeNode root){ Console.Write(node.val + " "); node = node.left; } + Console.WriteLine(); } public void Test() { diff --git "a/345円211円221円346円214円207円offer/DeleteNode.cs" "b/345円211円221円346円214円207円offer/DeleteNode.cs" new file mode 100644 index 0000000..5fa889a --- /dev/null +++ "b/345円211円221円346円214円207円offer/DeleteNode.cs" @@ -0,0 +1,64 @@ +/* +题目名称: +删除链表的节点 + +题目描述: +给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。 + +1.此题对比原题有改动 +2.题目保证链表中节点的值互不相同 +3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点 + +代码结构: +class Solution { + public ListNode deleteNode (ListNode head, int val) { + // write code here + } +} +*/ +using System; +using System.Collections.Generic; +namespace DeleteNode { + public class ListNode + { + public int val; + public ListNode next; + + public ListNode (int x) + { + val = x; + } + } + + class Solution { + public ListNode DeleteNode (ListNode head, int val) { + if(head == null) return null; + if(head.val == val) return head.next; + ListNode node = head; + while(node.next != null) { + if(node.next.val == val) { + node.next = node.next.next; + break; + } + node = node.next; + } + return head; + } + + public void Test() + { + ListNode head = new ListNode(2); + head.next = new ListNode(5); + head.next.next = new ListNode(1); + head.next.next.next = new ListNode(9); + int val = 5; + + ListNode result = DeleteNode(head, val); + while (head != null) { + Console.Write(head.val + " "); + head = head.next; + } + Console.WriteLine(); + } + } +} diff --git "a/345円211円221円346円214円207円offer/Duplicate.cs" "b/345円211円221円346円214円207円offer/Duplicate.cs" index edd04f4..bda3313 100644 --- "a/345円211円221円346円214円207円offer/Duplicate.cs" +++ "b/345円211円221円346円214円207円offer/Duplicate.cs" @@ -6,18 +6,17 @@ 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。 请找出数组中任意一个重复的数字。 -例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。 +例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1 代码结构: -class Solution -{ - public bool duplicate(int[] numbers, int[] duplication) - { +class Solution { + public int duplicate (List numbers) { // write code here } } */ using System; +using System.Collections.Generic; namespace Duplicate { class Solution { @@ -28,19 +27,18 @@ class Solution { /// 利用长度为n的辅助数组记录每个数字的出现次数 /// - public bool Duplicate(int[] numbers, int[] duplication) + public int Duplicate(List numbers) { if (numbers == null){ - return false; + return -1; } - int[] array = new int[numbers.Length]; - for(int i = 0; i < numbers.Length; i ++){ + int[] array = new int[numbers.Count]; + for(int i = 0; i < numbers.Count; i ++){ if(++ array[numbers[i]]> 1){ - duplication[0] = numbers[i]; - return true; + return numbers[i]; } } - return false; + return -1; } /// @@ -51,20 +49,19 @@ public bool Duplicate(int[] numbers, int[] duplication) /// 之后再遇到相同的数字时,发现对应的下标位置上的数已经小于0,则说明出现了重复元素 /// - public bool Duplicate2(int[] numbers, int[] duplication) + public int Duplicate2(List numbers) { if (numbers == null){ - return false; + return -1; } - for(int i = 0; i < numbers.Length; i ++){ - int num = numbers[i] < 0 ? numbers[i] + numbers.Length : numbers[i]; + for(int i = 0; i < numbers.Count; i ++){ + int num = numbers[i] < 0 ? numbers[i] + numbers.Count : numbers[i]; if(numbers[num] < 0){ - duplication[0] = num; - return true; + return num; } - numbers[num] -= numbers.Length; + numbers[num] -= numbers.Count; } - return false; + return -1; } /// @@ -73,43 +70,39 @@ public bool Duplicate2(int[] numbers, int[] duplication) /// 不使用辅助数组 /// 每访问到一个数字,判断这个数字与其下标是否相等,若不相等,则将该数字与以该数字值为下标的位置上的数字进行交换 /// 如果要交换的两个数字相等,则找到了重复数字 + /// 可以理解为,让每个数字都在以其值为下标的位置上,如果有重复的数字,那它的位置一定会被重复值占领 /// - public bool Duplicate3(int[] numbers, int[] duplication) + public int Duplicate3(List numbers) { if (numbers == null){ - return false; + return -1; } - for(int i = 0; i < numbers.Length; i ++){ + for(int i = 0; i < numbers.Count; i ++){ while(i != numbers[i]){ int temp = numbers[numbers[i]]; if(numbers[i] == temp){ - duplication[0] = numbers[i]; - return true; + return numbers[i]; } numbers[numbers[i]] = numbers[i]; numbers[i] = temp; } } - return false; + return -1; } public void Test() { - int[] numbers = new int[]{2,3,1,0,2,5,3}; + List numbers = new List{2,3,1,0,2,5,3}; // numbers = null; - // numbers = new int[]{}; - // numbers = new int[]{0}; - // numbers = new int[]{0, 0}; - // numbers = new int[]{0, 2, 3, 3, 2}; + // numbers = new List{}; + // numbers = new List{0}; + // numbers = new List{0, 0}; + // numbers = new List{0, 2, 3, 3, 2}; - int[] duplication = new int[1]; - - // Console.WriteLine(Duplicate(numbers, duplication)); - Console.WriteLine(Duplicate2(numbers, duplication)); - // Console.WriteLine(Duplicate3(numbers, duplication)); - - Console.WriteLine(duplication[0]); + Console.WriteLine(Duplicate(numbers)); + // Console.WriteLine(Duplicate2(numbers)); + // Console.WriteLine(Duplicate3(numbers)); } } } diff --git "a/345円211円221円346円214円207円offer/EntryNodeOfLoop.cs" "b/345円211円221円346円214円207円offer/EntryNodeOfLoop.cs" index ad206e4..9c44524 100644 --- "a/345円211円221円346円214円207円offer/EntryNodeOfLoop.cs" +++ "b/345円211円221円346円214円207円offer/EntryNodeOfLoop.cs" @@ -132,11 +132,12 @@ public ListNode EntryNodeOfLoop3(ListNode pHead) /// 假设链表起点到环的入口点距离为a,环的入口点到相遇点距离为b(顺时针),相遇点到环的入口点距离为c(顺时针) /// 定义快指针走两步,慢指针走一步,所以快指针走过的路程是慢指针的2倍 /// 则快指针走过的距离为 x = a + n(b + c)+ b - /// 慢指针走过的距离为 y = a + b - /// 由 x = 2y 得 a + n(b + c)+ b = 2 * (a + b) - /// 进一步推导得到 a = (n - 1) *(b + c)+ c - /// 表示从链表起点到环入口点的距离 = (n - 1)环长 + 相遇点到环入口点的长度(c) + /// 慢指针走过的距离为 y = a + b + m (b + c) + /// 由 x = 2y 得 a + n(b + c)+ b = 2 * (a + b + m(b + c)) + /// 进一步推导得到 a = (n - 2m - 1) *(b + c)+ c + /// 表示从链表起点到环入口点的距离 = (n - 2m - 1)环长 + 相遇点到环入口点的长度(c) /// 因此两个指针分别从链表起点和相遇点开始移动,一定会在环入口点相遇 + /// 注意由于是根据公式推导得出的结论,所以快指针和慢指针走过的距离计算一定要准确,保证是2倍的关系 /// public ListNode EntryNodeOfLoop4(ListNode pHead) diff --git "a/345円211円221円346円214円207円offer/GetLeastNumbers.cs" "b/345円211円221円346円214円207円offer/GetLeastNumbers.cs" index 9b285a8..c5c0e7d 100644 --- "a/345円211円221円346円214円207円offer/GetLeastNumbers.cs" +++ "b/345円211円221円346円214円207円offer/GetLeastNumbers.cs" @@ -13,6 +13,9 @@ public List GetLeastNumbers_Solution(int[] input, int k) // write code here } } + +参考: +https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/82837278 */ using System; using System.Collections.Generic; diff --git "a/345円211円221円346円214円207円offer/GetNext.cs" "b/345円211円221円346円214円207円offer/GetNext.cs" index c4ed8d1..5a558c9 100644 --- "a/345円211円221円346円214円207円offer/GetNext.cs" +++ "b/345円211円221円346円214円207円offer/GetNext.cs" @@ -3,7 +3,7 @@ 二叉树的下一个结点 题目描述: -给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。 +给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。 注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 代码结构: @@ -14,6 +14,8 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) // write code here } } + + */ using System; namespace GetNext { @@ -39,6 +41,7 @@ class Solution { /// 1. 节点为空,返回空 /// 2. 如果节点有右节点,则一直向下寻找该右节点的左节点,直到叶子节点为止,即为下一个节点 /// 3. 如果节点有父节点,且是父节点的左节点,则返回父节点。否则继续判断其父节点是否满足条件,直到父节点为空。 + /// 可以理解为中序遍历到一个子节点后,下一个节点一定是这个子节点所在左子树的父节点 /// 前中后续遍历介绍 https://blog.csdn.net/FightLei/article/details/89281600 /// diff --git "a/345円211円221円346円214円207円offer/IsNumeric.cs" "b/345円211円221円346円214円207円offer/IsNumeric.cs" index 963f758..d9b681e 100644 --- "a/345円211円221円346円214円207円offer/IsNumeric.cs" +++ "b/345円211円221円346円214円207円offer/IsNumeric.cs" @@ -32,9 +32,9 @@ class Solution { /// 4. 字符是'.',只能出现一次,且不能在首位(可以在尾部),且不能在'e'/'E'之后出现 /// - public bool IsNumeric(char[] str) + public bool IsNumeric(string str) { - if(str == null || str.Length == 0) return false; + if(string.IsNullOrEmpty(str)) return false; int dotCount = 0, eCount = 0; for(int i = 0; i < str.Length; i ++){ if(str[i]>= '0' && str[i] <= '9') continue; @@ -60,7 +60,7 @@ public bool IsNumeric(char[] str) /// 使用正则表达式进行匹配 /// - public bool IsNumeric2(char[] str) + public bool IsNumeric2(string str) { return Regex.IsMatch(new string(str), @"^[+-]?\d*(\.\d+)?([eE][+-]?\d+)?$"); } @@ -70,19 +70,10 @@ public bool IsNumeric2(char[] str) /// public void Test() { + string str = " -.12"; - char[] str = new char[]{'1', '.', '2', '.', '3'}; - // str = new char[]{'+', '1', '2'}; - // str = new char[]{'5', 'e', '2'}; - // str = new char[]{'3', '.', '1', '4'}; - // str = new char[]{'-', '1', 'E', '-', '5'}; - // str = new char[]{'1', '2', 'e'}; - // str = new char[]{'1', '2', 'e', '+', '4', '.', '3'}; - // str = new char[]{'1', 'a', '3', '.', '4'}; - // str = new char[]{'-', '.', '1', '2'}; - - // Console.WriteLine(IsNumeric(str)); - Console.WriteLine(IsNumeric2(str)); + Console.WriteLine(IsNumeric(str)); + // Console.WriteLine(IsNumeric2(str)); } } } diff --git "a/345円211円221円346円214円207円offer/Match.cs" "b/345円211円221円346円214円207円offer/Match.cs" index ed84643..55e5db4 100644 --- "a/345円211円221円346円214円207円offer/Match.cs" +++ "b/345円211円221円346円214円207円offer/Match.cs" @@ -9,10 +9,8 @@ 例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配 代码结构: -class Solution -{ - public bool match(char[] str, char[] pattern) - { +class Solution { + public bool match (string str, string pattern) { // write code here } } @@ -27,22 +25,24 @@ class Solution { /// 基本思路: /// 当出现x*时,即任意一个字符加上'*' /// 1. x*消耗0个字符,模式串后移两位,字符串不动 - /// 2. x*消耗1个字符,模式串后移两位,字符串后移一位 + /// 2. x*消耗1个字符,模式串后移两位,字符串后移一位。这种情况可以被1和3组合完成,代码中省略了这步,用于减少递归深度,避免超时 /// 3. x*消耗多个字符,即匹配下一个字符,模式串不动,字符串后移一位 /// (注意,当这个'x'和字符串对应位的字符不相等时,x*只能等于消耗0个字符) /// 未出现时 /// 1. 若模式串与字符串对应位字符相等,则都后移一位,匹配剩余的 /// 2. 若不相等,直接返回匹配失败 + /// 注意特殊情况,当字符串已经走完,但模式串还未遍历时,需要支持这种情况下模式串继续遍历 + /// 因为模式串可能还剩下x*这种,可以匹配0字符 /// - public bool MatchImpl(char[] str, int sIndex, char[] pattern, int pIndex) { + public bool MatchImpl(string str, int sIndex, string pattern, int pIndex) { if(sIndex == str.Length && pIndex == pattern.Length) return true; if(pIndex < pattern.Length - 1 && pattern[pIndex + 1] == '*'){ if (sIndex < str.Length && (str[sIndex] == pattern[pIndex] - || pattern[pIndex] == '.')) { - return MatchImpl(str, sIndex, pattern, pIndex + 2) - || MatchImpl(str, sIndex + 1, pattern, pIndex) - || MatchImpl(str, sIndex + 1, pattern, pIndex + 2); + || pattern[pIndex] == '.')) + { + return MatchImpl(str, sIndex, pattern, pIndex + 2) + || MatchImpl(str, sIndex + 1, pattern, pIndex); }else{ return MatchImpl(str, sIndex, pattern, pIndex + 2); } @@ -55,7 +55,7 @@ public bool MatchImpl(char[] str, int sIndex, char[] pattern, int pIndex) { } } - public bool Match(char[] str, char[] pattern) + public bool Match (string str, string pattern) { return MatchImpl(str, 0, pattern, 0); } @@ -63,6 +63,7 @@ public bool Match(char[] str, char[] pattern) /// /// 解法2,动态规划 /// 基本思路: + /// 不能直接用[i,j]表示字符串i位置和模式串j位置的匹配情况,因为当字符串长度为0时,无法表示对应的匹配情况 /// 构建动态规划转移方程f[i,j],表示字符串的前i个字符与模式串的前j个字符是否匹配 /// 对于不包含"*"的情况 /// f[i, j] = f[i - 1, j - 1] && (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.'); @@ -80,7 +81,7 @@ public bool Match(char[] str, char[] pattern) /// f[str.Length, pattern.Length]就表示字符串str与模式串pattern是否匹配 /// - public bool Match2(char[] str, char[] pattern) + public bool Match2 (string str, string pattern) { bool[,] f = new bool[str.Length + 1, pattern.Length + 1]; for(int i = 0; i <= str.Length; i ++){ @@ -104,27 +105,14 @@ public bool Match2(char[] str, char[] pattern) public void Test() { - char[] str = new char[]{'a', 'a', 'a'}; - // str = new char[]{}; - // str = new char[]{'a', 'b', 'b', 'b', '*'}; - // str = new char[]{'a', 'b', 'b', 'b'}; - // str = new char[]{'a', 'b', 'c', '.', '*', 'f', '*', 's'}; - // str = new char[]{'a'}; - // str = new char[]{'a', 'a'}; + string str = "aaa"; + str = "aaaaaaaaaaaaab"; - char[] pattern = new char[]{'a', '.', 'a'}; - // pattern = new char[]{'a', '*', 'a', 'a'}; - // pattern = new char[]{'b', '*', 'a', 'a', 'a'}; - // pattern = new char[]{'b', '*'}; - // pattern = new char[]{'a', 'b', '*'}; - // pattern = new char[]{'a', 'b', '*', '*'}; - // pattern = new char[]{'.', '*'}; - // pattern = new char[]{'*', '*'}; - // pattern = new char[]{}; - // pattern = new char[]{'.'}; + string pattern = "a.a"; + pattern = "a*a*a*a*a*a*a*a*a*a*c"; - // Console.WriteLine(Match(str, pattern)); - Console.WriteLine(Match2(str, pattern)); + Console.WriteLine(Match(str, pattern)); + // Console.WriteLine(Match2(str, pattern)); } } } diff --git "a/345円211円221円346円214円207円offer/Mirror.cs" "b/345円211円221円346円214円207円offer/Mirror.cs" index b3f8fcd..cb1aaf4 100644 --- "a/345円211円221円346円214円207円offer/Mirror.cs" +++ "b/345円211円221円346円214円207円offer/Mirror.cs" @@ -98,6 +98,7 @@ public void Print(TreeNode root){ queue.Enqueue(node.right); } } + Console.WriteLine(); } public void Test() { diff --git "a/345円211円221円346円214円207円offer/NumberOf1.cs" "b/345円211円221円346円214円207円offer/NumberOf1.cs" index c731d89..a63feb0 100644 --- "a/345円211円221円346円214円207円offer/NumberOf1.cs" +++ "b/345円211円221円346円214円207円offer/NumberOf1.cs" @@ -30,6 +30,9 @@ class Solution { /// 但是考虑负数的情况,和正数右移最高位补0不同的是,负数右移最高位补1,这样就会有无数个1,导致循环无法停止 /// 既然将目标数右移和1与行不通,那么我们可以反过来 /// 将1不断左移(从最低位到最高位每一位依次是1,其他位是0),然后和目标数相与来求1的个数 + /// 1的特点,就是最低位是1,其他位是0。数值和1与可以判断自己最低位的情况 + /// 将1不断左移,可以让每位都是1(其他位是0),数值和它与可以判断自己每位的情况 + /// 1在不断左移时,到最高位时时(一个较大的负数),移过最高位后就是0 /// public int NumberOf1(int n) diff --git "a/345円211円221円346円214円207円offer/PrintNumbers.cs" "b/345円211円221円346円214円207円offer/PrintNumbers.cs" new file mode 100644 index 0000000..19b07de --- /dev/null +++ "b/345円211円221円346円214円207円offer/PrintNumbers.cs" @@ -0,0 +1,44 @@ +/* +题目名称: +打印从1到最大的n位数 + +题目描述: +输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 +1. 用返回一个整数列表来代替打印 +2. n 为正整数,0 < n <= 5 + +代码结构: +class Solution { + public List printNumbers (int n) { + // write code here + } +} +*/ +using System; +using System.Collections.Generic; +namespace PrintNumbers { + class Solution { + public List PrintNumbers (int n) { + int num = 1; + while(n --> 0) { + num *= 10; + } + + List result = new List(num - 1); + for(int i = 0; i < num - 1; i ++) { + result.Add(i + 1); + } + return result; + } + + public void Test() + { + int n = 1; + foreach (var num in PrintNumbers(n)) + { + Console.Write(num + " "); + } + Console.WriteLine(); + } + } +} diff --git "a/345円270円270円347円224円250円347円256円227円346円263円225円/AStar.cs" "b/345円270円270円347円224円250円347円256円227円346円263円225円/AStar.cs" new file mode 100644 index 0000000..78ecea9 --- /dev/null +++ "b/345円270円270円347円224円250円347円256円227円346円263円225円/AStar.cs" @@ -0,0 +1,153 @@ +/* +算法名称: +A*寻路算法 + +算法描述: +A*寻路算法是启发式探索的一个典型实践。在寻路的过程中,给每个节点绑定一个估计值(即启发式), +在对节点的遍历过程中时采取估计值优先原则,估计值更优的节点会被优先遍历。 +*/ +using System; +using System.Collections.Generic; +namespace AStar { + + class Unit { + public float g; + public float h; + public int x; + public int y; + public Unit parent; + + public Unit(int x, int y) { + this.x = x; + this.y = y; + g = 0; + h = 0; + } + } + + class Solution { + + public float StraightWieght {get; set;} = 1.0f; + public float ObliqueWieght {get; set;} = 1.4f; + public bool EnableOblique {get; set;} = false; + public bool EnableCorner {get; set;} = false; + + private Dictionary mOpenList = new Dictionary(); + private Dictionary mCloseList = new Dictionary(); + private Dictionary mBlocks = new Dictionary(); + + private int[,] offsets = new int[8, 2] { + {-1, 0}, {1, 0}, {0, -1}, {0, 1}, + {-1, -1}, {1, -1}, {1, 1}, {-1, 1} + }; + + private void Reset(Unit start, Unit[] blocks) { + mOpenList.Clear(); + mOpenList.Add(Key(start), start); + mCloseList.Clear(); + mBlocks.Clear(); + foreach(var unit in blocks){ + mBlocks.Add(Key(unit), unit); + } + } + + /// + /// 基本思路: + /// 1. 将初始节点放入到open列表中 + /// 2. 判断open列表。如果为空,则搜索失败。如果open列表中存在目标节点,则搜索成功。 + /// 3. 从open列表中取出F值最小的节点作为当前节点,并将其加入到close列表中 + /// 4. 计算当前节点相邻的所有可到达节点,生成一组子节点。对于每个子节点: + /// 4.1. 如果该节点在close列表中,则丢弃它 + /// 4.2. 如果该节点在open列表中,则检查其通过当前节点计算得到的F值是否更小,如果更小则更新其F值,并将其父节点设置为当前节点 + /// 4.3. 如果该节点不在open列表中,则将其加入到open列表,并计算F值,设置其父节点为当前节点 + /// 5. 转到2步骤 + /// + + public List Navigate(Unit start, Unit end, Unit size, Unit[] blocks) { + Reset(start, blocks); + while(mOpenList.Count> 0) { + string endKey = Key(end); + if(mOpenList.ContainsKey(endKey)) { + return GetPath(mOpenList[endKey]); + } + Unit cur = GetNextUnit(); + string key = Key(cur); + mOpenList.Remove(key); + mCloseList.Add(key, cur); + List units = GetNearUnits(cur, size); + foreach(var unit in units) { + Estimate(cur, unit, end); + string unitKey = Key(unit); + if(mOpenList.ContainsKey(unitKey)) { + if(unit.g < mOpenList[unitKey].g) + mOpenList[unitKey] = unit; + }else{ + mOpenList.Add(unitKey, unit); + } + } + } + return null; + } + + private Unit GetNextUnit() { + Unit next = null; + foreach(var unit in mOpenList.Values) { + if(next == null || (unit.g + unit.h) < (next.g + next.h)) + next = unit; + } + return next; + } + + private List GetPath(Unit end) { + List list = new List(); + Unit unit = end; + while(unit != null) { + list.Add(unit); + unit = unit.parent; + } + list.Reverse(); + return list; + } + + private List GetNearUnits(Unit cur, Unit size) { + List list = new List(); + int count = EnableOblique ? 8 : 4; + for(int i = 0; i < count; i ++) { + int x = cur.x + offsets[i, 0], y = cur.y + offsets[i, 1]; + string key = Key(x, y); + if(mBlocks.ContainsKey(key) || mCloseList.ContainsKey(key)) + continue; + if(x>= 0 && x < size.x && y>= 0 && y < size.y) { + if(i>= 4 && !EnableCorner && (mBlocks.ContainsKey(Key(cur.x, y)) || mBlocks.ContainsKey(Key(x, cur.y)))) + continue; + list.Add(new Unit(x, y)); + } + } + return list; + } + + private void Estimate(Unit cur, Unit unit, Unit end) { + unit.g = Math.Abs(cur.x - unit.x) + Math.Abs(cur.y - unit.y)> 1 ? ObliqueWieght : StraightWieght; + unit.h = (Math.Abs(end.x - unit.x) + Math.Abs(end.y - unit.y)) * StraightWieght; + unit.parent = cur; + } + + private string Key(int x, int y) { + return x + "*" + y; + } + + private string Key(Unit unit) { + return Key(unit.x, unit.y); + } + + public void Test() { + List path = Navigate(new Unit(3, 3), new Unit(7, 3), new Unit(8, 6), new Unit[]{new Unit(5, 2), new Unit(5, 3), new Unit(5, 4)}); + + foreach(var u in path) { + Console.WriteLine(u.x + " " + u.y); + } + + + } + } +} diff --git "a/345円270円270円347円224円250円347円256円227円346円263円225円/Huffman.cs" "b/345円270円270円347円224円250円347円256円227円346円263円225円/Huffman.cs" new file mode 100644 index 0000000..18d503e --- /dev/null +++ "b/345円270円270円347円224円250円347円256円227円346円263円225円/Huffman.cs" @@ -0,0 +1,146 @@ +/* +算法名称: +哈夫曼Huffman + +算法描述: +哈夫曼树,是最优二叉树。给定n个权值作为n个叶子节点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树 +哈夫曼算法具有贪心选择性质与最优子结构 +哈夫曼编码就是哈夫曼树在电讯通信中的应用之一,可以通过哈夫曼树设计总长最短的二进制前缀编码。 +电文中的某个字符即对应哈夫曼树的某个叶子节点,每个字符的编码即为从根节点到每个叶子节点的路径上得到的0,1序列。(比如走了左子树则记0,走右子树则记1) + +补充: +为了保证解码的唯一性,要求任意字符的编码都不能是另一字符编码的前缀,这种编码称为前缀编码,哈夫曼编码就是一种前缀编码 +*/ +using System; +using System.Collections.Generic; +namespace Huffman { + + class Solution { + + public class TreeNode : IComparable
+ { + public float weight; // 权重 + public char symbol; // 符合 + public TreeNode left; + public TreeNode right; + public TreeNode (float x) + { + weight = x; + } + public TreeNode (float x, char c) + { + weight = x; + symbol = c; + } + public int CompareTo(TreeNode other) { + int ret = this.weight.CompareTo(other.weight); + // 这里是为了使SortedSet可以存放权重相同的节点。(SortedSet默认是会过滤重复值的元素的) + if(ret == 0) + return this.GetHashCode().CompareTo(other.GetHashCode()); + return ret; + } + public Dictionary CodingMap { + get { + Dictionary map = new Dictionary(); + caculateCodingMap(this, map, ""); + return map; + } + } + // 计算哈夫曼树的带权路径长度,WPL(Weighted Path Length of Tree) + public float WPL { + get { + return caculateWpl(this, 0); + } + } + // 计算哈夫曼树中对应字符的编码(默认规则是,走左子树编码加0,走右子树编码加1) + private float caculateWpl(TreeNode node, int depth) { + if(node == null) return 0.0f; + if(node.left != null || node.right != null) { + return caculateWpl(node.left, depth + 1) + caculateWpl(node.right, depth + 1); + } + return node.weight * depth; + } + private void caculateCodingMap(TreeNode node, Dictionary map, string str) { + if(node == null) return; + if(node.left == null || node.right == null) { + map.Add(node.symbol, str); + return; + } + caculateCodingMap(node.left, map, str + "0"); + caculateCodingMap(node.right, map, str + "1"); + } + } + + /// + /// 基本思路: + /// 假设有n个权值,则构造出来的哈夫曼树有n个叶子节点。n个权值分别设为w1,w2,...wn,则哈夫曼树的构造规则为 + /// 1. 将w1,w2,...wn看成是有n棵树的森林(每棵树仅有一个节点) + /// 2. 在森林中选出两个根节点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根节点权值为其左右子树根节点权值之和 + /// 3. 从森林中删除选取的两棵树,并将新树加入森林 + /// 4. 重复2、3步骤,直到森林中只剩下一棵树为止,该树即为所求的哈夫曼树 + /// + + public TreeNode HuffmanTree(float[] weights, char[] symbols = null) { + if(weights == null || weights.Length <= 0) return null; + SortedSet
set = new SortedSet
(); + for(int i = 0; i < weights.Length; i ++) { + TreeNode node = new TreeNode(weights[i]); + set.Add(node); + if(symbols != null) + node.symbol = symbols[i]; + } + + while(set.Count> 1) { + TreeNode left = set.Min; + set.Remove(left); + TreeNode right = set.Min; + set.Remove(right); + + TreeNode root = new TreeNode(left.weight + right.weight); + root.left = left; + root.right = right; + set.Add(root); + } + + return set.Min; + } + + // 层次遍历打印哈夫曼树 + public void Print(TreeNode root) { + Console.WriteLine("huffman tree:"); + Queue
queue = new Queue
(); + if(root != null) + queue.Enqueue(root); + while(queue.Count> 0) { + int count = queue.Count; + for(int i = 0; i < count; i ++) { + TreeNode node = queue.Dequeue(); + Console.Write(node.symbol + "_" + node.weight + " "); + if(node.left != null) + queue.Enqueue(node.left); + if(node.right != null) + queue.Enqueue(node.right); + } + Console.WriteLine(); + } + } + + public void Test() { + char[] symbols = new char[]{'A', 'B', 'C', 'D', 'E'}; + float[] weights = new float[]{2, 3, 4, 5, 4}; + + TreeNode tree = HuffmanTree(weights, symbols); + + Print(tree); + + Console.WriteLine("huffman tree wpl:"); + Console.WriteLine(tree.WPL); + + Console.WriteLine("huffman tree coding map:"); + foreach (var item in tree.CodingMap) + { + Console.WriteLine(item.Key + " " + item.Value); + } + } + } +} diff --git "a/345円270円270円347円224円250円347円256円227円346円263円225円/KMP.cs" "b/345円270円270円347円224円250円347円256円227円346円263円225円/KMP.cs" index ecc7a52..1918da8 100644 --- "a/345円270円270円347円224円250円347円256円227円346円263円225円/KMP.cs" +++ "b/345円270円270円347円224円250円347円256円227円346円263円225円/KMP.cs" @@ -13,6 +13,31 @@ namespace KMP { class Solution { + /// + /// 暴力匹配算法 Brute-Force + /// 基本思路: + /// 朴素的模式匹配算法,从目标串s的第一个字符开始和模式串p的第一个字符开始比较, + /// 如果相等,则进一步比较二者的后继字符 + /// 如果不相等,则从目标串s的第二个字符开始再重新与模式串p的第一个字符进行比较 + /// 暴力匹配就在于当发生失配时,目标串直接回溯到开始匹配字符的下一个字符,模式串直接回溯到第一个字符 + /// + public int BF(string s, string p){ + int i = 0, j = 0; + while(i < s.Length && j < p.Length) { + if(s[i] == p[j]) { + i ++; + j ++; + }else{ + i = i - j + 1; + j = 0; + } + } + if(j == p.Length) { + return i - j; + } + return -1; + } + /// /// 求解next数组 /// 基本思路: @@ -75,6 +100,8 @@ public void Test() { p = "abcac"; // p = "aaaaax"; + // Console.WriteLine(BF(s, p)); + Console.WriteLine(KMP(s, p)); } } diff --git "a/345円270円270円347円224円250円347円256円227円346円263円225円/PointIn.cs" "b/345円270円270円347円224円250円347円256円227円346円263円225円/PointIn.cs" new file mode 100644 index 0000000..c4dd332 --- /dev/null +++ "b/345円270円270円347円224円250円347円256円227円346円263円225円/PointIn.cs" @@ -0,0 +1,204 @@ +/* +算法名称: +判断点是否在某一区域内 + +算法描述: +判断点是否在某一区域内。比如圆形区域,环形区域,扇形区域,矩形区域,甚至是多边形区域。 +常用于在游戏开发中判断攻击技能是否命中目标 +*/ +using System; +using System.Collections.Generic; +namespace PointIn { + + class Solution { + + /// + /// 判断点是否在某一圆形区域内 + /// 基本思路: + /// 计算目标点到圆圆心的距离,距离小于圆的半径则在圆内 + /// 直接计算圆半径的平方来比较大小,可以避免开平方操作 + /// + /// 目标点的横坐标 + /// 目标点的纵坐标 + /// 圆圆心的横坐标 + /// 圆圆心的纵坐标 + /// 圆的半径 + public bool PointInCircle(double x, double y, double circleX, double circleY, double CircleRadius) { + return Math.Pow(circleX - x, 2) + Math.Pow(circleY - y, 2) <= Math.Pow(CircleRadius, 2); + } + + /// + /// 判断点是否在某一环形区域内 + /// 基本思路: + /// 计算目标点到圆圆心的距离,距离大于内圆的半径同时小于外圆的半径,则在环形区域内 + /// + /// 目标点的横坐标 + /// 目标点的纵坐标 + /// 圆圆心的横坐标 + /// 圆圆心的纵坐标 + /// 内圆的半径 + /// 外圆的半径 + public bool PointInRing(double x, double y, double circleX, double circleY, double innerRadius, double outerRadius) { + double distance = Math.Pow(circleX - x, 2) + Math.Pow(circleY - y, 2); + return distance>= Math.Pow(innerRadius, 2) && distance <= Math.Pow(outerRadius, 2); + } + + /// + /// 判断点是否在某一扇形区域内 + /// 基本思路: + /// 首先判断目标点是否在以扇形半径为半径的圆内,如果不在园内,则也一定不在扇形内 + /// 然后判断目标点到扇形圆心的方向与扇形中轴线方向的夹角是否小于扇形的角度的一半值,如果小于,则说明在扇形区域内 + /// + /// 目标点的横坐标 + /// 目标点的纵坐标 + /// 扇形圆心的横坐标 + /// 扇形圆心的纵坐标 + /// 扇形半径 + /// 扇形的角度的一半值 + /// 扇形中轴线方向的横坐标 + /// 扇形中轴线方向的纵坐标 + public bool PointInSector(double x, double y, double sectorX, double sectorY, double sectorRadius, double sectorHalfAngle, double sectorDirectionX, double sectorDirectionY) { + if(!PointInCircle(x, y, sectorX, sectorY, sectorRadius)) return false; + if(sectorHalfAngle>= 180) return true; // 扇形实际上就是圆 + double directionX = x - sectorX, directionY = y - sectorY; + Normalize(ref directionX, ref directionY); + Normalize(ref sectorDirectionX, ref sectorDirectionY); + return Dot(directionX, directionY, sectorDirectionX, sectorDirectionY)>= Math.Cos(Math.PI / 180 * sectorHalfAngle); + } + + /// + /// 判断点是否在某一矩形区域内 + /// 基本思路: + /// 判断一个点是否在目标区域内,可以转换为判断一个点是否在矩形的上下两条边之内以及左右两条边之内 + /// 判断一个点是否在两条线段之间,可以利用叉乘 + /// 引目标点到一条线段A的一个顶点生成一条线段C1,计算A与C的叉乘。根据叉乘(|A|*|C1|*sin(a))正负,可得知目标点是在线段A的顺时针方向还是逆时针方向 + /// 同理引目标点到线段B的一个顶点生成线段C2,通过叉乘判断目标点是在线段B的顺时针方向还是逆时针方向 + /// 结合目标点对于线段A和线段B的方向,即可得出目标点是否在线段A和线段B之间 + /// + /// 目标点的横坐标 + /// 目标点的纵坐标 + /// 矩形区域的四个顶点坐标,定义顺序为左上,右上,右下,左下 + /// + public bool PointInRect(double x, double y, double[,] rectPoints) { + if(rectPoints.GetLength(0) != 4 || rectPoints.GetLength(1) != 2) + throw new ArgumentException("rectPoints must be an array of 4 points"); + + return ModCrossVector(rectPoints[1, 0] - rectPoints[0, 0], rectPoints[1, 1] - rectPoints[0, 1], x - rectPoints[0, 0], y - rectPoints[0, 1]) + * ModCrossVector(rectPoints[3, 0] - rectPoints[2, 0], rectPoints[3, 1] - rectPoints[2, 1], x - rectPoints[2, 0], y - rectPoints[2, 1])>= 0 + && ModCrossVector(rectPoints[0, 0] - rectPoints[3, 0], rectPoints[0, 1] - rectPoints[3, 1], x - rectPoints[3, 0], y - rectPoints[3, 1]) + * ModCrossVector(rectPoints[2, 0] - rectPoints[1, 0], rectPoints[2, 1] - rectPoints[1, 1], x - rectPoints[1, 0], y - rectPoints[1, 1])>= 0; + } + + /// + /// 判断点是否在某一多边形区域内 + /// 算法思路: + /// 以目标点为端点向水平方向发射射线,统计该射线与多边形每条边的交点数。如果为奇数,则目标点在多边形内,为偶数,则目标在多边形外 + /// 特殊情况: + /// 1. 当射线与多边形的某个顶点(即多边形中两条相邻边的公共交点)相交时,如果该点是其所属边上纵坐标较大的顶点,则计数,否则忽略。(主要避免公共顶点被计算两次以及处理凹多边形的情况) + /// 2. 如果射线和多边形某条边重合,则这条边忽略不计 + /// + /// 目标点的横坐标 + /// 目标点的纵坐标 + /// 多边形的各个顶点坐标 + public bool PointInPolygon(double x, double y, double[,] polygonPoints) { + if(polygonPoints.GetLength(0) < 3 || polygonPoints.GetLength(1) != 2) + throw new ArgumentException("polygonPoints is an array of at least 3 points"); + bool flag = false; + for(int i = 0; i < polygonPoints.GetLength(0); i ++) { + double p1x = polygonPoints[i, 0], p1y = polygonPoints[i, 1]; // 多边形某条边的一个端点 + int index = i == polygonPoints.GetLength(0) - 1 ? 0 : i + 1; + double p2x = polygonPoints[index, 0], p2y = polygonPoints[index, 1]; // // 多边形某条边的另一个端点 + if(p1y != p2y) { // 过滤水平边 + double min = Math.Min(p1y, p2y), max = Math.Max(p1y, p2y); + if(y>= min && y < max && x <= (p2x - (p2x - p1x) / (p2y - p1y) * (p2y - y))) { + flag = !flag; + } + } + } + return flag; + } + + /// + /// 归一化向量 + /// + public void Normalize(ref double x, ref double y) { + double mod = Math.Sqrt(x * x + y * y); + x = x / mod; + y = y / mod; + } + + /// + /// 计算向量的点积 + /// + public double Dot(double x1, double y1, double x2, double y2) { + return x1 * x2 + y1 * y2; + } + + /// + /// 计算两个向量叉乘得到的新向量的模 + /// + public double ModCrossVector(double x1, double y1, double x2, double y2) { + return x1 * y2 - x2 * y1; + } + + public void Test() { + double x = 2, y = 1; + Console.WriteLine(PointInCircle(x, y, 1, 3, 2)); + + Console.WriteLine(PointInRing(x, y, 1, 3, 2, 3)); + + Console.WriteLine(PointInSector(x, y, 1, 3, 3, 60, 1, -1)); + // Console.WriteLine(PointInSector(x, y, 1, 3, 3, 60, 1, 1)); + // Console.WriteLine(PointInSector(x, y, 1, 3, 3, 60, -1, 1)); + // Console.WriteLine(PointInSector(x, y, 1, 3, 3, 60, -1, -1)); + + double[,] rectPoints = new double[,]{ + {0, 3}, + {3, 3}, + {3, 0}, + {0, 0} + }; + Console.WriteLine(PointInRect(x, y, rectPoints)); + + // rectPoints = new double[,]{ + // {0, 1}, + // {3, 3}, + // {4, 2}, + // {1, 0} + // }; + // Console.WriteLine(PointInRect(x, y, rectPoints)); + + // rectPoints = new double[,]{ + // {0, 1}, + // {3, 3}, + // {4, 2}, + // {1, 0} + // }; + // Console.WriteLine(PointInRect(3, 1, rectPoints)); + + double[,] polygonPoints = new double[,]{ + {0, 0}, + {1, 3}, + {1, 0} + }; + Console.WriteLine(PointInPolygon(x, y, polygonPoints)); + + // polygonPoints = new double[,]{ + // {1, 0}, + // {1, 1}, + // {2, 3}, + // {3, 1}, + // {2, 0} + // }; + // Console.WriteLine(PointInPolygon(x, y, polygonPoints)); + + // polygonPoints = new double[,]{ + // {1, 0}, + // {1, 1}, + // {2, 3}, + // {2, 0} + // }; + // Console.WriteLine(PointInPolygon(x, y, polygonPoints)); + } + } +} diff --git "a/346円216円222円345円272円217円347円256円227円346円263円225円/BubbleSort.cs" "b/346円216円222円345円272円217円347円256円227円346円263円225円/BubbleSort.cs" index ff122ad..dae30f1 100644 --- "a/346円216円222円345円272円217円347円256円227円346円263円225円/BubbleSort.cs" +++ "b/346円216円222円345円272円217円347円256円227円346円263円225円/BubbleSort.cs" @@ -82,6 +82,7 @@ public void Print(int[] array){ { Console.Write(item + " "); } + Console.WriteLine(); } } } diff --git "a/346円216円222円345円272円217円347円256円227円346円263円225円/QuickSort.cs" "b/346円216円222円345円272円217円347円256円227円346円263円225円/QuickSort.cs" index 63505af..a392b53 100644 --- "a/346円216円222円345円272円217円347円256円227円346円263円225円/QuickSort.cs" +++ "b/346円216円222円345円272円217円347円256円227円346円263円225円/QuickSort.cs" @@ -86,6 +86,7 @@ public void Print(int[] array){ { Console.Write(item + " "); } + Console.WriteLine(); } } } diff --git "a/346円216円222円345円272円217円347円256円227円346円263円225円/ShellSort.cs" "b/346円216円222円345円272円217円347円256円227円346円263円225円/ShellSort.cs" index 0ddafcb..4b46390 100644 --- "a/346円216円222円345円272円217円347円256円227円346円263円225円/ShellSort.cs" +++ "b/346円216円222円345円272円217円347円256円227円346円263円225円/ShellSort.cs" @@ -64,6 +64,7 @@ public void Print(int[] array){ { Console.Write(item + " "); } + Console.WriteLine(); } } } diff --git "a/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleInsertionSort.cs" "b/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleInsertionSort.cs" index f6684bd..f76b0f7 100644 --- "a/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleInsertionSort.cs" +++ "b/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleInsertionSort.cs" @@ -58,6 +58,7 @@ public void Print(int[] array){ { Console.Write(item + " "); } + Console.WriteLine(); } } } diff --git "a/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleSelectionSort.cs" "b/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleSelectionSort.cs" index 930a2d6..ac382c6 100644 --- "a/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleSelectionSort.cs" +++ "b/346円216円222円345円272円217円347円256円227円346円263円225円/SimpleSelectionSort.cs" @@ -66,6 +66,7 @@ public void Print(int[] array){ { Console.Write(item + " "); } + Console.WriteLine(); } } }

AltStyle によって変換されたページ (->オリジナル) /