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