文章目录
基本数据结构
1. 线性结构
-
线性结构定义:线性结构是一种有序数据项的集合,其中每个数据项都有唯一的前驱和后继
除了第一个没有前驱,最后一个没有后继
新的数据项加入到数据集中时,只会加入到原有某个数据项之前或之后具有这种性质的数据集,就称为线性结构 -
线性结构总有两端,在不同的情况下, 两端的称呼也不同
左右端,前后端,顶底端 -
两端的称呼并不是关键,不同线性结构的关键区别在于数据项增减的方式(有的结构只允许数据项从一端添加,而有的结构则允许数据项从两端移除)
-
4个最简单但功能强大的结构:栈Stack,队列Queue,双端队列Deque 和列表List
这些数据集的共同点在于,数据项之间只存在先后的次序关系,都是线性结构
1. 栈抽象数据类型及Python实现
- 栈:一种有次序的数据项集合,在栈中,数据项的加入和移除都仅发生在同一端。这一端叫栈“ 顶top” ,另一端叫栈“ 底base”
- 栈:后进先出(LIFO,Last-In First-Out),距离栈底越近的数据项,留在栈中的时间就越长
- 栈的特性:反转次序
进栈和出栈的次序正好相反
这种访问次序反转的特性,我们在某些计算机操作上碰到过
浏览器的“ 后退back”按钮,最先back的是最近访问的网页Word的“ Undo” 按钮,最先撤销的是最近操作
- 栈在python中的操作
Stack():创建一个空栈,不包含任何数据项
push(item):将item加入栈顶,无返回值
pop():将栈顶数据项移除,并返回,栈被修改
peek(): “ 窥视” 栈顶数据项,返回栈顶的数据项但不移除,栈不被修改
isEmpty():返回栈是否为空栈
size():返回栈中有多少个数据项
用Python实现ADT Stack
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(0, item)
def pop(self):
return self.items. pop(0)
def peek(self):
return self.items[0]
def size(self) :
return len(self.items )
ADT Stack的另一个实现
❖不同的实现方案保持了ADT接口的稳定性但性能有所不同,栈顶首端的版本(上),其push/pop的复杂度为O(n),而栈顶尾端的实现(下),其push/pop的复杂度为O(1)
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append( item)
def pop(self):
return self.items. pop( )
def peek(self):
return self.items[len(self . items)-1]
def size(self) :
return len(self.items )
- 栈的应用
- 简单括号匹配
从左到右扫描括号串,最新打开的左括号,应该匹配最先遇到的右括号。这样,第一个左括号(最早打开),就应该匹配最后一个右括号(最后遇到)这种次序反转的识别,正好符合栈的特性 - 十进制转换为二进制
进制转换为二进制,采用的是“除以2求余数” 的算法。将整数不断除以2,每次得到的余数就是由低到高的二进制位。“除以2”的过程,得到的余数是从低到高的次序,而输出则是从高到低,所以需要一个栈来反转次序
2. 队列抽象数据类型及Python实现
- 队列是一种有次序的数据集合,其特征是:新数据项的添加总发生在一端(通常称为“ 尾rear” 端)而现存数据项的移除总发生在另一端(通常称为“ 首front” 端)
当数据项加入队列,首先出现在队尾,随着队首数据项的移除,它逐渐接近队首 - 这种次序安排的原则称为(FIFO:First-in first-out)先进先出或“先到先服务first‐come first‐served”
- 抽象数据类型Queue由如下操作定义:
Queue():创建一个空队列对象,返回值为Queue对象;
enqueue(item):将数据项item添加到队尾,无返回值;
dequeue():从队首移除数据项,返回值为队首数据项,队列被修改;
isEmpty():测试是否空队列,返回值为布尔值
size():返回队列中数据项的个数。
Python实现ADT Queue
# 采 用 List 来 容纳Queue的数据项
# 将List首端作为队列尾端,List的末端作为队列首端
# enqueue()复杂度为O(n),dequeue()复杂度为O(1)
#首尾倒过来的实现,复杂度也倒过来
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
3. 双端队列抽象数据类型及Python实现
- 双端队列Deque是一种有次序的数据集,跟队列相似,其两端可以称作“ 首”“ 尾” 端,但deque中数据项既可以从队首加入,也可以从队尾加入;数据项也可以从两端移除。某种意义上说,双端队列集成了栈和队列的能力
- 但双端队列并不具有内在的LIFO或者FIFO特性
- deque定义的操作
Deque():创建一个空双端队列
addFront(item):将item加入队首
addRear(item):将item加入队尾
removeFront():从队首移除数据项,返回值为移除的数据项
removeRear():从队尾移除数据项,返回值为移除的数据项
isEmpty():返回deque是否为空
size():返回deque中包含数据项的个数
Python实现ADT Deque
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
4. 列表
- 一种数据项按照相对位置存放的数据集
1.抽象数据类型:无序表List
- 无序表List的操作
List():创建一个空列表
add(item):添加一个数据项到列表中,假设item原先不存在于列表中
remove(item):从列表中移除item,列表被修改, item原先应存在于表中
search(item):在列表中查找item,返回布尔类型值
isEmpty():返回列表是否为空
size():返回列表包含了多少数据项
append(item):添加一个数据项到表末尾,假设item原先不存在于列表中
index(item):返回数据项在表中的位置
insert(pos,item):将数据项插入到位置pos,假设item原先不存在与列表中,
同时原列表具有足够多个数据项,能让item占据位置pos
pop():从列表末尾移除数据项,假设原列表至少有1个数据项
pop(pos):移除位置为pos的数据项,假设原列表存在位置pos
2.采用链表实现无序表
- 为了实现无序表数据结构,可以采用链接表的方案。
- 虽然列表数据结构要求保持数据项的前后相对位置,但这种前后位置的保持,并不要求数据项依次存放在连续的存储空间
- 链表实现的最基本元素是节点Node每个节点至少要包含2个信息:数据项本身,以及指向下一个节点的引用信息
注意next为None的意义是没有下一个节点了,这个很重要 - 链表实现:节点Node
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
# 可以采用链接节点的方式构建数据集来实现无序表
"""链表的第一个和最后一个节点最重要如果想访问到链表中
的所有节点,就必须从第一个节点开始沿着链接遍历下去"""
"""所以无序表必须要有对第一个节点的引用信息
设立一个属性head,保存对第一个节点的引用空表的head为None"""
class UnorderedList:
def __init__(self):
self.head = None
#随着数据项的加入,无序表的head始终指向链条中的第一个节点
"""无序表mylist对象本身并不包含数据项
(数据项在节点中)
其中包含的head只是对首个节点Node的引用"""
# isEmpty add size search remove方法实现
class UnorderedList:
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def add(self,item):
temp = Node(item)
temp.setNext(self.head)
self.head =temp
def size(self):
current = self.head
count = 0
while current != None
count = count + 1;
current = current.getNext()
return count
def search(self,item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
def remove(self,item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
3. 抽象数据类型:有序表OrderedList
- 有序表是一种数据项依照其某可比性质(如整数大小、字母表先后)来决定在列表中的位置
- 越“小” 的数据项越靠近列表的头,越靠"前"
- OrderedList所定义的操作
OrderedList():创建一个空的有序表
add(item):在表中添加一个数据项,并保持整体顺序,此项原不存在
remove(item):从有序表中移除一个数据项,此项应存在,有序表被修改
search(item):在有序表中查找数据项,返回是否存在
isEmpty():是否空表
size():返回表中数据项的个数 index(item):返回数据项在表中的位置,此项应存在
pop():移除并返回有序表中最后一项,表中应至少存在一项
pop(pos):移除并返回有序表中指定位置的数据项,此位置应存在
- 有序表OrderedList实现
# 同样采用链表方法实现
# Node定义相同
# OrderedList也设置一个head来保存链表表头的引用
"""对于isEmpty/size/remove这些方法,与节点的次序无关 ,所以其实现跟UnorderedList是一样的。
search/add方法则需要有修改"""
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class orderedList:
def __init__(self):
self.head = None
def add(self,item):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.getData() > item:
stop = True
else:
previous = current
current = current.getNext()
temp = Node(item)
if previous == None:
temp.setNext(self.head)
self.head = temp
else:
temp.setNext(current)
previous.setNext(temp)
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1;
current = current.getNext()
return count
def search(self,item):
current = self.head
found = False
stop = False
while current != None and not found and not stop:
if current.getData() == item:
found = True
else:
if current.getData() > item:
stop = True
else:
current = current.getNext
return found
def remove(self,item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
4.链表实现的算法分析
- 对于链表复杂度的分析,主要是看相应的方法是否涉及到链表的遍历
- 对于一个包含节点数为n的链表
isEmpty是O(1),因为仅需要检查head是否为None
size是O(n),因为除了遍历到表尾,没有其它办法得知节点的数量
search/remove以及有序表的add方法,则是O(n),因为涉及到链表的遍历,按照概率其平均操作的次数是n/2
无序表的add方法是O(1),因为仅需要插入到表头 - 链表实现的List,跟Python内置的列表数据类型,在有些相同方法的实现上的时间复杂度不同
- 主要是因为Python内置的列表数据类型是基于顺序存储来实现的,并进行了优化