C#實(shí)現(xiàn)塊狀鏈表的項(xiàng)目實(shí)踐
在數(shù)據(jù)結(jié)構(gòu)的世界里,我們會(huì)認(rèn)識(shí)各種各樣的數(shù)據(jù)結(jié)構(gòu),每一種數(shù)據(jù)結(jié)構(gòu)都能解決相應(yīng)領(lǐng)域的問(wèn)題,當(dāng)然每個(gè)數(shù)據(jù)結(jié)構(gòu),有他的優(yōu)點(diǎn),必然就有它的缺點(diǎn),那么如何創(chuàng)造一種數(shù)據(jù)結(jié)構(gòu)來(lái)將某兩種數(shù)據(jù)結(jié)構(gòu)進(jìn)行揚(yáng)長(zhǎng)避短,那就非常完美了。這樣的數(shù)據(jù)結(jié)構(gòu)也有很多,比如:雙端隊(duì)列,還有就是今天講的塊狀鏈表。
我們都知道
- 數(shù)組 具有 O(1)的查詢時(shí)間,O(N)的刪除,O(N)的插入。。。
- 鏈表 具有 O(N)的查詢時(shí)間,O(1)的刪除,O(1)的插入。。。
那么現(xiàn)在我們就有想法了,何不讓“鏈表”和“數(shù)組”結(jié)合起來(lái),來(lái)一起均攤 CURD 的時(shí)間,做法將數(shù)組進(jìn)行分塊,然后用指針相連接,比如我有 N=100 個(gè)元素,那么最理想情況下,我就可以將數(shù)組分成 x=10 段,每段 b=10 個(gè)元素(排好序),那么我可以用 √N 的時(shí)間找到段,因?yàn)槎沃械脑厥且呀?jīng)排好序的,所以可以用 lg√N 的時(shí)間找到段中的元素,那么最理想的復(fù)雜度為 √N+lg√N≈√N。。。
下面我們看看怎么具體使用:
一、結(jié)構(gòu)定義
這個(gè)比較簡(jiǎn)單,我們?cè)诿總€(gè)鏈表節(jié)點(diǎn)中定義一個(gè) 頭指針,尾指針和一個(gè)數(shù)組節(jié)點(diǎn)。
public class BlockLinkNode { /// <summary> /// 指向前一個(gè)節(jié)點(diǎn)的指針 /// </summary> public BlockLinkNode prev; /// <summary> /// 指向后一個(gè)節(jié)點(diǎn)的指針 /// </summary> public BlockLinkNode next; /// <summary> /// 鏈表中的數(shù)組 /// </summary> public List<int> list; }
二、插入
剛才也說(shuō)了,每個(gè)鏈表節(jié)點(diǎn)的數(shù)據(jù)是一個(gè)數(shù)組塊,那么問(wèn)題來(lái)了,我們是根據(jù)什么將數(shù)組切開(kāi)呢?總不能將所有的數(shù)據(jù)都放在一個(gè)鏈表的節(jié)點(diǎn)吧,那就退化成數(shù)組了,在理想的情況下,為了保持 √N 的數(shù)組個(gè)數(shù),所以我們定了一個(gè)界限 2√N,當(dāng)鏈表中的節(jié)點(diǎn)數(shù)組的個(gè)數(shù)超過(guò) 2√N 的時(shí)候,當(dāng)下次插入數(shù)據(jù)的時(shí)候,我們有兩種做法:
在元素的數(shù)組插入處,將當(dāng)前數(shù)組切開(kāi),插入元素處之前為一個(gè)鏈表節(jié)點(diǎn),插入元素后為一個(gè)鏈表節(jié)點(diǎn)。將元素插入數(shù)組后,將數(shù)組從中間位置切開(kāi)。
/// <summary> /// 添加元素只會(huì)進(jìn)行塊狀鏈表的分裂 /// </summary> /// <param name="node"></param> /// <param name="num"></param> /// <returns></returns> private BlockLinkNode Add(BlockLinkNode node, int num) { if (node == null) { return node; } else { /* * 第一步:找到指定的節(jié)點(diǎn) */ if (node.list.Count == 0) { node.list.Add(num); total = total + 1; return node; } //下一步:再比較是否應(yīng)該分裂塊 var blockLen = (int)Math.Ceiling(Math.Sqrt(total)) * 2; //如果該節(jié)點(diǎn)的數(shù)組的最后位置值大于插入值,則此時(shí)我們找到了鏈表的插入節(jié)點(diǎn), //或者該節(jié)點(diǎn)的next=null,說(shuō)明是最后一個(gè)節(jié)點(diǎn),此時(shí)也要判斷是否要裂開(kāi) if (node.list[node.list.Count - 1] > num || node.next == null) { node.list.Add(num); //最后進(jìn)行排序下,當(dāng)然可以用插入排序解決,O(N)搞定 node.list = node.list.OrderBy(i => i).ToList(); //如果該數(shù)組里面的個(gè)數(shù)大于2*blockLen,說(shuō)明已經(jīng)過(guò)大了,此時(shí)需要對(duì)半分裂 if (node.list.Count > blockLen) { //先將數(shù)據(jù)插入到數(shù)據(jù)庫(kù) var mid = node.list.Count / 2; //分裂處的前段部分 var firstList = new List<int>(); //分裂后的后段部分 var lastList = new List<int>(); //可以在插入點(diǎn)處分裂,也可以對(duì)半分裂(這里對(duì)半分裂) firstList.AddRange(node.list.Take(mid)); lastList.AddRange(node.list.Skip(mid).Take(node.list.Count - mid)); //開(kāi)始分裂節(jié)點(diǎn),需要新開(kāi)辟一個(gè)新節(jié)點(diǎn) var nNode = new BlockLinkNode(); nNode.list = lastList; nNode.next = node.next; nNode.prev = node; //改變當(dāng)前節(jié)點(diǎn)的next和list node.list = firstList; node.next = nNode; } total = total + 1; return node; } return Add(node.next, num); } }
二、刪除
跟插入道理一樣,既然有裂開(kāi),就有合并,同樣也定義了一個(gè)界限值 √N /2 ,當(dāng)鏈表數(shù)組節(jié)點(diǎn)的數(shù)組個(gè)數(shù)小于這個(gè)界限值的時(shí)候,需要將此節(jié)點(diǎn)和后面的鏈表節(jié)點(diǎn)進(jìn)行合并。
/// <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 { //第一步: 判斷刪除元素是否在該節(jié)點(diǎn)內(nèi) if (node.list.Count > 0 && num >= node.list[0] && num <= node.list[node.list.Count - 1]) { //定義改節(jié)點(diǎn)的目的在于防止remove方法假刪除的情況發(fā)生 var prevcount = node.list.Count; node.list.Remove(num); total = total - (prevcount - node.list.Count); //下一步: 判斷是否需要合并節(jié)點(diǎn) var blockLen = (int)Math.Ceiling(Math.Sqrt(total) / 2); //如果當(dāng)前節(jié)點(diǎn)的數(shù)組個(gè)數(shù)小于 blocklen的話,那么此時(shí)改節(jié)點(diǎn)需要和后一個(gè)節(jié)點(diǎn)進(jìn)行合并 //如果該節(jié)點(diǎn)時(shí)尾節(jié)點(diǎn),則放棄合并 if (node.list.Count < blockLen) { if (node.next != null) { node.list.AddRange(node.next.list); //如果下一個(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為null,則將下下個(gè)節(jié)點(diǎn)的prev賦值 if (node.next.next != null) node.next.next.prev = node; node.next = node.next.next; } else { //最后一個(gè)節(jié)點(diǎn)不需要合并,如果list=0,則直接剔除該節(jié)點(diǎn) if (node.list.Count == 0) { if (node.prev != null) node.prev.next = null; node = null; } } } return node; } return Remove(node.next, num); } }
三、查詢
在理想的情況下,我們都控制在 √N,然后就可以用 √N 的時(shí)間找到區(qū)塊,lg√N 的時(shí)間找到區(qū)塊中的指定值,當(dāng)然也有人在查詢的時(shí)候做 鏈表的合并和分裂,這個(gè)就有點(diǎn)像伸展樹(shù)一樣,在查詢的時(shí)候動(dòng)態(tài)調(diào)整,拼的是均攤情況下的復(fù)雜度。
public string Get(int num) { var blockIndex = 0; var arrIndex = 0; var temp = blockLinkNode; while (temp != null) { //判斷是否在該區(qū)間內(nèi) if (temp.list.Count > 0 && num >= temp.list[0] && num <= temp.list[temp.list.Count - 1]) { arrIndex = temp.list.IndexOf(num); return string.Format("當(dāng)前數(shù)據(jù)在第{0}塊中的{1}個(gè)位置", blockIndex, arrIndex); } blockIndex = blockIndex + 1; temp = temp.next; } return string.Empty; }
好了,CURD 都分析好了,到這里大家應(yīng)該對(duì) 塊狀鏈表有個(gè)大概的認(rèn)識(shí)了吧,這個(gè)代碼是我下午抽閑寫(xiě)的,沒(méi)有仔細(xì)測(cè)試,最后是總的代碼:
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 隨機(jī)刪除150個(gè)元素 //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當(dāng)前要?jiǎng)h除元素:{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() { //初始化節(jié)點(diǎn) blockLinkNode = new BlockLinkNode() { list = new List<int>(), next = null, prev = null }; } /// <summary> /// 定義塊狀鏈表的總長(zhǎng)度 /// </summary> private int total; public class BlockLinkNode { /// <summary> /// 指向前一個(gè)節(jié)點(diǎn)的指針 /// </summary> public BlockLinkNode prev; /// <summary> /// 指向后一個(gè)節(jié)點(diǎn)的指針 /// </summary> public BlockLinkNode next; /// <summary> /// 鏈表中的數(shù)組 /// </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) { //判斷是否在該區(qū)間內(nèi) 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) { //判斷是否在該區(qū)間內(nèi) if (temp.list.Count > 0 && num >= temp.list[0] && num <= temp.list[temp.list.Count - 1]) { arrIndex = temp.list.IndexOf(num); return string.Format("當(dāng)前數(shù)據(jù)在第{0}塊中的{1}個(gè)位置", 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> /// 添加元素只會(huì)進(jìn)行塊狀鏈表的分裂 /// </summary> /// <param name="node"></param> /// <param name="num"></param> /// <returns></returns> private BlockLinkNode Add(BlockLinkNode node, int num) { if (node == null) { return node; } else { /* * 第一步:找到指定的節(jié)點(diǎn) */ if (node.list.Count == 0) { node.list.Add(num); total = total + 1; return node; } //下一步:再比較是否應(yīng)該分裂塊 var blockLen = (int)Math.Ceiling(Math.Sqrt(total)) * 2; //如果該節(jié)點(diǎn)的數(shù)組的最后位置值大于插入值,則此時(shí)我們找到了鏈表的插入節(jié)點(diǎn), //或者該節(jié)點(diǎn)的next=null,說(shuō)明是最后一個(gè)節(jié)點(diǎn),此時(shí)也要判斷是否要裂開(kāi) if (node.list[node.list.Count - 1] > num || node.next == null) { node.list.Add(num); //最后進(jìn)行排序下,當(dāng)然可以用插入排序解決,O(N)搞定 node.list = node.list.OrderBy(i => i).ToList(); //如果該數(shù)組里面的個(gè)數(shù)大于2*blockLen,說(shuō)明已經(jīng)過(guò)大了,此時(shí)需要對(duì)半分裂 if (node.list.Count > blockLen) { //先將數(shù)據(jù)插入到數(shù)據(jù)庫(kù) var mid = node.list.Count / 2; //分裂處的前段部分 var firstList = new List<int>(); //分裂后的后段部分 var lastList = new List<int>(); //可以在插入點(diǎn)處分裂,也可以對(duì)半分裂(這里對(duì)半分裂) firstList.AddRange(node.list.Take(mid)); lastList.AddRange(node.list.Skip(mid).Take(node.list.Count - mid)); //開(kāi)始分裂節(jié)點(diǎn),需要新開(kāi)辟一個(gè)新節(jié)點(diǎn) var nNode = new BlockLinkNode(); nNode.list = lastList; nNode.next = node.next; nNode.prev = node; //改變當(dāng)前節(jié)點(diǎn)的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 { //第一步: 判斷刪除元素是否在該節(jié)點(diǎn)內(nèi) if (node.list.Count > 0 && num >= node.list[0] && num <= node.list[node.list.Count - 1]) { //定義改節(jié)點(diǎn)的目的在于防止remove方法假刪除的情況發(fā)生 var prevcount = node.list.Count; node.list.Remove(num); total = total - (prevcount - node.list.Count); //下一步: 判斷是否需要合并節(jié)點(diǎn) var blockLen = (int)Math.Ceiling(Math.Sqrt(total) / 2); //如果當(dāng)前節(jié)點(diǎn)的數(shù)組個(gè)數(shù)小于 blocklen的話,那么此時(shí)改節(jié)點(diǎn)需要和后一個(gè)節(jié)點(diǎn)進(jìn)行合并 //如果該節(jié)點(diǎn)時(shí)尾節(jié)點(diǎn),則放棄合并 if (node.list.Count < blockLen) { if (node.next != null) { node.list.AddRange(node.next.list); //如果下一個(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為null,則將下下個(gè)節(jié)點(diǎn)的prev賦值 if (node.next.next != null) node.next.next.prev = node; node.next = node.next.next; } else { //最后一個(gè)節(jié)點(diǎn)不需要合并,如果list=0,則直接剔除該節(jié)點(diǎn) if (node.list.Count == 0) { if (node.prev != null) node.prev.next = null; node = null; } } } return node; } return Remove(node.next, num); } } /// <summary> /// 獲取塊狀鏈表中的所有個(gè)數(shù) /// </summary> /// <returns></returns> public int GetCount() { int count = 0; var temp = blockLinkNode; Console.Write("各節(jié)點(diǎn)數(shù)據(jù)個(gè)數(shù)為:"); while (temp != null) { count += temp.list.Count; Console.Write(temp.list.Count + ","); temp = temp.next; } Console.WriteLine("總共有:{0} 個(gè)元素", count); return count; } } }
到此這篇關(guān)于C#實(shí)現(xiàn)塊狀鏈表的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)C# 塊狀鏈表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C#數(shù)據(jù)結(jié)構(gòu)之單鏈表(LinkList)實(shí)例詳解
- C#實(shí)現(xiàn)的簡(jiǎn)單鏈表類(lèi)實(shí)例
- C#雙向鏈表LinkedList排序?qū)崿F(xiàn)方法
- C#定義并實(shí)現(xiàn)單鏈表實(shí)例解析
- C#數(shù)據(jù)結(jié)構(gòu)之雙向鏈表(DbLinkList)實(shí)例詳解
- C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘三 鏈表
- C#數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表的實(shí)例代碼
- C#集合之鏈表的用法
- C#通過(guò)鏈表實(shí)現(xiàn)隊(duì)列的方法
- C#集合本質(zhì)之鏈表的用法詳解
- C#模擬鏈表數(shù)據(jù)結(jié)構(gòu)的實(shí)例解析
- C#實(shí)現(xiàn)基于鏈表的內(nèi)存記事本實(shí)例
相關(guān)文章
c# StringBuilder.Replace 方法 (Char, Char, Int32, Int32)
c# StringBuilder.Replace 方法 (Char, Char, Int32, Int32)...2007-08-08C#?wpf使用DockPanel實(shí)現(xiàn)制作截屏框
做桌面客戶端的時(shí)候有時(shí)需要實(shí)現(xiàn)截屏功能,能夠在界面上框選截屏,本文就來(lái)為大家介紹一下wpf如何使用DockPanel制作截屏框吧,感興趣的可以了解下2023-09-09詳解C#打開(kāi)和關(guān)閉可執(zhí)行文件
這篇文章主要介紹了C#打開(kāi)和關(guān)閉可執(zhí)行文件,以QQ應(yīng)用程序?yàn)槔枰呐笥芽梢詤⒖枷?/div> 2015-12-12C#基于共享內(nèi)存實(shí)現(xiàn)跨進(jìn)程隊(duì)列
進(jìn)程通信一般情況下比較少用,但是也有一些使用場(chǎng)景,有些做視頻傳輸?shù)乃坪鯐?huì)用多進(jìn)程來(lái)實(shí)現(xiàn),還有在子進(jìn)程中調(diào)用特定的庫(kù)來(lái)避免內(nèi)存泄漏,筆者最近也遇到了需要使用多進(jìn)程的場(chǎng)景,本文介紹了C#基于共享內(nèi)存實(shí)現(xiàn)跨進(jìn)程隊(duì)列,需要的朋友可以參考下2024-07-07C#實(shí)現(xiàn)日期操作類(lèi)DateTime的方法示例
C#中日期和時(shí)間操作主要通過(guò)System.DateTime類(lèi)實(shí)現(xiàn),提供了創(chuàng)建、格式化、比較和計(jì)算等功能,下面就來(lái)具體介紹一下,感興趣的可以了解一下2025-03-03c# richtextbox更新大量數(shù)據(jù)不卡死的實(shí)現(xiàn)方式
這篇文章主要介紹了c# richtextbox更新大量數(shù)據(jù)不卡死的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04C# wpf使用ffmpeg命令行實(shí)現(xiàn)錄屏的示例代碼
本文主要介紹了C# wpf使用ffmpeg命令行實(shí)現(xiàn)錄屏的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08最新評(píng)論