日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二叉树 跳表_面试题之跳表

發布時間:2023/12/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二叉树 跳表_面试题之跳表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文主要講解跳表的原理、代碼實現以及與之相關的常見面試題。
跳表本質上是一種查找結構,相比于平衡樹,不僅實現簡單,而且插入、刪除、查找的時間復雜度均為O(logN)。跳表其實就是鏈表,只是對有序的鏈表增加上附加的前進鏈接(增加是以隨機化的方式進行的),所以在列表中的查找可以快速的跳過部分列表從而快速檢索。
由于跳表在Redis的使用,導致面試中經常會被提及,所以深入了解跳表的實現非常必要。

代碼實現

第一種實現方法

這種實現方法構建的跳表整體如下圖所示:

這種實現方式中的SkipNode定義如下所示。

private class SkipNode<K, V> {private K key;private V value;private SkipNode<K, V>[] forward;public SkipNode( K key, V value, int levels ) {this.key = key;this.value = value;this.forward = (SkipNode<K, V>[]) new SkipNode[levels+1];for (int i = 0; i <= levels; i++)this.forward[i] = null;}public K key() { return this.key; }public V value() { return this.value; }······}

SkipNode定義中需要留意就是forward數組,這個數組的大小是隨機的,大小代表這個節點的高度,數組中每個元素代表了這一層的下一個SkipNode。

下面來看insert的代碼實現。

/*** 將新值插入到鏈表中* @param k* @param newValue*/public void insert(K k, V newValue) {int newLevel = randomLevel();// 如果隨機的層大于現在的最大層, 進行層調整if (newLevel > level)adjustHead(newLevel);this.level = newLevel;SkipNode<K, V>[] update = (SkipNode<K, V>[]) new SkipNode[level+1];SkipNode<K, V> x = this.head;// 找尋每一層的插入位置for (int i=level; i>=0; i--) {while((x.forward[i] != null) &&((k.compareTo(x.forward[i].key())) > 0))x = x.forward[i];update[i] = x;}// 創建新節點x = new SkipNode<K, V>(k, newValue, newLevel);// 類似于鏈表插入for (int i=0; i <= newLevel; i++) {x.forward[i] = update[i].forward[i];update[i].forward[i] = x;}this.size++;}

從上面的代碼中可以看出insert主要有幾個步驟:

首先隨機產生層數,創建新節點
每層遍歷,得到新節點在每層插入的前一個節點
逐層插入新節點(類似于鏈表插入)

下面來看find的代碼實現。

public V find(K searchKey) {SkipNode<K, V> x = this.head;// 類似于一個下樓梯的過程for (int i=level; i>=0; i--)while ((x.forward[i] != null) &&(searchKey.compareTo(x.forward[i].key()) > 0))x = x.forward[i];x = x.forward[0];if ((x != null) && (searchKey == x.key()))return x.value();else return null;}

find的過程比較簡單,類似于生活中下樓梯,具體過程見上圖中的紅線所示。

第二種實現方法

這種方式最終創建的跳表如下所示。

SkipListEntry的定義如下。

public class SkipListEntry<K extends Comparable, V> {public K key;public V value;public int pos;public SkipListEntry up, down, left, right;// 構造函數public SkipListEntry(K k, V v) {key = k;value = v;up = down = left = right = null;} }

skipList的初始化操作。定義了頭和尾節點,并且把它們相連接。

public SkipList(){SkipListEntry p1, p2;p1 = new SkipListEntry<K, V>(null, null);p2 = new SkipListEntry<K, V>(null, null);head = p1;tail = p2;p1.right = p2;p2.left = p1;n = 0;h = 0;r = new Random();}

查找操作如下所示。查找操作依然類似于下樓梯。

public SkipListEntry<K, V> findEntry(K k) {SkipListEntry<K, V> p = head;while ( true ) {//首先向右走while ( p.right.key != null &&p.right.key.compareTo(k) <= 0 ) {p = p.right;}// 向下走if ( p.down != null ){p = p.down;}else break; }return(p);}

添加節點的實現如下。

public Object put (K k, V v){SkipListEntry p, q;int i;// 待插入的前一個位置p = findEntry(k);if ( k.equals( p.getKey())) {Object old = p.getValue();p.value = v;return old;}q = new SkipListEntry(k, v);q.left = p;q.right = p.right;p.right.left = q;p.right = q;i = 0; // Current level = 0// 隨機插入while ( r.nextDouble() < 0.5 ){// 當前插入的是第i層if ( i >= h ) {SkipListEntry p1, p2;h = h + 1;p1 = new SkipListEntry(null,null);p2 = new SkipListEntry(null,null);p1.right = p2;p1.down = head;p2.left = p1;p2.down = tail;head.up = p1;tail.up = p2;head = p1;tail = p2;}while ( p.up == null ){p = p.left;}p = p.up;SkipListEntry e = new SkipListEntry(k, null); e.left = p;e.right = p.right;e.down = q;p.right.left = e;p.right = e;q.up = e;q = e; i = i + 1; }n = n + 1;return null;}

和第一種實現方式不同的是,第二種實現方法的插入操作在每一層都需要重新創建節點進行插入,空間浪費。所以推薦第一種實現方法。

常見面試題

redis為什么使用跳表,為什么不用紅黑樹

相比于紅黑樹、平衡二叉樹,跳表不僅查找、插入、刪除時間復雜度都是O(logN),并且實現簡單很多。

跳表數據結構的實現

可以參考第一種實現方法。

參考文章

http://www.mathcs.emory.edu/~cheung/Courses/323/Syllabus/Map/skip-list-impl.htmlhttps://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html

總結

以上是生活随笔為你收集整理的二叉树 跳表_面试题之跳表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。