Python--第二阶段--01.数据结构
1.什么是数据结构
a.数据:存在计算机中能被计算机识别、存储和处理的符号总称。
b.数据元素:record。
c.数据结构:数据元素和数据元素之间的相互关系,或组织数据的形式
2.数据之间的结构关系
a.逻辑结构(对事物抽象–>转化成数学模型)
- ☆线性结构:一对一的结构----汽车一辆接着一辆----数据遍历
特点:有头有尾,有第一个和最后一个,都有唯一的后继,都有唯一的前驱 - 树形结构(层次关系):一对多的结构----【二叉树】国家管理机构,省长管n个市长,1个市长管n个县长—计算机文件目录
- 图状结构(网状结构):多对多的----高铁线路、航线、地铁线路----网络拓扑图
- 其他结构:
b.存储结构
- 定义:数据在存储器中(寄存器、外存储器、内存储器)的映像
- 存储结构类型:
- 顺序存储:在存储器中放在一片连续的存储空间中----结构紧凑、查找速度快----中间插入时占内存较大、连续存储空间不足时无法存储----列表、元祖、字符串(可以用索引号取值[])
- 链式存储:在存储器中分散的存在于存储空间中,前一个元素记录下一个元素的位置----充分灵活利用空间、减少存在数据的移动----关系构建相对困难,遍历查找慢----Python不存在链式存储
- 索引存储:在存储数据的同事简历一个附加的索引表,即索引存储结构=数据文件+索引表
3.线性表
a.顺序存储(逻辑结构----线性结构)
- 定义:在一片存储空间
- 特点:逻辑相邻,存储位置也相连;存储密度高,方便对数据的遍历;对表的插入和删除等运算效率差
- eg:列表:增加(append、insert、extend)、修改(list[index]=value、list[start:stop:step]=value)、删除(pop(index)、remove(sub)、del list(index))、查看(in/not in 、index(sub)、)
b.链式存储
- 定义:即保证分散,又保证关联,每个节点都建立与下一个节点的关联
- 特点:逻辑上相邻的元素,在存储位置上不一定相同;存储稀疏,不必开辟整块存储空间;对表的插入和删除等运算的效率较高;逻辑结构复杂,不利于遍历。
- eg:列表的元素是变量的这种形式,就可以看做是链式存储—
a = 1
b = 2
c = [a,b]
4.单链表
- 节点node
1 class Node:
2 '''
3 生成节点对象,包含当前数据及下一个节点的引用
4 '''
5 def __init__(self,value,next=None):
6 self.value = value #要存储的数据
7 self.next = next #关联的下一个节点
- 链表
class Linklist:
'''
生成单向的链式的列表,实现数据的增加、修改、删除、查询
'''
def __init__(self):
self.head = Node(None)
def init_list(self, list_):
p = self.head
for i in list_:
p.next = Node(i)
p = p.next
def show(self):
p = self.head.next # 第一个有效节点
while p is not None:
print(p.value)
p = p.next # p向后移动
# 判断链表为空
def is_empty(self):
if self.head.next is None:
return True
else:
return False
# 清空链表
def clear(self):
self.head.next = None
# 增加节点--尾部插入
def append_wei(self, value):
p = self.head
while p.next is not None:
p = p.next
p.next = Node(value)
# 增加节点--头部插入
def append_head(self, value):
node = Node(value)
node.next = self.head.next # 链接第二个
self.head.next = node # 头链接插入的这个
# 指定插入位置
def insert_(self, index, value):
p = self.head
for i in range(index):
if p.next is None: # 超出最大位置结束循环
break
p = p.next
node = Node(value)
node.next = p.next
p.next = node
# 删除节点
def remove_(self, value):
p = self.head
while p.next and p.next.value != value:
p = p.next
if p.next is None:
raise ValueError("x not in linklist")
else:
p.next = p.next.next
# 获取某个节点的值
def index_(self, index):
if index < 0:
raise IndexError('list index is not exsist')
p = self.head.next
for i in range(index):
if p.next is None:
raise IndexError('list index is not exsist')
p = p.next
return p.value
class Linklist
作业:
- 链表的画图整理
- 创建两个链表,两个链表中的值均为有序值,将两个链表合并为一个,合并后要求仍为有序值
- 《数据结构与算法》
自己做的作业答案:
test1.py
from linklist import *
link1 = Linklist()
link2 = Linklist()
link1.init_list([1, 4, 7])
link2.init_list([2, 5, 8, 10])
# 无序连接
# link1.insert_list(link2)
# link1.show()
# 有序连接第一种方法
# link1.insert_list2(link2)
# link1.show()
# 有序添加单个节点
# link1.insert_2(5)
# link1.show()
# 有序连接--第二种方法建立在有序添加单个节点上的
# link1.insert_list3(link2)
# link1.show()
linklist.py
class Node:
'''
生成节点对象,包含当前数据及下一个节点的引用
'''
def __init__(self, value, next=None):
self.value = value # 要存储的数据
self.next = next # 关联的下一个节点
class Linklist:
'''
生成单向的链式的列表,实现数据的增加、修改、删除、查询
'''
def __init__(self):
self.head = Node(None)
def init_list(self, list_):
p = self.head
list_.sort()
for i in list_:
p.next = Node(i)
p = p.next
def show(self):
p = self.head.next # 第一个有效节点
while p is not None:
print(p.value)
p = p.next # p向后移动
# 判断链表为空
def is_empty(self):
if self.head.next is None:
return True
else:
return False
# 清空链表
def clear(self):
self.head.next = None
# 增加节点--尾部插入
def append_wei(self, value):
p = self.head
while p.next is not None:
p = p.next
p.next = Node(value)
# 增加节点--头部插入
def append_head(self, value):
node = Node(value)
node.next = self.head.next # 链接第二个
self.head.next = node # 头链接插入的这个
# 指定插入位置
def insert_(self, index, value):
p = self.head
for i in range(index):
if p.next is None: # 超出最大位置结束循环
break
p = p.next
node = Node(value)
node.next = p.next
p.next = node
# 删除节点
def remove_(self, value):
p = self.head
while p.next and p.next.value != value:
p = p.next
if p.next is None:
raise ValueError("x not in linklist")
else:
p.next = p.next.next
# 获取某个节点的值
def index_(self, index):
if index < 0:
raise IndexError('list index is not exsist')
p = self.head.next
for i in range(index):
if p.next is None:
raise IndexError('list index is not exsist')
p = p.next
return p.value
def insert_list(self, linklist1):
# 无序添加
p = linklist1.head.next
while p is not None:
self.append_wei(p.value)
p = p.next
def insert_list2(self, linklist1):
# 有序添加
p1 = self.head.next
p2 = linklist1.head.next
index = 1
while p2 is not None:
if p1.next is None:
self.append_wei(p2.value)
index += 1
p2 = p2.next
elif p2.value >= p1.value and p2.value<p1.next.value:
self.insert_(index, p2.value)
index += 1
p2 = p2.next
p1 = p1.next
elif p2.value >= p1.value and p2.value >p1.next.value:
index += 1
p1 = p1.next
else:
self.insert_(index-1, p2.value)
p2 = p2.next
index += 1
# 修改insert函数似的实现两个单链表合并为按顺序排列的单链表
# 按序列增加
def insert_2(self,value):
p = self.head
while p.next is not None and p.next.value < value:
p = p.next
node = Node(value)
node.next = p.next
p.next = node
# 按序列增加linklist
def insert_list3(self,linklist):
p = linklist.head.next
while p is not None:
value = p.value
self.insert_2(value)
p = p.next
老师的答案
5.栈:
- 定义:先入后出,后进先出。像羽毛球桶一样,先放进去的羽毛球,只能后取出,后放入的羽毛球只能先取出
a.栈的顺序存储(列表的结尾作为栈顶,开头作为栈底):
Stack.py
# 自定义异常
class StackError(Exception):
pass
# 顺序栈类
class SStack:
def __init__(self):
self.list_ = [] # 构建一个栈的模型 先入后出,后入先出
# 入栈
def inner(self, value):
self.list_.append(value)
# 出栈
def outer(self):
# self.list_.pop()
if self.is_empty():
raise StackError('Stack is empty')
return self.list_.pop()
# 判断列表是否为空
def is_empty(self):
return self.list_ == []
# if self.list_ is None:
# return True
# else:
# return False
def top(self):
if self.is_empty():
raise StackError('Stack is empty')
return self.list_[-1]
if __name__ == '__main__':
st = SStack() # 初始化栈
st.inner('11')
st.inner('22')
st.inner('33')
while not st.is_empty():
print(st.outer())
逆波兰表达式
# 逆波兰表达式
# 1 3 + 4 - p
# 当输入数字入栈,遇到符号出就近的两个元素运算后入栈,p后打印结果
# 自定义异常
from sstack import *
st = SStack()
while True:
exp = input()
tmp = exp.split(' ') #按空格分裂为列表
print(tmp)
for i in tmp:
if i not in ['+','-','*','/','p']:
st.inner(float(i))
elif i == '+':
y = st.outer()
x = st.outer()
st.inner(x + y)
elif i == '-':
y = st.outer()
x = st.outer()
st.inner(x - y)
elif i == '*':
y = st.outer()
x = st.outer()
st.inner(x * y)
elif i == '/':
y = st.outer()
x = st.outer()
st.inner(x / y)
elif i == 'p':
print(st.top())
else:
print('输入错误')
b.栈的链式存储(节点的将头作为栈顶,结尾作为栈底):
栈的单链表代码
"""
思路分析:
1.源于链表结构
2.封装栈的操作方法,入栈和出栈,栈空,获取栈顶元素
3.确定栈顶位置,用链表的开头作为栈顶,不需要每次遍历
"""
class StackError(Exception):
pass
#节点类
class Node:
def __init__(self,value,next = None):
self.value = value
self.next = next
# 链式栈操作
class LinkList_Stack:
def __init__(self):
# self.head = Node()
# 标记栈顶位置
self._top = None
def is_empty(self):
if self._top == None:
return True
else:
return False
def inner(self,value):
# node = Node(value)
# node.next = self._top
# self._top = node
self._top = Node(value,self._top)
def outer(self):
if self._top == None:
raise StackError('Stack is empty')
value = self._top.value
self._top = self._top.next
return value
def top(self):
if self._top == None:
raise StackError('Stack is empty')
return self._top.value
pass
if __name__ == '__main__':
st = LinkList_Stack()
st.inner('11')
st.inner('22')
st.inner('33')
# while True:
print(st.outer())
print(st.is_empty())
6.队列
-
定义(先进先出,后进后出):
队列是限制在两端进行插入操作和删除操作的线性表,允许进行存入操作的一端称为“队尾”,允许进行删除操作的一段为“队头”。(排队买饭、排号叫号系统)
-
特点:
队列只能在队头和队尾进行数据操作
队列模型具有先进先出或者后进后出的规律
队列的顺序存储(列表的尾作为队尾,列表的头作为队头)
squeue.py
'''
思路分析:
1.基于列表完成数据存储
2.通过封装规定数据操作
'''
class QueueError(Exception):
pass
class Squeue:
def __init__(self):
self._list = []
# 判断是否为空
def is_empty(self):
return self._list == []
# 入队
def inner(self,value):
self._list.append(value)
# 出队
def outer(self):
if self.is_empty():
raise QueueError('没有元素了!')
else:
return self._list.pop(0)
def show(self):
print(self._list)
if __name__ == '__main__':
st = Squeue() # 初始化栈
# st.inner('12')
# st.inner('13')
# st.inner('14')
# st.inner('15')
# st.show()
# while not st.is_empty():
# print(st.outer())
for i in range(10):
st.inner(i)
st.show()
while not st.is_empty():
print(st.outer())
队列的链式存储
link_queue.py
"""
链式队列
重点代码
思路:
1.基于链表构建队列模型
2.链表的开端作为队头,链表的结尾位置作为队尾
3.单独定义队尾标记,避免每次插入数据遍历
"""
# 自定义异常
class QueueError(Exception):
pass
# 节点类
class Node:
def __init__(self, value,next = None):
self.value = value
self.next = next
# 队列操作
class LQueue:
def __init__(self):
# 定义队头和队尾的属性变量
self.front = self.rear = Node(None)
def is_empty(self):
return self.front == self.rear
def inner(self,value):
self.rear.next = Node(value)
self.rear =self.rear.next
def outer(self):
if self.front == self.rear:
raise QueueError('Queue is empty')
self.front = self.front.next
return self.front.value
if __name__ == "__main__":
lq = LQueue()
lq.inner(10)
lq.inner(20)
lq.inner(30)
print(lq.outer())
print(lq.outer())
print(lq.outer())
作业:
- 重点代码复习
- 编写一个接口程序,获取一段文字,判断文字中括号是否匹配正确,如果正确则打印正确,不正确则指出出错的地方。
自己的作业代码:
from linklist_stack import *
str = '((sdfasdf【adfas)】[adfasdf()]{}'
# list1 = []
# for i in str:
# list1.append(i)
# print(list1)
dict_L = {
')':'(',
']':'[',
'}':'{',
')':'(',
'】': '【'
}
LStack = LinkList_Stack()
LStack_count = LinkList_Stack()
count = 1
count_error = 0
for i in str:
if i in ['(','[','{','(','【','{']:
LStack.inner(i)
LStack_count.inner(count)
elif i in [')',']','}',')','】','}']:
p = LStack.outer()
p_count = LStack_count.outer()
if dict_L[i] != p:
print('括号错误,在第%s和%s个字符处'%(count,p_count))
count_error += 1
count += 1
if LStack.is_empty() and count_error == 0:
print('恭喜你,没有错误')
else:
print('有错误')
while not LStack_count.is_empty():
print(LStack_count.outer(),'字符处有问题')
7.非线性模型
树形结构
-
定义:有且只有一个特定的根节点;其余节点互不相交(子节点不能有两个上级节点,同一个人不能归于两个不同的部门)。
-
基本概念:
- 根:无父节点
- 子树:
- 度:
- 叶子:度为0的就是叶子,不同叶子可能不是同一层,无子节点
- 子节点:
- 父节点:parant
- 兄弟节点:
- 路径:从上到下的路径,不能往回走。
- 节点的层数(高度或深度):根为第一层
- 森林:多棵树的集合
二叉树
-
定义:树形中,每个子节点最多只有2个
-
特征:
- 二叉树第i层上的节点,最多有2的i-1次方个;
- 深度k的二叉树最多有2的k次-1个节点;
- 在任意一颗二叉树中,树叶的数目比度数为2的节点的数目多一;
- 满二叉树:深度为k(k>=1)时有2的k次方-1个节点的二叉树;
- 完全二叉树:只有最下面两层的度数小于2个的节点,且最下面一层的叶节点集中在最左边的若干位置上。
-
形式(严格区分左右节点):
- 空二叉树:没节点
- 只有根节点:
- 只有左孩子:
- 只有右孩子:
-
遍历方法:每当遇到一个节点时当做根节点,递归思想。
- 先序遍历(根左右):
- 中序遍历(左根右):
- 后序遍历(左右根):
- 层次遍历:
-
递归思想:
- 定义:在函数内部直接或间接的调用自身的函数。
- 包含两个阶段:
- 递推阶段:逐层调用,必须设定终止条件,终止条件写在调用自身之前。
- 回归阶段:按递归有终止条件。
- 优点:递归可以把问题简化,让思路清晰,代码简洁。
- 缺点:递归因系统环境影响大,当递归深度太大时,可能会得到不可预知的结果。
-
二叉树Python遍历:
二叉树的遍历:列表和节点
"""
列表二叉树(完全二叉树或者满二叉树的时候可以考虑用,其他时候不用)
"""
['A',
['B',None,None],
['C',['D',None,None],['E',None,None]]
]
'''
思路说明:
1.使用链式存储,一个Node节点表示一个树的节点
2.节点考虑使用两个属性变量分别表示左链接和右链接
'''
# 二叉树节点类
class Node:
def __init__(self,value,left=None,right=None):
self.value = value
self.left = left
self.right = right
class Bitree:
def __init__(self,root):
self.root = root
# 先序遍历
def preOrder(self,node):
if node is None: #终止条件
return
print(node.value,end = ' ')
self.preOrder(node.left)
self.preOrder(node.right)
# 中序遍历
def inOrder(self,node):
if node is None: #终止条件
return
self.inOrder(node.left)
print(node.value,end = ' ')
self.inOrder(node.right)
# 中序遍历
def behOrder(self,node):
if node is None: #终止条件
return
self.behOrder(node.left)
self.behOrder(node.right)
print(node.value,end = ' ')
if __name__ == '__main__':
# 后序遍历 :B F G D I H E C A
b = Node('B')
f = Node('F')
g = Node('G')
d = Node('D',f,g)
i = Node('I')
h = Node('H')
e = Node('E',i,h)
c = Node('C',d,e)
a = Node('A',b,c) #根节点
# 将a作为遍历的起始位置
bt = Bitree(a)
bt.preOrder(bt.root)
print('=='*20)
bt.inOrder(bt.root)
print('=='*20)
bt.behOrder(bt.root)
8. 基础算法
-
定义:
-
算法的特性:
- 有穷性——算法执行的步骤是有限的
- 确定性——每个计算步骤无二义性
- 可行性——每个计算步骤能够在有限的时间内完成
- 输入,输出——存在数据的输入和输出
-
评价算法好坏的方法
- 正确性:运行正确是一个算法的前提
- 可读性:容易理解、容易变成和调试、容易维护
- 健壮性:考虑情况全面,不容易出现运行错误
- 时间效率高:算法消耗的时间少
- 存储量低:占用较少的存储空间
-
时间效率的计算方法:
- 所有运算语句执行次数加和
- 如果获得的结果是常量则时间复杂度为1
- 如果得到的结果中存在变量n则取n的最高次幂作为时间复杂度
9.排序
冒泡排序
描述:两两相比交换位置,每轮选出最大的值,每轮最大值不参与下一次的比较。
冒泡排序代码:
def bubble(list_):
# 外层表示比较多少轮
n = len(list_)
for i in range(n - 1):
for j in range(n - 1 - i):
if list_[j] > list_[j + 1]:
list_[j], list_[j + 1] = list_[j + 1], list_[j]
选择排序
描述:
选择排序代码:
# 选择排序:假定一个最小的,然后后面的相比,更小的一个和假定的最小的进行交换位置
# 我自己做的
def choice_Sort(list_, count=0):
if count == len(list_):
print('转换完成')
else:
temp = [list_[count], count]
for i in range(count, len(list_)):
if list_[i] <= temp[0]:
temp = [list_[i], i]
list_[count], list_[temp[1]] = temp[0], list_[count]
choice_Sort(list_, count + 1)
# 老师做的
def select(list_):
# 每轮选出一个最小值,需要len(list_)-1轮
for i in range(len(list_) - 1):
min = i # 假设list_[i]为最小值
for j in range(i + 1, len(list_)):
if list_[min] > list_[j]:
min = j # 擂主换人
# 进行交换,将最小值换到应该在的位置
if min != i:
list_[i], list_[min] = list_[min], list_[i]
插入排序
# 插入排序:
def insert_Sort(list_):
for i in range(len(list_)):
for j in range(i, len(list_)):
if list_[i] > list_[j]:
list_[i], list_[j] = list_[j], list_[i]
快速排序
快速排序代码:
# 快速排序:
# 自己写的
# 老师写的
def sub_Sort(list_, low, high):
# 选定基准
x = list_[low]
# low向后,high向前
while low < high:
while list_[high] >= x and high > low:
high -= 1
list_[low] = list_[high]
while list_[low] < x and low < high:
low += 1
list_[high] = list_[low]
list_[low] = x
return low
def quick_Sort(list_,low,high):
if low < high:
key = sub_Sort(list_, low, high)
quick_Sort(list_,low,key-1)
quick_Sort(list_,key+1,high)
10.查找
二分查找
"""
二分查找方法训练
"""
# list_为有序数列,key为要查找的关键值,返回key在数列中的索引号
def search(list_, key):
low,high = 0 ,len(list_)-1
while low < high:
mid = (low + high) // 2
if list_[mid] < key:
low = mid+1
elif list_[mid] > key:
high = mid-1
else:
return mid
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print('KEY INDEX:',search(l, 11))