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

一:概念

既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下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

总的代码:

View Code

  1 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 }

经典算法题每日演练——第二十一题 十字链表相关推荐

  1. 经典算法题每日演练——第十一题 Bitmap算法

    在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场合下: ①:对10亿个不重复的整数进行排序. ②:找出1 ...

  2. 经典算法题每日演练——第二十二题 奇偶排序

    原文:经典算法题每日演练--第二十二题 奇偶排序 这个专题因为各种原因好久没有继续下去了,MM吧...你懂的,嘿嘿,不过还得继续写下去,好长时间不写,有些东西有点生疏了, 这篇就从简单一点的一个&qu ...

  3. 经典算法题每日演练——第六题 协同推荐SlopeOne 算法

    原文:经典算法题每日演练--第六题 协同推荐SlopeOne 算法 相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,"商品推荐","猜你喜欢&quo ...

  4. 经典算法题每日演练——第十题 树状数组

    原文:经典算法题每日演练--第十题 树状数组 有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的. 一:概序 假如我现在有个 ...

  5. 经典算法题每日演练——第七题 KMP算法

    原文:经典算法题每日演练--第七题 KMP算法 在大学的时候,应该在数据结构里面都看过kmp算法吧,不知道有多少老师对该算法是一笔带过的,至少我们以前是的, 确实kmp算法还是有点饶人的,如果说红黑树 ...

  6. 经典算法题每日演练——第二十四题 梳排序

    这篇再看看一个经典的排序,梳排序,为什么取名为梳,可能每个梳都有自己的gap吧,大梳子gap大一点,小梳子gap小一点. 上一篇我们看到鸡尾酒排序是在冒泡排序上做了一些优化,将单向的比较变成了双向,同 ...

  7. 经典算法题每日演练——第三题 猴子吃桃

    猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾就多吃了一个.第二天早上又将剩下的桃子吃了一半,还是不过瘾又多 吃了一个.以后每天都吃前一天剩下的一半再加一个.到第10天刚好剩一个.问猴子第一天摘了多 ...

  8. 经典算法题每日演练——第五题 字符串相似度

    原文地址:http://www.cnblogs.com/huangxincheng/archive/2012/11/11/2765633.html 这篇我们看看最长公共子序列的另一个版本,求字符串相似 ...

  9. 经典算法题每日演练——第十九题 双端队列

    经典算法题每日演练--第十九题 双端队列 原文:经典算法题每日演练--第十九题 双端队列 话说大学的时候老师说妹子比工作重要~,工作可以再换,妹子这个...所以...这两个月也就一直忙着Fall in ...

最新文章

  1. R语言条件Logistic回归模型案例:研究饮酒与胃癌的关系
  2. 用PAM实现用户和主机的Samba访问控制
  3. 唯品会API网关设计与实践--转
  4. ML之Cosin:基于输入图片RGB均值化转为单向vector利用Cosin(余弦相似度)算法进行判别
  5. 20应用统计考研复试要点(part16)--应用多元分析
  6. 【Linux使用】Centos 7 设置机器名/激活网络接口
  7. Java Web学习总结(5)——HttpServletResponse对象详解
  8. EasyUI 扩展自定义EasyUI校验规则 验证规则
  9. java 3d文字旋转_3d多物体点旋转
  10. 力扣-605 种花问题
  11. idea设置Java版本
  12. 王阳明的心学精髓是什么?
  13. html 库存管理页面,库存管理
  14. 浅谈外存分配的几种方式
  15. 数据仓库技术(Data Warehouse Technologien) 第三章节 多维数据模型(3)
  16. 浅谈JSP的发展历史
  17. python设置窗口位置_python中tkinter窗口位置
  18. Android相机预览,指定区域显示预览框,在区域内出现人脸进行人脸识别,并抓拍人脸照片存在本地,CameraX,虹软人脸识别
  19. 大恒水星相机+opencv4.0.1保存视频
  20. 荣誉系统排名是整个服务器,《魔兽世界怀旧服》荣誉系统怎么样 荣誉系统奖励大全...

热门文章

  1. ubuntu18+Eclipse+CDT开发QT5界面程序
  2. 计算机网络部分(共44题),2018年10月自考04741计算机网络原理试卷及答案
  3. 从S3获取数据在html表示,AWS Lambda从DynamoDB加载内容,以S3格式显示在HTML中(示例代码)...
  4. 2020-11-06 Python OpenCV给证件照换底色
  5. 计算机文献检索综合性实验报告,文献检索综合性实验报告模板.doc
  6. ubuntu1804系统设置在哪里_新风净化系统的风口到底该放在哪里?
  7. mybatis学习8之缓存
  8. 【Tools】怎样转载博客到CSDN博客(很实用)
  9. Spring - Java/J2EE Application Framework 应用框架 第 17 章 使用Spring邮件抽象层发送Email
  10. Javascript日期时间总结