Python 面向对象编程 + 基本数据结构实现【part 1】:链表、跳表

Python 面向对象编程 + 基本数据结构实现【part 1】:链表、跳表

本文使用的 python 编译器:Spyder 3.3.6

Cpt1. 简单实现

定义对象:类 class (这个定义是否与 C语言中的结构体相似?)

class Test:
	def pr(self):
		self.height = 20

在这个大类里,就添加了 height 这个信息。关于如何使用这个类:

a = Test()

这样就对 a 附上了 Test 这个类的信息:(当然也可以定义空的类。)
如何调用类
注: 在这部中,貌似我并不能直接调用出里面 a.height 的值,只能调用出 a 这个类中,pr 这个子函数的信息:

>>> <__main__.Test object at 0x0000019E8E036E08> <bound method Test.pr of <__main__.Test object at 0x0000019E8E036E08>>

Cpt2. 现实编程中的实例实现步骤:

  1. 创建方法
    1. __new__() 方法
    2. __init__() 方法
    3. 注:两种方法都可以从父类继承,或是直接创建
  2. 类说明:docstring 紧跟在类名行之后(就是自己写的对类进行解释的一个注释)查看类说明,用__doc__来查看。(或者用help(a)
    如:
    在这里插入图片描述

Cpt3. 描述对象的特征

类的属性:

  1. 实例属性:
    1. 一般使用 __init__() 进行创建初始化直接使用。
    2. 直接使用即定义:self.<属性名>
    3. 引用方法 self.<属性名>
      Remark
      • self 指类实例化之后的类本身。
      • 属于同一个类,但是是不同实例,他们的属性值是不相关的(比如 ab属于Test()这个类,我改变 a 中的实例属性值,b 的相应属性不改变)
    class Test:
        def __init__(self):
        	'''这是实例属性'''
            self.a = 0
            self.b = 10
        def info(self):
            print('a:', self.a, 'b:', self.b)
        if __name__ == "__main__":
            clsa = Test()
            clsa.info()
            '''在实例外部可以进行类属性的添加,一般避免这么使用''
            clsa.color = 'red'
            print(clsa.color)#直接用“.”可以调用
    
    输出结果:
    >>>a: 0 b: 10
    >>>red
    
  2. 类属性:属于同一类的不同实例,具有的相同的属性,就是给每个对象附上相同的值(写在__init__()函数的外部)。
    可以在外部进行类属性的修改,修改后,相同类的不同实例的类属性都会改变。
    在这里插入图片描述
  3. 私有属性:__ab,双下划线开头。不能在外部进行修改。但是直接访问 a.__ab 可以访问这个值(python内部用别的变量名存储这个值)
    在这里插入图片描述
  4. 特殊属性(这里不探讨了)
    1. __doc__ 注释
    2. __name__ 名称
    3. __dict__ 保存实例属性所有的实例名与值的一个字典
    4. __module__ 所在模块名
    5. __base__ 父类

Cpt4. 基本数据结构的实现:

① 链表的实现

python 中的链表与 C语言 中的链表形式上是相同的,都是由若干个节点,像一条链连接起来。每个节点包含:1. 当前节点存储的值;2. 下一个节点的位置。

注:链表与数组的不同在于,链表的存储地址不是连续的,就像在一堆彩虹糖(别的占用内存较大的数据结构)的间隙之间,放入几粒大米(链表的节点),因此只能顺着节点顺序访问,而不能抽取其中的一个值。

C语言 中,一般记录的是指针,也就是地址的位置,那么在 python 中,我们可以直接把一个数据结构赋值赋在前一个节点的 next 指针上。他的数据结构如下:

Step1. 首先定义单向链表节点

之前说过,__init__() 是对一个节点的初始化。然后里面如上所说,val 存储值,next 存储下一个节点的地址。为了使用方便,头节点并不像 C语言 中一样,将头结点的值的定义为空。采用 Leetcode 上面经常出现的定义方式:

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None
Step2. 定义链表

因为很多算法题目中,都是预先确定好一个序列,然后我们从一个确定的序列构建链表进行做题。那我这里就写了一个从列表构建链表的函数:

class SingleLinkList(object):
    def __init__(self, node = None):
        self.head = node
    def construct(self, nums):
        if not nums:
            return None
        else:
            for i in range(0, len(nums)):
                if i == 0:
                    cur = ListNode(nums[i])
                    self.head = cur
                else:
                    node = ListNode(nums[i])
                    cur.next = node
                    cur = cur.next
    #这个是遍历并输出整个链表的子函数
    def travel(self):
        cur = self.head
        print("head->", end = "")
        while cur != None:
            print(cur.val, end = "->")
            cur = cur.next
        print("end")

在使用的时候也非常简单,在主程序中先创建 SingleLinkList() 这个类,并直接调用这个类内函数就可以:

if __name__ == "__main__":
	l1 = SingleLinkList()
	nums1 = [1,2,4]
	l1.construct(nums1)

这样我们就有了一个链表,而且在我们使用 l1 的时候,其实是 l1 开头这个链表的初始位置,也就是链表头节点。

Step3. 如何进行外部操作

对于怎么在类的外部添加对这个实例操作:就拿简单的合并两个有序链表来举例(Leetcode 21)。

我们可以先写好题解:(这里直接采用了递归的形式)

class solution(object):
    def mergeTwoLists(self, l1, l2):
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        elif l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2

然后主程序就长这样:在我们调用的时候,调用的是一个链表的头指针,我们的返回值也是一个头指针也就是 ListNode 这个树结构,因此不能直接调用 SingleLinkList() 类中的 travel() 函数,那只能通过找出我们返回值的位置到底在哪个指针上,再进行结果的输出。如下:

if __name__ == "__main__":
    l1 = SingleLinkList()
    l2 = SingleLinkList()
    nums1 = [1,2,4]
    nums2 = [1,3,4]
    l1.construct(nums1)
    l2.construct(nums2)
    Res = solution()
    res = Res.mergeTwoLists(l1.head, l2.head)
    if res == l1.head:
        l1.travel()
    else:
        l2.travel()

因此,整个能够运行的程序如下:

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None
class SingleLinkList(object):
    def __init__(self, node = None):
        self.head = node
    def construct(self, nums):
        if not nums:
            return None
        else:
            for i in range(0, len(nums)):
                if i == 0:
                    cur = ListNode(nums[i])
                    self.head = cur
                else:
                    node = ListNode(nums[i])
                    cur.next = node
                    cur = cur.next
    def travel(self):
        cur = self.head
        print("head->", end = "")
        while cur != None:
            print(cur.val, end = "->")
            cur = cur.next
        print("end")
class solution(object):
    def mergeTwoLists(self, l1, l2):
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        elif l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2
if __name__ == "__main__":
    l1 = SingleLinkList()
    l2 = SingleLinkList()
    nums1 = [1,2,4]
    nums2 = [1,3,4]
    l1.construct(nums1)
    l2.construct(nums2)
    Res = solution()
    res = Res.mergeTwoLists(l1.head, l2.head)
    if res == l1.head:
        l1.travel()
    else:
        l2.travel()

以上

② 跳表的实现

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

实现代码:

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

# 引入几个全局变量
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 相对简单一点,只需要逐层遍历即可。通过此,就可以完整的看到自己的链表实现的是否正确。

一般的操作函数search, add, erase

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

search()函数

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

add()函数

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

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

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

erase()函数

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

完整代码如下:(包括主函数)
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)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值