基于C#实现十字链表
上一篇我们看了矩阵的顺序存储,这篇我们再看看一种链式存储方法“十字链表”,当然目的都是一样,压缩空间。
一、概念
既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下 5 个元素(row,col,val,down,right),其中:
row:矩阵中的行。
col:矩阵中的列。
val:矩阵中的值。
right:指向右侧的一个非零元素。
down:指向下侧的一个非零元素。

现在我们知道单个节点该如何表示了,那么矩阵中同行的非零元素的表示不就是一个单链表吗?比如如下:

那么进一步来说一个多行的非零元素的表示不就是多个单链表吗,是的,这里我把单链表做成循环链表,我们来看看如何用十字链表来表示稀疏矩阵。

从上面的十字链表中要注意两个问题:
第一:这里有一个填充色的节点,是十字链表中的总结点,它是记录该矩阵中的(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];//遍历每一行的rightfor (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;}#endregionNode[] 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];//遍历每一行的rightfor (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}}

相关文章:
基于C#实现十字链表
上一篇我们看了矩阵的顺序存储,这篇我们再看看一种链式存储方法“十字链表”,当然目的都是一样,压缩空间。 一、概念 既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下 5 个元素(row,col,val,down,right),其中&…...
【MySQL】常用内置函数:数值函数 / 字符串函数 / 日期函数 / 其他函数
文章目录 数值函数round():四舍五入ceiling():上限函数floor():地板函数abs():计算绝对值rand():生成0-1的随机浮点数 字符串函数length():获取字符串中的字符数upper() / lower():将字符串转化…...
Python内置函数与标准库函数的详细解读
一、内置函数与标准库函数的区分 Python 解释器自带的函数叫做内置函数,这些函数可以直接使用,不需要导入某个模块。 Python 解释器也是一个程序,它给用户提供了一些常用功能,并给它们起了独一无二的名字,这些常用功能…...
计算机应用基础_错题集_Internet应用1---网络教育统考工作笔记004
5、下面关于搜索引擎的说法,不正确的是____。 A.搜索引擎既是用于检索的软件,又是提供查询﹑检索的网站 B.搜索引擎按其工作方式分为两类:全文搜索引擎和基于关键词的搜索引擎 C.现在很多搜索引擎提供网页快照的功能,当这个网页被删除或链接失效时,用户仍可使用网页快照…...
STM32之定时器--超声波测距
1、模块介绍 型号:HC-SR04 超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。 2、超声波模块的使用方法 怎么让它发送波 Trig ,给Trig端口至…...
微信小程序 老年人心血管健康知识科普系统
本系统的功能有管理员:个人中心,用户管理,热点信息管理,疾病管理,疾病类型管理,治疗管理,治疗类型管理,护理管理,护理类型管理,科普管理,科普类型…...
influxdb2.x安装配置指南
influxdb的教程已经是很清楚了,但没有中文版翻译,以下是个人安装配置总结 如果机器上只需要一个influxdb实例,或docker安装,直接yum install 就可以了,或者采用离线安装: sudo yum localinstall influxdb…...
android APP使用指定网络上网的原理
【精选】Android app 指定网络发送数据包的实现与原理分析_bindprocesstonetwork-CSDN博客 补充: frameworks/base/core/java/android/net/ConnectivityManager.java 函数: bindProcessToNetwork 调用到了 NetworkUtils.bindProcessToNetwork 但是N…...
git-2
1.分离头指针情况下的注意事项 分离头指针指的是变更没有基于某个branch去做,所以当进行分支切换的时候,在分离头指针上产生的commit,很可能会被git当作垃圾清理掉,如果你认为是重要的内容,切记需要绑定分支 2.进一步…...
Vue实现可拖拽边界布局
Vue实现可拖拽边界布局 在前端开发中,有时需要实现一种可拖拽边界的布局,通过拖动分隔线来调整不同区域大小。例如,下图是一个典型的可拖拽边界布局,它由左右两个区域组成,左边是一个树形菜单,右边是一个上…...
Day41力扣打卡
打卡记录 第 N 位数字(找规律) 链接 class Solution:def findNthDigit(self, n: int) -> int:count, digit, start 9, 1, 1while n > count:n - countdigit 1start * 10count start * 9 * digitnum start (n - 1) // digitreturn int(str(n…...
SpringBoot项目发送邮件
📑前言 本文主要是【SpringBoot】——SpringBoot项目发送邮件的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 dz…...
Mac单独修改应用语言
方法1: 方法2: defaults write com.microsoft.Excel AppleLanguages ("zh-cn") defaults write com.microsoft.Word AppleLanguages ("zh-cn")参考:https://www.zhihu.com/question/24976020...
Unity 通过代码控制Texture进行缩放
在实际应用开发中,有时候需要通过代码对Texture进行缩放。 有两个方法,一个是通过控制宽高进行缩放,另一个是通过比例值进行等比例缩放。 1、控制宽高的方法: /// <summary>/// 纹理缩放方法一,指定宽高/// &…...
C语言:输入3个整数,按由小到大的顺序输出(指针)
分析: 定义三个整型变量 a、b、c,和三个指向整型变量的指针变量 i、j、k。然后使用 scanf 函数从标准输入(键盘)中读取输入的三个整数,并将它们存储到 a、b、c 中。注意,使用 &a、&b、&c 进行赋…...
C# 模拟鼠标操作工具类
写在前面 用WinForm做RPA项目时经常需要模拟鼠标操作,通过调用Windows Api 可以实现控制鼠标的移动、点击以及滚轮滚动,做到跟人工一样的操作。 代码实现 public static class MouseKeyController{[DllImport("user32")]private static exte…...
content_script.js、background.js和popup.js之间的通讯
1. content_script.js 和 background.js 之间的通信: 使用 chrome.runtime.sendMessage 发送消息,然后在 background.js 中使用 chrome.runtime.onMessage 监听消息并作出相应处理。使用 chrome.extension.sendMessage 发送消息,然后在 back…...
python的requests请求参数带files
踩坑接口请求参数含文件 requests接口请求既有file,也有json。划重点params requests 官网地址 https://requests.readthedocs.io/en/stable/user/quickstart/#post-a-multipart-encoded-file 接口请求既有file,也有json。划重点params import reques…...
Elk:filebeat 日志收集工具和logstash
Elk:filebeat 日志收集工具和logstash Filebeat是一个轻量级的日志手机工具,所使用的系统资源比logstash部署和启动时使用的资源要小得多 Filebeat可以在非java环境使用,他可以代理logstash在非java环境上收集日志 缺点 Filebeat无法实现数据的过滤,一般是结合l…...
[设计模式] 常见的设计模式
文章目录 设计模式的 6 大设计原则设计模式的三大分类常见的设计模式有哪几种1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。(连接池)1. 饿汉式2. 懒汉式3. 双重检测 2. 工厂模式3. 观察者模式● 推模型● 拉…...
Tsuru容器网络终极性能测试指南:7大CNI插件深度对比与优化策略
Tsuru容器网络终极性能测试指南:7大CNI插件深度对比与优化策略 【免费下载链接】tsuru Open source and extensible Platform as a Service (PaaS). 项目地址: https://gitcode.com/gh_mirrors/ts/tsuru Tsuru作为开源可扩展的PaaS平台,其容器网络…...
MATLAB App Designer打包实战:从GUI到独立安装包的完整部署指南
1. MATLAB App Designer打包前的准备工作 第一次把MATLAB开发的GUI程序打包成独立安装包时,我踩了不少坑。记得当时给合作方演示算法,对方电脑没有MATLAB环境,只能干着急。后来花了三天时间才搞明白整个打包流程,现在把这些经验系…...
GTE中文向量模型实战教程:基于test_uninlu.py扩展支持中文繁体与简体兼容测试
GTE中文向量模型实战教程:基于test_uninlu.py扩展支持中文繁体与简体兼容测试 1. 引言:当GTE模型遇上中文繁简转换 如果你正在使用GTE中文向量模型处理文本,可能会遇到一个不大不小的麻烦:用户输入的文本,有时是简体中…...
BiliTools终极指南:3分钟掌握跨平台B站资源管理工具
BiliTools终极指南:3分钟掌握跨平台B站资源管理工具 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 还在…...
YOLOv8实战:如何用Python脚本批量预测验证码并提升识别准确率?
YOLOv8实战:Python脚本批量预测验证码与准确率优化指南 验证码识别一直是计算机视觉领域的经典挑战。传统方法依赖复杂的图像预处理和模板匹配,而基于YOLOv8的解决方案通过端到端训练实现了质的飞跃。本文将手把手带你实现从模型部署到批量预测的全流程&…...
FreeRTOS任务间通信怎么选?队列、信号量、邮箱,一个智能家居传感器数据采集与上报的完整案例
FreeRTOS任务间通信实战:智能家居传感器数据采集与上报的完整设计 在嵌入式开发领域,任务间通信机制的选择直接影响系统稳定性和实时性表现。以智能家居环境监测节点为例,当我们需要同时处理温湿度传感器数据采集、OLED屏幕显示、Wi-Fi数据上…...
MOCo. x 姆明快闪店美陈设计为何刷屏出圈?肆墨设计
在商业空间同质化加剧的当下,快闪店已成为品牌破圈、链接消费者的核心载体。MO&Co. 携手经典 IP 姆明打造的「姆明划水俱乐部」全国首展,以成熟的美陈设计逻辑,实现了品牌调性、IP 情感与商业转化的三重统一,为 IP 联名快闪美…...
ai辅助开发新体验:在快马平台上打造会思考的智能成片ppt生成网站
最近在做一个智能PPT生成网站的项目,发现结合AI辅助开发真的能带来不少惊喜。今天就来分享一下如何利用InsCode(快马)平台快速实现一个会思考的PPT生成工具。 项目背景与核心功能 传统PPT制作需要手动整理内容、设计版式,整个过程耗时费力。而智能PPT生成…...
Pixel Script Temple参数详解:LoRA秩(Rank)对剧本专业度与风格稳定性的权衡
Pixel Script Temple参数详解:LoRA秩(Rank)对剧本专业度与风格稳定性的权衡 1. 理解LoRA秩(Rank)的基本概念 1.1 什么是LoRA秩 LoRA(Low-Rank Adaptation)是一种高效的大模型微调技术&#x…...
当网盘变成龟速:如何优雅地找回你的下载自由?
当网盘变成龟速:如何优雅地找回你的下载自由? 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…...
