首页 > 代码库 > 经典算法题每日演练——第二十一题 十字链表

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

原文:经典算法题每日演练——第二十一题 十字链表

 

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

一:概念

    既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下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         #endregion23 24         #region 统一“表头节点”和“非零节点”25         /// <summary>26         /// 统一“表头节点”和“非零节点”27         /// </summary>28         public class Unit29         {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 =http://www.mamicode.com/ 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 = http://www.mamicode.com/0,42                     next = temp.unit.next43                 };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 =http://www.mamicode.com/ 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             //遍历每一行的right11             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 =http://www.mamicode.com/ 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 = http://www.mamicode.com/0,116                     next = temp.unit.next117                 };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         #endregion130 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         #endregion165 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 =http://www.mamicode.com/ 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         #endregion207 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             //遍历每一行的right218             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         #endregion235     }236 }

技术分享

 

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