3.1 数据结构定义

数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。简单来说,数据结构就是设计数据以何种方式组织并存储再计算机中。比如:列表、集合与字典等都是一种数据结构。

数据结构按照其逻辑结构可分为线性结构、树结构、图结构。

  • 线性结构:数据结构中的元素存在一对一的相互关系
  • 树结构:数据结构中的元素存在一对多的相互关系
  • 图结构:数据结构中的元素存在多对多的相互关系

数组和列表有两点不同:1)数组的元素类型要相同;2)数组长度固定

3.2 栈

栈是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表

栈的特点:后进先出

栈的基本操作:

  • 进栈(压栈):push
  • 出栈:pop
  • 取栈顶:gettop
# 栈
class Stack:def __init__(self):self.stack = []def push(self, element):self.stack.append(element)def pop(self):return self.stack.pop()def get_top(self):if len(self.stack) > 0:return self.stack[-1]  # 取栈顶else:return Nonestack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())

括号匹配问题:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。

# 栈
class Stack:def __init__(self):self.stack = []def push(self, element):self.stack.append(element)def pop(self):return self.stack.pop()def get_top(self):if len(self.stack) > 0:return self.stack[-1]  # 取栈顶else:return Nonedef is_empty(self):return len(self.stack) == 0# stack = Stack()
# stack.push(1)
# stack.push(2)
# stack.push(3)
# print(stack.pop())
def brace_match(s):match = {'}' :'{', ']': '[', ')': '('}stack = Stack()for ch in s:if ch in {'(', '[', '{'}:stack.push(ch)else:  # ch in {'}', ']', ')'}if stack.is_empty():return Falseelif stack.get_top() == match[ch]:  # 栈顶元素是匹配的stack.pop()else:  # stack.get_top() != match[ch]return Falseif stack.is_empty():return Trueelse:return Falseprint(brace_match('[{([])}]'))
print(brace_match('[{([)}]'))

3.3 队列

队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。

进行插入的一端称为队尾(rear),插入动作称为进队或入队;

进行删除的一端称为队头(front),删除动作成为出队

队列的性质:先进先出

队列的实现方式--环形队列:当队尾指针front == Maxsize + 1时,再前进一个位置就自动到0。

队首指针前进1:front = (front + 1) % Maxsize

队尾指针前进1:rear = (rear + 1) % Maxsize

队空条件:rear == front

队满条件:(rear + 1) % Maxsize == front

双向队列:两端都支持进队和出队操作。

# 队列
class Queue:def __init__(self, size=100):  # 控制队列大小self.queue = [0 for _ in range(size)]self.size = sizeself.rear = 0  # 队尾指针self.front = 0  # 队首指针def push(self, element):if not self.is_filled():self.rear = (self.rear + 1) % self.sizeself.queue[self.rear] = elementelse:# 随意设置个报错raise IndexError('Queue is full.')def pop(self):if not self.is_empty():self.front = (self.front + 1) % self.sizereturn self.queue[self.front]else:raise IndexError('Queue is empty.')# 判空def is_empty(self):return self.rear == self.front# 判断队是否满def is_filled(self):return (self.rear + 1) % self.size == self.frontq = Queue(5)
for i in range(4):q.push(i)
print(q.is_filled())  # True
print(q.pop())  # 结果为0

3.4 python的队列内置模块

# 内置模块
from collections import deque
q = deque()
q.append(1)  # 队尾进队
print(q.popleft())  # 队首出队
# 用于双向队列
# q.appendleft(1)  # 队首进队
# q.pop()  # 队尾出队
q1 = deque([1, 2, 3])  # 创建了一个队列,并且有了三个元素
q1.append(4)
print(q1.popleft())
q2 = deque([1, 2, 3, 4], 5)  # 5表示队列的size
q2.append(5)  # q2已经满了,再进去一个元素5按道理会报错,但是在内置模块里,它会自动出队一个元素1,再入队元素5
print(q2.popleft())  # 出队元素为1

3.5 栈和队列的应用--迷宫问题

给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。

思考:

  • 利用栈--深度优先搜索:回溯法,从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。使用栈存储当前路径。(路径不一定最短)
# 用栈解决迷宫问题
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
# x,y四个方向,上:x-1,y;下:x+1,y;左:x,y-1;右:x,y+1
dirs = [lambda x, y: (x+1, y),  # 下lambda x, y: (x-1, y),  # 上lambda x, y: (x, y+1),  # 右lambda x, y: (x, y-1),  # 左
]
# x1 y1 x2 y2表示起点和终点位置
def maze_path(x1, y1, x2, y2):stack = []stack.append((x1, y1))  # 添加一个元组# 只要栈不空,栈空表示没有路可以走while(len(stack) > 0):curNode = stack[-1]  # 当前节点# 当前节点是否为终点if curNode[0] == x2 and curNode[1] == y2:# 到终点了for p in stack:print(p)return True  # 有路径就返回True# curNode[0]是x,curNode[1]是y# 搜索四个方向for dir in dirs:nextNode = dir(curNode[0], curNode[1])  # 当前列表节点# 如果下一个节点能走if maze[nextNode[0]][nextNode[1]] == 0:stack.append(nextNode)# 不能走回头路maze[nextNode[0]][nextNode[1]] = 2  # 2表示走过了break  # 能找到一个能走的点就退出else:maze[nextNode[0]][nextNode[1]] = 2stack.pop()else:print('没有路')return Falsemaze_path(1, 1, 8, 8)
  • 利用队列--广度优先搜索:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。使用队列存储当权正在考虑的节点。(路径最短)
# 用队列解决迷宫问题
from collections import deque
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
# x,y四个方向,上:x-1,y;下:x+1,y;左:x,y-1;右:x,y+1
dirs = [lambda x, y: (x+1, y),  # 下lambda x, y: (x-1, y),  # 上lambda x, y: (x, y+1),  # 右lambda x, y: (x, y-1),  # 左
]def print_r(path):# path是很多路径的,我们要找到是最少路径的curNode = path[-1]realpath = []  # 记录最少的路径while curNode[2] != -1:  # 当位置不是最起始节点realpath.append(curNode[0:2])  # 就添加进去curNode = path[curNode[2]]  # 再换下一个节点realpath.append(curNode[0:2])  # 最后加上起点realpath.reverse()  # 倒序for node in realpath:print(node)def maze_path_queue(x1, y1, x2, y2):queue = deque()queue.append((x1, y1, -1))  # 起点的位置是-1path = []  # 来记pop出来的节点# 队不空就循环,队空就表示没有路了while len(queue) > 0:curNode = queue.pop()  # 把当前节点存在curNodepath.append(curNode)if curNode[0] == x2 and curNode[1] == y2:# 找到终点了print_r(path)return Truefor dir in dirs:# 下一个节点nextNode = dir(curNode[0], curNode[1])# 下一个节点能走if maze[nextNode[0]][nextNode[1]] == 0:# nextNode是由curNode带进来的,所以要记住带它来的那个节点的位置,为了后面能找到回来的路径queue.append((nextNode[0], nextNode[1], len(path) - 1))maze[nextNode[0]][nextNode[1]] = 2  # 表示已经走过else:print('没有路')return Falsemaze_path_queue(1, 1, 8, 8)

3.6 链表

链表是由一系列节点组成的元素集合。每个节点包含两部分,数据域item和指向下一个节点的指针next。通过节点之间的相互连接,最终串联成一个链表。

# 链表
class Node:def __init__(self, item):self.item = itemself.next = None
a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c
print(a.next.item)  # b
print(a.next.next.item)  # c

3.6.1 创建链表

  • 头插法
  • 尾插法
def create_linklist_head(li):head = Node(li[0])  # 头节点for element in li[1:]:node = Node(element)  # 创建了一个新节点node.next = headhead = nodereturn head# 尾插法
def create_linklist_tail(li):head = Node(li[0])tail = headfor element in li[1:]:node = Node(element)tail.next = nodetail = nodereturn headdef print_linklist(lk):while lk:print(lk.item, end=' ')lk = lk.nextprint()lk = create_linklist_head([1, 2, 3])
# print(lk.item)
# print(lk.next.item)
print_linklist(lk)  # 倒序的
lk1 = create_linklist_tail([1, 2, 3, 4, 5, 6])
print_linklist(lk1)

遍历:

3.6.2 链表的插入和删除

假设插入一个p,语句为:

p.next = curNode.next

curNode.next = p

假设删除一个p,语句为:

p = curNode.next

curNode.next = curNode.next.next

del p

3.6.3 双链表

双链表的每个节点有两个指针:一个指向后一个节点,另一个指向前一个节点。

class Node(object):def __init__(self, item):self.item = itemself.next = Noneself.prior = None

双链表的插入:

p.next = curNode.next
curNode.next.prior = p
p.prior = curNode
curNode.next = p

双链表的删除:

p = curNode.next
curNode.next = p.next、
p.next.prior = curNode
del p

3.7 总结

顺序表(列表/数组)与链表对比

  • 按元素查找:顺序表和链表都是O(n)
  • 按下标查找:顺序表快O(1),链表O(n)
  • 在某元素后插入:顺序表O(n),链表O(1)
  • 删除某元素:顺序表O(n),链表O(1)

链表的内存可以更灵活的分配而且链表的链式存储的数据结构对树和图的结构有很大的启发性。

3.8 哈希表

哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:

insert(key, value):插入键值对(key, value)

get(key):如果存在键为key的键值对则返回其value,否则返回空值

delete(key):删除键为key的键值对

直接寻址表:

缺点:

  • 当域U很大时,需要消耗大量内存,很不实际;
  • 如果域U很大而实际出现的key很少,则大量空间被浪费;
  • 无法处理关键字不是数字的情况。

对直接寻址表做改造->>>就成了哈希表

哈希(Hashing):

  • 构建大小为m的寻址表T;
  • key为k的元素放在h(k)位置上;
  • h(k)是一个函数,其将域U映射到T[0, 1, ..., m-1]。

哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下表。

假设有一个长度为7的哈希表,哈希函数h(k) = k%7。元素集合{14,22, 3, 5}的存储方式如图:

但是会出现一个新的问题:哈希冲突!

由于哈希表的大小是有限的,而要存储的值得总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突。

比如h(k) = k % 7,h(0) = h(7)=h(14)=...

1.解决哈希冲突的方法--开放寻址法

开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。

  • 线性探查:如果i被占用,则探查i+1, i+2, ....
  • 二次探查:如果位置i被占用,则用探查i+,i-, i+, i-, ....
  • 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2, h3, ...

2.解决哈希冲突的方法--拉链法

拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。

常见的哈希函数:

  • 除法哈希法:h(k) = k% m
  • 乘法哈希法:h(k) = floor(m*(A*key%1))
  • 全域哈希法:mod p) mod m  a,b=1, 2, ..., p-1
# 拉链法
class linklist:class Node:def __init__(self, item=None):self.item = itemself.next = None# 迭代器类,为了避免遍历链表总是要循环去遍历class linklistIterator:def __init__(self, node):self.node = nodedef __next__(self):if self.node:cur_node = self.nodeself.node = cur_node.nextreturn cur_node.itemelse:raise StopIterationdef __iter__(self):return self# iterable表示列表def __init__(self, iterable=None):self.head = Noneself.tail = Noneif iterable:self.extend(iterable)# 尾插法def append(self, obj):s = linklist.Node(obj)if not self.head:self.head = sself.tail = selse:self.tail.next = sself.tail = sdef extend(self, iterable):for obj in iterable:self.append(obj)def find(self, obj):for n in self:if n == obj:return Trueelse:return False# 返回列表迭代器def __iter__(self):return self.linklistIterator(self.head)# 返回字符串def __repr__(self):return "<<" + ",".join(map(str, self)) + ">>"# lk = linklist([1, 2, 3, 4, 5])
# print(lk)  # <<1,2,3,4,5>> 直接打印链表
# for element in lk:
#     print(element)# 类似集合,不允许重复
class HashTable:def __init__(self, size=101):self.size = sizeself.T = [linklist() for i in range(self.size)]  # 空列表def h(self, k):return k % self.sizedef insert(self, k):i = self.h(k)if self.find(k):print("Duplicated Insert.")else:self.T[i].append(k)def find(self, k):i = self.h(k)return self.T[i].find(k)ht = HashTable()
ht.insert(0)
ht.insert(1)
ht.insert(3)
ht.insert(102)
print(','.join(map(str, ht.T)))
print(ht.find(4))

哈希表的应用--集合和字典

字典与集合都是通过哈希表来实现的。例如:a = {'name': 'Alice', 'age': 18, 'gender': 'Man'}

使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假设h('name') = 3, h('age') = 1, h('gender') = 4,则哈希表存储为[None, 18, None, 'Alice', 'Man']

如果发生哈希冲突,则通过拉链法或开放寻址法解决。

哈希表的应用--md5算法

MD5(Message-Digest Algorithm 5)曾经是密码学中常用的哈希函数,可以把任意长度的数据映射为128位的哈希值,其曾经包含如下特征:

  • 同样的消息,其MD5值必定相同;
  • 可以快速计算出任意给定消息的MD5值;
  • 除非暴力的枚举所有可能的消息,否则不可能从哈希值反推出消息本身;
  • 两条消息之间即使只有微小的差别,其对应的MD5值也应该是完全不同、完全不相关的;
  • 不能在有意义的时间内人工的构造两个不同的消息,使其具有相同的MD5值。、

哈希表的应用--SHA2算法(和MD5类似的性质)

3.Python数据结构相关推荐

  1. python3 namedtuple_你不能低估的Python数据结构Namedtuple(二)

    上篇你不能低估的Python数据结构Namedtuple(一)讲了namedtuple的一些基本用法,本篇继续. namedtuples和数据类(Data Class)之间有什么区别? 功能 在Pyt ...

  2. python线性表和队列_[笔记]python数据结构之线性表:linkedlist链表,stack栈,queue队列...

    python数据结构之线性表 python内置了很多高级数据结构,list,dict,tuple,string,set等,在使用的时候十分舒心.但是,如果从一个初学者的角度利用python学习数据结构 ...

  3. python数据结构 树_python数据结构之二叉树的建立实例

    先建立二叉树节点,有一个data数据域,left,right 两个指针域 复制代码 代码如下: # -*- coding: utf - 8 - *- class TreeNode(object): d ...

  4. python数据结构包括什么_Python中的数据结构详解

    概述 在深入研究数据科学和模型构建之前,Python中的数据结构是一个需要学习的关键内容 了解Python提供的不同数据结构,包括列表.元组等 介绍 数据结构听起来是一个非常直截了当的话题,但许多数据 ...

  5. python数据结构树和二叉树,python数据结构树和二叉树简介

    一.树的定义 树形结构是一类重要的非线性结构.树形结构是结点之间有分支,并具有层次关系的结构.它非常类似于自然界中的树. 树的递归定义: 树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树 ...

  6. python数据结构推荐书-关于数据结构,有哪些不错的参考书推荐?

    学习编程,数据结构是你必须要掌握的基础知识,那么数据结构到底是什么呢? 其实数据结构就是用来描述计算机里存储数据的一种数学模型,因为计算机里要存储很多乱七八糟的数据,所以也需要不同的数据结构来描述. ...

  7. CodeSalt | Python数据结构的实现 — 链表

    Python数据结构实现-链表 1. 简单介绍 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Point ...

  8. Python数据结构——tuple

    tuple 元组 什么是tuple 元组是一种和列表非常相似的线性数据结构,也支持不同的数据类型. 最大的区别就是元组一旦创建不可改变,和string一样,所有改变元组内容的操作都会返回一个新的元组. ...

  9. Python数据结构——list

    list 列表 什么是list 列表是Python中特有的一种线性数据结构,列表是可变的,有序的,我们可以用选择操作符来改变任意位置的值,和数组不同的是,列表可以同时保存不同类型的元素(异构). 在C ...

  10. Python数据结构——array

    array 数组 array是什么 一般来说,array基本是所有程序语言都有的一种基础线性结构,元素以特定的顺序存储在一段连续的内存中. 在Python中其实也有array这种数据结构,和其他语言的 ...

最新文章

  1. 一文详解脑科学研究与产业发展方向
  2. 子弹短信体验分析:一个单纯想“快”的IM工具 子弹短信体验分析:锤科情怀缩影,打败微信有点远
  3. Common Lisp 函数 require 和 provide 源代码分析
  4. spark算子_Spark 性能优化(四)——程序开发调优
  5. pytorch torch.ones
  6. dw二级联动下拉菜单插件 宋君墨_Excel下拉菜单不会做?15秒教会你制作一二三级联动下拉菜单,从此做表不求人!...
  7. centos php日志分析,Centos日志查看分析
  8. Ionic 开发环境搭建
  9. java 的.class 反编译软件
  10. windows7梦幻桌面
  11. Mac 快捷键 桌面壁纸
  12. 【动态规划 记忆化搜索】JZOJ_6287 扭动的树
  13. 杭电2018复试上机真题
  14. 佛罗里达大学计算机专业世界排名,2020年中佛罗里达大学排名TFE Times美国最佳计算机科学硕士专业排名第107...
  15. CSS中的圆角与倒角
  16. 关于火狐IE浏览器的滚动条问题
  17. java虚拟机堆空间
  18. SuperSlide-v2.1.1
  19. 论文中图像三维重建的思路
  20. Thumbs.ms\com1.{d3e34b21-9d75-101a-8c3d-00aa001a1652}

热门文章

  1. BasicAnimation:纯Swift的基础动画库,支持 iOS 属性动画:缩放、旋转、平移、背景颜色、透明度、阴影等和弹性动画
  2. python放大局部图像(画中画形式展示)
  3. 微机原理与接口技术基本概念
  4. QPSK/DQPSK 调制解调系统仿真
  5. 博微光伏清单首版试用—新能源计价软件推新,为新能源建设发展提供新动能
  6. 在Win10以及SDK为33的环境下——小米便签项目的搭建
  7. [附源码]Python计算机毕业设计SSM驾校预约考试管理系统(程序+LW)
  8. 数字逻辑与数字电路指导(课后题)
  9. ldap 单点登录 php,LDAP用户验证功能简介
  10. 5亿用户呼唤“小度小度”,百度地图掌舵AI赛道