python数据结构
介绍
参考链接:https://jackkuo666.github.io/Data_Structure_with_Python_book/index.html
我们来思考一下程序是什么?是如何在电脑上运行起来的?
- 程序其实无非就是由各种字母符号搭建起来的一段文本,只是其中的字符被python解释器或是其他语言的编译器/解释器赋予了特殊的含义,经由解释器或编译器“翻译”,计算机CPU接收到它能“听得懂”的指令,再去调度计算机各个配件执行所需功能。何为听得懂呢?CPU所包含的指令集就是CPU能“听得懂”的指令。简单来说,计算机程序本质上就是告诉计算机一个确定的执行顺序从而执行某项任务,达到某个目的。而好的算法就是能够以精简有效的方法实现所需,就像是数学题的解法,有纯计算的死板方法,还有充满技巧性的灵活解法。在算法中,我们要寻找的就是充满技巧性的灵活解法,从而大大提高计算效率。
- 而在程序执行过程中,还有一点很重要,就是数据的读取与存入。如果对于程序所需要的每一个数据都定义一个对应类型的变量来存储,这显然是不现实的。为其设计一种合适的结构,将所需要的数据“优雅”的结合起来,这就是数据结构,即数据的存储方式。
- 不同的数据结构就会导致需要不同的算法来进行处理。综上所述,程序 = 算法 + 数据结构,而计算机语言只是其表达形式。
数据结构的基本操作
- 增:增加元素
- 删:删除元素
- 改:修改元素
- 查:查找元素
- 排:对元素进行排序
算法的五大特性
- 输入:算法具有0或多个(有限个)输入
- 输出:算法至少1个输出
- 有穷性:算法在有限的步骤后自动停止,并不会无穷进行下去,并且每一个步骤都要在可接受的时间内执行完。
- 确定性:算法的每一个步骤都是确定的,不具有二义性。
- 可行性:算法的每一个步骤都是可行的,即每一个步骤都能在有限次后完成
数据结构
线性表
顺序表
-
形式
“顺序”,即按照“顺序”连续存储数据元素,从而都以元素下标i为其逻辑地址,物理地址则可以通过表的起始地址Loc(e0)与存储单元大小c,较为简单的算出来:Loc(ei)=Loc(e0)+i*c。
-
各元素的存储单元大小一致如a),则直接将各元素存放于顺序表中。
-
各元素的存储单元大小不一如b),则数据元素另外存储,顺序表中存放数据元素的地址信息(链接)
-
-
结构实现
完整的顺序表主要包含两类信息:
- 数据元素
- 顺序表的相关信息:顺序表的容量,已存入的数据个数。
将这两类信息结合起来放于顺序表中的方式有两种:
- 一体式结构:直接顺序存储,表信息在前,元素存储区在后。
- 分离式结构:数据元素另存在一个存储区,表中存储:表信息和该存储区的地址(链接)。
元素存储区的迁移:
- 一体式结构:由于表信息与元素存储区一同“顺序”存储于顺序表中,这意味着元素存储区的迁移势必牵连整个顺序表。即整体迁移。
- 分离式结构:由于顺序表中存储的是:表信息和元素存储区的地址,因而元素存储区的迁移只需要修改元素存储区的地址即可。
元素存储区的扩充:
- 一体式结构:同迁移一样,由于这种一体式的结构,元素存储区的大小和位置在顺序表创建之初就已经确定好了,因而无论是元素存储区的迁移亦或是扩充,都需要对整张表进行修改。
- 分离式结构:对元素存储区进行扩充,修改顺序表中的链接即可。这种分离式结构的好处就是,只要计算机还有存储空间,这个顺序表就不会因为满了而无法进行操作。因而也称这种表为动态顺序表,因为其容量是不定的。
- 线性扩充:每次扩充固定个数的存储空间
- 优点:省空间
- 缺点:操作频繁
- 倍数扩充:每次扩充容量加倍
- 优点:无需频繁扩充
- 缺点:浪费存储空间
- 线性扩充:每次扩充固定个数的存储空间
-
操作
-
增:增加元素
- 在尾部增加元素,O(1)
- 插入元素
- 保顺序插入元素,O(n)
- 不保顺序插入元素,O(1)
-
删:删除元素
- 删除尾部元素,O(1)
- 删除指定位置的元素
- 保顺序删除,O(n)
- 不保顺序删除,O(1)
-
改:修改元素
-
查:查找元素
-
排:对元素进行排序
-
-
python中的顺序表
-
tuple:固定不可变,且不支持任何操作的顺序表。
-
list:
- 下标访问元素O
- 动态添加元素,元素类型不定 —>> 分离式顺序表
d = ["hello", "goodbye"] for i in range(len(d)): print(id(d[i])) d.append("sun") for i in range(len(d)): print(id(d[i])) >>>> 2209268051248 2209268051312 2209268051248 2209268051312 2209268092720
可以看到元素区虽然进行了扩充,但原元素区域并未受到影响。另外id值并不连续,说明元素并非连续存储,而应该是通过链接相联系的。
Cpython 中的列表实现类似于下面的 C 结构体。
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
其中,
-
PyObject_VAR_HEAD:宏定义文件头
#define PyObject_VAR_HEAD PyVarObject ob_base
而PyVarObject的定义如下
typedef struct { PyObject ob_base; Py_ssize_t ob_size; //已存入的元素个数 } PyVarObject;
其中PyObject的定义如下
// Include/object.h #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _object *_ob_prev; typedef struct _object { _PyObject_HEAD_EXTRA // 双向链表,用于追踪堆中所有对象,在开启了 Py_TRACE_REFS 宏的时候有用 Py_ssize_t ob_refcnt; // 引用计数,用于垃圾回收 PyTypeObject *ob_type; // 指针,指向当前对象的类型对象,用于查询对象的类型 } PyObject;
-
PyObject **ob_item:是指向列表对象的指针数组
- **ob_item.ob_size:已存入的元素个数
**ob_item.*ob_type
:当前对象的类型对象
-
Py_ssize_t allocated:申请内存的槽的个数
l = [] # 创建空列表 l.append(1) # 尾部添加元素 l.append(2) l.append(3) l.append(4)
-
链表
顺序表需要预先知道数据的大小,从而开辟一块连续的内存空间予以使用。要知道“连续”的内存空间是很珍贵的,当内存不断开辟又释放,很容易出现内存碎片化。当需要对数据进行扩充时,需要对整张表进行迁移,寻找一块足够大的可以容纳当前数据的连续内存,操作十分不灵活。
而链表结构则可以充分的使用计算机的内存,即使内存碎片化也可以充分利用起来,使得计算机的内存管理比较灵活。
链表中由于并没有像顺序表中那样的物理连续关系,故而除了存储元素值自身以外,还需要将下一元素的位置信息记录下来,|元素值|下一元素的位置信息|这样的结构记为节点。链表由这样的多个节点构成。
单向链表
1)定义:节点中除自身元素信息外,只包含了下一节点的位置信息(指针/链接),故而是单向的。
2)节点:单向链表的节点只包含了两个部分
- 数据域:存储本节点的数据信息
- 指针域:存储下一节点的指针
3)头结点:由一个指针head指向头结点,继而从head出发可以访问整个单链表
4)尾结点:尾结点的next=NULL
5)节点实现:数据域+指针域
class SingleNode(object):
"""
单链表节点:
1.数据域item
2.指针域next
"""
def __init__(self, item):
self.item = item
self.next = None
6)单链表操作及其实现:
-
单链表信息
-
length():长度
def length(self): """获取链表的长度""" _len = 0 node = self.head while node: _len += 1 node = node.next return _len
-
is_empty():是否为空
def is_empty(self): """判断链表是否为空""" return self.head == None
-
travel():遍历
def travel(self): """遍历链表数据,进行打印""" # 1.检查链表是否为空 if self.is_empty(): print("当前链表为空,无法遍历") # 2.若不为空,则遍历打印 else: node = self.head while node: print(node.item) node = node.next print("end")
-
-
增:
-
add(item):表头增加节点
def add(self, item): """表头增加节点""" # 1.为元素创建新节点 new_node = SingleNode(item) # 2.若链表为空,将新节点设为头结点 if not self.head: self.head = new_node # 3.若链表不为空,将新节点指向原头结点,再将新节点设为头结点 else: new_node.next = self.head self.head = new_node
-
insert(item):表中插入节点
def insert(self, pos, item): """ 表中插入节点: 1. pos:指插入节点的位置 2. item:插入节点的数据值 分情况 1. pos <= 0,表头添加节点add 2. pos正常,表中插入 3. pos >= 表的长度,表尾添加节点append """ # 1.为item创建新节点 new_node = SingleNode(item) # 2.pos <= 0 if pos <= 0: self.add(new_node) # 3.pos >= length elif pos >= (self.length()-1): self.append(new_node) # 4.一般情况 else: # 4.1.index变量用于记录位置 index = 0 # 4.2.指向头结点 node = self.head # 4.3.while循环,不断指向下一节点,通过index记录当前节点的位置,最终得到pos-1处的节点 while index < pos-1: node = node.next index = index + 1 # 4.4.将新节点插入到指定位置node和node.next之间 new_node.next = node.next node.next = new_node
-
append(item)表尾增加节点
def append(self, item): """表尾加入节点""" # 1.为item创建新节点 new_node = SingleNode(item) # 2.如果链表为空,则将新节点设为头节点 if self.is_empty(): self.head = new_node # 3.如果链表不为空,遍历至尾结点,尾结点的特征是.next = None,将尾结点指向新节点 else: node = self.head while node.next: node = node.next node.next = new_node
-
-
删:remove(item)
def remove(self, item): """删除元素""" # 1. node记录前一个节点 node = self.head # 2. next_node记录后一个节点 next_node = node.next # 3.如果头结点即为要删除的元素,则删除原头节点 if node.item == item: print("") self.head = next_node # 4.如果头结点并不是要删除的节点 else: # 4.1 对后一个节点执行while循环 while next_node: # 若后一个节点为要删除的节点,则前一个节点指向后一个节点的后一个 if next_node.item == item: node.next = next_node.next # 节点后移 else: node = node.next next_node = node.next
-
改:无
-
查:search(item)
def search(self, item): """查找元素""" node = self.head while node: if node.item == item: return True node = node.next return False
-
排:无
7)单链表的完整实现:
class SingleNode(object):
"""
单链表节点:
1.数据域item
2.指针域next
"""
def __init__(self, item):
self.item = item
self.next = None
class SingleLinkList(object):
"""
单链表:
1.构成:头结点与其他节点,和尾节点。头结点记录为类属性,尾结点则可以通过.next == None来判断
2.表的相关信息:
2.1 length:表长度
2.2 is_empty:表是否为空
2.3 travel:遍历
3.增:
3.1 add(item):表头增加节点
3.2 insert(pos,item):表中插入节点
3.3 append(item):表尾增加节点
4.删: remove(item)
5.查: search(item)
"""
def __init__(self):
"""初始化头结点"""
self.head = None
def length(self):
"""获取链表的长度"""
_len = 0
node = self.head
while node:
_len += 1
node = node.next
return _len
def is_empty(self):
"""判断链表是否为空"""
return self.head == None
def travel(self):
"""遍历链表数据,进行打印"""
# 1.检查链表是否为空
if self.is_empty():
print("当前链表为空,无法遍历")
# 2.若不为空,则遍历打印
else:
node = self.head
while node:
print(node.item)
node = node.next
# print("end")
def add(self, item):
"""表头增加节点"""
# 1.为元素创建新节点
new_node = SingleNode(item)
# 2.若链表为空,将新节点设为头结点
if not self.head:
self.head = new_node
# 3.若链表不为空,将新节点指向原头结点,再将新节点设为头结点
else:
new_node.next = self.head
self.head = new_node
def insert(self, pos, item):
"""
表中插入节点:
1. pos:指插入节点的位置
2. item:插入节点的数据值
分情况
1. pos <= 0,表头添加节点add
2. pos正常,表中插入
3. pos >= 表的长度,表尾添加节点append
"""
# 1.pos <= 0
if pos <= 0:
self.add(item)
# 2.pos >= length
elif pos >= self.length():
self.append(item)
# 3.一般情况
else:
# 3.1.为item创建新节点
new_node = SingleNode(item)
# 3.2.index变量用于记录位置
index = 0
# 3.3.指向头结点
node = self.head
# 3.4.while循环,不断指向下一节点,通过index记录当前节点的位置,最终得到pos-1处的节点
while index < pos-1:
node = node.next
index = index + 1
# 4.4.将新节点插入到指定位置node和node.next之间
new_node.next = node.next
node.next = new_node
def append(self, item):
"""表尾加入节点"""
# 1.为item创建新节点
new_node = SingleNode(item)
# 2.如果链表为空,则将新节点设为头节点
if self.is_empty():
self.head = new_node
# 3.如果链表不为空,遍历至尾结点,尾结点的特征是.next = None,将尾结点指向新节点
else:
node = self.head
while node.next:
node = node.next
node.next = new_node
def remove(self, item):
"""删除元素"""
# 1. pre记录前一个节点
pre = None
# 2. node记录后一个节点
node = self.head
# 3.如果头结点即为要删除的元素,则删除原头节点
if node.item == item:
self.head = node.next
# 4.如果头结点并不是要删除的节点
else:
# 4.1 对后一个节点执行while循环
while node:
# 若后一个节点为要删除的节点,则前一个节点指向后一个节点的后一个
if node.item == item:
pre.next = node.next
# 节点后移
else:
pre = node
node = node.next
def search(self, item):
"""查找元素"""
node = self.head
while node:
if node.item == item:
return True
node = node.next
return False
8)单链表实例
if __name__ == "__main__":
# 1.创建单向链表
s = SingleLinkList()
# 2.表头增加元素
for i in range(3):
s.add(i)
# 3.表尾增加元素
for i in range(3, 5):
s.append(i+2)
# 4.指定位置增加元素
s.insert(-1, 'negative')
s.insert(3, 'common')
s.insert(10, 'out')
# 4.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
# 5.查询单向链表
print(s.search(4))
print(s.search(2))
# 6.删除元素
s.remove(2)
# 7.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
>>>
negative
2
1
common
0
5
6
out
单链表的长度 8
False
True
negative
1
common
0
5
6
out
单链表的长度 7
单向循环链表
1)定义:要实现“循环”的效果,则需要头尾相接。即单向循环链表在单向链表的基础上,将头结点和尾节点连接起来了。
2)操作实现:和单链表的操作基本一致,不过在尾结点的判断上有所不同。
-
单向链表的尾结点的判断依据是:next = None
-
单向循环链表的尾结点的判断依据是:next = 头结点head,不过需要注意的是当单向循环链表中只有一个节点时,该节点既是头结点,也是尾结点。
3)单向循环链表的完整实现:
class Node(object):
def __init__(self, item):
self.item = item
self.next = None
class SinCycLinkList(object):
"""
循环单向链表:
1.构成:
1.1 头结点 self.head
1.2 其他节点
1.3 尾结点 .next = self.head
2.表的相关信息:
2.1 length:表长度
2.2 is_empty:表是否为空
2.3 travel:遍历
3.增:
3.1 add(item):表头增加节点
3.2 insert(pos,item):表中插入节点
3.3 append(item):表尾增加节点
4.删: remove(item)
5.查: search(item)
"""
def __init__(self):
"""初始化头结点"""
self.head = None
def length(self):
"""获取链表的长度"""
if self.is_empty():
return 0
_len = 1
node = self.head
while node.next != self.head:
_len += 1
node = node.next
return _len
def is_empty(self):
"""判断链表是否为空"""
return self.head == None
def travel(self):
"""遍历链表数据,进行打印"""
# 1.检查链表是否为空
if self.is_empty():
print("当前链表为空,无法遍历")
# 2.若不为空,则遍历打印
else:
node = self.head
print(node.item)
while node.next != self.head:
node = node.next
print(node.item)
def add(self, item):
"""表头增加节点"""
# 1.为元素创建新节点
new_node = Node(item)
# 2.若链表为空,将新节点设为头结点
if not self.head:
self.head = new_node
self.head.next = self.head
# 3.若链表不为空,尾节点指向新节点,新节点指向原头结点
else:
# 3.1 node指向头结点,
node = self.head
# 3.2 以node为起点遍历整个单向循环链表,直至尾节点
while node.next != self.head:
node = node.next
# 3.3 尾结点指向新节点
node.next = new_node
# 3.4 新节点指向头结点
new_node.next = self.head
# 3.5 新节点变为头结点
self.head = new_node
def insert(self, pos, item):
"""表中插入节点"""
# 1.pos <= 0
if pos <= 0:
self.add(item)
# 2.pos >= length
elif pos >= (self.length()-1):
self.append(item)
# 3.一般情况
else:
# 3.1.为item创建新节点
new_node = Node(item)
# 3.2.index变量用于记录位置
index = 0
# 3.3.指向头结点
node = self.head
# 3.4.while循环,不断指向下一节点,通过index记录当前节点的位置,最终得到pos-1处的节点
while index < (pos - 1):
node = node.next
index = index + 1
# 4.4.将新节点插入到指定位置node和node.next之间
new_node.next = node.next
node.next = new_node
def append(self, item):
"""表尾加入节点"""
# 1.为item创建新节点
new_node = Node(item)
# 2.如果链表为空,则将新节点设为头节点
if self.is_empty():
self.head = new_node
self.head.next = self.head
# 3.如果链表不为空,遍历至尾结点,尾结点的特征是.next = self.head,将尾结点指向新节点
else:
node = self.head
while node.next != self.head:
node = node.next
node.next = new_node
new_node.next = self.head
self.head = new_node
def remove(self, item):
"""删除元素"""
# 1. node记录后一个节点
after_node = self.head
# 2. pre记录前一个节点
node = self.head
while node.next != self.head:
node = node.next
pre = node
# 3.如果头结点即为要删除的元素,则将头结点的下一个节点设为头结点,并让尾结点指向它
if node.item == item:
self.head = node.next
pre.next = node
# 4.如果头结点并不是要删除的节点
else:
# 4.1 对后一个节点执行while循环
while after_node:
# 若后一个节点为要删除的节点,则前一个节点指向后一个节点的后一个
if after_node.item == item:
pre.next = after_node.next
break
# 节点后移
else:
pre = after_node
after_node = after_node.next
def search(self, item):
"""查找元素"""
node = self.head
while node.next != self.head:
if node.item == item:
return True
node = node.next
return False
4)检验
if __name__ == "__main__":
# 1.创建单向链表
s = SinCycLinkList()
# 2.表头增加元素
for i in range(3):
s.add(i)
# 3.表尾增加元素
for i in range(3, 5):
s.append(i+2)
# 4.指定位置增加元素
s.insert(-1, 'negative')
s.insert(3, 'common')
s.insert(10, 'out')
# 4.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
# 5.查询单向链表
print(s.search(4))
print(s.search(2))
# 6.删除元素
s.remove(2)
# 7.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
>>>
out
negative
6
5
common
2
1
0
单链表的长度 8
False
True
out
negative
6
5
common
1
0
单链表的长度 7
双向链表
1)定义:除了元素本身以外,还需要存储上一节点和下一节点的位置信息,从而可以从本节点出发,向前或向后访问另一节点信息。
2)节点:
- 数据域:本节点的数据信息
- 指针域:上一节点和下一节点的位置信息,即pre和next
3)头结点:pre = None
4)尾结点:next = None
5)操作实现:
- 链表信息
- length():链表长度
- is_empty():链表是否为空
- travel():遍历链表
- 增:
- add(item):表头增加节点
- insert(item):表中插入节点
- append(item):表尾增加节点
- 删:remove(item)
- 改:无
- 查:search(item)
- 排:无
6)双向链表的完整实现:
class Node(object):
"""双向链表节点"""
def __init__(self, item):
self.item = item
self.pre = None
self.next = None
class DLinkList(object):
"""
双向链表:
1.构成:
1.1 头结点 .pre = None
1.2 其他节点
1.3 尾结点 .next = None
2.表的相关信息:
2.1 length:表长度
2.2 is_empty:表是否为空
2.3 travel:遍历
3.增:
3.1 add(item):表头增加节点
3.2 insert(pos,item):表中插入节点
3.3 append(item):表尾增加节点
4.删: remove(item)
5.查: search(item)
"""
def __init__(self):
self.head = None
def length(self):
"""双向链表长度"""
# 1.变量_len记录长度
_len = 0
# 2.node指向头结点
node = self.head
while node:
_len += 1
node = node.next
return _len
def is_empty(self):
"""双向链表是否为空"""
return self.head == None
def travel(self):
"""遍历双向链表并打印"""
node = self.head
while node:
print(node.item)
node = node.next
def add(self, item):
"""在表头添加节点"""
# 1.为item创建新节点
new_node = Node(item)
# 2.若表为空,则将新节点设为头节点
if self.is_empty():
self.head = new_node
# 3.若表不为空,新节点指向头节点,并将新节点设为头结点
else:
# 3.1 原头节点的pre为新节点
self.head.pre = new_node
# 3.2 原头节点的上一节点的after为原头节点
self.head.pre.next = self.head
# 3.3 更新头节点
self.head = self.head.pre
def insert(self, pos, item):
"""在指定位置插入节点"""
# 1.pos<=0
if pos <= 0:
self.add(item)
# 2.pos>=length
elif pos >= (self.length()-1):
self.append(item)
# 3.一般情况
else:
# 3.1 为item创建新节点
new_node = Node(item)
# 3.2 node指向头结点,index用来记录位置
node = self.head
index = 0
# 3.3 遍历得到pos-1处的节点
while index < (pos-1):
node = node.next
index += 1
# 3.4 插入新节点:
# 新节点的上一节点为node,下一节点为node.next
new_node.next = node.next
new_node.pre = node
# node 的下一节点为新节点
node.next = new_node
# node.next 的上一节点为新节点
node.next.pre = new_node
def append(self, item):
"""表尾增加节点"""
# 1.为item创建新节点:
new_node = Node(item)
# 2.若链表为空
if self.is_empty():
self.head = new_node
# 3.若链表不为空
else:
# 3.1 node指向头结点
node = self.head
# 3.2.从头结点出发遍历至尾结点
while node.next:
node = node.next
# 3.3 插入新节点
node.next = new_node
node.next.pre = node
def remove(self, item):
"""删除链表中的元素"""
node = self.head
# 1.如果链表为空,则无法删除
if self.is_empty():
return
# 2.如果删除元素为头节点
elif node.item == item:
# 2.1 更新头节点
self.head = node.next
# 2.2 将新头节点的pre设为None
self.head.pre = None
else:
# 3.从node开始遍历双向链表
while node:
# 2.1如果元素值相同,则删除节点
if node.item == item:
# 如果要删除的元素是尾结点
if not node.next:
node.pre.next = None
# 一般节点
else:
# 前一节点的next指向后一节点
node.pre.next = node.next
# 后一节点的pre指向前一节点
node.pre.next.pre = node.pre
break
# 2.2更新节点
else:
node = node.next
def search(self, item):
"""查询双向链表"""
# 1.node指向头节点
node = self.head
# 2.遍历链表
while node:
if node.item == item:
return True
node = node.next
return False
7)检验:
if __name__ == "__main__":
# 1.创建单向链表
s = DLinkList()
# 2.表头增加元素
for i in range(3):
s.add(i)
# 3.表尾增加元素
for i in range(3, 5):
s.append(i+2)
# 4.指定位置增加元素
s.insert(-1, 'negative')
s.insert(3, 'common')
s.insert(10, 'out')
# 4.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
# 5.查询单向链表
print(s.search(4))
print(s.search(2))
# 6.删除元素
s.remove(2)
# 7.遍历单向链表与链表长度
s.travel()
print("单链表的长度", s.length())
>>>
negative
2
1
common
0
5
6
out
单链表的长度 8
False
True
negative
1
common
0
5
6
out
单链表的长度 7
栈
1)定义:栈为为后进先出结构(LIFO:Last In First Out)栈,尤其强调了元素的先后关系,舍弃了位置的概念,只允许在栈的一段对栈进行操作。
![栈](python数据结构与算法.assets/栈.png)
2)操作:
-
栈的相关信息
- 栈的长度:size()
- 栈是否为空:is_empty()
-
增:push(item)
-
删:pop(item)
-
获取栈顶元素:top()
3)实现:
-
基于python列表
-
实现:
class StackOnList(object): """ 基于列表list实现的栈 1.栈的相关信息: 1.1 size:栈的尺寸 1.2 is_empty:栈是否为空 2.增:push(item) 3.删:pop 4.获取栈顶元素top """ def __init__(self): self.item = [] def size(self): """栈的尺寸""" return len(self.item) def is_empty(self): """栈是否为空""" return self.item == [] def push(self, item): """增加元素""" self.item.append(item) def pop(self): """获取并删除元素""" return self.item.pop() def top(self): """获取栈顶元素""" return self.item[len(self.item)-1]
-
测试
if __name__ == "__main__": # 1.创建Stack实例 s = StackOnList() # 2.元素入栈 s.push("good") s.push("good") s.push("study") # 3.栈的尺寸 print(s.size()) # 4.栈顶元素 print(s.top()) # 5.元素出栈 print(s.pop()) print(s.pop()) print(s.pop())
-
-
基于单链表
-
实现
from SingleLinkList import SingleLinkList class StackOnSinLinkList(object): """ 基于列表list实现的栈 1.栈的相关信息: 1.1 size:栈的尺寸 1.2 is_empty:栈是否为空 2.增:push(item) 3.删:pop 4.获取栈顶元素top """ def __init__(self): self.item = SingleLinkList() def size(self): """栈的尺寸""" return self.item.length() def is_empty(self): """栈是否为空""" return self.item.is_empty() def push(self, item): """增加元素""" self.item.append(item) def pop(self): """获取并删除元素""" node = self.item.head while node.next: node = node.next item = node.item self.item.remove(item) return item def top(self): """获取栈顶元素""" node = self.item.head while node.next: node = node.next item = node.item return item
-
测试
if __name__ == "__main__": # 1.创建Stack实例 s = StackOnSinLinkList() # 2.元素入栈 s.push("good") s.push("good") s.push("study") # 3.栈的尺寸 print(s.size()) # 4.栈顶元素 print(s.top()) # 5.元素出栈 print(s.pop()) print(s.pop()) print(s.pop()) >>>
队列Queue
-
队列
1)定义:队列是一种先进先出的结构,就像日常排队一般,排队在前的人先走,后面的人依此等候,队列也只允许一段进行插入操作,一端进行删除操作。
2)操作:
- 队列的相关信息:
- 队列长度:size()
- 队列是否为空:is_empty
- 增:enqueue(item)
- 删:dequeue()
3)实现:
-
基于python list
-
代码实现
class QueueOnList(object): """ 基于python的list实现的队列 1.队列信息 1.1 size():队列大小 1.2 is_empty():是否为空 2.增 enqueue(item) 3.删 dequeue() """ def __init__(self): self.item = [] def size(self): """队列大小""" return len(self.item) def is_empty(self): """队列是否为空""" return self.item == [] def enqueue(self, item): """元素入队""" self.item.insert(0, item) def dequeue(self): """元素出队""" return self.item.pop()
-
测试
if __name__ == "__main__": # 1.创建队列实例 q = QueueOnList() # 2.元素入队 q.enqueue("good") q.enqueue("good") q.enqueue("study") # 3.队列的大小 print(q.size()) # 4.元素出队 print(q.dequeue()) print(q.dequeue()) print(q.dequeue()) >>> 3 good good study
-
-
基于单链表:
-
代码实现
from SingleLinkList import SingleLinkList class QueueOnSinLinkList(object): """ 基于单链表实现的队列 1.队列信息 1.1 size():队列大小 1.2 is_empty():是否为空 2.增 enqueue(item) 3.删 dequeue() """ def __init__(self): self.item = SingleLinkList() def size(self): """队列大小""" return self.item.length() def is_empty(self): """队列是否为空""" return self.item.is_empty() def enqueue(self, item): """元素入队""" self.item.append(item) def dequeue(self): """元素出队""" item = self.item.head.item self.item.remove(item) return item
-
测试
if __name__ == "__main__": # 1.创建队列实例 q = QueueOnList() # 2.元素入队 q.enqueue("good") q.enqueue("good") q.enqueue("study") # 3.队列的大小 print(q.size()) # 4.元素出队 print(q.dequeue()) print(q.dequeue()) print(q.dequeue()) >>> 3 good good study
-
双端队列
1)定义:允许两端都能入队出队的队列
2)操作:
- 队列相关信息
- size():队列大小
- is_empty:队列是否为空
- 队列操作
- add_front:队头入队
- add_rear:队尾入队
- remove_front:队头出队
- remove_rear:队尾出队
3)实现:
class remove_front(object):
"""
双向队列:remove_front(double ended queue)
1.队列相关信息
1.1 size():队列大小
1.2 is_empty:队列是否为空
2.队列操作
1.1 add_front:队头入队
1.2 add_rear:队尾入队
1.3 remove_front:队头出队
1.4 remove_rear:队尾出队
"""
def __init__(self, ):
self.item = []
def size(self):
"""队列大小"""
return len(self.item)
def is_empty(self):
"""队列是否为空"""
return self.item == []
def add_front(self, item):
"""队头入队"""
self.item.insert(0, item)
def add_rear(self, item):
"""队尾入队"""
self.item.append(item)
def remove_front(self):
"""队头出队"""
return self.item.pop(0)
def remove_rear(self):
"""队尾出队"""
return self.item.pop()
if __name__ == "__main__":
# 1.创建队列实例
q = remove_front()
# 2.元素队头入队
q.add_front("good")
q.add_front("good")
q.add_front("study")
# 3.队列的大小
print(q.size())
# 4.元素队尾入队
q.add_rear("day")
q.add_rear("day")
q.add_rear("up")
# 5.队列元素
print(q.item)
# 6.元素队头出队
print(q.remove_front())
print(q.remove_front())
print(q.remove_front())
# 7.元素队尾出队
print(q.remove_rear())
print(q.remove_rear())
print(q.remove_rear())
树Tree
1)定义
树结构具有“树”的形状,不过是倒过来的“树”,由根节点出发开枝散叶,表现了枝叶的层级关系。
2)特点
- 根节点:没有父节点的节点称为根节点。
- 节点与子节点:每个节点有0个或多个子节点。
- 节点与父节点:除了根节点没有父节点外,所有的节点都有一个父节点。
3)基本概念
-
节点的层次:从根节点开始,根节点为第一层,根节点的子节点为第二层,依此类推;
-
树的高度:树中节点的最大层次
-
关系节点
- 父节点/父亲节点:每一个节点的前驱节点称为该节点的父节点。
- 子节点/孩子节点:每一个节点的后继节点称为该节点的子节点。
- 兄弟节点:具有相同父节点的节点互称为兄弟节点。
- 堂兄弟节点:父节点在同一层的节点互为堂兄弟节点。
- 节点的祖先:从根节点至某节点所经分支上的所有节点,都是该节点的祖先。
- 子孙:以节点为根节点的所有子树中的所有节点都是该节点的子孙。
-
度:
- 节点的度:节点的子树的个数
- 树的度:树中所有节点的度的最大值。若树的度为m,也称树为m次树。
-
分支节点:度不为0的节点
-
叶节点:度为0的节点。
4)定理(节点个数)
-
树的节点个数等于树中所有节点的度之和再加一
-
m次树的第i层的节点最多为m^(i-1)
-
高度为h的m次树的节点最多为
m h − 1 m − 1 \frac{m^h-1}{m-1} m−1mh−1 -
n个节点的m次树的高度最小为
log m [ n ( m − 1 ) + 1 ] \log_m [n(m-1)+1] logm[n(m−1)+1]
5)种类
- 无序树
- 有序树
- 二叉树:树中所有节点的度最多为2
- 完整二叉树
- 平衡二叉树
- 排序二叉树
- 霍夫曼树
- B树
- 二叉树:树中所有节点的度最多为2
6)二叉树:
-
节点的分类:
- 双分支节点:度为2
- 单分支节点:度为1
- 叶子节点:度为0
-
定理:
-
叶子节点 = 双分支节点 + 1
-
二叉树的第i层的节点个数最多为2^(i-1)
-
高度位h的二叉树的节点个数最多位2^h-1
-
n个节点的二叉树的高度为
l o g 2 ( n + 1 ) log_2(n+1) log2(n+1)
-
-
节点实现:
class Node(object): """ 二叉树的节点: 1.元素 2.左节点 3.右节点 """ def __init__(self, item, lchild, rchild): """初始化二叉树节点""" self.item = item self.lchild = lchild self.rchild = rchild
-
完整实现:
class Tree(object): """ 二叉树 1. 组成:根节点和其他节点 2. 表信息: 2.1 travel():遍历 2.增:add(item) """ def __init__(self): """初始化二叉树""" self.root = None def add(self, elem): """二叉树增加元素""" # 1.为元素创建新节点 node = Node(elem) # 2.若根节点为空,将新节点设为根节点 if not self.root: self.root = node # 3.若根节点不为空,按照先左后右的顺序遍历二叉树,找到子节点为空的节点,将新节点设为其子节点 else: """为先左后右,使用链表维护成的队列""" queue = [self.root] cur = queue.pop(0) while cur: # 若左子节点为空 if not cur.lchild: cur.lchild = node return # 若右子节点为空 elif not cur.rchild: cur.rchild = node return # 若两子节点均为空,则按照左右顺序依此入队列 else: queue.append(cur.lchild) queue.append(cur.rchild)
7) 深度优先遍历
沿着树的深度遍历树的节点,尽可能的深的搜索树的分支
-
先序遍历:根节点->左子树->右子树
def preorder(self, root): """先序遍历二叉树:根节点->左子树->右子树""" if not root: return print(root.item) self.preorder(root.lchild) self.preorder(root.rchild)
-
中序遍历:左子树->根节点->右子树
def inorder(self, root): """中序遍历二叉树:左子树->根节点->右子树""" if not root: return self.preorder(root.lchild) print(root.item) self.preorder(root.rchild)
-
后序遍历:左子树->右子树->根节点
def postorder(self, root): """后序遍历二叉树:左子树->右子树->根节点""" if not root: return self.preorder(root.lchild) self.preorder(root.rchild) print(root.item)
8) 广度优先遍历
从根节点开始,从上到下从左到右遍历整个树的节点
def breath_travel(self):
"""广度优先遍历二叉树:先上在下,先左再右"""
# 先上再下
queue = [self.root]
while queue:
cur = queue.pop(0)
print(cur.item)
# 先左在右
if cur.lchild:
queue.append(cur.lchild)
if cur.rchild:
queue.append(cur.rchild)
9) 实例
if __name__ == "__main__":
t = Tree()
for i in range(10):
t.add(i)
t.preorder(t.root)
print("*"*10)
t.inorder(t.root)
print("*"*10)
t.postorder(t.root)
print("*"*10)
t.breath_travel()
>>>
0
1
3
7
8
4
9
2
5
6
**********
1
3
7
8
4
9
0
2
5
6
**********
1
3
7
8
4
9
2
5
6
0
**********
0
1
2
3
4
5
6
7
8
9