python 数据结构一 之 线性表

来源:互联网 发布:哪里有淘宝店铺出租 编辑:程序博客网 时间:2024/06/05 16:42

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

一、简介
二、线性表的抽象数据类型
三、顺序表的实现
四、链接表的实现
1.单链表
2.带尾指针的单链表
3.循环单链表
4.双链表
5.循环双链表
五、线性表的应用—Josephus问题
1.顺序表解法
2.循环单链表解法

一、简介

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

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

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

ADT ListList(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)              #在尾部添加新元素 6k = len(list1)               #返回表长度list1.insert(k,7)            #在位置k插入7list1.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输出

原创粉丝点击