python 数据结构一 之 线性表

python数据结构教程第一课
从这里将会正式开始讲解python的一些实用的数据结构,原理加上实例源码。

一、简介

在程序里经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,一组数据中包含的元素个数可能发生变化,也可能会用元素在序列里的位置和顺序,表示实际应用中的某种有意义信息。线性表就是这样一组元素的抽象,其具体实现方式有两种,顺序表和链接表

二、线性表的抽象数据类型(ADT)

线性表的基本操作应当有创建空表、返回长度信息、插入、删除等操作,其基本的ADT如下:

ADT List:
   List(self)           #创建一个新表
   is_empty(self)       #判断self是否是一个空表
   len(self)            #返回表长度
   prepend(self,elem)   #在表头插入元素
   append(self,elem)    #在表尾加入元素
   insert(self,elem,i)  #在表的位置i处插入元素
   del_first(self)      #删除第一个元素
   def_last(self)       #删除最后一个元素
   del(self,i)          #删除第I个元素
   search(self,elem)    #查找元素在表中第一次出现的位置
   forall(self,op)      #对表元素的遍历操作,op操作

三、顺序表的实现##

python内部的tuple与list采用的就是顺序表结构,其不同点在于tuple是固定结构,一旦创建就无法进行改动,而list则支持变动操作,具有上述ADT所描述的全部操作,这里不再具体重写类代码,其主要使用命令如下

list1 = list([1,2,3,4,5])    #创建新表
list1.append(6)              #在尾部添加新元素 6
k = len(list1)               #返回表长度
list1.insert(k,7)            #在位置k插入7
list1.pop()                  #返回并删除尾部元素
print(list1)                 #输出表的全部元素
list2 = list1[2:]            #表的切片操作

顺序表的优势在于O(1)时间的定位元素访问,很多简单的操作效率也比较高,比较麻烦的地方在于,表中间位置元素的插入删除操作,由于元素在顺序表的存储区里连续排列,插入/删除操作可能要移动很多元素,代价很高

四、链接表的实现##

基于链接技术实现的线性表称为链接表或者链表,用链接关系显式表示元素之间的顺序关系,链接表结构的基本思想如下:
1.把表中的元素分别存储在一批独立的存储块(结点)里
2.保证从组成表结构中的任一个结点可找到与其相关的下一个结点
3.在前一结点里用链接的方式显式地记录与下一结点之间的关联

在python里链表的实现有诸多方式和变形,接下来将选取主要的结构进行源码讲解

1.单链表
单链表是最基本也是最常用的链表结构,以下描述了链表的各种方法,包括,插入、排序、删除、融合等

import copy

#单链表结点类
class LNode:            
    def __init__(self, elem,next_=None):
        self.elem = elem
        self.next = next_

#链表位置定位错误
class LinkedListUnderflow(ValueError): 
    pass
        
#单链表类的具体实现
class LList:           
    def __init__(self):               #初始化操作
        self._head = None
        self.num = 0                  #num记录结点数
        
    def is_empty(self):               #空表判定
        return self._head is None
    
    def len(self):                    #返回表长
        return self.num
   
    #定位到链表的第loc个元素 
    def located(self,loc):            
        if (loc > self.num or loc < 1):
            raise LinkedListUnderflow('in located')
        temp = self._head
        i = 1
        if loc == 1:
            return temp
        else:
            while i < loc:
                temp = temp.next
                i += 1
            return temp
        
            
    #在链表的第loc个位置添加元素elem
    def located_add(self,loc,elem):
        temp = self.located(loc)            
        node = LNode(elem)
        if loc == 1:
            node.next = self._head
            self._head = node
        else:
            node.next = temp.next
            temp.next = node
        self.num += 1
    
    #在链表的第loc个位置删除元素    
    def located_del(self,loc):
        temp = self.located(loc)
        if loc == 1:
            self._head = self._head.next
        else:
            temp.next = temp.next.next
        self.num -= 1
           
    #表头插入元素
    def prepend(self,elem):
        self._head = LNode(elem,self._head)
        self.num += 1
    
    #返回并删除表头元素    
    def pop(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop')
        e = self._head.elem
        self._head = self._head.next
        self.num -= 1
        return e
    
    #在表尾添加元素
    def append(self,elem):
        if self._head is None:
            self._head = LNode(elem)
            self.num += 1
            return
        p = self._head
        while p.next is not None:
            p = p.next
        p.next = LNode(elem)
        self.num += 1
    
    #返回并删除表尾元素    
    def pop_last(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop_last')
        p = self._head
        if p.next is None:
            e = p.elem
            self._head  = None
            self.num -= 1
            return e
        while p.next.next is not None:
            p = p.next
        e = p.next.elem
        p.next = None
        self.num -= 1
        return e
    
    #返回表中所有满足pred()操作的元素
    def filter(self,pred):
        p = self._head
        while p is not None:
            if pred(p.elem):
                yield p.elem            
            p = p.next
    
    #输出表中的全部元素        
    def printall(self):
        p = self._head
        while p is not None:
            print(p.elem,end='')
            if p.next is not None:
                print(', ',end='')
            p = p.next
        print('')
    
    #对表中的所有元素执行proc操作
    def for_each(self,proc):
        p = self._head
        while p is not None:
            proc(p.elem)
            p = p.next
    
    #使链表支持iterator操作        
    def elements(self):
        p = self._head
        while p is not None:
            yield p.elem
            p = p.next
    
    #链表倒置
    def rev(self):
        p = None
        while self._head is not  None:
            q = self._head
            self._head = q.next
            q.next = p
            p = q
        self._head = p
     
    #链表从小到大排序    
    def sort(self):
        if self._head is None:
            return
        crt = self._head.next
        while crt is not None:
            x = crt.elem
            p = self._head
            while p is not crt and p.elem <= x:
                p = p.next
            while p is not crt:
                y = p.elem
                p.elem = x
                x = y
                p = p.next
            crt.elem = x
            crt = crt.next
    
    #第二种排序算法
    def sort1(self):
         p = self._head
         if p is None or p.next is None:
             return
         rem = p.next
         p.next = None
         while rem is not None:
             p = self._head
             q = None
             while rem is not None and p.elem <= rem.elem:
                 q = p
                 p = p.next
             if q is None:
                 self._head = rem
             else:
                 q.next = rem
             q = rem
             rem = rem.next
             q.next = p
    
    #第三种排序算法
    def sort2(self):
        list1 = copy.deepcopy(self)
        if list1._head.next is None:
            return
        list1._head.next.next = None
            
        if list1._head.next.elem < list1._head.elem:
            a = list1._head
            list1._head = list1._head.next
            list1._head.next = a
            list1._head.next.next = None
        
        temp = self._head.next.next       
        
        while temp is not None:
            p = list1._head
            q = list1._head.next
            if temp.elem < list1._head.elem:
                a = temp.next
                temp.next = list1._head
                list1._head = temp
                temp = a
                if temp is not None:
                    print(temp.elem)
                    list1.printall()
            elif temp.elem >= list1._head.elem:                        
                while q is not None:
                    if q.elem >= temp.elem:
                        a = temp.next
                        temp.next = q
                        p.next = temp
                        temp = a
                        break
                    elif q.elem < temp.elem:
                        q = q.next
                        p = p.next
                if q is None:
                    p.next = temp
                    a = temp.next
                    temp.next = None
                    temp = a                
        self._head = list1._head

                
    #链表深拷贝操作        
    def deep_copy(self):
        Co = copy.deepcopy(self)
        return Co
             
    #链表相等判断   
    def __eq__(self,List1):
        Co1 = self.deep_copy()
        Co2 = List1.deep_copy()
        Co1.sort()
        Co2.sort()
        temp1 = Co1._head
        temp2 = Co2._head
        while Co1.len() == Co2.len() and temp1 is not None and temp2 is not None and temp1.elem == temp2.elem:
            temp1 = temp1.next
            temp2 = temp2.next
        return temp1 is None and temp2 is None 
    
    #链表按字典序,< 运算函数
    def __lt__(self,other):
        temp1 = self._head
        temp2 = other._head
        while temp1 is not None and temp2 is not None:
            if temp1.elem < temp2.elem:
                return True
            elif temp1.elem > temp2.elem:
                return False
            else:
                temp1 = temp1.next
                temp2 = temp2.next
        if temp1 is None and temp2 is not None:
            return True
        else:
            return False
    
    #链表按字典序,=< 运算函数        
    def __le__(self,other):
        temp1 = self._head
        temp2 = other._head
        while temp1 is not None and temp2 is not None:
            if temp1.elem < temp2.elem:
                return True
            elif temp1.elem > temp2.elem:
                return False
            else:
                temp1 = temp1.next
                temp2 = temp2.next
        if temp1 is None:
            return True
        else:
            return False
    
    #链表按字典序 >= 运算函数           
    def __ge__(self,other):
        temp1 = self._head
        temp2 = other._head
        while temp1 is not None and temp2 is not None:
            if temp1.elem > temp2.elem:
                return True
            elif temp1.elem < temp2.elem:
                return False
            else:
                temp1 = temp1.next
                temp2 = temp2.next
        if temp2 is None:
            return True
        else:
            return False
    
    #链表按字典序,> 运算函数    
    def __gt__(self,other):
        temp1 = self._head
        temp2 = other._head
        while temp1 is not None and temp2 is not None:
            if temp1.elem > temp2.elem:
                return True
            elif temp1.elem < temp2.elem:
                return False
            else:
                temp1 = temp1.next
                temp2 = temp2.next
        if temp2 is None and temp1 is not None:
            return True
        else:
            return False
    
    #链表反向遍历,执行对每个元素执行op操作
    def rev_visit(self,op):
        temp = copy.deepcopy(self)
        temp.rev()
        head = temp._head
        while head is not None:
            op(head.elem)
            head = head.next
   
    #删除表中的elem
    def del_elem(self,elem):
        a = self._head
        b = self._head.next
        if a is None:
            return 
        if a.elem == elem:
            self._head = b
        while b is not None:
            if b.elem == elem:
                a.next = b.next
            a = a.next
            b = b.next       
    
    #删除表中最小元素
    def del_minimal(self):
        temp = copy.deepcopy(self)
        temp.sort()
        elem = temp._head.elem
        self.del_elem(elem)
    
    #删除表中所有满足pred操作的元素    
    def del_if(self,pred):
        temp = self._head
        while temp is not None:
            if pred(temp.elem):
                self.del_elem(temp.elem)
            temp = temp.next
    
    #返回一个字典,字典记录了表中每个元素出现的次数
    def elem_num(self):
        temp = self._head
        adict = dict()
        while temp is not None:
            if temp.elem not in adict:
                adict[temp.elem] = 1
            else:
                adict[temp.elem] += 1
            temp = temp.next
        return adict
    
    #删除链表中出现的重复项,第一次不变
    def del_duplicate(self):
        temp1 = self._head
        temp2 = self._head.next
        adict = self.elem_num()
        
        if adict[temp1.elem] > 1:
            adict[temp1.elem] *= -1
        
        while temp2 is not None:
            if adict[temp2.elem] > 1:
                adict[temp2.elem] *= -1
                temp1 = temp1.next
            elif adict[temp2.elem] < 0:
                temp1.next = temp2.next
            else:
                temp1 = temp1.next
            temp2 = temp2.next
            print(adict)
    
    #两个链表的交叉融合为一个链表
    def interleaving(self,another):
        temp1 = self._head
        temp2 = another._head
        while temp1 is not None and temp2 is not None:
            p = temp1.next
            temp1.next = temp2
            q = temp2.next
            temp2.next = p
            temp1 = p
            temp2 = q
        if temp1 is None:
            p = self._head
            while p.next is not None:
                p = p.next
            p.next = temp1

以上描述了单链表的众多方法,单链表还存在很多别的形态,可以让很多操作变的简洁有效率

2.带尾结点的单链表
单链表对尾部结点的访问效率是十分低下的,需要遍历表中之前的全部结点,当单链表带上尾部指针时,这种操作就会变的有效率很多

#带尾结点的单链表,继承自单链表,支持其的全部属性和方法
class LList1(LList):        
    def __init__(self): #初始化,新添了—rear作为尾结点
        LList.__init__(self)
        self._rear = None
        
    #首部结点插入方法
    def prepend(self,elem):
        self._head = LNode(elem,self._head)
        if self._rear is None:
            self._rear = self._head
     
    #尾部结点方法重写       
    def append(self,elem):
        if self._head is None:
            self._head = LNode(elem,self._head)
            self._rear = self._head
        else:
            self._rear.next = LNode(elem)
            self._rear = self._rear.next
    
    #返回并删除最后一个结点
    def pop_last(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop_last')
        p = self._head
        if p.next is None:
            e = p.elem
            self._head = None
            return e
        while p.next.next is not None:
            p = p.next
        e = p.next.elem
        p.next = None
        self._rear = p
        return e

3.循环单链表
使单链表的尾指针指向首结点,就构成了循环单链表,其与单链表的不同在于,其扫描循环结束的控制判断

class LCList:              #循环单链表
    def __init__(self):
        self._rear = None
    
    #空链表判断
    def is_empty(self):
        return self._rear is None
    
    #前端插入
    def prepend(self,elem):
        p = LNode(elem)
        if self._rear is None:
            p.next = p
            self._rear = p
        else:
            p.next = self._rear.next
            self._rear.next = p
           
    #尾端插入   
    def append(self,elem):
        self.prepend(elem)
        self._rear = self._rear.next
    #尾端返回并删除    
    def pop(self):
        if self._rear is None:
            raise LinkedListUnderflow('in pop of CLList')
        p = self._rear.next
        if self._rear is p:
            self._rear = None
        else: 
            self._rear.next = p.next
        return p.elem
    
    #输出所有结点内容
    def printall(self):
        if self.is_empty():
            return
        p = self._rear.next
        while True:
            print(p.elem,end = " ")
            if p is self._rear:
                break
            p = p.next
    
    #两个链表交叉融合为一个链表
    def interleaving(self,another):
        temp1 = self._rear.next
        temp2 = another._rear.next
        
        while temp1 is not self._rear and temp2 is not another._rear:
            a = temp2.next
            temp2.next = temp1.next
            temp1.next = temp2
            temp2 = a
            temp1 = temp1.next.next
        if temp1 is self._rear:
            while temp2 is not another._rear:
                self.append(temp2.elem)
                temp2 = temp2.next

4.双链表
在单链表中,除了首结点和尾结点外,每个元素不但指向它的下一个结点,还会指向它的上一个结点,双链表支持更简单的反向遍历操作,双链表需要双结点类支持

#双结点类
class DLNode(LNode):
    def __init__(self,elem,prev = None,next_ = None):
        LNode.__init__(self,elem,next_)
        self.prev = prev

#双链表继承自带首尾指针的单链表,不过需要重写添加和删除方法
class DLList(LList1):
    def __init__(self):       #初始化
        LList1.__init__(self)
     
    #使用双向结点前端插入   
    def prepend(self,elem):
        p = DLNode(elem,None,self._head)
        if self._head is None:
            self._rear = p
        else:
            p.next.prev = p
        self._head = p
    
    #首端返回并删除
    def pop(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop of DLList')
        e = self._head.elem
        self._head = self._head.next
        if self._head is not None:
            self._head.prev = None
        return e
    
    #尾端返回并删除
    def pop_last(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop_last of DLList')
        e = self._rear.elem
        self._rear = self._rear.prev
        if self._rear is None:
            self._head = None
        else:
            self._rear.next = None
        return e

5.循环双链表
双链表的尾部首部互指,构成循环双链表

class DCLList():
    def __init__(self):   #双链表类
        self._head = None
        self.__num = 0
    
    #尾端插入   
    def append(self,elem):
        p = DLNode(elem,None,None)
        if self._head is None:
            p.next = p 
            p.prev = p
            self._head = p
        else:
            p.prev = self._head.prev
            p.next = self._head
            self._head.prev.next = p
            self._head.prev = p
        self.__num += 1
    
    #尾部返回并删除
    def pop(self):
        if self._head is None:
            raise LinkedListUnderflow('in pop_last of DCLList')
        elem = self._head.prev.elem
        self._head.prev.prev.next = self._head
        self._head.prev = self._head.prev.prev
        self.__num -= 1
        return elem
       
    #返回长度
    def len(self):
        return self.__num
    
    #链表倒置
    def reverse(self):
        q = self._head
        p = self._head.prev
        n = 1
        while p is not q and n <= self.len()/2:
            t = p.elem
            p.elem = q.elem
            q.elem = t
            q = q.next
            p = p.prev
            n += 1
     
     #链表元素排序       
    def sort(self):
        i = 0
        while i < self.len():
            j = 0
            p = self._head
            while j < self.len()-i-1:
                if p.elem > p.next.elem:
                    t = p.elem
                    p.elem = p.next.elem
                    p.next.elem = t
                j += 1
                p = p.next
            self.printall()
            i += 1
    
    #链表倒置算法2
    def reverse1(self):
        li = DCLList()
        p = self._head.prev
        for i in  range(self.len()):
            li.append(p.elem)
            p = p.prev
            i += 1
        self._head = li._head
    
    #链表排序算法2    
    def sort1(self):
        
        i = 0
        while i < self.len()-1:
            j = 0
            p = self._head.next
            while j < self.len()-i-2:
                if p.elem > p.next.elem:
                    a = p.prev
                    b = p.next.next
                    c = p.next
                    a.next = c
                    c.prev = a
                    c.next = p
                    p.prev = c
                    p.next = b
                    b.prev = p
                else:
                    p = p.next
                j += 1
            i += 1
        i = 0 
        p = self._head.next
        elem = self._head.elem
        while i < self.len()-1:
            if p.elem <= elem and p.next.elem > elem:
                a = self._head
                b = self._head.prev
                c = self._head.next
                b.next = c
                c.prev = b
                a.next = p.next
                p.next.prev = a
                p.next = a
                a.prev = p
                self._head = c
                break
            i += 1
            p = p.next
        if i == self.len()-1:
            self._head = self._head.next
            
    #输出链表元素            
    def printall(self):
        p = self._head
        for i in range(self.len()):
            print(p.elem,end = ' ')
            p = p.next
        print()

以上介绍了链表的众多基本与高级操作,以及链表的各种形态变形,链表的优势在于,表元素之间的顺序由它们所在的结点之间的链接显式表示,因此表结点可以任意安排位置,灵活的调整结构。
同时,为了实现链接表,每个结点都增加了一个链接域,付出了额外的空间代价,链表的位置访问代价很高,需要一个个结点的遍历,使用链表最合理的方式是前端操作和顺序访问

五、线性表的应用—Josephus问题

这里举出一个经典的问题来描述链表的用法
Josephus问题:
假设有n个人围坐一圈,现要求从第k个人开始报数,报到第m个数的人退出。然后从下一个人开始继续报数并按同样的规则退出,直至所有人退出,要求按顺序输出各出列人的编号

方法1.我们可以用list实现算法:

def josephus_A(n,k,m):
    people = list(range(1,n+1))
    
    i = k - 1
    for num in range(n):
        count = 0
        while count < m:
            if people[i] > 0:
                count += 1
            if count == m:
                print(people[i],end = ' ')
                people[i] = 0
            i = (i+1) % n
        if num < n - 1:
            print(',',end = '')
        else:
            print('')
    return

方法2.如果我们该用循环单链表,会发现问题简单了很多:

class Josephus(LCList):   
    def turn(self,m):  
        for i in range(m):
            self._rear = self._rear.next

    def __init__(self,n,k,m):
        LCList.__init__(self)
        for i in range(n):
            self.append(i+1)
        self.turn(k-1)
        while not self.is_empty():
            self.turn(m-1)
            print(self.pop(),end = ('\n' if self.is_empty() else ','))

假设有13个人,从第5个人开始报数,数为6,则两种算法的使用和结果为:
算法1:

josephus_A(13,5,6) 

算法1 输出
算法2:

Josephus(13,5,6)

算法2输出

六、更多资源下载

微信搜索“老和山算法指南”获取更多下载链接与技术交流群
在这里插入图片描述
有问题可以私信博主,点赞关注的一般都会回复,一起努力,谢谢支持。

  • 12
    点赞
  • 2
    评论
  • 17
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值