• 数据结构


    数据结构

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

      程序 = 数据结构 + 算法

    数据结构分类

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

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

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

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

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

    定义:

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

    特点:

      后进先出(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

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

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

  • 相关阅读:
    PAT B1027 打印沙漏 (20 分)
    PAT B1025 反转链表 (25 分)
    PAT B1022 D进制的A+B (20 分)
    PAT B1018 锤子剪刀布 (20 分)
    PAT B1017 A除以B (20 分)
    PAT B1015 德才论 (25 分)
    PAT B1013 数素数 (20 分)
    PAT B1010 一元多项式求导 (25 分)
    HDU 1405 The Last Practice
    HDU 1165 Eddy's research II
  • 原文地址:https://www.cnblogs.com/Alexephor/p/11414830.html
Copyright © 2020-2023  润新知