目录
数据结构使用来描述一种或多种数据元素之间的特定关系,算法是程序设计中对数据操作的描述,数据结构和算法组成了程序。对于简单的任务,只要使用编程语言提供的基本数据类型就足够了;而对于较复杂的任务,就需要使用基本的数据类型来构造出更加复杂的数据结构。
表、堆栈和队列都是基本的线性数据结构。由于 Python 具有良好的数据结构,因此其列表可以当做表来使用,而且列表的某些特性与链表相似,因此,在 Python 中,表的实现非常简单。对于栈和队列,则可以自己编写脚本来构建。
一、表
表是最基本的数据结构,在 Python 中可以使用列表来创建表。而在 C 语言中,一般使用数组来创建表。使用数组所创建的表,在对表中元素进行插入和删除操作时开销较大。当插入一个元素时,要先将该元素后的所有元素,从最后一个元素开始,依次向后移动一个位置。完成元素移动后,再将元素插入到数组中。同样,要删除表中的元素时,首先删除元素,然后将位于该元素之后的元素从前向后,依次向前移动一个位置。
如果一个表中含有的元素较多,而要进行插入或删除的位置又比较靠近表的前端,则移动表中元素的操作将耗费大量的时间。为了减少插入或删除元素的线性开销,于是就出现了使用链表代替表。在 C 语言中,链表中不仅保存了数据,还保存了指向下一个元素的指针,如图 1 所示。当进行插入操作时,要先将位于插入元素前的元素的指针赋值给插入元素。完成赋值后再将插入元素的地址赋值给位于其前面的元素,如图 2 所示。当删除元素时,只需将要删除元素的指针赋值给其前面的元素即可,如图 3 所示。
使用链表,可以降低插入、删除元素的线性开销。然而,由于链表中不仅存储了数据,而且还保存了指向下一个元素的指针,因此使用链表将占用更大的存储空间。
而在 Python 中,列表本身就提供了插入和删除操作。因此,在 Python 中,列表也可以充当链表使用,而不用自己另外编写脚本来构建。
还有一种链表,称之为双向链表,如图 4 所示。双向链表中不仅保存了指向下一元素地址的指针,而且还保存了指向上一个元素地址的指针。相对于单向链表,双向链表需要占用更多的存储空间,但使用双向链表可以完成正序和倒序扫描链表。
二、栈(后进先出)
桟可以看做在同一位置上进行插入和删除的表,这个位置一般称为栈顶。栈的基本操作是进栈和出栈,栈可以看做一个容器,如图 5 所示,先入栈的数据保存在容器底部,后入栈的数据保存在容器顶部。出栈的时候,后入栈的数据先出,而先入栈的数据后出,因此栈有一个特性叫做后进先出(LIFO)。
在 Python 中,仍然可以使用列表来存储堆栈数据。通过创建一个堆栈类,实现对堆栈进行操作的方法。例如,进栈 PUSH 方法、出栈 POP 方法,编写检查栈是否为满栈,或者是否为空栈的方法,等等。
下面所示的 pystack.py 在 Python 中创建了一个简单的堆栈结构。
# -*- coding:UTF-8 -*-
# file: pystack.py
class PyStack: # 堆栈类
def __init__(self, size=20):
self.stack = [] # 堆栈列表
self.size = size # 堆栈大小
self.top = -1 # 堆顶位置
def setSize(self, size): # 设置堆栈大小
self.size = size
def push(self, element): # 元素进栈
if self.isFull():
raise StackException('PyStackOverflow') # 如果栈满则引发异常
else:
self.stack.append(element)
self.top = self.top + 1
def pop(self): # 元素出栈
if self.isEmpty():
raise StackException('PyStackUnderflow') # 如果栈为空则引发异常
else:
element = self.stack[-1]
self.top = self.top - 1
del self.stack[-1]
return element
def Top(self): # 后面需要引用
return self.top # 获取栈顶位置
def empty(self): # 清空栈
self.stack = []
self.top = -1
def isEmpty(self): # 是否为空栈
if self.top == -1:
return True
else:
return False
def isFull(self): # 是否为满栈
if self.top == self.size - 1:
return True
else:
return False
class StackException(Exception): # 自定义异常类
def __init__(self, data):
self.data = data
def __str__(self):
return self.data
if __name__ == '__main__':
stack = PyStack() # 创建栈
for i in range(10):
stack.push(i) # 元素进栈
print(stack.Top()) # 输出栈顶位置
for i in range(10):
print(stack.pop()) # 元素出栈
stack.empty() # 清空栈
for i in range(21):
stack.push(i) # 此处将引发异常
运行 pystack.py 脚本后,输出内容如下(异常的位置不固定)。
三、队列(先进先出)
队列和栈的结构类似,如图 6 所示,不同的是,队列的出队操作是在队首元素进行的删除操作。因此对于队列而言,先入队的元素将先出队。因此队列的特性可以称之为先进先出(FIFO)。
和堆栈类似,在 Python 中同样可以使用列表来构建一个队列,并完成对队列的操作。下面所示的 pyqueue.py 脚本构建了一个简单的队列。
# -*- coding:UTF-8 -*-
# file: pyqueue.py
class PyQueue: # 创建队列
def __init__(self, size = 20):
self.queue = [] # 队列
self.size = size # 队列大小
self.end = -1 # 队尾
def setSize(self, size): # 设置队列大小
self.size = size
def In(self, element): # 入队
if self.end < self.size - 1:
self.queue.append(element)
self.end = self.end + 1
else:
raise QueueException('PyQueueFull') # 如果队列满则引发异常
def Out(self): # 出队
if self.end != -1:
element = self.queue[0]
self.queue = self.queue[1:]
self.end = self.end - 1
return element
else:
raise QueueException('PyQueueEmpty') # 如果队列为空则引发异常
def End(self): # 输出队尾
return self.end
def empty(self): # 清除队列
self.queue = []
self.end = -1
class QueueException(Exception): # 自定义异常类
def __init__(self, data):
self.data = data
def __str__(self):
return self.data
if __name__ == '__main__':
queue = PyQueue()
for i in range(10):
queue.In(i) # 元素入队
print(queue.End())
for i in range(10):
print(queue.Out()) # 元素出队
for i in range(20):
queue.In(i) # 元素入队
queue.empty() # 清空队列
for i in range(20):
print(queue.Out()) # 此处将引发异常
运行 pyqueue.py 脚本后,输出内容如下(异常的位置不固定)。