既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下5个元素(row,col,val,down,right),其中:
row:矩阵中的行。
col:矩阵中的列。
val:矩阵中的值。
right:指向右侧的一个非零元素。
down:指向下侧的一个非零元素。
现在我们知道单个节点该如何表示了,那么矩阵中同行的非零元素的表示不就是一个单链表吗?比如如下:
那么进一步来说一个多行的非零元素的表示不就是多个单链表吗,是的,这里我把单链表做成循环链表,我们来看看如何用十字链表
来表示稀疏矩阵。
从上面的十字链表中要注意两个问题:
第一:这里有一个填充色的节点,是十字链表中的总结点,它是记录该矩阵中的(row,col,value)和一个指向下一个头节点的next指针。
第二:每个链表都有一个头指针,总结点用next指针将它们贯穿起来。
刚才也说了,十字链表的总结点有一个next指针,而其他非零节点没有,所以为了方便,我们用一个Unit类包装起来。
这一步,我们初始化总结点,并且用next指针将每个单链表的头节点链接成单链表(也就是上图中十字链表的第一行)
根据插入节点的row和col将节点插入到十字链表中指定的位置即可。
我们只要遍历每行链表的right指针即可。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Threading; using System.IO; namespace ConsoleApplication2 { public class Program { public static void Main() { Crosslist crosslist = new Crosslist(); int[,] nums = { {2,0,4 }, {0,3,0 }, {0,0,9 } }; var nodes = crosslist.Insert(nums, 3, 3, 4); crosslist.Print(nodes); Console.Read(); } } /// <summary> /// 十字链表 /// </summary> public class Crosslist { #region 单一节点 /// <summary> /// 单一节点 /// </summary> public class Node { //行号 public int rows; //列号 public int cols; //向下的指针域 public Node down; //向右的指针域 public Node right; //单元值(头指针的next和val) public Unit unit; } #endregion #region 统一“表头节点”和“非零节点” /// <summary> /// 统一“表头节点”和“非零节点” /// </summary> public class Unit { //表头节点的next域 public Node next; //非零元素的值 public int value; } #endregion Node[] nodes; #region 十字链表中的“行数,列数,非零元素个数” /// <summary> /// 十字链表中的“行数,列数,非零元素个数” /// </summary> /// <param name="rows"></param> /// <param name="cols"></param> /// <param name="count"></param> public void Init(int rows, int cols, int count) { var len = Math.Max(rows, cols) + 1; //从下标1开始算起 nodes = new Node[len]; //十字链表的总头节点 nodes[0] = new Node(); nodes[0].rows = rows; nodes[0].cols = cols; nodes[0].unit = new Unit() { value = count, next = null, }; //down和right都指向自身 nodes[0].right = nodes[0]; nodes[0].down = nodes[0]; var temp = nodes[0]; //初始化多条链表的头结点 for (int i = 1; i < len; i++) { nodes[i] = new Node(); nodes[i].rows = 0; nodes[i].cols = 0; nodes[i].unit = new Unit() { value = 0, next = temp.unit.next }; //给上一个节点的next域赋值 temp.unit.next = nodes[i]; //将当前节点作为下一次循环的上一个节点 temp = nodes[i]; nodes[i].right = nodes[i]; nodes[i].down = nodes[i]; } } #endregion #region 在指定的“行,列”上插入节点 /// <summary> /// 在指定的“行,列”上插入节点 /// </summary> /// <param name="node"></param> /// <returns></returns> public void InsertNode(Node node) { //先定位行 Node pnode = nodes[node.rows]; //在指定的“行”中找,一直找到该行最后一个节点(right指针指向自己的为止) while (pnode.right != nodes[node.rows] && pnode.right.cols < node.cols) pnode = pnode.right; //将最后一个节点的right指向插入节点的right,以此达到是循环链表 node.right = pnode.right; //将插入节点给最后一个节点的right指针上 pnode.right = node; //再定位列 pnode = nodes[node.cols]; //同理 while (pnode.down != nodes[node.cols] && pnode.down.rows < node.rows) { pnode = pnode.down; } node.down = pnode.down; pnode.down = node; } #endregion #region 插入十字链表中 /// <summary> /// 插入十字链表中 /// </summary> /// <param name="nums">矩阵</param> /// <param name="rows">矩阵的行数</param> /// <param name="cols">矩阵的列数</param> /// <param name="count">非0元素个数</param> /// <returns></returns> public Node[] Insert(int[,] nums, int rows, int cols, int count) { //初始化操作 Init(rows, cols, count); //插入操作 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { //直插入"非0元素" if (nums[i, j] != 0) { var node = new Node(); node.rows = i + 1; node.cols = j + 1; node.unit = new Unit() { value = nums[i, j] }; node.right = node; node.down = node; InsertNode(node); } } } return nodes; } #endregion #region 打印十字链表 /// <summary> /// 打印十字链表 /// </summary> /// <param name="nodes"></param> public void Print(Node[] nodes) { var head = nodes[0]; //遍历每一行的right for (int i = 1; i < head.rows + 1; i++) { var p = nodes[i]; while (p.right != nodes[i]) { Console.WriteLine("({0},{1})\tval => {2}", p.right.rows, p.right.cols, p.right.unit.value); //指向下一个节点 p = p.right; } } } #endregion } }