十字链表

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

一:概念

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值