2

【算法】国庆加班,火锅与Linq.AddRange的奇妙螺旋 - lanedm

 7 months ago
source link: https://www.cnblogs.com/lan80/p/17744494.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【算法】国庆加班,火锅与Linq.AddRange的奇妙螺旋

在国庆假期的一个傍晚,小悦正在家中享受火锅美食。她嘴里咀嚼着鲜嫩的牛肉,脸上洋溢着满足的微笑。突然,手机铃声响起,打破了这温馨的氛围。她拿起手机一看,是公司打来的电话。

“小悦,有个紧急的项目需要处理,你能来公司加一下班吗?”电话那头传来领导焦急的声音。

小悦顿时嘟起嘴,不太情愿地离开了火锅桌,踏上前往公司的路程。

一到公司,小悦就开始研究领导交给她的任务:处理一个关于小视频螺旋排序算法的问题。这个问题让她感到有些棘手,但她知道没有退缩的余地。于是,她深吸了一口气,开始认真地研究问题。

在图像处理中,螺旋排序算法是一个非常有用的工具。通过将图像按照螺旋顺序排列,可以更方便地对图像进行处理和分析。例如,可以将图像分成多个小块进行特征提取、图像压缩等操作。小悦明白,要解决这个问题,首先需要理解螺旋排序算法的原理。

她开始在纸上画出图像的分块示意图,尝试寻找规律。经过一番推导和实践,她逐渐掌握了螺旋排序算法的核心思想。接着,她开始编写代码来实现这个算法。

时间在悄然流逝,窗外的夜幕渐渐降临。尽管劳累了一天,但小悦的心情却渐渐变得愉悦起来。她在解决问题的过程中感受到了挑战的乐趣和收获的喜悦。当她最终完成代码并成功运行出结果时,她的心中充满了成就感。

“终于解决了!”小悦长出一口气,脸上露出了满意的微笑。她收拾好东西,准备离开公司。走出办公室的那一刻,她抬头看到夜空中繁星点点,心中不禁感慨万分。这个国庆假期,或许没有如愿以偿地休息和放松,但她却在工作中取得了宝贵的进步。

回到家中,小悦继续享受火锅美食。她夹起一块牛肉放进嘴里,细细品味着它的鲜美。这个国庆假期,虽然没有游山玩水的快乐,但她却收获了成长和充实。

小悦面临的问题如下:给定一个n乘n二维数组,按照从最外层元素到中间元素的顺序,排序,顺时针移动,返回排序后的一维数组。

array = [[1,2,3],
         [4,5,6],
         [7,8,9]]
snail(array) #=> [1,2,3,6,9,8,7,4,5]

图解:

2357672-20231006140231606-1089937161.png

算法实现1:

 1 public static int[] Snail(int[][] array)
 2 {
 3     // 获取数组的长度
 4     int l = array[0].Length;
 5     // 创建一个用于存储排序后元素的数组
 6     int[] sorted = new int[l * l];
 7     // 递归调用 Snail 方法进行螺旋排序
 8     Snail(array, -1, 0, 1, 0, l, 0, sorted);
 9     // 返回排序后的数组
10     return sorted;
11 }
12 
13 public static void Snail(int[][] array, int x, int y, int dx, int dy, int l, int i, int[] sorted)
14 {
15     // 如果数组长度为 0,则直接返回
16     if (l == 0)
17         return;
18     // 循环遍历当前层的元素
19     for (int j = 0; j < l; j++)
20     {
21         // 更新当前元素的坐标,并将其存储到排序数组中
22         x += dx;
23         y += dy;
24         sorted[i++] = array[y][x];
25     }
26     // 递归调用 Snail 方法进行下一层的螺旋排序
27     Snail(array, x, y, -dy, dx, dy == 0 ? l - 1 : l, i, sorted);
28 }

假设我们有一个二维数组array,其中包含了3行3列的整数。数组的内容如下:

我们希望通过螺旋排序算法将这个二维数组按照螺旋顺序排列,并得到一个结果数组r,其中包含按照螺旋顺序排列的元素。结果数组的内容如下:

我们将调用Test方法来测试螺旋排序算法。在Test方法中,首先将二维数组array转换为字符串,并将结果与预期的结果数组r进行比较。如果转换后的字符串与预期结果字符串相等,则测试通过;否则,测试失败。

接下来,让我们详细解释一下螺旋排序算法的运行过程:

  1. 首先,算法会初始化一个空的结果数组snail,用于存储按照螺旋顺序排列的元素。

  2. 然后,算法会定义四个变量:topbottomleftright,分别表示当前螺旋排序的上边界、下边界、左边界和右边界。初始时,top为0,bottom为2,left为0,right为2。

  3. 接着,算法会进入一个循环,直到上边界大于下边界或左边界大于右边界为止。

  4. 在每一次循环中,算法会按照螺旋顺序遍历数组的四条边,并将遍历到的元素添加到结果数组snail中。

  5. 首先,算法会从左到右遍历上边界,并将遍历到的元素添加到结果数组snail中。在这个测试用例中,遍历到的元素为1 2 3

  6. 然后,算法会将上边界下移一行,即top++,此时上边界变为1。

  7. 接着,算法会从上到下遍历右边界,并将遍历到的元素添加到结果数组snail中。在这个测试用例中,遍历到的元素为6 9

  8. 然后,算法会将右边界左移一列,即right--,此时右边界变为1。

  9. 接着,算法会从右到左遍历下边界,并将遍历到的元素添加到结果数组snail中。在这个测试用例中,遍历到的元素为8 7

  10. 然后,算法会将下边界上移一行,即bottom--,此时下边界变为1。

  11. 接着,算法会从下到上遍历左边界,并将遍历到的元素添加到结果数组snail中。在这个测试用例中,遍历到的元素为4

  12. 最后,在这个测试用例中,算法会发现左边界大于右边界,因此跳出循环。

  13. 最后,算法会返回结果数组snail,其中包含按照螺旋顺序排列的元素。在这个测试用例中,返回的结果数组为1 2 3 6 9 8 7 4 5

  14. Test方法中,我们将转换后的结果数组与预期的结果数组进行比较。如果它们相等,则测试通过;否则,测试失败。


算法实现2:

 1     public static int[] Snail(int[][] grid)
 2     {
 3         var result = new List<int>();
 4 
 5         //通过grid.SelectMany(row => row).Any()判断二维数组grid是否为空。如果为空,则直接返回空的结果数组。
 6         if (!grid.SelectMany(row => row).Any())
 7             return result.ToArray();
 8 
 9         //将二维数组的第一行添加到结果数组中
10         result.AddRange(grid.First());
11 
12         //将二维数组的最右一列(除去第一行)添加到结果数组中
13         result.AddRange(grid.Skip(1).Select(row => row.Last()));
14 
15        //将二维数组的最后一行(除去第一行和最后一个元素)逆序添加到结果数组中
16         result.AddRange(grid.Last().Reverse().Skip(1));
17 
18        //将二维数组的最左一列(除去第一行、最后一行和第一个元素)逆序添加到结果数组中
19         result.AddRange(grid.Skip(1).SkipLast(1).Select(row => row.First()).Reverse());
20 
21         var newGrid = grid
22                 .Skip(1)
23                 .SkipLast(1)
24                 .Select(row => row
25                     .Skip(1)
26                     .SkipLast(1)
27                     .ToArray())
28                 .ToArray();
29 
30        //通过递归调用Snail方法,将去掉了二维数组的第一行、最后一行、第一列和最后一列后的新二维数组传入,并将返回的结果数组添加到当前的结果数组中
31         result.AddRange(Snail(newGrid));
32 
33         return result.ToArray();
34     }

这段代码的逻辑与算法1的功能相同,只是在创建新二维数组newGrid时,使用了链式调用的方式来去掉边界元素。

AddRange 是一个常用在 LINQ 中的方法,通常在 List<T> 或 Dictionary<TKey, TValue> 等集合类型中使用。这个方法用于将另一个集合中的元素添加到当前集合中。

首先,代码通过Skip(1)去掉了第一行,然后通过SkipLast(1)去掉了最后一行,接着通过Select(row => row.Skip(1).SkipLast(1).ToArray())去掉了每一行的第一个元素和最后一个元素,最后通过ToArray()将结果转换为新的二维数组。这样,递归调用Snail方法时传入的新二维数组就是去掉了边界的数组。


 假设我们有一个100x100像素的图像,我们想要将其分成10x10个小块进行特征提取或图像压缩。在没有使用螺旋排序算法之前,我们可以按照图像的行优先顺序将像素按顺序分成小块。这意味着我们首先将图像的第一行的前10个像素作为第一个小块,然后是第一行的下一个10个像素作为第二个小块,依此类推,直到图像的第10行。然后,我们继续将图像的第11行的前10个像素作为第11个小块,以此类推,直到图像的最后一行。

这种方法的问题是,它没有考虑到图像的局部结构。相邻的像素在图像中可能是相关的,而按行顺序分块可能会将相关的像素分散在不同的块中。这可能导致特征提取或图像压缩的结果不够准确或失真。

现在,如果我们使用螺旋排序算法来分块图像,我们可以更好地保留图像的局部结构。螺旋排序算法按照螺旋顺序遍历图像的像素,并将它们分成小块。这意味着我们首先将图像的中心像素作为第一个小块,然后按照螺旋顺序将相邻的像素添加到下一个小块,直到所有像素都被分到小块中。

通过使用螺旋排序算法,我们可以更好地保留图像的局部结构,因为相邻的像素通常在空间上也是相邻的。这有助于提高特征提取或图像压缩的准确性和质量。

所以螺旋排序算法在各个领域都有一定的应用价值,可以帮助提高数据处理和分析的效率,优化系统设计和布局,以及改善数据的可视化效果:

  1. 图像处理:在图像处理中,螺旋排序算法可以用于对图像进行分块处理。通过将图像按照螺旋顺序排列,可以更方便地对图像进行处理和分析。例如,可以将图像分成多个小块进行特征提取、图像压缩等操作。

  2. 数据存储和检索:螺旋排序算法可以用于对二维数组或矩阵进行存储和检索。通过按照螺旋顺序对数据进行排序,可以提高数据的访问效率。例如,在二维地图导航系统中,可以将地图数据按照螺旋顺序进行存储,以便快速检索和显示地图信息。

  3. 数据可视化:螺旋排序算法可以用于数据可视化,特别是对于二维数据的可视化。通过按照螺旋顺序对数据进行排序,可以将数据按照一定的规律展示出来,更直观地观察数据的分布和变化。例如,在热力图中,可以按照螺旋顺序对温度数据进行排序,以便更清晰地显示温度的变化趋势。

  4. 电路布局:在电路设计中,螺旋排序算法可以用于电路布局的优化。通过按照螺旋顺序对电路元件进行排序,可以减少电路布线的长度和复杂度,提高电路的性能和可靠性。


测试用例:

  1 using NUnit.Framework;
  2 using System;
  3 using System.Linq;
  4 public class SnailTest
  5 {
  6 
  7     private int[] Tsnail(int[][] array)
  8     {
  9         int[] sorted = new int[array.Length * array.Length];
 10         Tsnail(array, -1, 0, 1, 0, array.Length, 0, sorted);
 11         return sorted;
 12     }
 13 
 14     private void Tsnail(int[][] array, int x, int y, int dx, int dy, int l, int i, int[] sorted)
 15     {
 16         if (l == 0)
 17             return;
 18         for (int j = 0; j < l; j++)
 19         {
 20             x += dx;
 21             y += dy;
 22             sorted[i++] = array[y][x];
 23         }
 24         Tsnail(array, x, y, -dy, dx, dy == 0 ? l - 1 : l, i, sorted);
 25 
 26     }
 27 
 28     public string Int2dToString(int[][] a)
 29     {
 30         return $"[{string.Join("\n", a.Select(row => $"[{string.Join(",", row)}]"))}]";
 31     }
 32 
 33     public void Test(int[][] array, int[] result)
 34     {
 35         var text = $"{Int2dToString(array)}\nshould be sorted to\n[{string.Join(",", result)}]\n";
 36         Console.WriteLine(text);
 37         Assert.AreEqual(result, SnailSolution.Snail(array));
 38     }
 39 
 40     [Test]
 41     public void SnailTest1()
 42     {
 43         int[][] array =
 44         {
 45             new []{1, 2, 3},
 46             new []{4, 5, 6},
 47             new []{7, 8, 9}
 48         };
 49         var r = new[] { 1, 2, 3, 6, 9, 8, 7, 4, 5 };
 50         Test(array, r);
 51     }
 52 
 53     [Test]
 54     public void SnailTest2()
 55     {
 56         int[][] array =
 57         {
 58             new []{1, 2, 3, 9},
 59             new []{4, 5, 6, 4},
 60             new []{7, 8, 9, 1},
 61             new []{1, 2, 3, 4}
 62         };
 63         var r = new[] { 1, 2, 3, 9, 4, 1, 4, 3, 2, 1, 7, 4, 5, 6, 9, 8 };
 64         Test(array, r);
 65     }
 66 
 67     [Test]
 68     public void SnailTest2Empty()
 69     {
 70         int[][] a = { new int[] { } };
 71         Test(a, new int[0]);
 72     }
 73 
 74     [Test]
 75     public void SnailTestOne()
 76     {
 77         int[][] a = { new[] { 1 } };
 78         Test(a, new[] { 1 });
 79     }
 80 
 81 
 82     [Test]
 83     public void SnailRandomTest()
 84     {
 85         Console.WriteLine("Random Tests");
 86         Random rand = new Random();
 87         for (int n = 0; n < 100; n++)
 88         {
 89             var l = rand.Next(1, 31);
 90             var array = new int[l][];
 91             for (int i = 0; i < l; i++)
 92             {
 93                 array[i] = new int[l];
 94                 for (int j = 0; j < l; j++)
 95                     array[i][j] = rand.Next(1, 1001);
 96             }
 97             Test(array, Tsnail(array));
 98         }
 99     }
100 }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK