上一篇我们看了矩阵的顺序存储,这篇我们再看看一种链式存储方法“十字链表”,当然目的都是一样,压缩空间。

一:概念

既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下5个元素(row,col,val,down,right),其中:

row:矩阵中的行。

col:矩阵中的列。

val:矩阵中的值。

right:指向右侧的一个非零元素。

down:指向下侧的一个非零元素。

现在我们知道单个节点该如何表示了,那么矩阵中同行的非零元素的表示不就是一个单链表吗?比如如下:

那么进一步来说一个多行的非零元素的表示不就是多个单链表吗,是的,这里我把单链表做成循环链表,我们来看看如何用十字链表

来表示稀疏矩阵。

从上面的十字链表中要注意两个问题:

第一:这里有一个填充色的节点,是十字链表中的总结点,它是记录该矩阵中的(row,col,value)和一个指向下一个头节点的next指针。

第二:每个链表都有一个头指针,总结点用next指针将它们贯穿起来。

二:操作

1:数据结构

刚才也说了,十字链表的总结点有一个next指针,而其他非零节点没有,所以为了方便,我们用一个Unit类包装起来。

 1         #region 单一节点
 2         /// <summary>
 3         /// 单一节点
 4         /// </summary>
 5         public class Node
 6         {
 7             //行号
 8             public int rows;
 9
10             //列号
11             public int cols;
12
13             //向下的指针域
14             public Node down;
15
16             //向右的指针域
17             public Node right;
18
19             //单元值(头指针的next和val)
20             public Unit unit;
21         }
22         #endregion
23
24         #region 统一“表头节点”和“非零节点”
25         /// <summary>
26         /// 统一“表头节点”和“非零节点”
27         /// </summary>
28         public class Unit
29         {
30             //表头节点的next域
31             public Node next;
32
33             //非零元素的值
34             public int value;
35         }
36         #endregion

2:初始化

这一步,我们初始化总结点,并且用next指针将每个单链表的头节点链接成单链表(也就是上图中十字链表的第一行)

 1 #region 十字链表中的“行数,列数,非零元素个数”
 2         /// <summary>
 3         /// 十字链表中的“行数,列数,非零元素个数”
 4         /// </summary>
 5         /// <param name="rows"></param>
 6         /// <param name="cols"></param>
 7         /// <param name="count"></param>
 8         public void Init(int rows, int cols, int count)
 9         {
10             var len = Math.Max(rows, cols) + 1;
11
12             //从下标1开始算起
13             nodes = new Node[len];
14
15             //十字链表的总头节点
16             nodes[0] = new Node();
17
18             nodes[0].rows = rows;
19             nodes[0].cols = cols;
20             nodes[0].unit = new Unit()
21             {
22                 value = count,
23                 next = null,
24             };
25
26             //down和right都指向自身
27             nodes[0].right = nodes[0];
28             nodes[0].down = nodes[0];
29
30             var temp = nodes[0];
31
32             //初始化多条链表的头结点
33             for (int i = 1; i < len; i++)
34             {
35                 nodes[i] = new Node();
36
37                 nodes[i].rows = 0;
38                 nodes[i].cols = 0;
39                 nodes[i].unit = new Unit()
40                 {
41                     value = 0,
42                     next = temp.unit.next
43                 };
44
45                 //给上一个节点的next域赋值
46                 temp.unit.next = nodes[i];
47
48                 //将当前节点作为下一次循环的上一个节点
49                 temp = nodes[i];
50
51                 nodes[i].right = nodes[i];
52                 nodes[i].down = nodes[i];
53             }
54         }
55         #endregion

3:插入节点

根据插入节点的row和col将节点插入到十字链表中指定的位置即可。

 1 #region 插入十字链表中
 2         /// <summary>
 3         /// 插入十字链表中
 4         /// </summary>
 5         /// <param name="nums">矩阵</param>
 6         /// <param name="rows">矩阵的行数</param>
 7         /// <param name="cols">矩阵的列数</param>
 8         /// <param name="count">非0元素个数</param>
 9         /// <returns></returns>
10         public Node[] Insert(int[,] nums, int rows, int cols, int count)
11         {
12             //初始化操作
13             Init(rows, cols, count);
14
15             //插入操作
16             for (int i = 0; i < rows; i++)
17             {
18                 for (int j = 0; j < cols; j++)
19                 {
20                     //直插入"非0元素"
21                     if (nums[i, j] != 0)
22                     {
23                         var node = new Node();
24
25                         node.rows = i + 1;
26                         node.cols = j + 1;
27                         node.unit = new Unit()
28                         {
29                             value = nums[i, j]
30                         };
31                         node.right = node;
32                         node.down = node;
33
34                         InsertNode(node);
35                     }
36                 }
37             }
38
39             return nodes;
40         }
41         #endregion

4:打印链表

我们只要遍历每行链表的right指针即可。

 1 #region 打印十字链表
 2         /// <summary>
 3         /// 打印十字链表
 4         /// </summary>
 5         /// <param name="nodes"></param>
 6         public void Print(Node[] nodes)
 7         {
 8             var head = nodes[0];
 9
10             //遍历每一行的right
11             for (int i = 1; i < head.rows + 1; i++)
12             {
13                 var p = nodes[i];
14
15                 while (p.right != nodes[i])
16                 {
17                     Console.WriteLine("({0},{1})\tval => {2}",
18                         p.right.rows,
19                         p.right.cols,
20                         p.right.unit.value);
21
22                     //指向下一个节点
23                     p = p.right;
24                 }
25             }
26         }
27         #endregion

总的代码:

 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Diagnostics;
  6 using System.Threading;
  7 using System.IO;
  8
  9 namespace ConsoleApplication2
 10 {
 11     public class Program
 12     {
 13         public static void Main()
 14         {
 15             Crosslist crosslist = new Crosslist();
 16
 17             int[,] nums = {
 18             {2,0,4 },
 19             {0,3,0 },
 20             {0,0,9 }
 21            };
 22
 23             var nodes = crosslist.Insert(nums, 3, 3, 4);
 24
 25             crosslist.Print(nodes);
 26
 27             Console.Read();
 28         }
 29     }
 30
 31     /// <summary>
 32     /// 十字链表
 33     /// </summary>
 34     public class Crosslist
 35     {
 36         #region 单一节点
 37         /// <summary>
 38         /// 单一节点
 39         /// </summary>
 40         public class Node
 41         {
 42             //行号
 43             public int rows;
 44
 45             //列号
 46             public int cols;
 47
 48             //向下的指针域
 49             public Node down;
 50
 51             //向右的指针域
 52             public Node right;
 53
 54             //单元值(头指针的next和val)
 55             public Unit unit;
 56         }
 57         #endregion
 58
 59         #region 统一“表头节点”和“非零节点”
 60         /// <summary>
 61         /// 统一“表头节点”和“非零节点”
 62         /// </summary>
 63         public class Unit
 64         {
 65             //表头节点的next域
 66             public Node next;
 67
 68             //非零元素的值
 69             public int value;
 70         }
 71         #endregion
 72
 73         Node[] nodes;
 74
 75         #region 十字链表中的“行数,列数,非零元素个数”
 76         /// <summary>
 77         /// 十字链表中的“行数,列数,非零元素个数”
 78         /// </summary>
 79         /// <param name="rows"></param>
 80         /// <param name="cols"></param>
 81         /// <param name="count"></param>
 82         public void Init(int rows, int cols, int count)
 83         {
 84             var len = Math.Max(rows, cols) + 1;
 85
 86             //从下标1开始算起
 87             nodes = new Node[len];
 88
 89             //十字链表的总头节点
 90             nodes[0] = new Node();
 91
 92             nodes[0].rows = rows;
 93             nodes[0].cols = cols;
 94             nodes[0].unit = new Unit()
 95             {
 96                 value = count,
 97                 next = null,
 98             };
 99
100             //down和right都指向自身
101             nodes[0].right = nodes[0];
102             nodes[0].down = nodes[0];
103
104             var temp = nodes[0];
105
106             //初始化多条链表的头结点
107             for (int i = 1; i < len; i++)
108             {
109                 nodes[i] = new Node();
110
111                 nodes[i].rows = 0;
112                 nodes[i].cols = 0;
113                 nodes[i].unit = new Unit()
114                 {
115                     value = 0,
116                     next = temp.unit.next
117                 };
118
119                 //给上一个节点的next域赋值
120                 temp.unit.next = nodes[i];
121
122                 //将当前节点作为下一次循环的上一个节点
123                 temp = nodes[i];
124
125                 nodes[i].right = nodes[i];
126                 nodes[i].down = nodes[i];
127             }
128         }
129         #endregion
130
131         #region 在指定的“行,列”上插入节点
132         /// <summary>
133         /// 在指定的“行,列”上插入节点
134         /// </summary>
135         /// <param name="node"></param>
136         /// <returns></returns>
137         public void InsertNode(Node node)
138         {
139             //先定位行
140             Node pnode = nodes[node.rows];
141
142             //在指定的“行”中找,一直找到该行最后一个节点(right指针指向自己的为止)
143             while (pnode.right != nodes[node.rows] && pnode.right.cols < node.cols)
144                 pnode = pnode.right;
145
146             //将最后一个节点的right指向插入节点的right,以此达到是循环链表
147             node.right = pnode.right;
148
149             //将插入节点给最后一个节点的right指针上
150             pnode.right = node;
151
152             //再定位列
153             pnode = nodes[node.cols];
154
155             //同理
156             while (pnode.down != nodes[node.cols] && pnode.down.rows < node.rows)
157             {
158                 pnode = pnode.down;
159             }
160
161             node.down = pnode.down;
162             pnode.down = node;
163         }
164         #endregion
165
166         #region 插入十字链表中
167         /// <summary>
168         /// 插入十字链表中
169         /// </summary>
170         /// <param name="nums">矩阵</param>
171         /// <param name="rows">矩阵的行数</param>
172         /// <param name="cols">矩阵的列数</param>
173         /// <param name="count">非0元素个数</param>
174         /// <returns></returns>
175         public Node[] Insert(int[,] nums, int rows, int cols, int count)
176         {
177             //初始化操作
178             Init(rows, cols, count);
179
180             //插入操作
181             for (int i = 0; i < rows; i++)
182             {
183                 for (int j = 0; j < cols; j++)
184                 {
185                     //直插入"非0元素"
186                     if (nums[i, j] != 0)
187                     {
188                         var node = new Node();
189
190                         node.rows = i + 1;
191                         node.cols = j + 1;
192                         node.unit = new Unit()
193                         {
194                             value = nums[i, j]
195                         };
196                         node.right = node;
197                         node.down = node;
198
199                         InsertNode(node);
200                     }
201                 }
202             }
203
204             return nodes;
205         }
206         #endregion
207
208         #region 打印十字链表
209         /// <summary>
210         /// 打印十字链表
211         /// </summary>
212         /// <param name="nodes"></param>
213         public void Print(Node[] nodes)
214         {
215             var head = nodes[0];
216
217             //遍历每一行的right
218             for (int i = 1; i < head.rows + 1; i++)
219             {
220                 var p = nodes[i];
221
222                 while (p.right != nodes[i])
223                 {
224                     Console.WriteLine("({0},{1})\tval => {2}",
225                         p.right.rows,
226                         p.right.cols,
227                         p.right.unit.value);
228
229                     //指向下一个节点
230                     p = p.right;
231                 }
232             }
233         }
234         #endregion
235     }
236 }

经典算法21--十字链表相关推荐

  1. 游戏算法-AOI十字链表入门简单版python

    AOI十字链表python简单实现,仅用作学习,理解基本原理 AOI一般是C++去实现,本文章python写仅用来入门了解 场景内维护两个实体链表 list_x和list_y 所有实体根据x坐标大小排 ...

  2. 经典算法学习——逆转链表

    链表在数据结构中是一种非常重要的线性数据结构,也是各类面试笔试中常常涉及的问题.其中最常见的一个问题就是"输入一个链表的头结点,从尾到头反过来打印出每个节点的值",简单说就是逆转链 ...

  3. 稀疏矩阵的三元组表与十字链表存储

    三元组表:存储稀疏矩阵的非零元素,以及该元素所在的行.列信息,极大的节省了空间(如相比于一般的二维数组的存储),而且三元组表的某些算法的时间效率也要优于经典算法,如基于三元组表的一次快速转置算法等等 ...

  4. 经典算法题每日演练——第二十一题 十字链表

    上一篇我们看了矩阵的顺序存储,这篇我们再看看一种链式存储方法"十字链表",当然目的都是一样,压缩空间. 一:概念 既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下5个元素(ro ...

  5. 算法:游戏内AOI视野算法(十字链表)

    为什么要有这个算法? 对于一个游戏场景内的实体来说,当战斗频繁的时候,可能存在上千实体的数据同步,例如上千实体移动时的坐标同步,大型战斗场景时全场景实体的属性的同步等等,这样就会造成一个问题,同步数据 ...

  6. 经典算法题 -- 判断单链表是否成环及寻找成环节点

    引言 判断单链表是否成环是一个计算机领域的经典算法问题 如何通过程序判断传入的链表是否存在环,并且求出环长度.成环点等问题 下面就是一个存在环的单链表 基本算法 -- hash 最简单的方法是创建一个 ...

  7. 经典算法——单向链表反转

    1. 题目 单向链表反转是一道经典的求职面试笔试或机试题.给定如下如下链表的节点定义: struct LinkNode {int value;LinkNode* next; }; 比如有一个链表是这样 ...

  8. 稀疏矩阵的十字链表存储表示和实现(第五章 P104 算法5.4)

    稀疏矩阵的十字链表存储 当矩阵的非零元个数和位置在操作过程中变化较大时,就不宜采用顺序存储结构来表示三元组的线性表.对这种类型的矩阵,采用链式存储结构表示三元组的线性表更为恰当. 在链表中,每个非零元 ...

  9. 【限时】21天学习挑战赛 - 经典算法

    ​学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩:迟一天就多一天平庸的困扰.各位小伙伴,如果您: 想系统/深入学习某技术知识点- 一个人摸索学习很难坚持,想组团高效学习- 想写博客但无从下手,急 ...

最新文章

  1. 敏捷开发中的故事点到底是什么?如何预估故事点?
  2. 「AlphaGo 之父」David Silver最新演讲,传授强化学习的十大原则
  3. CloudBees发布“Jenkins X”:面向部署到Kubernetes中的现代云应用的CI/CD解决方案
  4. java sdf.parse_用DateFormat的parse方法验证字符串是否是日期的问题
  5. Select的OnChange()事件
  6. 醉话没有测试(QA)的测试
  7. 数据库面试题【十一、InnoDB引擎的4大特性】
  8. 用深度学习解决Bongard问题
  9. Java高级语法笔记-向上层抛出异常
  10. 4.线性和卷积——边界问题、解决边界方法和Matlab实战_3
  11. Linux Man手册的使用示例
  12. 6Linux 终端命令格式
  13. 学数值计算可以从事计算机算法吗,数值计算方法
  14. Angr安装与使用之安装篇
  15. 软件测试方法和技术有哪些?
  16. 人机对弈黑白棋游戏用Python tkinter的Canvas实现图形界面
  17. python写梦幻西游手游脚本辅助_GitHub - Sandry666/mhxy_fz: 一个基于计算机视觉开发的梦幻西游辅助脚本...
  18. linux 关闭桌面休眠,Linux关闭休眠和屏保模式
  19. 粘结剂菱镁板建筑材料英国UKCA认证—EN 14016-1
  20. 结对项目——可循环的最大子数组

热门文章

  1. 函数传递的参数是原参数的副本
  2. 苹果能不能分屏_安卓机也能运行电脑软件?好东西不能苹果独享
  3. iPhone港版、美版、日版、国行,到底有什么区别?看完涨知识
  4. pd.DataFrame增删改查
  5. 制作A4纸打印的网页像素大小设置(转)
  6. 360自动保存密码影响到html,360浏览器怎样查看保存过的密码 查看曾经自动保存的密码方法...
  7. python怎么输入分数_我想在python中的函数中输入一个分数
  8. Wechaty :成功申报中国科协开源评选
  9. EasyExcel 设置字体样式(字体、字体大小、字体颜色、字体加粗、字体斜体、字体下划线、字体上标下标、字体删除线)
  10. 前后端一些下载与配置(第二篇 第10天过后)nuxt banner redis 短信服务 ECharts