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:\tNot 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:\t", 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("\nDETAIL:")
157         index = self.link[0].next
158         count = 1
159         while index:
160             print("SN:\t%d\tDATA:\t%s\tADDR:\t%d\tNEXT:\t%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:\t%d\nRear:\t%d\n" % (self.length, self.rear))
166 
167 
168 if __name__ == '__main__':
169     SL = SLinkList()
170     while True:
171         print()
172         opt = input("请选择创建链表的方式: 1. 尾插法 2. 头插法\nOPTION:\t")
173         if opt == '1':
174             data_str = input("INPUT DATA:\t")
175             SL.CreateSLinkList_R(data_str)
176             break
177         elif opt == '2':
178             data_str = input("INPUT DATA:\t")
179             SL.CreateSLinkList_F(data_str)
180             break
181         else:
182             print("请做出正确选择!")
183             continue
184 
185     while True:
186         opt = input("\n请选择操作: 1. 插入数据 2. 删除数据 3. 遍历链表 4. 查看链表详细结构 5. 在链表末尾添加数据 6. 退出程序\nOPTION:\t")
187         if opt == '1':
188             data_str = input("请输入插入数据:\t")
189             pos = input("请输入插入位置:\t")
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("请输入删除数据:\t")
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("请输入数据:\t")
209             node_addr = SL.Append(data_str[0])
210             print("成功添加元素:\t%s\t元素地址为:\t%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 

posted @ 2016-09-19 20:15  King_DIG  阅读(186579)  评论(9编辑  收藏  举报