基于C#实现十字链表

news2024/11/26 0:38:56

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

一、概念

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 8新特性Optional的使用以及判空案例

Java 8新特性&#xff1a;Optional的使用及判空案例 在Java 8中&#xff0c;引入了一个重要的类Optional&#xff0c;它是为了解决空指针异常&#xff08;NullPointerException&#xff09;而设计的。Optional可以作为一个容器&#xff0c;可以包含一个非空的值或者为空。 Opti…

1评论收藏分享抖店不要再无脑铺货了!这个方法学会,7天流量就起飞~

这2023年都马上过完了&#xff0c;你还在上一堆链接到抖店吗&#xff1f;要知道这样无脑铺货是拿不到大流量的。 哪今天我给大家分享一个&#xff0c;比较适合新手操作&#xff0c;也能快速起流量出单的方法。 。首先你的店铺拿不到流量&#xff0c;一定要先查清楚你为什么拿…

商城免费搭建之java商城 鸿鹄云商 B2B2C产品概述

【B2B2C平台】&#xff0c;以传统电商行业为基石&#xff0c;鸿鹄云商支持“商家入驻平台自营”多运营模式&#xff0c;积极打造“全新市场&#xff0c;全新 模式”企业级B2B2C电商平台&#xff0c;致力干助力各行/互联网创业腾飞并获取更多的收益。从消费者出发&#xff0c;助…

第二证券:投资股票有哪些坑?如何避免?

出资股票有哪些坑&#xff1f; 1、情绪化生意&#xff0c;追涨杀跌。许多出资者看到一只股票涨得很快&#xff0c;就想赶忙买入&#xff0c;怕失掉时机&#xff0c;或当自己持有的股票跌了就受不了只想卖出。这样的行为其实是十分不理性的&#xff0c;由于股票的价格是由商场供…

实战案例!用1行Python代码识别身份证信息,准确率超过99%,YYDS

录入身份证信息是一件繁琐的工作&#xff0c;如果可以自动识别并且录入系统&#xff0c;那可真是太好了。 今天我们就来学习一下&#xff0c;如何自动识别身份证信息并且录入系统~ 识别身份证信息 识别身份证信息的代码最简单&#xff0c;只需要1行代码&#xff0c;如下所示…

详解Python对Excel处理

Excel是一种常见的电子表格文件格式&#xff0c;广泛用于数据记录和处理。Python提供了多个第三方库&#xff0c;可以方便地对Excel文件进行读写、数据操作和处理。本文将介绍如何使用Python对Excel文件进行处理&#xff0c;并提供相应的代码示例和详细说明。 一、安装第三方库…

HT97220与HT97230耳机放大器芯片对比

HT97230有两个不同开启时间(tON)版本&#xff0c;版本A、C和E的导通时间tON为5.5ms&#xff0c;用于耳机驱动&#xff1b;B和D则具有130ms的tON&#xff0c;用于机顶盒设计&#xff08;目前仅提供A版本&#xff0c;其他版本需预定&#xff09;。内部电荷泵对输入电源反相&#…

Linux操作系统 1.初识Linux

一、Linux学习大致内容 二、操作系统概述 操作系统的作用&#xff1a; 常见操作系统&#xff1a; 1、pc&#xff08;电脑端&#xff09;&#xff1a;windows、Linux、MacOS 2、移动端&#xff1a;Android、ios、鸿蒙系统 总结 1.计算机由哪两个部分组成&#xff1f;、 硬件…

RabbitMQ工作模式2 整合springboot 和MQ高级特性

RabbitMQ工作模式 1.路由模式 创建交换机 , 连接队列 (生产者) public class MyTestExDirect {Testpublic void bbb() throws IOException, TimeoutException {ConnectionFactory connectionFactory new ConnectionFactory();//连接mqconnectionFactory.setUsername("…

Vue3中调用外部iframe链接方法

业务场景&#xff0c;点击某个按钮需要跳转到外部iframe的地址&#xff0c;但是需要在本项目内显示。以前项目中写过调用外部链接的功能&#xff0c;是有菜单的&#xff0c;但是这次是按钮&#xff0c;所以不能直接把地址配到菜单里。 实现方法&#xff1a;在本地路由文件里写个…

neo4j在Linux上安装及使用

1、简介 neo4j安装主要有两个步骤&#xff1a; 环境配置&#xff1a;Java安装工具下载&#xff1a;neo4j安装 2、java 安装 2.1 检查 安装前可以检查下&#xff0c;当前环境是否有Java 查看是否安装&#xff1a;java -version 说明当前环境没有&#xff0c;那么去下载 …

推荐你一个基于Koin, Ktor Paging等组件的KMM Compose Multiplatform项目

推荐你一个基于Koin, Ktor & Paging等组件的KMM Compose Multiplatform项目 Kotlin Multiplatform Mobile&#xff08;KMM&#xff09;已经从一个雄心勃勃的想法发展成为一个稳定而强大的框架&#xff0c;为开发人员提供了在多个平台上无缝共享代码的能力。通过最近的稳定…

2023.11.25电商项目平台建设2 -四大业务之核销主题建模

1.数仓建模步骤 自下而上 ADS-DWS-DWM-DWD 2.DWD方案(清洗转换,降维拉宽) DWD层的表 dwd_sale_store_sale_dtl_i 门店销售明细宽表 维度dim 销售sale合成成的宽表 dwd_dim_date_f 日期表 store_sale_dtl 门店销售明细表 dwd_sale_store_sale_dtl_i 门店销售明细表 …

漏洞分析 | 经典的Shiro反序列化

0x01、前言 相信大家总是面试会问到java反序列化&#xff0c;或者会问到标志性的漏洞&#xff0c;比如shiro反序列化&#xff0c;或者weblogic反序列化漏洞。 那我就这篇文章为大家讲解一下&#xff0c;不懂的哥哥直接背一下&#xff0c;理解一下就好了。 至于为什么要选择sh…

力扣(LeetCode)907. 子数组的最小值之和(C++)

枚举 请对题目有疑惑的小伙伴看枚举思想&#xff0c;有助于掌握最基本的解题思路。对于本题数据范围&#xff0c;枚举算法会超时。 请看题目描述&#xff1a;给定一个整数数组 arr&#xff0c;找到 min(b) 的总和&#xff0c;其中 b 的范围为 arr 的每个&#xff08;连续&…

锂电池污水如何处理

锂电池是目前应用广泛的重要电池类型&#xff0c;然而其生产过程和废弃处理中产生的污水对环境造成了不可忽视的影响。本文将探讨锂电池污水的处理方法&#xff0c;以期为环境保护和可持续发展作出贡献。 首先&#xff0c;了解锂电池污水的组成是解决问题的关键。锂电池污水通…

解释LED显示屏的裸眼3D特效原理

LED电子大屏幕的3D特效技术正在不断发展&#xff0c;而实现这一技术的原理主要包括分光、分色、分时和光栅等四种方法。这些原理都有各自的特点和应用场景&#xff0c;下面将对它们进行详细介绍。 1. 分光方法 分光方法是一种基于偏振光的3D显示技术。通过使用偏振滤镜或偏振片…

webshell之编码免杀

Unicode编码 jsp支持unicode编码&#xff0c;如果杀软不支持unicode查杀的话&#xff0c;基本上都能绕过 注意这里的\uuuu00可以换成\uuuu00uuu...可以跟多个u达到绕过的效果 将代码&#xff08;除page以及标签&#xff09;进行unicode编码&#xff0c;并条件到<%%>标签…

Drupal Core 8 PECL YAML 反序列化任意代码执行漏洞(CVE-2017-6920)

漏洞描述 影响软件&#xff1a;Drupal方式&#xff1a;反序列化参考链接&#xff1a;CVE-2017-6920:Drupal远程代码执行漏洞分析及POC构造效果&#xff1a;任意代码执行 漏洞环境及利用 搭建docker环境 环境启动后&#xff0c;访问 将会看到drupal的安装页面&#xff0c;一路…

仅2万粉,带了2.6万件的货!TikTok Shop美区达人周榜(11.13-11.19)

11月24日&#xff0c;TikTok Shop近日公布了美国市场和英国市场的全托管黑五大促战绩。数据显示&#xff0c;11月14日至11月20日&#xff0c;其美国市场的订单量环比10月20日-10月26日增长了205%。 家居户外热销品有&#xff1a;数码触摸屏相框、毛绒地毯、家居毛毯。黑马商品…