说明:数据类型是一个值的集合和定义在此集合上一组操作(通常是增删改查或者操作读写的方法)的总称
数据类型:int、str、boolean、byte
说明:数据以什么方式构成,如何进行存储(数据结构是数据类型中的一种:结构类型)
数据结构:数组、栈、队列、链表、树、图、堆、散列表等
python数据结构:列表、集合、字典、元祖
进栈(压栈):push
出栈:pop
取栈顶:gettop
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Stack(object):
def __init__(self):
self.stack = [] # 初始化一个栈
def push(self,item): # 入栈
self.stack.append(item)
def gettop(self): # 获取栈顶元素
return self.stack[-1]
def pop(self): # 出栈
return self.stack.pop()
if __name__ == '__main__':
s = Stack()
s.push(1)
s.push(2)
print(s.stack)
def check_kuohao(s):
stack = []
for char in s:
if char in ['(','[','{']:
stack.append(char)
elif char == ')':
if len(stack)>0 and stack[-1] == '(':
stack.pop()
else:
return False
elif char == ']':
if len(stack) > 0 and stack[-1] == '[':
stack.pop()
else:
return False
elif char == '}':
if len(stack) > 0 and stack[-1] == '{':
stack.pop()
else:
return False
if len(stack) == 0:
return True
else:
return False
print(check_kuohao('(){}{}[]')) #True
from queue import Queue
#1. 基本FIFO队列 先进先出 FIFO即First in First Out,先进先出
#2. maxsize设置队列中,数据上限,小于或等于0则不限制,容器中大于这个数则阻塞,直到队列中的数据被消掉
q = Queue(maxsize=0)
#3. 写入队列数据
q.put(0)
q.put(1)
q.put(2)
#4. 输出当前队列所有数据
print(q.queue)
#5. 删除队列数据,并返回该数据
q.get()
#6. 输也所有队列数据
print(q.queue)
队列主要的功能是在多个进程间共享数据,实现业务解耦,提高效率
生产者线程只需要把任务放入队列中,消费者线程只需要到队列中取数据进行处理
class Node(object):
def __init__(self, item,next=None):
self.item = item
self.next = next
l = Node(1,Node(2,Node(3,Node(4))))
print(l.item)
print(l.next.item)
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node(object):
def __init__(self, item):
self.item = item
self.next = None
class DLinkList(object):
def __init__(self):
self._head = None
def is_empty(self):
return self._head == None
def append(self, item):
'''尾部追加元素'''
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
cur.next = node
def add(self, item):
"""头部插入元素"""
node = Node(item)
if self.is_empty():
self._head = node # 如果是空链表,将_head指向node
else:
node.next = self._head # 将node的next指向_head的头节点
self._head = node # 将_head 指向node
def travel(self):
cur = self._head
while cur != None:
print cur.item,
cur = cur.next
print ""
def remove(self, item):
"""删除元素"""
if self.is_empty():
return
else:
cur = self._head
if cur.item == item:
# 如果首节点的元素即是要删除的元素
if cur.next == None: # 如果链表只有这一个节点
self._head = None
else: # 将_head指向第二个节点
self._head = cur.next
return
while cur != None:
if cur.next.item == item:
cur.next = cur.next.next
break
cur = cur.next
def insert(self, pos, item):
"""在指定位置添加节点"""
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
cur = self._head
count = 0
# 移动到指定位置的前一个位置
while count < (pos - 1):
count += 1
cur_next = cur.next
# 将node的next指向cur的下一个节点
cur.next = node
node.next = cur_next
def length(self):
"""返回链表的长度"""
cur = self._head
count = 0
while cur != None:
count += 1
cur = cur.next
return count
if __name__ == '__main__':
ll = DLinkList()
# 1、将链表后面追加三个元素:1,2,3
ll.append(1)
ll.append(2)
ll.append(3)
ll.travel() # 1 2 3
# 2、将链表头部插入一个元素:0
ll.add(0)
ll.travel() # 1 2 3 ==> 0 1 2 3
# 3、删除链表中的元素:3
ll.remove(3)
ll.travel() # 0 1 2 3 ==> 0 1 2
# 4、在链表的第2号位置插入元素:8
ll.insert(2,8)
ll.travel() # 0 1 2 ==> 0 8 1 2
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def list_reverse(head):
if head == None:
return None
L, R, cur = None, None, head # 左指针、有指针、游标
while cur.next != None:
L = R # 左侧指针指向以前右侧指针位置
R = cur # 右侧指针前进一位指向当前游标位置
cur = cur.next # 游标每次向前进一位
R.next = L # 右侧指针指向左侧实现反转
cur.next = R # 当跳出 while 循环时 cur(原链表最后一个元素) R(原链表倒数第二个元素)
return cur
if __name__ == '__main__':
'''
原始链表:1 -> 2 -> 3 -> 4
反转链表:4 -> 3 -> 2 -> 1
'''
l1 = Node(1)
l1.next = Node(2)
l1.next.next = Node(3)
l1.next.next.next = Node(4)
l = list_reverse(l1)
print l.val # 4 反转后链表第一个值4
print l.next.val # 3 第二个值3
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
# 归并法: 对链表排序
class Solution:
def sortList(self, head):
if head is None or head.next is None:
return head
pre = head
slow = head # 使用快慢指针来确定中点
fast = head
while fast and fast.next:
pre = slow
slow = slow.next
fast = fast.next.next
left = head
right = pre.next
pre.next = None # 从中间打断链表
left = self.sortList(left)
right = self.sortList(right)
return self.merge(left, right)
def merge(self, left, right):
pre = ListNode(-1)
first = pre
while left and right:
if left.val < right.val:
pre.next = left
pre = left
left = left.next
else:
pre.next = right
pre = right
right = right.next
if left:
pre.next = left
else:
pre.next = right
return first.next
node1 = ListNode(4)
node2 = ListNode(3)
node3 = ListNode(2)
node4 = ListNode(1)
node1.next = node2
node2.next = node3
node3.next = node4
s = Solution()
result = s.sortList(node1)
while (result != None):
print result.val, # 1 2 3 4
result = result.next
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def mergesort(seq):
if len(seq) <= 1:
return seq
mid = int(len(seq) / 2)
left = mergesort(seq[:mid])
right = mergesort(seq[mid:])
return merge(left, right)
def merge(left, right):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
if __name__ == '__main__':
seq = [10,4,6,3,8,2,5,7]
print mergesort(seq) # [2, 3, 4, 5, 6, 7, 8, 10]
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node(object):
"""双向链表节点"""
def __init__(self, item):
self.item = item
self.next = None
self.prev = None
class DLinkList(object):
"""双向链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
return self._head == None
def length(self):
"""返回链表的长度"""
cur = self._head
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self._head
while cur != None:
print cur.item,
cur = cur.next
print ""
def add(self, item):
"""头部插入元素"""
node = Node(item)
if self.is_empty():
# 如果是空链表,将_head指向node
self._head = node
else:
# 将node的next指向_head的头节点
node.next = self._head
# 将_head的头节点的prev指向node
self._head.prev = node
# 将_head 指向node
self._head = node
def append(self, item):
"""尾部插入元素"""
node = Node(item)
if self.is_empty():
# 如果是空链表,将_head指向node
self._head = node
else:
# 移动到链表尾部
cur = self._head
while cur.next != None:
cur = cur.next
# 将尾节点cur的next指向node
cur.next = node
# 将node的prev指向cur
node.prev = cur
def search(self, item):
"""查找元素是否存在"""
cur = self._head
while cur != None:
if cur.item == item:
return True
cur = cur.next
return False
def insert(self, pos, item):
"""在指定位置添加节点"""
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
cur = self._head
count = 0
# 移动到指定位置的前一个位置
while count < (pos - 1):
count += 1
cur = cur.next
# 将node的prev指向cur
node.prev = cur
# 将node的next指向cur的下一个节点
node.next = cur.next
# 将cur的下一个节点的prev指向node
cur.next.prev = node
# 将cur的next指向node
cur.next = node
def remove(self, item):
"""删除元素"""
if self.is_empty():
return
else:
cur = self._head
if cur.item == item:
# 如果首节点的元素即是要删除的元素
if cur.next == None:
# 如果链表只有这一个节点
self._head = None
else:
# 将第二个节点的prev设置为None
cur.next.prev = None
# 将_head指向第二个节点
self._head = cur.next
return
while cur != None:
if cur.item == item:
# 将cur的前一个节点的next指向cur的后一个节点
cur.prev.next = cur.next
# 将cur的后一个节点的prev指向cur的前一个节点
cur.next.prev = cur.prev
break
cur = cur.next
if __name__ == "__main__":
ll = DLinkList()
ll.add(1)
ll.add(2)
# ll.append(3)
# ll.insert(2, 4)
# ll.insert(4, 5)
# ll.insert(0, 6)
# print "length:",ll.length()
# ll.travel()
# print ll.search(3)
# print ll.search(4)
# ll.remove(1)
print "length:",ll.length()
ll.travel()
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node(object):
def __init__(self, item):
self.item = item
self.next = None
self.prev = None
class DLinkList(object):
def __init__(self):
self._head = None
def is_empty(self):
return self._head == None
def append(self, item):
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
cur.next = node
node.prev = cur
def travel(self):
cur = self._head
while cur != None:
print cur.item,
cur = cur.next
if __name__ == '__main__':
ll = DLinkList()
ll.append(1)
ll.append(2)
ll.append(3)
# print ll._head.item # 打印第一个元素:1
# print ll._head.next.item # 打印第二个元素:2
# print ll._head.next.next.item # 打印第三个元素:3
ll.travel() # 1 2 3
数组定义
python中list与数组比较
注:字典类型是Python中最常用的数据类型之一,它是一个键值对的集合,字典通过键来索引,关联到相对的值,理论上它的查询复杂度是 O(1)
哈希表 (hash tables)
哈希表(也叫散列表),根据关键值对(Key-value)而直接进行访问的数据结构。
它通过把key和value映射到表中一个位置来访问记录,这种查询速度非常快,更新也快。
而这个映射函数叫做哈希函数,存放值的数组叫做哈希表。
通过把每个对象的关键字k作为自变量,通过一个哈希函数h(k),将k映射到下标h(k)处,并将此对象存储在这个位置。
具体操作过程
数据添加:把key通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,
将value存储在以该数字为下标的数组空间里。
数据查询:再次使用哈希函数将key转换为对应的数组下标,并定位到数组的位置获取value。
**{“name”:”zhangsan”,”age”:26} 字典如何存储的呢? **
二叉树是一种特殊的树,二叉树的特点是每个结点最多有两个儿子。
二叉树使用范围最广,一颗多叉树也可以转化为二叉树。
二叉树中每个内部节点都有两个儿子,满二叉树所有的叶节点都有相同的深度。
满二叉树是一棵深度为h且有2h−1个结点的二叉树。
如果一棵完全二叉树的父节点编号为K,则其左儿子的编号是2K,右儿子的结点编号为2K+1
已知完全二叉树的总节点数为n求叶子节点个数:
当n为奇数时:(n+1)/2
当n为偶数时 : (n)/2
已知完全二叉树的总节点数为n求父节点个数:为:n/2
已知完全二叉树的总节点数为n求叶子节点为2的父节点个数:
当n为奇数时:n/2
当n为偶数时 : n/2-1
如果一棵完全二叉树有N个结点,那么这棵二叉树的深度为【log2(N+1)log2(N+1)】(向上取整)
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node:
def __init__(self,value=None,left=None,right=None):
self.value=value
self.left=left #左子树
self.right=right #右子树
if __name__=='__main__':
root=Node('D',Node('B',Node('A'),Node('C')),Node('E',right=Node('G',Node('F'))))
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node:
def __init__(self,value=None,left=None,right=None):
self.value=value
self.left=left #左子树
self.right=right #右子树
def preTraverse(root):
'''
前序遍历
'''
if root==None:
return
print(root.value)
preTraverse(root.left)
preTraverse(root.right)
if __name__=='__main__':
root=Node('D',Node('B',Node('A'),Node('C')),Node('E',right=Node('G',Node('F'))))
print('前序遍历:')
preTraverse(root) # DBACEGF
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node:
def __init__(self,value=None,left=None,right=None):
self.value=value
self.left=left #左子树
self.right=right #右子树
def midTraverse(root):
'''
中序遍历
'''
if root == None:
return
midTraverse(root.left)
print(root.value)
midTraverse(root.right)
if __name__=='__main__':
root=Node('D',Node('B',Node('A'),Node('C')),Node('E',right=Node('G',Node('F'))))
print('中序遍历:')
midTraverse(root) # ACBFGED
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node:
def __init__(self,value=None,left=None,right=None):
self.value=value
self.left=left #左子树
self.right=right #右子树
def afterTraverse(root):
'''
后序遍历
'''
if root == None:
return
afterTraverse(root.left)
afterTraverse(root.right)
print(root.value)
if __name__=='__main__':
root=Node('D',Node('B',Node('A'),Node('C')),Node('E',right=Node('G',Node('F'))))
print('后序遍历:')
afterTraverse(root) # ACBFGED
前序排列原理:
#####此时执行preTraverse(root.left) 函数
'''
1、第一步 root=Node(D) print D,D入栈[D]
2、第二步 root=Node(D).left=Node(B) print B, B入栈[D,B]
3、第三步 root=Node(B).left=Node(A) print A, A入栈[D,B,A]
4、第四步 root=Node(A).left=None,没有进入递归,顺序执行preTraverse(root.right)
5、第五步 Node(A).right==None,也没有进入递归,此时preTraverse(A) 函数才会正真返回,A出栈[D,B]
6、第六步 A的上级调用函数为:preTraverse(B.left),所以接着会顺序执行preTraverse(B.right),B的左右节点访问后B出栈[D]
7、第七步 Node(B).right==Node(C) print C,C入栈[D,C]
8、第八步 Node(C).left==None, Node(C).right==None,访问完C的左右节点后函数返回C出栈,返回上级调用[D]
9、第九步 此时返回上级调用执行preTraverse(D.right)=Node(E) print E,D出栈,E入栈[E]
'''
'''此时输出结果:DBACE'''
#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Node:
def __init__(self,value=None,left=None,right=None):
self.value=value
self.left=left #左子树
self.right=right #右子树
def layered_print( root):
if not root:
return []
curLayer = [root] # 当前层的所有节点
while curLayer:
layerValue = [] # 当前层的值
nextLayer = [] # 下一层的所有节点
for node in curLayer: # 循环当前层所有节点并并获取所有value值
layerValue.append(node.value)
if node.left:
nextLayer.append(node.left) # 将当前层的左节点加入列表
if node.right:
nextLayer.append(node.right) # 将当前层的右节点加入列表
print layerValue # 打印当前层的值
curLayer = nextLayer # 将循环下移一层
'''
['D']
['B', 'E']
['A', 'C', 'G']
['F']
'''
if __name__=='__main__':
root=Node('D',Node('B',Node('A'),Node('C')),Node('E',right=Node('G',Node('F'))))
layered_print(root)
hash树描述(就是散列树)
散列树选择从2开始的连续质数来建立一个十层的哈希树。
第一层结点为根结点,根结点下有2个结点;
第二层的每个结点下有3个结点;
依此类推,即每层结点的子节点数目为连续的质数。
hash树特点
注:关系型数据库中,索引大多采用B/B+树来作为存储结构,而全文搜索引擎的索引则主要采用hash的存储结构,这两种数据结构有什么区别?
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;
当然了,这个前提是,键值都是唯一的,如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,
有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
建立hash树
选择从2开始的连续质数来建立一个十层的哈希树。
第一层结点为根结点,根结点下有2个结点;第二层的每个结点下有3个结点;
依此类推,即每层结点的子节点数目为连续的质数。到第十层,每个结点下有29个结点。
同一结点中的子结点,从左到右代表不同的余数结果。
例如:第二层结点下有三个子节点。那么从左到右分别代表:除3余0,除3余1,除3余2.对质数进行取余操作得到的余数决定了处理的路径。
其实也可以把所有的键-值节点放在哈希树的第10层叶节点处,这第10层的满节点数就包含了所有的整数个数,
但是如果这样处理的话,所有的非叶子节点作为键-值节点的索引,这样使树结构庞大,浪费空间。
查找编辑
删除编辑
哈希树的节点删除过程也很简单,哈希树在删除的时候,并不做任何结构调整。
只是先查到到要删除的节点,然后把此节点的“占位标记”置为false即可(即表示此节点为空节点,但并不进行物理删除)。
hash树优点
从删除算法中可以看出,哈希树在删除的时候,并不做任何结构调整。
常规树结构在增加元素和删除元素的时候都要做一定的结构调整,否则他们将可能退化为链表结构,而导致查找效率的降低。
哈希树采取的是一种“见缝插针”的算法,从来不用担心退化的问题,也不必为优化结构而采取额外的操作,因此大大节约了操作时间。
缺点编辑
哈希树不支持排序,没有顺序特性。
如果在此基础上不做任何改进的话并试图通过遍历来实现排序,那么操作效率将远远低于其他类型的数据结构。
hash索引使用范围
总结:哈希适用在小范围的精确查找,在列数据很大,又不需要排序,不需要模糊查询,范围查询时有用
hash索引仅满足“=”、“IN”和“<=>”查询,不能使用范围查询
因为hash索引比较的是经常hash运算之后的hash值,因此只能进行等值的过滤,不能基于范围的查找,
因为经过hash算法处理后的hash值的大小关系,并不能保证与处理前的hash大小关系对应。
hash索引无法被用来进行数据的排序操作
由于hash索引中存放的都是经过hash计算之后的值,而hash值的大小关系不一定与hash计算之前的值一样,
所以数据库无法利用hash索引中的值进行排序操作。
对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,
而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。
这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
相关博客:https://blog.csdn.net/Hanmin_hm/article/details/104572959