zoukankan      html  css  js  c++  java
  • python数据结构之链表(一)

    2020-07-09更新

    细细琢磨了一下以前的这篇文章,感觉这样不太能体现链表的精髓,要想真的想深入研究链表这种数据结构,在没有指针的语言中,还是应该用静态链表来模拟真正链表比较好。

    对于静态链表,个人认为要先想想下面几点:

    • 静态链表的存储结构是什么?
    • 没有指针,怎么来模拟指针?怎么模拟C语言中地址的概念
    •  怎么去模拟内存管理?

            OK, 先来聊聊第1、2点,静态链表在没有指针的语言中用数组来实现,用一组地址连续的存储单元来存放数据(第一次了解到这里,我也是懵圈的,数组???这不就是顺序表吗,怎么和链表扯上关系?),有意思的就来了,我们就用数组的下标来代替地址吧!!!对,要学会静态链表,你就先要把数组看做一个内存空间,数组下标就是这个空间的地址。C有指针就相当于可以在整个内存空间的海洋里遨游,而静态链表就是只能在自己搭建的舞台上起舞。

            当你认识到数组来模拟一片内存空间,下标就是地址的时候,我们就要来解决实际问题了,也就是怎么模拟内存的管理?

            一、肯定是定义节点item类,其中data保存数据,next保存下一个节点的下标(也可以理解为下一个的位置或者地址),静态链表类SLinkList,在初始化函数__init__里创建一个size+1大小的列表link(因为我的设计是将0节点设为头结点,不保存数据,用来记录链表长度length,链表尾部rear等信息)。

            现在内存空间创建好了,那么问题又来了,我们怎么知道哪些节点是链表里面的,哪些节点是空闲的?在这里我想到了一个比较巧妙的方法,不用另外创建一个数组去存储空闲空间信息。因为初始化的时候所有的空间都是空闲的,我们可以先将所有的空闲节点连起来(也就是第i个节点保存第i+1个节点的位置),然后头结点用一个变量space指向第一个空闲空间的位置,这样我们就保持space永远指向一个空闲节点的位置,即可随时知道那个位置是空闲的了。代码如下,通过代码可以比较直观的理解我的意思。

     1 class item (object):
     2     def __init__(self, data):
     3         self.data = data
     4         self.next = None
     5 
     6 class SLinkList(object):
     7     '''
     8     静态链表,就是用数组来模拟链表,那么数组的下标就当做是节点的地址,
     9     这个概念是静态链表的核心
    10     '''
    11     def __init__(self, size = 100):
    12         '''
    13         初始化主要是用于初始化链表的大小,而非创建链表
    14         '''
    15         self.link = [item(None) for i in range(size + 1)]    # 申请size大小的节点空间[0,1,2,...,size],其中下标0的节点作为头结点
    16         self.link[0].next = None    # 表示空表
    17         self.link[0].space = 1   # 指向第一个节点,因为初始化时第一个节点为空闲节点
    18 
    19         i = 1
    20         while i < size:
    21             self.link[i].next = i+1    # 利用空闲节点连成一个新的表,并且头结点的space始终指向下一个空闲的节点
    22             i += 1
    23 
    24         self.link[i].next = None    # 空闲表尾指向None
    25 
    26         self.length = 0    # 链表长度
    27         self.rear = 0    # 表尾指针

             二、空闲空间已经有了,那么要怎么管理这片空间呢?C语言中新建一个节点会先用malloc函数申请一个内存空间,删除一个节点我们会用free函数来释放内存,在静态链表中我们需要自己实现,因为之前头结点space永远都会指向一个空闲节点的位置,所有我设计的malloc_SL函数,直接从space获取一个空闲节点的位置,然后space再指向下一个空闲节点位置即可,而释放空间函数free_SL同理,先将待释放的点保存space指向的空闲位置,再将space指向该点即可模拟内存回收的过程。代码如下:

     1     def Malloc_SL(self):
     2         '''
     3         类似于C中malloc函数申请空间,返回空闲节点的下标
     4         '''
     5         i = self.link[0].space
     6         if self.link[0].space:
     7             self.link[0].space = self.link[i].next
     8 
     9         return i
    10 
    11     def Free_SL(self, k):
    12         '''
    13         释放空间,并返回下标k节点的值
    14         '''
    15         self.link[k].data = None
    16         self.link[k].next = self.link[0].space
    17         self.link[0].space = k

            三、完成上面两点,静态链表的核心基本就说完了,其他的增删改查操作和c中的链表就差不多了,再提一次就是,静态链表的数组下标就相当于位置,地址!这里我再介绍一下的往链表末尾添加节点的操作函数Append(),流程就是先用Malloc_SL函数申请一个空间节点的位置,然后在该位置添加数据,next指向None,成为新的表尾,再将表尾指针指向该新加入的节点,表长length + 1,代码如下:

     1     def Append(self, data):
     2         '''往链表表尾添加元素, 并返回新添加元素的下标'''
     3         node_index = self.Malloc_SL()
     4 
     5         if not node_index:
     6             print("Append: NO SPACE!")
     7             return False
     8         
     9         self.link[node_index].data = data
    10         self.link[node_index].next = None
    11         self.link[self.rear].next = node_index
    12         self.rear = node_index
    13         self.length += 1
    14         return node_index

            四、最后我实现了几个链表的操作,其他操作有读者自己去琢磨:

    • CreateSLinkList_R---->用尾插法插入元素,创建一个链表
    • CreateSLinkList_F---->用头插法插入元素,创建一个链表
    • DeleteElement--------->根据数据删除第一个匹配到的节点
    • InsertBefore------------>在第k个节点前插入新节点
    • Walk--------------------->遍历整个链表,并输出数据
    • Detail-------------------->输出各节点的详细信息

    全部代码如下:

      1 class item (object):
      2     def __init__(self, data):
      3         self.data = data
      4         self.next = None
      5 
      6 class SLinkList(object):
      7     '''
      8     静态链表,就是用数组来模拟链表,那么数组的下标就当做是节点的地址,
      9     这个概念是静态链表的核心
     10     '''
     11     def __init__(self, size = 100):
     12         '''
     13         初始化主要是用于初始化链表的大小,而非创建链表
     14         '''
     15         self.link = [item(None) for i in range(size + 1)]    # 申请size大小的节点空间[0,1,2,...,size],其中下标0的节点作为头结点
     16         self.link[0].next = None    # 表示空表
     17         self.link[0].space = 1   # 指向第一个节点,因为初始化时第一个节点为空闲节点
     18 
     19         i = 1
     20         while i < size:
     21             self.link[i].next = i+1    # 利用空闲节点连成一个新的表,并且头结点的space始终指向下一个空闲的节点
     22             i += 1
     23 
     24         self.link[i].next = None    # 空闲表尾指向None
     25 
     26         self.length = 0    # 链表长度
     27         self.rear = 0    # 表尾指针
     28 
     29     def CreateSLinkList_R(self, data):
     30         '''
     31         用尾插法创建链表
     32         '''
     33         if self.length > 0:     # 非空表无需创建
     34             print("THIS SLINKLIST IS NOT NULL")
     35             return
     36             
     37         for each in data:
     38            if not self.Append(each):
     39                print("CreateR: NO SPACE!")
     40                return
     41 
     42 
     43     def CreateSLinkList_F(self, data):
     44         '''
     45         用头插法创建链表
     46         '''
     47         if self.length > 0:     # 非空表无需创建
     48             print("THIS SLINKLIST IS NOT NULL")
     49             return
     50 
     51         self.Append(data[0])    # 先添加第一个节点
     52         for each in data[1:]:    # 在第一个位置前插入
     53             if self.InsertBefore(each, 1) == 'NS':
     54                 print("CreateF: NO SPACE!")
     55 
     56 
     57     def LocateElement(self, data):
     58         '''
     59         定位第一个与data值相同的节点,并返回该节点的下标
     60         '''
     61         i = self.link[0].next
     62         while i and self.link[i] != data:
     63             i = self.link[i].next
     64         return i
     65 
     66     def Malloc_SL(self):
     67         '''
     68         类似于C中malloc函数申请空间,返回空闲节点的下标
     69         '''
     70         i = self.link[0].space
     71         if self.link[0].space:
     72             self.link[0].space = self.link[i].next
     73 
     74         return i
     75 
     76     def Free_SL(self, k):
     77         '''
     78         释放空间,并返回下标k节点的值
     79         '''
     80         self.link[k].data = None
     81         self.link[k].next = self.link[0].space
     82         self.link[0].space = k
     83 
     84     def DeleteElement(self, data):
     85         '''
     86         删除第一个匹配到的节点,并返回删除节点的下标
     87         '''
     88         prior = index = 0
     89         while index != None and self.link[index].data != data:
     90             prior = index
     91             index = self.link[index].next
     92 
     93         if not index:
     94             print("DELETE:	Not Found!")
     95             return False
     96 
     97         if not self.link[index].next:    # 判断是否是删除表尾元素,是则改变表尾指针
     98             self.rear = prior
     99 
    100         self.link[prior].next = self.link[index].next
    101         self.length -= 1
    102         self.Free_SL(index)
    103         return index
    104 
    105     def InsertBefore(self, data, k):
    106         '''
    107         在第k个元素前插入元素, 并返回新节点下标
    108         '''
    109         if self.link[0].space == None:    # 表满
    110             print("no space!!!")
    111             return "NS"
    112 
    113         if k <= 0 or k > self.length:  # 超出当前链表可插入范围
    114             print("out of range!!!")
    115             return "OFR"
    116 
    117         count = 0
    118         index = 0
    119         while count < k - 1 :    # 找到第k-1个节点,直接在改节点后插入新的元素 
    120             index = self.link[index].next
    121             count += 1
    122 
    123         node_index = self.Malloc_SL()    # 申请一个新的节点
    124         self.link[node_index].data = data    # 写入数据
    125         self.link[node_index].next = self.link[index].next    # 将新节点的next指向第k个节点
    126         self.link[index].next = node_index    # 将第k-1个节点的next指向新节点node_index
    127         self.length += 1
    128         return node_index
    129 
    130     def Append(self, data):
    131         '''往链表表尾添加元素, 并返回新添加元素的下标'''
    132         node_index = self.Malloc_SL()
    133 
    134         if not node_index:
    135             print("Append: NO SPACE!")
    136             return False
    137         
    138         self.link[node_index].data = data
    139         self.link[node_index].next = None
    140         self.link[self.rear].next = node_index
    141         self.rear = node_index
    142         self.length += 1
    143         return node_index
    144 
    145     def Walk(self):
    146         '''打印链表中所有元素'''
    147         print("WALK:	", end = '')
    148         index = self.link[0].next
    149         while index:
    150             print(self.link[index].data, end = '')
    151             index = self.link[index].next
    152         print("")
    153 
    154     def Detail(self):
    155         '''输出链表中各元素的详细信息'''
    156         print("
    DETAIL:")
    157         index = self.link[0].next
    158         count = 1
    159         while index:
    160             print("SN:	%d	DATA:	%s	ADDR:	%d	NEXT:	%s" % 
    161                 (count, self.link[index].data, index, str(self.link[index].next)))
    162             index = self.link[index].next
    163             count += 1
    164 
    165         print("Length:	%d
    Rear:	%d
    " % (self.length, self.rear))
    166 
    167 
    168 if __name__ == '__main__':
    169     SL = SLinkList()
    170     while True:
    171         print()
    172         opt = input("请选择创建链表的方式: 1. 尾插法 2. 头插法
    OPTION:	")
    173         if opt == '1':
    174             data_str = input("INPUT DATA:	")
    175             SL.CreateSLinkList_R(data_str)
    176             break
    177         elif opt == '2':
    178             data_str = input("INPUT DATA:	")
    179             SL.CreateSLinkList_F(data_str)
    180             break
    181         else:
    182             print("请做出正确选择!")
    183             continue
    184 
    185     while True:
    186         opt = input("
    请选择操作: 1. 插入数据 2. 删除数据 3. 遍历链表 4. 查看链表详细结构 5. 在链表末尾添加数据 6. 退出程序
    OPTION:	")
    187         if opt == '1':
    188             data_str = input("请输入插入数据:	")
    189             pos = input("请输入插入位置:	")
    190             node_addr = SL.InsertBefore(data_str[0], int(pos))
    191             if node_addr != 'NS' and node_addr != 'OFR':
    192                 print("成功删除元素%s,元素地址为:%d" % (data_str[0], node_addr))
    193             else:
    194                 print("INSERT ERROR!!!")
    195 
    196         elif opt == '2':
    197             data_str = input("请输入删除数据:	")
    198             data_pos = SL.DeleteElement(data_str[0])
    199             if data_pos:
    200                 print("成功删除元素%s,元素地址为:%d" % (data_str[0],data_pos))
    201 
    202         elif opt == '3':
    203             print('')
    204             SL.Walk()
    205         elif opt == '4':
    206             SL.Detail()
    207         elif opt == '5':
    208             data_str = input("请输入数据:	")
    209             node_addr = SL.Append(data_str[0])
    210             print("成功添加元素:	%s	元素地址为:	%d" % (data_str[0], node_addr))
    211         elif opt == '6':
    212             break
    213         else:
    214             print("请做出正确选择!")
    View Code

    下面是运行测试结果:

    1. 尾插法保存    123456789(注意walk结果)

    2. 头插法保存    123456789(注意walk结果)

     3. 经过一系列增删操作后的链表结构,可以看出静态链表较好的模拟了链表的动态分配内存的特点,不是按数组下标来顺序存储数据:

     以上就是静态链表的基本内容,再留下一些问题让读者思考一下:

    1. 用Python语言怎么修改可以让链表的空间动态增加?这样就成了动态链表了

    2. Python用静态链表来实现链表结构和用多重对象的嵌套来实现链表结构哪种方法好点?为什么?(我也在琢磨这个事情)

    这里以下是旧文章 ↓↓↓


    数据结构是计算机科学必须掌握的一门学问,之前很多的教材都是用C语言实现链表,因为c有指针,可以很方便的控制内存,很方便就实现链表,其他的语言,则没那么方便,有很多都是用模拟链表,不过这次,我不是用模拟链表来实现,因为python是动态语言,可以直接把对象赋值给新的变量。

    好了,在说我用python实现前,先简单说说链表吧。在我们存储一大波数据时,我们很多时候是使用数组,但是当我们执行插入操作的时候就是非常麻烦,看下面的例子,有一堆数据1,2,3,5,6,7我们要在3和5之间插入4,如果用数组,我们会怎么做?当然是将5之后的数据往后退一位,然后再插入4,这样非常麻烦,但是如果用链表,我就直接在3和5之间插入4就行,听着就很方便。

    那么链表的结构是怎么样的呢?顾名思义,链表当然像锁链一样,由一节节节点连在一起,组成一条数据链。

    链表的节点的结构如下:

    data next

    data为自定义的数据,next为下一个节点的地址。

    链表的结构为,head保存首位节点的地址:

    接下来我们来用python实现链表

    python实现链表

    首先,定义节点类Node:

    class Node:
        '''
        data: 节点保存的数据
        _next: 保存下一个节点对象
        '''
        def __init__(self, data, pnext=None):
            self.data = data
            self._next = pnext
    
        def __repr__(self):
            '''
    用来定义Node的字符输出, print为输出data
    ''' return str(self.data)

    然后,定义链表类:

    链表要包括:

    属性:

    链表头:head

    链表长度:length

    方法:

    判断是否为空: isEmpty()

    def isEmpty(self):
        return (self.length == 0

    增加一个节点(在链表尾添加): append()

    def append(self, dataOrNode):
        item = None
        if isinstance(dataOrNode, Node):
            item = dataOrNode
        else:
            item = Node(dataOrNode)
    
        if not self.head:
            self.head = item
            self.length += 1
    
        else:
            node = self.head
            while node._next:
                node = node._next
            node._next = item
            self.length += 1

    删除一个节点: delete()

    #删除一个节点之后记得要把链表长度减一
    def delete(self, index):
        if self.isEmpty():
            print "this chain table is empty."
            return
    
        if index < 0 or index >= self.length:
            print 'error: out of index'
            return
        #要注意删除第一个节点的情况
        #如果有空的头节点就不用这样
        #但是我不喜欢弄头节点
        if index == 0:
            self.head = self.head._next
            self.length -= 1
            return
    
        #prev为保存前导节点
        #node为保存当前节点
        #当j与index相等时就
        #相当于找到要删除的节点
        j = 0
        node = self.head
        prev = self.head
        while node._next and j < index:
            prev = node
            node = node._next
            j += 1
    
        if j == index:
            prev._next = node._next
            self.length -= 1

    修改一个节点: update()

    def update(self, index, data):
        if self.isEmpty() or index < 0 or index >= self.length:
            print 'error: out of index'
            return
        j = 0
        node = self.head
        while node._next and j < index:
            node = node._next
            j += 1
    
        if j == index:
            node.data = data

    查找一个节点: getItem()

    def getItem(self, index):
        if self.isEmpty() or index < 0 or index >= self.length:
            print "error: out of index"
            return
        j = 0
        node = self.head
        while node._next and j < index:
            node = node._next
            j += 1
    
        return node.data

    查找一个节点的索引: getIndex()

    def getIndex(self, data):
        j = 0
        if self.isEmpty():
            print "this chain table is empty"
            return
        node = self.head
        while node:
            if node.data == data:
                return j
            node = node._next
            j += 1
    
        if j == self.length:
            print "%s not found" % str(data)
            return

    插入一个节点: insert()

    def insert(self, index, dataOrNode):
        if self.isEmpty():
            print "this chain tabale is empty"
            return
    
        if index < 0 or index >= self.length:
            print "error: out of index"
            return
    
        item = None
        if isinstance(dataOrNode, Node):
            item = dataOrNode
        else:
            item = Node(dataOrNode)
    
        if index == 0:
            item._next = self.head
            self.head = item
            self.length += 1
            return
    
        j = 0
        node = self.head
        prev = self.head
        while node._next and j < index:
            prev = node
            node = node._next
            j += 1
    
        if j == index:
            item._next = node
            prev._next = item
            self.length += 1

    清空链表: clear()

    def clear(self):
        self.head = None
        self.length = 0

    以上就是链表类的要实现的方法。

    执行的结果:

    接下来是完整代码:

      1 # -*- coding:utf8 -*-
      2 #/usr/bin/env python
      3 
      4 class Node(object):
      5     def __init__(self, data, pnext = None):
      6         self.data = data
      7         self._next = pnext
      8 
      9     def __repr__(self):
     10         return str(self.data)
     11 
     12 class ChainTable(object):
     13     def __init__(self):
     14         self.head = None
     15         self.length = 0
     16 
     17     def isEmpty(self):
     18         return (self.length == 0)
     19 
     20     def append(self, dataOrNode):
     21         item = None
     22         if isinstance(dataOrNode, Node):
     23             item = dataOrNode
     24         else:
     25             item = Node(dataOrNode)
     26 
     27         if not self.head:
     28             self.head = item
     29             self.length += 1
     30 
     31         else:
     32             node = self.head
     33             while node._next:
     34                 node = node._next
     35             node._next = item
     36             self.length += 1
     37 
     38     def delete(self, index):
     39         if self.isEmpty():
     40             print "this chain table is empty."
     41             return
     42 
     43         if index < 0 or index >= self.length:
     44             print 'error: out of index'
     45             return
     46 
     47         if index == 0:
     48             self.head = self.head._next
     49             self.length -= 1
     50             return
     51 
     52         j = 0
     53         node = self.head
     54         prev = self.head
     55         while node._next and j < index:
     56             prev = node
     57             node = node._next
     58             j += 1
     59 
     60         if j == index:
     61             prev._next = node._next
     62             self.length -= 1
     63 
     64     def insert(self, index, dataOrNode):
     65         if self.isEmpty():
     66             print "this chain tabale is empty"
     67             return
     68 
     69         if index < 0 or index >= self.length:
     70             print "error: out of index"
     71             return
     72 
     73         item = None
     74         if isinstance(dataOrNode, Node):
     75             item = dataOrNode
     76         else:
     77             item = Node(dataOrNode)
     78 
     79         if index == 0:
     80             item._next = self.head
     81             self.head = item
     82             self.length += 1
     83             return
     84 
     85         j = 0
     86         node = self.head
     87         prev = self.head
     88         while node._next and j < index:
     89             prev = node
     90             node = node._next
     91             j += 1
     92 
     93         if j == index:
     94             item._next = node
     95             prev._next = item
     96             self.length += 1
     97 
     98     def update(self, index, data):
     99         if self.isEmpty() or index < 0 or index >= self.length:
    100             print 'error: out of index'
    101             return
    102         j = 0
    103         node = self.head
    104         while node._next and j < index:
    105             node = node._next
    106             j += 1
    107 
    108         if j == index:
    109             node.data = data
    110 
    111     def getItem(self, index):
    112         if self.isEmpty() or index < 0 or index >= self.length:
    113             print "error: out of index"
    114             return
    115         j = 0
    116         node = self.head
    117         while node._next and j < index:
    118             node = node._next
    119             j += 1
    120 
    121         return node.data
    122 
    123 
    124     def getIndex(self, data):
    125         j = 0
    126         if self.isEmpty():
    127             print "this chain table is empty"
    128             return
    129         node = self.head
    130         while node:
    131             if node.data == data:
    132                 return j
    133             node = node._next
    134             j += 1
    135 
    136         if j == self.length:
    137             print "%s not found" % str(data)
    138             return
    139 
    140     def clear(self):
    141         self.head = None
    142         self.length = 0
    143 
    144     def __repr__(self):
    145         if self.isEmpty():
    146             return "empty chain table"
    147         node = self.head
    148         nlist = ''
    149         while node:
    150             nlist += str(node.data) + ' '
    151             node = node._next
    152         return nlist
    153 
    154     def __getitem__(self, ind):
    155         if self.isEmpty() or ind < 0 or ind >= self.length:
    156             print "error: out of index"
    157             return
    158         return self.getItem(ind)
    159 
    160     def __setitem__(self, ind, val):
    161         if self.isEmpty() or ind < 0 or ind >= self.length:
    162             print "error: out of index"
    163             return
    164         self.update(ind, val)
    165 
    166     def __len__(self):
    167         return self.length
    python链表

    作者:陈栋权

    时间:2016/09/19

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,

    且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    如有特别用途,请与我联系邮箱:kingchen.gd@foxmail.com

    最后有兴趣的同学可以关注我的微信公众号,可以随时及时方便看我的文章。*^_^*

    扫码关注或者搜索微信号:King_diary 

  • 相关阅读:
    大数据量磁盘文件排序
    Unix文件系统基本结构
    TCP三次握手和四次挥手
    哈希表的尴尬
    Http Get/Post请求的区别
    关于数据仓库中缓慢变化维的总结
    客户端回调过程..
    有关闭包的理解
    OWA半中文半英文问题,OWA实现邮件群发问题
    win2k3卷影副本服务的使用
  • 原文地址:https://www.cnblogs.com/king-ding/p/pythonchaintable.html
Copyright © 2011-2022 走看看