实现根据条件删除_常见数据结构的实现(一):跳跃表
知乎的小伙伴們好,這是我在知乎寫的第一篇文章哈。我寫這篇文章的目的主要是和大家分享一些想法,交流學(xué)習(xí)一下。
這系列的文章是分析常見數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),包括跳躍表、二叉堆、前綴樹、紅黑樹等等。。。數(shù)據(jù)結(jié)構(gòu)這門課在學(xué)習(xí)與工作中都非常重要,所以我覺得有必要把自己的想法拿出來和大家分享交流,互相學(xué)習(xí)。
下面就開始正題吧!(第一次寫文章,如果某些地方語言、思路表達不當(dāng)或者有誤,希望大家包容一下吧哈哈)
介紹
鏈表是一種基本的數(shù)據(jù)結(jié)構(gòu),而跳躍表是一種特殊的有序鏈表。
- 跳躍表是由多層有序鏈表組合而成的,最底一層的鏈表保存了所有的數(shù)據(jù),每向上的一層鏈表依次保存了下一層鏈表的部分數(shù)據(jù)。
- 相鄰的兩層鏈表中元素相同的節(jié)點之間存在引用關(guān)系,一般是上層節(jié)點中存在一個指向下層節(jié)點的引用
- 跳躍表的目的在于提高了查詢效率,同時也犧牲了一定的存儲空間。
下面的一張圖是我自己繪制的跳躍表,每層包含了頭節(jié)點并且是單向鏈表:
skip_list.png分析
按照上面的圖,所有節(jié)點包含以下內(nèi)容:存儲的元素,指向同一層中下一個節(jié)點的引用,指向下一層中對應(yīng)節(jié)點的引用。
那么我們可以如下構(gòu)建節(jié)點類和跳躍表類:
//跳躍表的實現(xiàn) //元素有序且不重復(fù) public class SkipList {private Node head;//頂層頭節(jié)點 private int rate;//相鄰兩層元素個數(shù)的比例 private int level;//跳躍表層數(shù) private int length;//底層節(jié)點個數(shù) private int size;//所有層節(jié)點個數(shù)private final boolean order;//true表示正序,false表示逆序 private Random random;//隨機數(shù) private Stack<Node> stack;//保存查詢時遍歷的節(jié)點//節(jié)點類private static class Node {private Comparable comparable;private Node right;//同一層的右邊節(jié)點private Node down;//下一層的對應(yīng)節(jié)點public Node(Comparable comparable) {this.comparable = comparable;this.right = null;this.down = null;}}public SkipList(int rate, int level, boolean order) {this.rate = rate;this.level = level;this.length = 0;this.size = 0;this.order = order;this.random = new Random();this.stack = new Stack<Node>();this.head = new Node(null);//頭節(jié)點的值默認為nullNode temp = head;for (int i = 1; i < level; i++) {temp.down = new Node(null);temp = temp.down;}}}- rate表示相鄰兩層鏈表的元素數(shù)量之比,也就是下一層每添加多少個元素時,就向上一層繼續(xù)添加一個元素。這個值可以由個人決定
- order表示有序鏈表保存元素的順序,對于整型數(shù)據(jù),從前向后,元素從小到大為true,對于字符型數(shù)據(jù),從前向后,元素的字典序依次靠后為true,等等
- random用于添加操作時生成隨機數(shù)的種子
- stack表示查詢操作時保存查詢路徑上某些節(jié)點的棧,Stack類可以自己實現(xiàn)
SkipList類構(gòu)造方法的最后幾句代碼表示初始化幾個頭節(jié)點,其中head指向最上層的頭節(jié)點。我的實現(xiàn)中跳躍表保存的元素不重復(fù),如果想要保存重復(fù)的元素,只需要在原來的基礎(chǔ)上稍作修改即可
查找元素
添加、刪除操作都依賴于查找操作,所以先介紹查找操作
查找操作是自頂向下、從左向右的,也就是先在最上一層鏈表中從左向右查找,然后依次向下層查找,直到最下一層的某個節(jié)點結(jié)束。具體的操作依賴于保存元素的順序,假設(shè)保存的是整型數(shù)據(jù),order為true,那么元素從前向后依次變大:
- 先在最上一層查找小于所指定元素的最右的節(jié)點,將節(jié)點入棧,然后轉(zhuǎn)向下一層
- 在當(dāng)前層繼續(xù)向后查找滿足上述條件的節(jié)點,同樣將其入棧,向下一層繼續(xù)查找,直到查找到最下一層滿足條件的節(jié)點
從上面的分析中可以知道,stack中保存的是查找路徑中每層最右邊的節(jié)點,最底層的那個節(jié)點除外。
舉個例子,在第一張圖中,查找元素6時,查找路徑為head、3、3、3、5、5,stack中保存的元素依次為3、3、5。查找元素10時,查找路徑為head、3、3、7、7、7、8,stack中保存的元素依次為3、7、7。特殊情況下,頭節(jié)點也會入棧
為什么這里設(shè)計成這樣的查找路徑,以及棧中保存查找路徑上每層最右的節(jié)點?主要是在單鏈表的情況下,查找元素時可以統(tǒng)一元素存在和不存在兩種情況,同時添加、刪除元素時方便改變節(jié)點之間的引用關(guān)系。下面貼出查找操作的代碼:
//查詢元素,自頂向下//正序時,返回底層【小于】給定值的最大的節(jié)點,包含頭節(jié)點//逆序時,返回底層【大于】給定值的最小的節(jié)點,包含頭節(jié)點private Node search(Comparable comparable) {stack.clear();Node temp = head;//從頂層開始while (true) {while (temp.right != null) {if (order && temp.right.comparable.compareTo(comparable) >= 0)//正序時查找當(dāng)前層【小于】給定值的最大的節(jié)點break;if (!order && temp.right.comparable.compareTo(comparable) <= 0)//逆序時查找當(dāng)前層【大于】給定值的最小的節(jié)點break;temp = temp.right;}if (temp.down == null)//找到底層的節(jié)點break;stack.push(temp);//stack保存遍歷路徑中每一層最右邊的節(jié)點,除底層外temp = temp.down;//轉(zhuǎn)到下一層}return temp;}如果理解了查找操作的過程,那么上面的代碼就很容易看懂了。查找操作返回的是最下一層滿足那個條件的節(jié)點(有可能是頭節(jié)點)。
添加元素
在我的實現(xiàn)中,跳躍表不保存重復(fù)的元素,所以只有當(dāng)所指定元素不存在時,才執(zhí)行添加操作。
添加操作是自底向上的,并且根據(jù)指定的rate按照一定的概率向上層添加節(jié)點。添加時需要維護同層節(jié)點之間的關(guān)系,同時也要維護當(dāng)前節(jié)點與下一層對應(yīng)節(jié)點的關(guān)系。這里只需要注意的一點是,什么條件下才能向上層繼續(xù)添加?只有當(dāng)隨機數(shù)滿足條件并且當(dāng)前層不是最上一層時,才能繼續(xù)添加。
//添加元素//若元素已存在,則返回,保證無重復(fù)元素public void insert(Comparable comparable) {Node temp = search(comparable);if (temp.right != null && temp.right.comparable.compareTo(comparable) == 0)//元素已存在return;Node node = new Node(comparable);Node other;//根據(jù)隨機數(shù),自底向上添加每層的新節(jié)點while (true) {node.right = temp.right;temp.right = node;//當(dāng)前層添加完畢size++;if (random.nextInt(rate) != 0 || stack.isEmpty())break;//若隨機數(shù)為0且還未到頂層,則向上層添加元素temp = stack.pop();other = node;node = new Node(comparable);node.down = other;}length++;return;}再強調(diào)一下,查找操作返回的是最下一層滿足條件的節(jié)點。注意下,while循環(huán)里面向上層添加單個節(jié)點的過程實際上分解到了兩次循環(huán)中,先維護上下層節(jié)點的關(guān)系,再維護同層節(jié)點的關(guān)系。
刪除元素
當(dāng)所指定元素存在時,刪除操作需要刪除所有層中的對應(yīng)元素。如果某一層中不存在指定的元素,那么上面的所有層中肯定也不會存在,因此可以直接跳出循環(huán),操作結(jié)束。
//刪除元素//若元素不存在,則返回,否則刪除所有層中包含的元素public void delete(Comparable comparable) {Node temp = search(comparable);if (temp.right == null || temp.right.comparable.compareTo(comparable) != 0)//元素不存在return;while (true) {if (temp.right == null || temp.right.comparable.compareTo(comparable) != 0) //當(dāng)前層的元素不存在break;//從底層開始,依次刪除每層的元素temp.right = temp.right.right;size--;if (stack.isEmpty())//到達頂層break;temp = stack.pop();//轉(zhuǎn)到上一層}length--;return;}后記
從上面的討論中可以看到,跳躍表的查找、添加、刪除操作其實并不難理解,這個數(shù)據(jù)結(jié)構(gòu)比較簡單。當(dāng)然這篇文章是我的個人理解,歡迎感興趣的讀者一起來交流,提出建議。后面我會介紹其他一些常用數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),希望大家繼續(xù)關(guān)注哦~
欣賞美可以使人愉悅~~~總結(jié)
以上是生活随笔為你收集整理的实现根据条件删除_常见数据结构的实现(一):跳跃表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 曝徕卡Q3有相位对焦加持 最快五月发布
- 下一篇: 降温效果媲美空调,科学家研发出彩色薄膜