数据结构绪论
数据结构学科定义:是一门研究非数值计算的程序设计中的操作对象,它们之间的关系和操作等相关问题的数据学科。
数据组成
数据元素:组成数据的基本单位。
数据项:数据元素可以优若干个数据项构成,数据不可分割的最小单位。
数据对象:数据相同的元素组成的集合,是数据的子集合。
example:
[类数据:人类, 元素:{张三, 李四,…}, 数据项:{眼睛,鼻子,…}, 数据对象(眼睛):{张三的眼睛, 李四的眼睛…}]
单独说说数据结构
不同的元素的数据元素之间不是相互独立的,而是存在关系的,这种关系称之为结构。
逻辑结构:集合、线性、树形、拓扑图
物理结构:数据的逻辑结构在计算机中的存储形式顺序存储结构:数据存储在对地址连续的存储单元,逻辑关系与物理关系一致。
连式存储结构:数据元素位置任意,需要指针存放数据元素的地址信息。
算法
时间复杂度
这里仅仅记录下几个重要的定义:
算法的时间复杂度:T(n),随着n的变化,执行次数的变化,仅仅关注T(n)的最高阶。O(1):常数阶、O(n):线性阶、O($ n^2 $)平方阶 。下面分别给出不同等差数列求和为例:
# 1+2...+n
# O(1)
def sum_1(n):
return int((1 + n) * n / 2)
# n
def sum_2(n):
count = 0
for i in range(1, n+1):
count += i
return count
# n*n
def sum_3(n):
count = 0
for i in range(1, n+1):
for j in range(i):
count += 1
return count
常用的算法复杂度比较:
$$ O(1) < O(n) < O(logn) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) $$
最坏复杂度和平均复杂度:n个数顺序存储,现在需要插入某个元素,最好的情况1, 最坏的情况n+1,那么平均复杂度是多少了?,搜寻任意x的期望值(n+2)/2。一般没有特殊说明,复杂度都是指最坏的情况。
空间复杂度
定义和时间复杂度类似,重要的是我们可以通过空间复杂度来换取时间。
example:设计一个算法,复杂度为O($n^2$), 这里把算法的结果顺序存储下来,复杂度O(1)。
线性表
最大的确点插入和删除:算法时间复杂度大。为了解决这个问题就得牺牲下空间—连式存储。
连式存储
每个数据储存叫节点Node,Node由数域和指针域,前者存储数值后者存储后继的地址信息。存在头指针,每次必须从头指针开始,最后一个指针域为空。搜寻
想得到某个指定地址数据,每次必须从头开始,往下搜寻下去找到即停止,否者直到指针域为空停止说明数据不存在。时间复杂度O(n).插入
p节点后插入s节点,s->next=p->next, p-next=s,p的后继赋值给s后继,p后继改为s删除
p->next = p->next->next
ps:静态链表,循环链表,双向链表
栈
想象下弹夹
- 是线性单链表
- 仅仅允许在链表尾部,即栈顶进行插入和删除操作
- 1-n数依次进栈(仅仅是进去的顺序),出站顺序必须满足任意想编号x后面的编号小于x的必须倒序排列。
- 两个站共享内存,从中间某个位置作为两个栈顶, 两端为栈为。如果容积为n当两个站的顶部地址差为1时代表栈满。
- 链栈不存在栈满的情况???
- 栈的递归运用,斐波那契数列数列为例,每次递归到最底层就把数据压入栈的底部,慢慢向上回溯到顶部得到最后顶部值。
# 方法1
def Fib():
a = 1
yield a
b = a
yield b
while 1:
c = a + b
yield c
a = b
b = c
# 这里采用递归的方法解决,稍微改变下需求,求第i个fib数列
def Fib_i(i):
if i <= 2:
return 1
else:
return Fib_i(i-1) + Fib_i(i-2)
串(string)
朴素的模式匹配方法
001 匹配000000001,从第一个起点index=0开始001,发现前两个00能匹配到第1个不行,然后index=1,直到尾部,如果两个长度分别为m, n (n >= m),则算法复杂度为(n – m + 1)* m。KMP模式匹配
abcd和abcabcd进行匹配
step1:
abcabcd
abcd
index=3时不匹配
step2
abcabcd
`abcd
index=1终止
step3
abcabcd
“abcd
index=2终止
!!!!!!其实上面step2 step3都是无效
因为在step1时a=a,向后一个个平移到abcabcd第二个a时才可以进一步可能,因此step2可以直接跳到下面
abcabcd
···abcd
二叉树
- 二叉树的性质
- 第i层节点数最多可能:$2^{i-1}$
- 深度为n的二叉树至多:$2^{n}-1$个节点
- 叶节点为$n_{leaf}$,度为2 的节点数$n_{node2}$,则$n_{leaf}=n_{node2}+1$
- n个节点完全二叉树的深度$[log_2(n)] + 1$,[]为取整运算
- 把完全二叉树n个节点按层进行编号。如果i=1则为root,i>1这这个节点的父节点为$[i/2]$;如果节点$2i > n$就没有左孩子,否则左孩子为$2i$;如果$2 * i +1 > n$则无孩子,否则其有孩子为$2 * i +1$.
- 满树
n个节点,除了每个叶节点,每个节点都有左右左右子树,并且所有的叶节点都在最底层,层次m与节点n存在 $2^m – 1=n$关系。
- 完全二叉树
对应一个二叉树按层次对所以节点编号包括叶节点,如果所有节点编号与同样深度的满二叉树编号结果一致,那么该树为满二叉树。
性质、属性:
- 叶节点只能在最下面2层,倒数第二层存在叶节点则,一定集中在树右侧且连续,二最下面一层则集中在左侧且连续。
- 同样节点数的树,完全二叉树深度最小
满树是完全二叉树的子集
- 二叉树的遍历
- 前序:根节点左孩子右孩子
- 中序:左孩子根节点右孩子
- 后续:左孩子右孩子根节点
- 层次遍历:顾名思义吧
图
单独成文
查找
单独成文
排序
内排序:与下相反
外排序:需要辅助空间
排序方法:
1.冒泡排序:两两比较,不符合则交换,复杂度$O(n^2)$。
下面看看这两者的时间对比:# out:0.34375643730163574; 0.10623884201049805
def buddleSort(L):
n = len(L)
for i in range(n):
count = 0
for j in range(n - 1):
if L[j] > L[j + 1]:
c = L[j]
L[j] = L[j + 1]
L[j + 1] = c
count = 1
else:
pass
if count == 0:
# 如果序列不在发生改变跳出循环
break
return L
print(buddleSort([4, 3, 1, 2]))
# 升级算法:比如:
# 3, 2, 1, 4, 5 初始状态
# 2, 1, 3, 4, 5 第一个外层循环后,内层在j=2时后面的顺序不在改变
# 1, 2, 3, 4, 5 在第二外层,j=2和后面都不要在遍历了,前面一个外层已经知道
def buddleSort2(L):
n = len(L)
m = n
for i in range(n):
count = 1
# print('m', m, i)
flag = None
for j in range(m - 1):
if L[j] > L[j + 1]:
c = L[j]
L[j] = L[j + 1]
L[j + 1] = c
count = 0
flag = j + 1
else:
pass
# print('flag:', flag)
m = flag
if count:
# 如果序列不在发生改变跳出循环
break
return L
Z = [1, 4, 3, 2] + list(range(10, 1000000))
print(Z[:7])
s = time.time()
res = buddleSort(Z)
cost = time.time() - s
print(Z[:7]) # 行数调用的时候吧Z顺序已经改变了,fuck
Z = [1, 4, 3, 2] + list(range(10, 1000000))
s = time.time()
res2 = buddleSort2(Z)
cost2 = time.time() - s
print(cost, cost2)
print(res2[:7])
简单排序法:找到最小的排在第一位,在剩下的元素中在找最小的排在第二位。。。。。复杂度$O(n^2)$
直接插入排序:(利用链式存储)复杂度$O(n^2)$。参考
(http://bubkoo.com/2014/01/15/sort-algorithm/shell-sort/)
def insert(i, j, L):
# i < j
c = L[j]
for z in range(j, i - 1, -1):
L[z] = L[z - 1]
L[i] = c
return L
x = [1, 2, 4, 5]
print(insert(0, 3, x))
def InsertSort(L):
n = len(L)
if L[0] > L[1]:
L = insert(0, 1, L)
for i in range(2, n):
for z in range(i - 1, -1, -1):
if (L[i] < L[z]) and (L[i] >= L[z-1]):
L = insert(z, i, L)
return L
x = [1, 4, 1, 3, 4, 6, 2]
print(InsertSort(x))