数据结构

数据结构

  设计数据以何种方式组织起来并存储在计算机中。

  程序 = 数据结构 + 算法

数据结构分类

  线性结构:数据结构中的元素存在一对一相互关系  (节点前后有数据,前驱和后驱 链表、栈、队列、双向队列、数组等)

  树结构:数据结构中的元素存在一对多的相互关系  (树(堆))

  图结构:数据结构中的元素存在多对多的相互关系(图论、社交网络 微信推送好友这些都是图结构的)

  顺序表:数组、列表连续存储

  线性表:顺序表+链表合称线性表

定义:

  只能在一端进行插入或者删除操作

特点:

  后进先出(LIFO)

 

  常规操作:

    进栈:push

    出栈:pop

    取栈顶  gettop

  实现栈的一些的操作:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Stack(object):
 6     # 定义一个空栈
 7     def __init__(self):
 8         self.items = []
 9 
10     # 添加一个元素
11     def push(self, item):
12         self.items.append(item)
13 
14     # 删除一个元素
15     def pop(self):
16         return self.items.pop()
17 
18     # 返回栈顶
19     def peek(self):
20         return self.items[-1]
21 
22     # 清空栈
23     def clear(self):
24         del self.items
25 
26     # 判断是否为空
27     def isEmpty(self):
28         return self.items == []
29 
30     # 返回栈中元素个数
31     def size(self):
32         return len(self.items)
33 
34     # 打印栈
35     def print(self):
36         print(self.items)
37 
38 
39 if __name__ == '__main__':
40     s = Stack()
41     print(s.isEmpty())
42     for i in range(5):
43         s.push(i)
44     s.print()
45     s.pop()
46     s.print()
47     print(s.size())
48     print(s.peek())
49     s.clear()

  举个例子 给定一个序列ABC,判定序列是否是它的出栈序列?

  ABC ACB BCA BAC CBA都可以

  CAB不可能是出栈序列,违背栈特点(ABC可以进去一个出一个)

  卡特兰数:通项Cn+1 = C1Cn + C2Cn-1 + ... + CnC1

   Python中实现栈的例子:创建一个列表,append() pop()pop不能带参数,删除并取出的是最后一元素

队列/双向队列

定义:

  队列:

  仅允许在列表的一端进行插入,另一端进行删除操作

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

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

  双向队列:

  队列的两端都允许进行进队和出队操作

特点:

  先进先出(FIFO)

队列的实现:  

1 初步设想:列表+两个下标指针
2 创建一个列表和两变量,front指向队首,rear指向队尾,初始时:front rear都为0
3 进队操作:元素写到li[rear]的位置,rear自加1
4 出队操作:返回li[front]的元素,front自减1 

这样实现的缺点(浪费大量空间)

  

改进方式:将队列的队尾和队首连接起来,这样成了一个环形队列

环形队列的实现原理:  

1 当队尾指针rear==Maxsize+1,再前进一个位置就自动到0
2 实现方式:求余数
3 队首指针前进1:front = (front+1)%Maxsize
4 队尾指针前进1:rear = (reart+1)%Maxsize
5 队空条件:rear == front
6 队满条件:(rear+1)%Maxsize == front

实现队列:

  Python内置模块

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 # 多线程用queue
 6 import queue
 7 # 跟线程进程无关用deque
 8 from collections import deque
 9 q = deque()  # 双向队列
10 # 进队
11 q.append(1)
12 q.append(2)
13 q.append(3)
14 q.appendleft(0)
15 # print(q.pop()) # 右边出去
16 print(q.popleft())  # 出队左边出
17 
18 # 队列实现linux上head和tail
19 print(deque(
20     open('test', 'r', encoding='utf8'),
21     maxlen=5))

  自己简单实现

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Queue(object):
 6     # 定义一个空队列
 7     def __init__(self):
 8         self.items = []
 9 
10     # 队列(只能在队尾)添加一个元素
11     def enqueue(self, item):
12         self.items.append(item)
13 
14     # 删除队列(只能在对头)一个元素
15     def dequeue(self):
16         self.items.pop(0)
17 
18     # 判断队列是否为空
19     def isEmpty(self):
20         return self.items == []
21 
22     # 清空队列
23     def clear(self):
24         del self.items  # 该队列就不存在了,而不是清空元素
25 
26     # 返回队列项的数量
27     def size(self):
28         return len(self.items)
29 
30     # 打印队列
31     def print(self):
32         print(self.items)
33 
34 
35 if __name__ == '__main__':
36     q = Queue()
37     print(q.isEmpty())
38     for i in range(5):
39         q.enqueue(i)
40     print(q.size())
41     q.print()
42     q.dequeue()
43     q.print()
44     q.clear()
45     print(q)

两个栈实现队列

进队:1号栈进栈

出队:2号栈出栈,如果2号栈空,就去1号栈依次出栈并放在2号栈

1 1号:
2 [1,2,3,4,5,6]
3 2号
4 [4,5,6]
5 [1,2,3]->6,5,4
6 []->6,5,4,3,2,1
 1 class Queue:
 2     def __init__(self):
 3         self.stack1 = []
 4         self.stack2 = []
 5 
 6     def push(self, node):
 7         self.stack1.append(node)
 8 
 9     def pop(self):
10         if self.stack1 == []:
11             return None
12         else:
13             for i in range(len(self.stack1)):
14                 self.stack2.append(self.stack1.pop())
15             out = self.stack2.pop()
16             for j in range(len(self.stack2)):
17                 self.stack1.append(self.stack2.pop())
18             return out
19 
20 
21 if __name__ == '__main__':
22     times = 5
23     testList = list(range(times))
24     testQueue = Queue()
25     for i in range(times):
26         testQueue.push(testList[i])
27     print(testList)
28     for i in range(times):
29         a = testQueue.pop()
30         print(a, ',', end='')

链表 

定义

  通过各个节点之间的相互连接,最终串联成一个链表

  单链表

特性

  链表中每一个元素都是一个对象,每个对象称之为一个节点,包含有数据域key和指向下一个节点的指针next

链表的出现

  大多数原因可能因为顺序表(列表)插入删除慢,要对里边的元素进行挪动,时间复杂度被提高了

变量标识本质

 

 单链表的操作

1 is_empty
2 length
3 travel 遍历整个链表
4 add
5 append
6 insert 指定位置添加元素
7 remove
8 search 查找节点是否存在

遍历

 头插法

 尾插法

上边画图有点点问题,就是应该是第三个节点的链接区指向400节点的数据区才合适(画图没注意到)

插入

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 
  4 
  5 class Node(object):
  6     """节点类"""
  7     def __init__(self, elem):
  8         # 数据区
  9         self.elem = elem
 10         # 链表区
 11         self.next = None
 12 
 13 
 14 class SingleLinkList(object):
 15     """单链表"""
 16 
 17     def __init__(self, node=None):
 18         self.__head = node
 19 
 20     def is_empty(self):
 21         """判断链表是否为空"""
 22         return self.__head == None
 23 
 24     def length(self):
 25         """链表长度"""
 26         # cur游标
 27         cur = self.__head
 28         count = 0  # 刚好可以包含空链表的特殊情况
 29         while cur != None:
 30             count += 1
 31             cur = cur.next
 32         return count
 33 
 34     def travel(self):
 35         """遍历整个链表"""
 36         cur = self.__head
 37         while cur != None:
 38             print(cur.elem, end=" ")
 39             cur = cur.next
 40 
 41     def add(self, item):
 42         """链表头部添加元素 头插法"""
 43         # 能够处理原有链表是空链表
 44         node = Node(item)
 45         # 新建的node节点的链表区指向头节点
 46         node.next = self.__head
 47         self.__head = node
 48 
 49     def append(self, item):
 50         """链表尾部追加元素 尾插法"""
 51         node = Node(item)
 52         if self.is_empty():
 53             self.__head = node
 54         else:
 55             cur = self.__head
 56             while cur.next != None:
 57                 cur = cur.next
 58             cur.next = node
 59 
 60     def insert(self, pos, item):
 61         """
 62         指定位置添加元素
 63         :param pos 从0开始
 64         """
 65         # 小于0我认为头插法
 66         if pos <= 0:
 67             self.add(item)
 68         elif pos > self.length() - 1:
 69             self.append(item)
 70         else:
 71             pre = self.__head
 72             count = 0
 73             while count < (pos - 1):
 74                 count += 1
 75                 pre = pre.next
 76             # 当循环退出之后,pre指向pos-1位置
 77             node = Node(item)
 78             node.next = pre.next
 79             pre.next = node
 80 
 81     def remove(self, item):
 82         """删除节点"""
 83         # 创建双游标
 84         cur = self.__head
 85         pre = None  # 是cur游标的前驱位置
 86         while cur != None:
 87             if cur.elem == item:
 88                 # 先判断删除的节点是否是头节点
 89                 # 头节点 直接指向头节点下节点
 90                 if cur == self.__head:
 91                     self.__head = cur.next
 92                 else:
 93                     # 包含尾节点的特殊情况了
 94                     pre.next = cur.next
 95                 break
 96             else:
 97                 pre = cur
 98                 cur = cur.next
 99         return -1
100 
101     def search(self, item):
102         """查找节点是否存在"""
103         cur = self.__head
104         while cur != None:  # 如果条件cur.next != None这样可能会漏掉cur节点
105             if cur.elem == item:
106                 return True
107             else:
108                 cur = cur.next
109         return False

哈希表

直接寻址

  当关键字的全局u比较小时,直接寻址是一种简单而有效的方法

 

   直接寻址的缺点:

    当域u很大时,消耗大量内存  U很大,key很少

    无法处理关键字不是数字的情况

哈希

  直接寻址表:key为k的元素放到k位置上

  改进直接寻址表:

    构建大小为m的寻指表T

    key为k的元素放到h(k)位置上

    h(k)是一个函数,将域u映射到表T[0,1,2,3...,m-1]

哈希表定义

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

常见哈希函数  

  除法哈希 h(k)=k mod m
  乘法哈希 h(k)=floor(m(kA mod 1)) 0<A<1

解决哈希冲突

  线性探查:如果位置i被占用,探查i+1, i+2.....

  二次探查:如果位置i被占用,探查i+1^2,i-1^2,i+2^2,i-2^2.....

  二度哈希:有n个哈希函数,但是用第一个哈希函数h1发生冲突,则尝试使用h2,h3......

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

  装载因子:n/m n代表元素个数 m链表大小

Python 字典和集合都是哈希表来实现的
哈希的运用:

  1.哈希表,不同的key对应的值应该是平均分配(哈希函数)

  2.md5摘要算法 如果判断两个文件是不是相同的(两个文件大小2G),可以用但不一定正确,如果存在哈希冲突(2G 500T),但是概率太小了,假如有30位2**30那么2**31次方就有可能有冲突,单向的算法,但是目前用的最多的就是SHA-2系列

https://www.cnblogs.com/Alexephor/p/11379263.html  

二叉树

定义

  将二叉树的节点定义为一个对象,节点之间通过类似链表的连接方式来连接

二叉树的遍历 

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 
  4 
  5 class BiTreeNode(object):
  6     """
  7     二叉树定义
  8     """
  9 
 10     def __init__(self, data):
 11         self.data = data
 12         self.lchild = None
 13         self.rchild = None
 14 
 15 
 16 class BST(object):
 17     """
 18     二叉搜索树
 19     """
 20 
 21     def __init__(self, li):
 22         self.root = None
 23         # 把列表转成二叉树
 24         if li:
 25             for val in li:
 26                 self.insert(val)
 27 
 28     def insert(self, key):
 29         """
 30         我这里只存的key,当然也可以把key的值存起来
 31         :param key:
 32         :return:
 33         """
 34         if not self.root:
 35             self.root = BiTreeNode(key)
 36         else:
 37             p = self.root
 38             while p:
 39                 if key < p.data:  # key存储在左子树
 40                     if p.lchild:  # 如果左子树有节点,往左子树走
 41                         p = p.lchild
 42                     else:  # 若左子树为空,就插入到左孩子的位置
 43                         p.lchild = BiTreeNode(key)
 44                         break
 45                 elif key > p.data:  # 若key > p.data
 46                     if p.rchild:
 47                         p = p.rchild
 48                     else:
 49                         p.rchild = BiTreeNode(key)
 50                         break
 51                 else:  # 不考虑key==p.data 字典的key唯一的嘛  也不可能相同
 52                     break
 53 
 54     def traverse(self):
 55         def in_order(root):
 56             if root:
 57                 in_order(root.lchild)
 58                 print(root.data, end=',')
 59                 in_order(root.rchild)
 60         in_order(self.root)
 61 
 62     def query(self, key):
 63         p = self.root
 64         while p:
 65             if key < p.data:
 66                 p = p.lchild
 67             elif key > p.data:
 68                 p = p.rchild
 69             else:
 70                 return True
 71         return False
 72 
 73 
 74 tree = BST([5, 4, 1, 4, 9, 6, 3, 2])
 75 tree.traverse()
 76 print(tree.query(1))
 77 a = BiTreeNode('A')
 78 b = BiTreeNode('B')
 79 c = BiTreeNode('C')
 80 d = BiTreeNode('D')
 81 e = BiTreeNode('E')
 82 f = BiTreeNode('F')
 83 g = BiTreeNode('G')
 84 
 85 e.lchild = a
 86 e.rchild = g
 87 a.rchild = c
 88 c.lchild = b
 89 c.rchild = d
 90 g.rchild = f
 91 
 92 root = e
 93 
 94 
 95 def pre_order(root):
 96     """
 97     前序遍历
 98     :param root: 根节点
 99     :return:
100     """
101     if root:
102         print(root.data, end='')
103         pre_order(root.lchild)
104         pre_order(root.rchild)
105 
106 
107 def in_order(root):
108     """
109     中序遍历
110     :param root: 根节点
111     :return:
112     """
113     if root:
114         in_order(root.lchild)
115         print(root.data, end='')
116         in_order(root.rchild)
117 
118 
119 def last_order(root):
120     """
121     后序遍历
122     :param root: 根节点
123     :return:
124     """
125     if root:
126         last_order(root.lchild)
127         print(root.data, end='')
128         last_order(root.rchild)
129 
130 
131 def level_order(root):
132     from collections import deque
133     """
134     层次遍历
135     :param root: 根节点
136     :return: 
137     """
138     q = deque()
139     q.append(root)
140     while len(q) > 0:  # 不是空队
141         x = q.popleft()
142         print(x.data, end='')
143         if x.lchild:
144             q.append(x.lchild)
145         if x.rchild:
146             q.append(x.rchild)
147 
148 # pre_order(root)
149 # in_order(root)
150 # last_order(root)
151 # level_order(root)

 创建树的图

深度优先(代码p107)
  前序遍历找根:EACBDGF
  中序遍历找左右孩子:ABCDEGF
  后序遍历也定根:BDCAFGE
  进出栈思想推出序列
广度优先
  队列思想推出序列
  层次遍历:EAGCFBD   

二叉搜索树

  定义

    是一棵二叉树,且满足:设x是二叉树的一个节点,如果y是x的左子树的一个节点,那么y.key<=x.key;如果y是x右子树的一个节点,那么y.key>=x.key 

  二叉搜索树的创建(代码p16)
    二叉搜索树的遍历(中序遍历)
    二叉搜索树的查询/插入/删除

  删除三种情况    

  •  如果要删除的节点是叶子节点:直接删除
  •  如果要删除节点只有一个孩子:将此节点的父亲与孩子连接,然后删除该节点
  •  如果要删除的节点有两孩子:将其右子树的最小节点(该节点最多有一个右孩子)删除,并替换当前节点

  二叉搜索树的效率

     平均情况下,二叉搜索树进行搜索的时间复杂度为O(logn) 

     最坏的情况下,二叉搜索树可能非常偏斜

     解决方式:

      随机插入

      AVL树

  AVL树

    是一棵自平衡的二叉搜索树

    性质:根的左右子树的高度之差的绝对值不能超过1

       根的左右子树都是平衡二叉树

 持续更新中..................

原文地址:https://www.cnblogs.com/Alexephor/p/11414830.html