Leetcode 1206 设计跳表

Leetcode 1206 设计跳表

这道题提交的人数和题解特别少,我就稍微贡献一份力吧。

跳表是链表数据结构的一种拓展,能够在 O ( log N ) O(\log N) 时间复杂度下对一个有序链表进行访问(包括插入删除)。每一层上的节点通过一定概率向上构造一个节点,也就是 P r ( i i 1 ) = 1 Pr(i层有节点|i-1层有节点)=1 P r ( i 1 i ) = p Pr(i-1层有节点|i层有节点)=p p p 是人为定义的,为了方便起见,这里设置为 p = 0.5 p=0.5

题干:

不使用任何库函数,设计一个跳表。

跳表是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。

例如,一个跳表包含 [30, 40, 50, 60, 70, 90],然后增加 80、45 到跳表中,以下图的方式操作:

在这里插入图片描述

跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。

在本题中,你的设计应该要包含这些函数:

bool search(int target) : 返回target是否存在于跳表中。
void add(int num): 插入一个元素到跳表。
bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回False. 如果存在多个 num ,删除其中任意一个即可,并输出True

了解更多 : https://en.wikipedia.org/wiki/Skip_list

注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。

样例:

Skiplist skiplist = new Skiplist();

skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
skiplist.search(0);   // 返回 false
skiplist.add(4);
skiplist.search(1);   // 返回 true
skiplist.erase(0);    // 返回 false,0 不在跳表中
skiplist.erase(1);    // 返回 true
skiplist.search(1);   // 返回 false,1 已被擦除

约束条件:

0 <= num, target <= 20000
最多调用 50000 次 search, add, 以及 erase操作。

实现代码:

首先定义单个节点:包含值、右指针、下指针三个值。

# 引入几个全局变量
Maxlevel = 16
th = [pow(0.5, n) for n in range(Maxlevel, 0, -1)]
import random
# 构造单个节点
class Node(object):
        def __init__(self, x = "inf"):
            self.val = x
            self.next = None
            self.below = None

然后对整条链进行构造:因为不同层的头节点是始终存在的,因此将其通过一条向下的链,连接起来,这也就是我们的初始化步骤。

class Skiplist(object):
    def __init__(self):
        self.head = Node("inf")
        cur = self.head
        for i in range(0, Maxlevel):
            cur.below = Node("inf")
            cur = cur.below

补充代码:给定升序序列的列表,构造跳表construct();遍历跳表函数travel

从序列构造跳表construct()这是我花时间花的最久的步骤,因为在构建时比较复杂,同时需要向下、向右构建链接。

遍历跳表 travel 相对简单一点,只需要逐层遍历即可。通过此,就可以完整的看到自己的链表实现的是否正确。

class Skiplist(object):
    def __init__(self):
        self.head = Node("inf")
        cur = self.head
        for i in range(0, Maxlevel):
            cur.below = Node("inf")
            cur = cur.below
            
    def construct(self, lst):
        if not lst:
            return None
        else:
            # 初始化头节点
            self.head = Node("inf")
            cur = self.head
            heads = [None] * Maxlevel
            for i in range(0, Maxlevel):
                cur.below = Node("inf")
                # 记录下每层的头节点
                heads[i] = cur
                cur = cur.below
            # 开始从上往下加入数字
            curs = heads
            for item in lst:
                pr = random.random()
                lvnum = 0
                flag = 0
                for i in range(0, Maxlevel):
                    if (i < Maxlevel - 1) & (pr > th[i]):
						#记录在第几层
                        lvnum = lvnum + 1
                    else:
                        if flag == 0:
                            temp = i
                            flag = 1
                        curs[i].next = Node(item)
                        curs[i] = curs[i].next
                # 添加向下的链接
                for j in range(temp, Maxlevel - 1):
                    curs[j].below = curs[j+1]
            return self.head
	def travel(self):
        lv = self.head
        lvnum = 0
        while lv:
            cur = lv
            print("Level", lvnum, end = ": ")
            while cur:
                print(cur.val, end = "->")
                cur = cur.next
            print("end")
            lv = lv.below
            if not lv.below:
                break
            lvnum = lvnum + 1

题目的要求:search()add()erase()函数的实现:

为什么要写之前那个按列表构造的函数呢?答案来了,因为底下这些小函数用到的思路与那个的思路完全一致。

search()函数

寻找,在每一层上找,如果找到了,输出True;如果右边节点比target大,那么如果本节点有下一层,就往下一层找,如果没有下一层,那就说明寻找完成,没有发现该节点,即条表中不存在该节点。

add()函数

search() 函数一样,在每一层搜索到需要插入的位置,将所有的插入位置的指针写入一个列表curs中。然后我们只需要对列表中的点进行操作就可以了:

  1. 先生成随机数,看需要从第几层开始插入。
  2. 如果当前指针没有后继
    1. 如果不需要插入,则往下走
    2. 如果需要插入,建立一个节点,建立起链接,并更新列表curs
  3. 如果有后继指针,则如最简单的插入法,插入,并更新列表curs

最后,建立向下的指针,这里需要注意:我们只能对更新过的指针建立向下链接。所以只需要判断当前这个指针的值是否为我们插入的值就可以了。

erase()函数

先调用search()函数,如果不在跳表中,直接输出False,如果在条表中,如add()函数中,先寻址,然后每层都删除就可。

函数代码如下:

	def search(self, target):
        """
        :type target: int
        :rtype: bool
        """
        cur = self.head
        while cur:
            while cur.next:
                if cur.next.val > target:
                    break
                elif cur.next.val == target:
                    return True
                else:
                    cur = cur.next
            cur = cur.below
        return False

    def add(self, num):
        """
        :type num: int
        :rtype: None
        """
        cur = self.head
        pos = []
        while True:
            if cur:
                if cur.next:
                    if cur.next.val < num:
                        cur = cur.next
                    else:
                        pos.append(cur)
                        if cur.below:
                            cur = cur.below
                        else:
                            break
                else:
                    pos.append(cur)
                    cur = cur.below
                    if not cur:
                        break
        pr = random.random()
        for i in range(0, Maxlevel):
            if (i < Maxlevel - 1) & (pr > th[i]):
                continue
            else:
                if pos[i].next == None:
                    pos[i].next = Node(num)
                    pos[i] = pos[i].next
                else:
                    temp = pos[i].next
                    pos[i].next = Node(num)
                    pos[i] = pos[i].next
                    pos[i].next = temp
        for i in range(0, Maxlevel-1):
            if pos[i].val == num:
                pos[i].below = pos[i+1]
        
    def erase(self, num):
        """
        :type num: int
        :rtype: bool
        """
        if not self.search(num):
            return False
        else:
            cur = self.head
            pos = []
            while True:
                if cur:
                    if cur.next:
                        if cur.next.val < num:
                            cur = cur.next
                        else:
                            pos.append(cur)
                            if cur.below:
                                cur = cur.below
                            else:
                                break
                    else:
                        pos.append(cur)
                        cur = cur.below
                        if not cur:
                            break
            for i in range(0, Maxlevel):
                if pos[i].next:
                    if pos[i].next.val != num:
                        continue
                    else:
                        pos[i].next = pos[i].next.next
        return True

完整代码如下:(包括主函数)

Maxlevel = 16
th = [pow(0.5, n) for n in range(Maxlevel, 0, -1)]
import random

class Node(object):
        def __init__(self, x = "inf"):
            self.val = x
            self.next = None
            self.below = None
            
class Skiplist(object):
    def __init__(self):
        self.head = Node("inf")
        cur = self.head
        for i in range(0, Maxlevel):
            cur.below = Node("inf")
            cur = cur.below
            
    def construct(self, lst):
        if not lst:
            return None
        else:
            # 初始化头节点
            self.head = Node("inf")
            cur = self.head
            heads = [None] * Maxlevel
            for i in range(0, Maxlevel):
                cur.below = Node("inf")
                # 记录下每层的头节点
                heads[i] = cur
                cur = cur.below
            # 开始从上往下加入数字
            curs = heads
            for item in lst:
                pr = random.random()
                lvnum = 0
                flag = 0
                for i in range(0, Maxlevel):
                    if (i < Maxlevel - 1) & (pr > th[i]):
                        lvnum = lvnum + 1
                    else:
                        if flag == 0:
                            temp = i
                            flag = 1
                        curs[i].next = Node(item)
                        curs[i] = curs[i].next
                # 添加向下的链接
                for j in range(temp, Maxlevel - 1):
                    curs[j].below = curs[j+1]
            return self.head
    
    def travel(self):
        lv = self.head
        lvnum = 0
        while lv:
            cur = lv
            print("Level", lvnum, end = ": ")
            while cur:
                print(cur.val, end = "->")
                cur = cur.next
            print("end")
            lv = lv.below
            if not lv.below:
                break
            lvnum = lvnum + 1
    
    def search(self, target):
        """
        :type target: int
        :rtype: bool
        """
        cur = self.head
        while cur:
            while cur.next:
                if cur.next.val > target:
                    break
                elif cur.next.val == target:
                    return True
                else:
                    cur = cur.next
            cur = cur.below
        return False

    def add(self, num):
        """
        :type num: int
        :rtype: None
        """
        cur = self.head
        pos = []
        while True:
            if cur:
                if cur.next:
                    if cur.next.val < num:
                        cur = cur.next
                    else:
                        pos.append(cur)
                        if cur.below:
                            cur = cur.below
                        else:
                            break
                else:
                    pos.append(cur)
                    cur = cur.below
                    if not cur:
                        break
        pr = random.random()
        for i in range(0, Maxlevel):
            if (i < Maxlevel - 1) & (pr > th[i]):
                continue
            else:
                if pos[i].next == None:
                    pos[i].next = Node(num)
                    pos[i] = pos[i].next
                else:
                    temp = pos[i].next
                    pos[i].next = Node(num)
                    pos[i] = pos[i].next
                    pos[i].next = temp
        for i in range(0, Maxlevel-1):
            if pos[i].val == num:
                pos[i].below = pos[i+1]
        
    def erase(self, num):
        """
        :type num: int
        :rtype: bool
        """
        if not self.search(num):
            return False
        else:
            cur = self.head
            pos = []
            while True:
                if cur:
                    if cur.next:
                        if cur.next.val < num:
                            cur = cur.next
                        else:
                            pos.append(cur)
                            if cur.below:
                                cur = cur.below
                            else:
                                break
                    else:
                        pos.append(cur)
                        cur = cur.below
                        if not cur:
                            break
            for i in range(0, Maxlevel):
                if pos[i].next:
                    if pos[i].next.val != num:
                        continue
                    else:
                        pos[i].next = pos[i].next.next
        return True
        
        
# Your Skiplist object will be instantiated and called as such:
if __name__ == "__main__":
    obj = Skiplist()
    
    obj.add(1)
    obj.add(2)
    obj.add(3)
    obj.search(0)
    obj.add(4)
    obj.search(1)
    obj.erase(0)
    obj.erase(1)
    obj.search(1)
    
    lst = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
    obj.construct(lst)
    target = 10
    param_1 = obj.search(target)
    print("param_1 =", param_1)    
    num = 65
    obj.add(num)    
    param_2 = obj.search(num)
    print("param_2 =", param_2)    
    param_3 = obj.erase(num)
    obj.travel()
    param_4 = obj.search(num)
    print("param_4 =", param_4)

提交结果:

执行时间:392ms,超过53.33%

内存消耗:23.1MB,超过100%

在这里插入图片描述

发布了12 篇原创文章 · 获赞 6 · 访问量 608

猜你喜欢

转载自blog.csdn.net/weixin_44618103/article/details/104144045
今日推荐