基于C#实现块状链表

news2024/11/26 12:52:19

在数据结构的世界里,我们会认识各种各样的数据结构,每一种数据结构都能解决相应领域的问题,当然每个数据结构,有他的优点,必然就有它的缺点,那么如何创造一种数据结构来将某两种数据结构进行扬长避短,那就非常完美了。这样的数据结构也有很多,比如:双端队列,还有就是今天讲的块状链表。
我们都知道
数组 具有 O(1)的查询时间,O(N)的删除,O(N)的插入。。。
链表 具有 O(N)的查询时间,O(1)的删除,O(1)的插入。。。
那么现在我们就有想法了,何不让“链表”和“数组”结合起来,来一起均摊 CURD 的时间,做法将数组进行分块,然后用指针相连接,比如我有 N=100 个元素,那么最理想情况下,我就可以将数组分成 x=10 段,每段 b=10 个元素(排好序),那么我可以用 √N 的时间找到段,因为段中的元素是已经排好序的,所以可以用 lg√N 的时间找到段中的元素,那么最理想的复杂度为 √N+lg√N≈√N。。。
下面我们看看怎么具体使用:

一、结构定义

这个比较简单,我们在每个链表节点中定义一个 头指针,尾指针和一个数组节点。

 public class BlockLinkNode
 {
     /// <summary>
     /// 指向前一个节点的指针
     /// </summary>
     public BlockLinkNode prev;

     /// <summary>
     /// 指向后一个节点的指针
     /// </summary>
     public BlockLinkNode next;

     /// <summary>
     /// 链表中的数组
     /// </summary>
     public List<int> list;
 }

二、插入

刚才也说了,每个链表节点的数据是一个数组块,那么问题来了,我们是根据什么将数组切开呢?总不能将所有的数据都放在一个链表的节点吧,那就退化成数组了,在理想的情况下,为了保持 √N 的数组个数,所以我们定了一个界限 2√N,当链表中的节点数组的个数超过 2√N 的时候,当下次插入数据的时候,我们有两种做法:

  1. 在元素的数组插入处,将当前数组切开,插入元素处之前为一个链表节点,插入元素后为一个链表节点。
  2. 将元素插入数组后,将数组从中间位置切开。

image.png

 /// <summary>
 /// 添加元素只会进行块状链表的分裂
 /// </summary>
 /// <param name="node"></param>
 /// <param name="num"></param>
 /// <returns></returns>
 private BlockLinkNode Add(BlockLinkNode node, int num)
 {
     if (node == null)
     {
         return node;
     }
     else
     {
         /*
          *  第一步:找到指定的节点
          */
         if (node.list.Count == 0)
         {
             node.list.Add(num);

             total = total + 1;

             return node;
         }

         //下一步:再比较是否应该分裂块
         var blockLen = (int)Math.Ceiling(Math.Sqrt(total)) * 2;

         //如果该节点的数组的最后位置值大于插入值,则此时我们找到了链表的插入节点,
         //或者该节点的next=null,说明是最后一个节点,此时也要判断是否要裂开
         if (node.list[node.list.Count - 1] > num || node.next == null)
         {
             node.list.Add(num);

             //最后进行排序下,当然可以用插入排序解决,O(N)搞定
             node.list = node.list.OrderBy(i => i).ToList();

             //如果该数组里面的个数大于2*blockLen,说明已经过大了,此时需要对半分裂
             if (node.list.Count > blockLen)
             {
                 //先将数据插入到数据库
                 var mid = node.list.Count / 2;

                 //分裂处的前段部分
                 var firstList = new List<int>();

                 //分裂后的后段部分
                 var lastList = new List<int>();

                 //可以在插入点处分裂,也可以对半分裂(这里对半分裂)
                 firstList.AddRange(node.list.Take(mid));
                 lastList.AddRange(node.list.Skip(mid).Take(node.list.Count - mid));


                 //开始分裂节点,需要新开辟一个新节点
                 var nNode = new BlockLinkNode();

                 nNode.list = lastList;
                 nNode.next = node.next;
                 nNode.prev = node;

                 //改变当前节点的next和list
                 node.list = firstList;
                 node.next = nNode;
             }

             total = total + 1;

             return node;
         }

         return Add(node.next, num);
     }
 }

二、删除

跟插入道理一样,既然有裂开,就有合并,同样也定义了一个界限值 √N /2 ,当链表数组节点的数组个数小于这个界限值的时候,需要将此节点和后面的链表节点进行合并。
image.png

 /// <summary>
 /// 从块状链表中移除元素,涉及到合并
 /// </summary>
 /// <param name="node"></param>
 /// <param name="num"></param>
 /// <returns></returns>
 private BlockLinkNode Remove(BlockLinkNode node, int num)
 {
     if (node == null)
     {
         return node;
     }
     else
     {
         //第一步: 判断删除元素是否在该节点内
         if (node.list.Count > 0 && num >= node.list[0] && num <= node.list[node.list.Count - 1])
         {
             //定义改节点的目的在于防止remove方法假删除的情况发生
             var prevcount = node.list.Count;

             node.list.Remove(num);

             total = total - (prevcount - node.list.Count);

             //下一步: 判断是否需要合并节点
             var blockLen = (int)Math.Ceiling(Math.Sqrt(total) / 2);

             //如果当前节点的数组个数小于 blocklen的话,那么此时改节点需要和后一个节点进行合并
             //如果该节点时尾节点,则放弃合并
             if (node.list.Count < blockLen)
             {
                 if (node.next != null)
                 {
                     node.list.AddRange(node.next.list);

                     //如果下一个节点的下一个节点不为null,则将下下个节点的prev赋值
                     if (node.next.next != null)
                         node.next.next.prev = node;

                     node.next = node.next.next;
                 }
                 else
                 {
                     //最后一个节点不需要合并,如果list=0,则直接剔除该节点
                     if (node.list.Count == 0)
                     {
                         if (node.prev != null)
                             node.prev.next = null;

                         node = null;
                     }
                 }
             }

             return node;
         }

         return Remove(node.next, num);
     }
 }

三、查询

在理想的情况下,我们都控制在 √N,然后就可以用 √N 的时间找到区块,lg√N 的时间找到区块中的指定值,当然也有人在查询的时候做 链表的合并和分裂,这个就有点像伸展树一样,在查询的时候动态调整,拼的是均摊情况下的复杂度。

 public string Get(int num)
 {
     var blockIndex = 0;
     var arrIndex = 0;

     var temp = blockLinkNode;

     while (temp != null)
     {
         //判断是否在该区间内
         if (temp.list.Count > 0 && num >= temp.list[0] && num <= temp.list[temp.list.Count - 1])
         {
             arrIndex = temp.list.IndexOf(num);

             return string.Format("当前数据在第{0}块中的{1}个位置", blockIndex, arrIndex);
         }

         blockIndex = blockIndex + 1;
         temp = temp.next;
     }

     return string.Empty;
 }

好了,CURD 都分析好了,到这里大家应该对 块状链表有个大概的认识了吧,这个代码是我下午抽闲写的,没有仔细测试,最后是总的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
             List<int> list = new List<int>() { 8959, 30290, 18854, 7418, 28749, 17313, 5877, 27208, 15771, 4335 };
 
             //list.Clear();
 
             //List<int> list = new List<int>();
 
             //for (int i = 0; i < 100; i++)
             //{
             //    var num = new Random((int)DateTime.Now.Ticks).Next(0, short.MaxValue);
 
             //    System.Threading.Thread.Sleep(1);
 
             //    list.Add(num);
             //}
 
 
             BlockLinkList blockList = new BlockLinkList();
 
             foreach (var item in list)
             {
                 blockList.Add(item);
             }
 
             //var b = blockList.IsExist(333);
             //blockList.GetCount();
 
             Console.WriteLine(blockList.Get(27208));
 
 
             #region MyRegion
             随机删除150个元素
             //for (int i = 0; i < 5000; i++)
             //{
             //    var rand = new Random((int)DateTime.Now.Ticks).Next(0, list.Count);
 
             //    System.Threading.Thread.Sleep(2);
 
             //    Console.WriteLine("\n**************************************\n当前要删除元素:{0}", list[rand]);
 
             //    blockList.Remove(list[rand]);
 
             //    Console.WriteLine("\n\n");
 
             //    if (blockList.GetCount() == 0)
             //    {
             //        Console.Read();
             //        return;
             //    }
             //} 
             #endregion
 
             Console.Read();
         }
     }
 
     public class BlockLinkList
     {
         BlockLinkNode blockLinkNode = null;
 
         public BlockLinkList()
         {
             //初始化节点
             blockLinkNode = new BlockLinkNode()
             {
                 list = new List<int>(),
                 next = null,
                 prev = null
             };
         }
 
         /// <summary>
         /// 定义块状链表的总长度
         /// </summary>
         private int total;
 
         public class BlockLinkNode
         {
             /// <summary>
             /// 指向前一个节点的指针
             /// </summary>
             public BlockLinkNode prev;
 
             /// <summary>
             /// 指向后一个节点的指针
             /// </summary>
             public BlockLinkNode next;
 
             /// <summary>
             /// 链表中的数组
             /// </summary>
             public List<int> list;
         }

         /// <summary>
         /// 判断指定元素是否存在
         /// </summary>
         /// <param name="num"></param>
         /// <returns></returns>
         public bool IsExist(int num)
         {
             var isExist = false;
 
             var temp = blockLinkNode;
 
             while (temp != null)
             {
                 //判断是否在该区间内
                 if (temp.list.Count > 0 && num >= temp.list[0] && num <= temp.list[temp.list.Count - 1])
                 {
                     isExist = temp.list.IndexOf(num) > 0 ? true : false;
 
                     return isExist;
                 }
 
                 temp = temp.next;
             }
 
             return isExist;
         }
 
         public string Get(int num)
         {
             var blockIndex = 0;
             var arrIndex = 0;
 
             var temp = blockLinkNode;
 
             while (temp != null)
             {
                 //判断是否在该区间内
                 if (temp.list.Count > 0 && num >= temp.list[0] && num <= temp.list[temp.list.Count - 1])
                 {
                     arrIndex = temp.list.IndexOf(num);
 
                     return string.Format("当前数据在第{0}块中的{1}个位置", blockIndex, arrIndex);
                 }
 
                 blockIndex = blockIndex + 1;
                 temp = temp.next;
             }
 
             return string.Empty;
         }
 
         /// <summary>
         /// 将元素加入到块状链表中
         /// </summary>
         /// <param name="num"></param>
         public BlockLinkNode Add(int num)
         {
             return Add(blockLinkNode, num);
         }
 
         /// <summary>
         /// 添加元素只会进行块状链表的分裂
         /// </summary>
         /// <param name="node"></param>
         /// <param name="num"></param>
         /// <returns></returns>
         private BlockLinkNode Add(BlockLinkNode node, int num)
         {
             if (node == null)
             {
                 return node;
             }
             else
             {
                 /*
                  *  第一步:找到指定的节点
                  */
                 if (node.list.Count == 0)
                 {
                     node.list.Add(num);
 
                     total = total + 1;
 
                     return node;
                 }
 
                 //下一步:再比较是否应该分裂块
                 var blockLen = (int)Math.Ceiling(Math.Sqrt(total)) * 2;
 
                 //如果该节点的数组的最后位置值大于插入值,则此时我们找到了链表的插入节点,
                 //或者该节点的next=null,说明是最后一个节点,此时也要判断是否要裂开
                 if (node.list[node.list.Count - 1] > num || node.next == null)
                 {
                     node.list.Add(num);
 
                     //最后进行排序下,当然可以用插入排序解决,O(N)搞定
                     node.list = node.list.OrderBy(i => i).ToList();
 
                     //如果该数组里面的个数大于2*blockLen,说明已经过大了,此时需要对半分裂
                     if (node.list.Count > blockLen)
                     {
                         //先将数据插入到数据库
                         var mid = node.list.Count / 2;
 
                         //分裂处的前段部分
                         var firstList = new List<int>();
 
                         //分裂后的后段部分
                         var lastList = new List<int>();
 
                         //可以在插入点处分裂,也可以对半分裂(这里对半分裂)
                         firstList.AddRange(node.list.Take(mid));
                         lastList.AddRange(node.list.Skip(mid).Take(node.list.Count - mid));
 
 
                         //开始分裂节点,需要新开辟一个新节点
                         var nNode = new BlockLinkNode();
 
                         nNode.list = lastList;
                         nNode.next = node.next;
                         nNode.prev = node;
 
                         //改变当前节点的next和list
                         node.list = firstList;
                         node.next = nNode;
                     }
 
                     total = total + 1;
 
                     return node;
                 }
 
                 return Add(node.next, num);
             }
         }
 
         /// <summary>
         /// 从块状链表中移除元素
         /// </summary>
         /// <param name="num"></param>
         /// <returns></returns>
         public BlockLinkNode Remove(int num)
         {
             return Remove(blockLinkNode, num);
         }
 
         /// <summary>
         /// 从块状链表中移除元素,涉及到合并
         /// </summary>
         /// <param name="node"></param>
         /// <param name="num"></param>
         /// <returns></returns>
         private BlockLinkNode Remove(BlockLinkNode node, int num)
         {
             if (node == null)
             {
                 return node;
             }
             else
             {
                 //第一步: 判断删除元素是否在该节点内
                 if (node.list.Count > 0 && num >= node.list[0] && num <= node.list[node.list.Count - 1])
                 {
                     //定义改节点的目的在于防止remove方法假删除的情况发生
                     var prevcount = node.list.Count;
 
                     node.list.Remove(num);
 
                     total = total - (prevcount - node.list.Count);
 
                     //下一步: 判断是否需要合并节点
                     var blockLen = (int)Math.Ceiling(Math.Sqrt(total) / 2);
 
                     //如果当前节点的数组个数小于 blocklen的话,那么此时改节点需要和后一个节点进行合并
                     //如果该节点时尾节点,则放弃合并
                     if (node.list.Count < blockLen)
                     {
                         if (node.next != null)
                         {
                             node.list.AddRange(node.next.list);
 
                             //如果下一个节点的下一个节点不为null,则将下下个节点的prev赋值
                             if (node.next.next != null)
                                 node.next.next.prev = node;
 
                             node.next = node.next.next;
                         }
                         else
                         {
                             //最后一个节点不需要合并,如果list=0,则直接剔除该节点
                             if (node.list.Count == 0)
                             {
                                 if (node.prev != null)
                                     node.prev.next = null;
 
                                 node = null;
                             }
                         }
                     }
 
                     return node;
                 }

                 return Remove(node.next, num);
             }
         }
 
         /// <summary>
         /// 获取块状链表中的所有个数
         /// </summary>
         /// <returns></returns>
         public int GetCount()
         {
             int count = 0;
 
             var temp = blockLinkNode;
 
             Console.Write("各节点数据个数为:");
 
             while (temp != null)
             {
                 count += temp.list.Count;
 
                 Console.Write(temp.list.Count + ",");
 
                 temp = temp.next;
             }
 
             Console.WriteLine("总共有:{0} 个元素", count);
 
             return count;
         }
     }
 }

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

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

相关文章

AntDB数据库,通信行业20年变迁的见证者

2000年至今&#xff0c;通信行业发展已过了20多年。面对通信行业巨大的数据信息&#xff0c;数据库在行业发展中发挥了巨大的作用&#xff0c;AntDB数据库便是其中较为知名的一款数据库。在通信行业快速发展的阶段&#xff0c;打破国外产品与技术垄断是产业发展的重点与难点。面…

一个基于.NET Core开源、跨平台的仓储管理系统

前言 今天给大家推荐一个基于.NET Core开源、跨平台的仓储管理系统&#xff0c;数据库支持MSSQL/MySQL&#xff1a;ZEQP.WMS。 仓储管理系统介绍 仓储管理系统&#xff08;Warehouse Management System&#xff0c;WMS&#xff09;是一种用于管理和控制仓库操作的软件系统&…

SAP创建ODATA服务-Structure

SAP创建ODATA服务-Structure 1、创建数据字典 进入se11创建透明表ZRICO_USR,并创建对应字段 2、创建OData service 首先创建Gateway service project&#xff0c;事务码&#xff1a;SEGW&#xff0c;点击Create Project 按钮 Gateway service Project分四个部分&#xff1a…

RocketMq 主题(TOPIC)

RocketMq是阿里出品&#xff08;基于MetaQ&#xff09;的开源中间件&#xff0c;已捐赠给Apache基金会并成为Apache的顶级项目。基于java语言实现&#xff0c;十万级数据吞吐量&#xff0c;ms级处理速度&#xff0c;分布式架构&#xff0c;功能强大&#xff0c;扩展性强。 官方…

【C++高阶(五)】哈希思想--哈希表哈希桶

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 哈希结构 1. 前言2. unordered系列容器3. 哈希概…

FreeRTOS深入教程(软件定时器源码分析)

文章目录 前言一、软件定时器结构体二、软件定时器的工作机制三、创建软件定时器四、启动软件定时器五、软件定时器如何知道什么时候被调用总结 前言 除了有硬件定时器&#xff0c;还有软件定时器&#xff0c;那么这篇文章将带大家学习一下软件定时器是如何工作的&#xff0c;…

金鸣表格文字识别客户端输出项该如何选择?

智能布局&#xff1a;根据提交的图片自动设置输出的打印纸张大小和方向&#xff0c;其中表格识别默认为A4纵向&#xff0c;勾选“合并”可将N张图片批量识别成一个文件、一个表。 表格识别&#xff1a; excel&#xff1a;输出可编辑的excel。 word&#xff1a;输出可编辑的w…

express习惯养成小程序-计算机毕设 附源码 32209

习惯养成小程序的设计与实现 摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;习惯养成小程序被用户普遍使…

虾皮买手号怎么弄的

想要拥有虾皮买手号&#xff0c;可以使用shopee买家通系统进行自动化注册&#xff0c;这款软件目前支持菲律宾、泰国、马来西亚、越南、巴西、印度尼西亚等国家使用。 软件注册流程简单方便&#xff0c;首先我们需要先准备好手机号&#xff0c;因为现在注册虾皮买家号基本上都是…

TechSmith Camtasia2024中文版简单好用的视频处理软件

TechSmith Camtasia 2024中文版是由techsmith公司推出的一款简单好用的视频处理软件&#xff0c;它集视频录制与视频后期处理为一体&#xff0c;用户可以使用软件来进行屏幕录制&#xff0c;其中包括了影像、音效、鼠标移动的轨迹、解说声音等任何模式下的电脑屏幕状态&#xf…

IIP3参数的含义

IIP3参数的含义 三阶交调频率分量 混频器的输入端的总输入信号通常由射频输入&#xff08;载波被调制信号&#xff09;和本振组成。以输入总信号由3个正弦信号为例&#xff0c;输入端的总输入信号为&#xff1a; u u 1 cos ⁡ ω 1 t u 2 cos ⁡ ω 2 t u 3 cos ⁡ ω 3 …

COMP2121 Discrete Mathematics

COMP2121 Discrete Mathematics 需要可WeChat: zh6-86

国产1433D/E/F/H手持式信号发生器,可覆盖到50GHz

1433D/E/F/H信号发生器 1433系列信号发生器是中电科思仪科技股份有限公司专为外场测试设计的一款手持式仪器&#xff0c;具有连续波信号输出、频率/幅度/脉冲多种调制、大动态范围幅度调节、步进/列表扫描等功能&#xff0c;采用8.4寸大屏幕液晶及电容触摸屏一体化设计&#xf…

获取数据库中最占用内存的sql语句

SELECT TOP 20 total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数], qs.total_worker_time/qs.execution_count/1000 AS [平均消耗CPU 时间(ms)], last_execution_time AS [最后一次执行时间],min_worker_time /1000 AS [最小执行时间(ms…

《opencv实用探索·三》opencv Mat与数组互转

1、利用Mat来存储数据&#xff0c;避免使用数组等操作 //创建一个两行一列的矩阵cv::Mat mean (cv::Mat_<float>(2, 1) << 0.77, 0.33);std::cout() << mean << std::endl;float a mean.at<float>(0, 0); //0.77float b mean.at<float&…

『Linux升级路』基础开发工具——make/Makefile

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、认识make/Makefile &#x1f4d2;1.1make/Makefile的优点 &#x1f4d2;…

windows dockerdesktop 安装sqlserver2022

1.下载windows dockertop软件 下载连接 2.安装完成配置&#xff0c;下载源地址 {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": …

flink源码分析之功能组件(三)-rpc组件

简介 本系列是flink源码分析的第二个系列,上一个《flink源码分析之集群与资源》分析集群与资源,本系列分析功能组件,kubeclient,rpc,心跳,高可用,slotpool,rest,metrics,future。 本文解释rpc组件,rpc组件用于个核心组件,包括作业管理器,资源管理器和任务管理器之…

基于C#实现鸡尾酒排序(双向冒泡排序)

通俗易懂点的话&#xff0c;就叫“双向冒泡排序”。 冒泡是一个单向的从小到大或者从大到小的交换排序&#xff0c;而鸡尾酒排序是双向的&#xff0c;从一端进行从小到大排序&#xff0c;从另一端进行从大到小排序。 从图中可以看到&#xff0c;第一次正向比较&#xff0c;我们…