基于C#实现十字链表

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

一、概念

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

row:矩阵中的行。
col:矩阵中的列。
val:矩阵中的值。
right:指向右侧的一个非零元素。
down:指向下侧的一个非零元素。

image.png
现在我们知道单个节点该如何表示了,那么矩阵中同行的非零元素的表示不就是一个单链表吗?比如如下:
image.png
那么进一步来说一个多行的非零元素的表示不就是多个单链表吗,是的,这里我把单链表做成循环链表,我们来看看如何用十字链表来表示稀疏矩阵。
image.png
从上面的十字链表中要注意两个问题:
第一:这里有一个填充色的节点,是十字链表中的总结点,它是记录该矩阵中的(row,col,value)和一个指向下一个头节点的 next 指针。
第二:每个链表都有一个头指针,总结点用 next 指针将它们贯穿起来。

二、操作

2.1、数据结构

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

 #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

2.2、初始化

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

#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

2.3、插入节点

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

#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

2.4、打印链表

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

#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

2.5、总的代码:

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

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/195476.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity之NetCode多人网络游戏联机对战教程(10)--玩家动画同步

文章目录 前言NetworkAnimation服务端权威客户端权威 前言 这次的动画同步与位置同步&#xff0c;可以说实现思路是一样的&#xff0c;代码相似度也非常高 NetworkAnimation 如果直接挂载这个脚本只有Host&#xff08;服务端&#xff09;才可以同步&#xff0c;Client是没有…

视频封面:视频图片提取技巧,从指定时长中捕捉需求的图片

在当今的数字时代&#xff0c;视频已成为日常生活中不可或缺的一部分。无论是社交媒体、博客&#xff0c;视频都发挥着重要的作用。而一个吸引的视频封面往往能吸引更多的观众点击观看&#xff0c;选择清晰度高、色彩鲜艳且能吸引人的图片。同时&#xff0c;确保图片与视频内容…

MySQL的Linux安装

在MySQL官网下载压缩包MySQL :: Download MySQL Community Server (Archived Versions) 下载完成后将压缩包上传到Linux中。我这里是下的CentOS的压缩包。 并且用的是FinalShell连接工具&#xff0c;可以选择压缩包直接上传。 ​ 上传完毕后&#xff0c;新建mysql文件夹&…

计算机视觉面试题-03

1、简单介绍一下sigmoid&#xff0c;relu&#xff0c;softplus&#xff0c;tanh&#xff0c;RBF及其应用场景 这里简单介绍几个激活函数及其应用场景&#xff1a; Sigmoid 函数&#xff08;Logistic 函数&#xff09;&#xff1a; 公式&#xff1a; s i g m a ( x ) 1 1 e …

【香橙派】实战记录2——烧录安卓镜像及基本功能

文章目录 一、安卓烧录二、安卓基本功能1、蓝牙2、相机功能3、投屏 一、安卓烧录 检查环境&#xff1a;检查PC系统&#xff0c;确保有Microsoft Visual C 2008 Redistrbutable - x86&#xff0c;否则在官网下载的官方工具 - 安卓镜像烧录工具里运行vcredist_x86.exe。 插入存储…

模板上新|2023年10月DataEase模板市场上新动态

DataEase开源数据可视化分析平台于2022年6月正式发布模板市场&#xff08;https://dataease.io/templates/&#xff09;。模板市场旨在为DataEase用户提供专业、美观、拿来即用的仪表板模板&#xff0c;方便用户根据自身的业务需求和使用场景选择对应的仪表板模板&#xff0c;并…

Authing CEO 谢扬来信 |我的原则

从忙碌的工作中短暂抽身&#xff0c;有很多感想&#xff0c;不吐不快&#xff0c;借此机会&#xff0c;倾我所有&#xff0c;诉我原则。 原则一&#xff1a;坚强信念&#xff0c;坚定意志 商人大多「无利不起早」&#xff0c;而创业者的反馈周期比商人长非常非常多。 相比「商品…

【转】C代码利用CPU L1 cache一秒内算出十亿以内质数的个数

我去年发表了一篇 Python 代码&#xff0b;Numpy 库 Sieve算法实现一秒内计算出一亿以内的质数的个数&#xff1a; https://blog.csdn.net/Scott0902/article/details/128193368 今天在 GitHub 上找到国外牛人在三年前已经用 C 语言编写出利用 CPU L1 cache 来进行超高速计算…

Java 之 lambda 表达式(二)---- Stream 操作 API

目录 一. 前言 二. Stream 创建 2.1. 使用集合来创建 Stream 2.2. 使用数组创建 Stream 2.3. 由值创建 Stream 2.4. 由函数创建无限流 Stream 2.5. 代码示例 三. Stream 操作 3.1. 中间型操作 3.1.1. filter() 3.1.2. map() 3.1.3. mapToInt()、mapToLong()、mapTo…

Zookeeper 实战 | Zookeeper 和Spring Cloud相结合解决分布式锁、服务注册与发现、配置管理

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

HarmonyOS-Service服务开发(一)

文章目录 创建新项目启动Serviceets获取service的bundleName DataAbility开发指导开发Data步骤创建Data 创建新项目 ServiceAbility开发指导 在config.json中也有配置出现 启动Service ets获取service的bundleName 项目的bundleName service的bundleName 这里serviceAbil…

青少年CTF之PHP特性练习(1-5)

青少年CTF-PHP特性练习 文章目录 青少年CTF-PHP特性练习PHP特性01PHP特性02PHP特性03PHP特性04PHP特性05 PHP特性01 看给出的源码&#xff0c;两个变量的值加密后的MD5相同 <?php$s1 "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47…

Nacos整合实际应用案例

Nacos数据隔离模型 公司->命名空间->分组->服务 命名空间通常用于隔离不同微服务之间的配置 分组用于隔离相同微服务下不同环境的配置 版本对应关系 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 应用案例 <par…

easyExcel自定义导出,指定列,设置请求头背景色,加入合计行,设置合计行字体,背景色等等

效果图 1.引入easyExcel pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.1</version></dependency> 2.工具类-自定义样式handler-CustomCellWriteHandler import java.util…

GAN:DCGAN-深度卷积生成对抗网络

论文&#xff1a;https://arxiv.org/pdf/1511.06434.pdf 发表&#xff1a;ICLR 2016 一、架构创新 1&#xff1a;全卷积网络&#xff1a;用逐步卷积代替确定性的空间池化函数&#xff08;如maxpooling&#xff09;&#xff0c;使网络学习自己的空间下采样。使用这种方法&#…

【文献阅读笔记】关于GANomaly的异常检测方法

文章目录 1、GANomaly: Semi-Supervised Anomaly Detection via Adversarial Training模型主要创新 2、Skip-GANomaly: Skip Connected and AdversariallyTrained Encoder-Decoder Anomaly Detection模型主要创新点 3、Industrial surface defect detection and localization u…

15 网关实战: 微服务集成Swagger实现在线文档

上节介绍了网关层面聚合API文档,通过网关的路由信息找到了各个服务的请求地址,这节讲一下微服务如何集成Swagger。 网关的API文档默认调用的是微服务的**/v2/api-docs**这个接口获取API详细信息,比如文章服务的URL:http://localhost:9000/blog-article/v2/api-docs,返回信…

【DeepLearning.AI】吴恩达系列课程——使用Gradio构建AI应用

目录 前言一、Gradio介绍1-1、Gradio介绍1-2、安装1-3、小栗子 二、使用Gradio构建AI应用2-1、NLP任务2-1-1、文本摘要2-1-2、命名实体识别 2-2、聊天任务&#xff08;ChatYuan&#xff09;2-2-1、模型介绍2-2-2、模型下载、参数设置2-2-3、模型测试2-2-4、嵌入到Gradio里2-2-5…

leetcode:2864. 最大二进制奇数(python3解法)

难度&#xff1a;简单 给你一个 二进制 字符串 s &#xff0c;其中至少包含一个 1 。 你必须按某种方式 重新排列 字符串中的位&#xff0c;使得到的二进制数字是可以由该组合生成的 最大二进制奇数 。 以字符串形式&#xff0c;表示并返回可以由给定组合生成的最大二进制奇数。…

SVN代码回滚之Update item to thisversion和Revert to this version 区别

背景 在使用SVN管理代码时免不了进行代码的合并或者回退&#xff0c;本篇主要讲回退至某个版本的SVN操作。 内容 对目标代码右键查看log&#xff0c;选中某个你想回滚的版本&#xff0c;然后右键会看到如下图 假设log中已经有5个版本&#xff0c;分别为1&#xff0c;2&…