在資料結構的世界裡,我們會認識各種各樣的資料結構,每一種資料結構都能解決相應領域的問題,每一種資料結構都像
是降龍十八掌中的某一掌,掌掌斃命。。。 當然每個資料結構,有他的優點,必然就有它的缺點,那麼如何創造一種資料結構
來將某兩種資料結構進行揚長避短,那就非常完美了。這樣的資料結構也有很多,比如:雙端佇列,還有就是今天講的 塊狀鍊錶,
我們都知道 陣列 具有 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
二: 插入
剛才也說了,每個鍊錶節點的資料是乙個陣列塊,那麼問題來了,我們是根據什麼將陣列切開呢?總不能將所有的資料都放在乙個
鍊錶的節點吧,那就退化成陣列了,在理想的情況下,為了保持√n的陣列個數,所以我們定了乙個界限2√n,當鍊表中的節點陣列
的個數超過2√n的時候,當下次插入資料的時候,我們有兩種做法:
① 在元素的陣列插入處,將當前陣列切開,插入元素處之前為乙個鍊錶節點,插入元素後為乙個鍊錶節點。
② 將元素插入陣列後,將陣列從中間位置切開。
/// /// 新增元素只會進行塊狀鍊錶的**
///
///
///
///
private blocklinknode add(blocklinknode node, int num)
else
var blocklen = (int)math.ceiling(math.sqrt(total)) * 2;
//如果該節點的陣列的最後位置值大於插入值,則此時我們找到了鍊錶的插入節點,
//或者該節點的next=null,說明是最後乙個節點,此時也要判斷是否要裂開
if (node.list[node.list.count - 1] > num || node.next == null)
total = total + 1;
return node;
}return add(node.next, num);}}
二:刪除
跟插入道理一樣,既然有裂開,就有合併,同樣也定義了乙個界限值√n /2 ,當鍊表陣列節點的陣列個數小於這個界限值
的時候,需要將此節點和後面的鍊錶節點進行合併。
/// /// 從塊狀鍊錶中移除元素,涉及到合併
///
///
///
///
private blocklinknode remove(blocklinknode node, int num)
else
else}}
return node;
}return remove(node.next, num);}}
四: 查詢
在理想的情況下,我們都控制在√n,然後就可以用√n的時間找到區塊,lg√n的時間找到區塊中的指定值,當然也有人在查詢
的時候做 鍊錶的合併和**,這個就有點像伸展樹一樣,在查詢的時候動態調整,拼的是均攤情況下的複雜度。這裡順便提醒你一
下,其實你也可以這麼做。。。
public string get(int num)
塊中的個位置", blockindex, arrindex);
}blockindex = blockindex + 1;
temp = temp.next;
}return string.empty;
}
好了,curd都分析好了,到這裡大家應該對 塊狀鍊錶 有個大概的認識了吧,這個**是我下午抽閒寫的,沒有仔細測試,
最後是總的**:
using system;
using system.collections.generic;
using system.linq;
using system.text;
;
//listlist = new list();
//for (int i = 0; i < 100; i++)
//blocklinklist blocklist = new blocklinklist();
foreach (var item in list)
//var b = blocklist.i***ist(333);
console.writeline(blocklist.get(27208));
#region myregion
隨機刪除150個元素
//for (int i = 0; i < 5000; i++)
//", list[rand]);
// blocklist.remove(list[rand]);
// console.writeline("\n\n");
// if (blocklist.getcount() == 0)
//
//}
#endregion
console.read();}}
public class blocklinklist;}
/// /// 定義塊狀鍊錶的總長度
///
private int total;
public class blocklinknode
/// /// 判斷指定元素是否存在
///
///
///
public bool i***ist(int num)
temp = temp.next;
}return i***ist;
}public string get(int num)
塊中的個位置", blockindex, arrindex);
}blockindex = blockindex + 1;
temp = temp.next;
}return string.empty;
}/// /// 將元素加入到塊狀鍊錶中
///
///
public blocklinknode add(int num)
/// /// 新增元素只會進行塊狀鍊錶的**
///
///
///
///
private blocklinknode add(blocklinknode node, int num)
else
var blocklen = (int)math.ceiling(math.sqrt(total)) * 2;
//如果該節點的陣列的最後位置值大於插入值,則此時我們找到了鍊錶的插入節點,
//或者該節點的next=null,說明是最後乙個節點,此時也要判斷是否要裂開
if (node.list[node.list.count - 1] > num || node.next == null)
total = total + 1;
return node;
}return add(node.next, num);}}
/// /// 從塊狀鍊錶中移除元素
///
///
///
public blocklinknode remove(int num)
/// /// 從塊狀鍊錶中移除元素,涉及到合併
///
///
///
///
private blocklinknode remove(blocklinknode node, int num)
else
else}}
return node;
}return remove(node.next, num);}}
/// /// 獲取塊狀鍊錶中的所有個數
///
///
public int getcount()
console.writeline("總共有: 個元素", count);
return count;}}
}
劍指offer第二十五題
題目描述 輸入乙個複雜鍊錶 每個節點中有節點值,以及兩個指標,乙個指向下乙個節點,另乙個特殊指標指向任意乙個節點 返回結果為複製後複雜鍊錶的head。注意,輸出結果中請不要返回引數中的節點引用,否則判題程式會直接返回空 思路 1 在原來的鍊錶每個節點後面新增乙個節點,該節點下面連線著原來節點的下乙個...
經典演算法題每日演練 第二十四題 梳排序
這篇再看看乙個經典的排序,梳排序,為什麼取名為梳,可能每個梳都有自己的gap吧,大梳子gap大一點,小梳子gap小一點。氣泡排序上我們的選擇是相鄰的兩個數做比較,就是他們的gap為1,其實梳排序提出了不同的觀點,如果將這裡的gap設定為一定的大小,效率反而必gap 1要高效的多。下面我們看看具體思想...
經典演算法題每日演練 第二十四題 梳排序
原文 經典演算法題每日演練 第二十四題 梳排序 這篇再看看乙個經典的排序,梳排序,為什麼取名為梳,可能每個梳都有自己的gap吧,大梳子gap大一點,小梳子gap小一點。氣泡排序上我們的選擇是相鄰的兩個數做比較,就是他們的gap為1,其實梳排序提出了不同的觀點,如果將這裡的gap設定為一定的大小,效率...