• Title


    一、算法

    1.算法的时间复杂度

    大 O 记法,是描述算法复杂度的符号
    O(1)
      常数复杂度,最快速的算法。
      取数组第 1000000 个元素
      字典和集合的存取都是 O(1)
      数组的存取是 O(1)

    O(logN)
      对数复杂度
      假设有一个有序数组,以二分法查找

    O(n)
      线性复杂度
      假设有一个数组,以遍历的方式在其中查找元素 最坏情况是全部过一遍

    O(nlogn)
      求两个数组交集,其中一个是有序数组
      A 数组每一个元素都要在 B 数组中进行查找操作
      每次查找如果使用二分法则复杂度是 logN

    O(N^2)
      平方复杂度
      求两个无序数组的交集

    2.常见问题
    """
    常见问题:
    O(1)和O(n)区别是什么?
        O(1) 是确定的
        O(n) 是随着数据规模增长而增长的  最坏情况是全部过一遍
        
    如果第一次就找到了 还是O(n)吗?
        我们衡量的是性质 不是特例 以最坏情况来计算
        
    复杂度就意味着查询时间的多少吗? 常数复杂度是直接取第一百万个元素还是按顺序来?
        程序中循环的次数,而非真正意义上的时间,因为不同的机器可能执行相同的程序所耗时不同
        常数复杂度是说 执行的时间 是确定的
        
    数组的长度不一样,存取元素的时间复杂度是不一样的吗?
        数组是一个随机存取的数据结构
        无论多长, 存取元素的时间都是确定的, O(1)
    """
    # 都是O(1)
    # def fun1():
    #     for i in range(1000000):
    #         print(i)
    #     for i in range(1000000):
    #         print(i*2)
    #
    # def fun2():
    #     for i in range(1000000):
    #         print(i)
    #         print(i*2)

    二、数据结构基础

    1.数组

      连续的一块内存,存取元素时间是 O(1)插入、删除是 O(n)

    2.链表

      像火车,车厢挂车厢

      以下标方式读取元素的时间是 O(n)

      插入、删除是 O(1)

      栈和队列是链表的改良型

      栈:先进后出(压弹夹)

      队列:先进先出(银行取号排队)

    class Node():
        def __init__(self, element=None):
            self.e = element
            self.next = None
    
    head = Node()
    n1 = Node(111)
    n2 = Node(222)
    n3 = Node(333)
    n1.next = n2
    n2.next = n3
    
    head.next = n1
    
    def append(node, element):
        """
        我们往 node 的末尾插入一个元素
        :param node: 是一个 Node 实例
        :param element: 任意类型的元素
        """
        n = node
        # 这里不应该使用 while n.next: 这样的隐含类型转换来判定
        while n.next is not None:
            n = n.next
        # n 现在是最后一个元素
        new_node = Node(element)
        n.next = new_node
    
    def prepend(head, element):
        n = Node(element)
        n.next = head.next
        head.next = n
    
    def pop(head):
        """
        pop 是 stack 的两个操作之一
        push 入栈
        pop 出栈
    
        现在我们有这么 3 个函数
        append 添加一个元素到末尾
        prepend 添加一个元素到开头
        pop 删除并返回末尾的元素
    
        prepend + pop 就实现了 队列 的 入队(enqueue)和出队(dequeue)操作
        append + pop 就实现了 栈 的 入栈(push) 和 出栈(pop)操作
        """
        tail = head 
        while tail.next is not None:
            tail = tail.next
        # 现在 tail 是最后一个元素了
        n = head
        while n.next is not tail:
            n = n.next
        # 现在 n 是 tail 之前的元素了
        n.next = None
        return tail.e
    
    
    def log_list(node):
        n = node
        s = ''
        while n is not None:
            s += (str(n.e) + ' > ')
            n = n.next
        print(s)
    
    # log_list(n1)
    # append(n1, 'gua')
    # log_list(n1)
    # prepend(head, 'hello')
    # log_list(head)
    # print('pop head ', pop(head))
    # log_list(head)
    链表
    """
     
    队列的特点是「先进先出」,一般有这几个操作
    
    # enqueue 将一个元素存入队列中
    # dequeue 将一个元素从队列中取出,并在队列中删除它
    
    # empty 查看栈是否是空的
    
    
    可以把队列看做排队,银行叫号机就是队列,先取号的先入队,叫号的时候也就先出队
    
    """
    
    
    # Node类是一个节点,有两个属性,一个存储元素,一个存储指向另一个节点的引用
    class Node():
        def __init__(self, element=None, next=None):
            self.element = element
            self.next = next
    
        # 这个函数是会在print的时候被自动调用,就是把这个Node显示出来
        def __repr__(self):
            return str(self.element)
    
    
    class Myqueue():
        # 初始化函数,自动被调用
        # 初始化Queue()类的时候,它有2个属性,分别指向头尾
        def __init__(self):
            self.head = Node()
            self.tail = self.head
        
        # 如果head的next属性为空,则说明队列是空的
        def empty(self):
            return self.head.next is None
    
        # 创建一个node
        # 让tail.next指向它
        # 让tail指向它,tail现在就是新的队尾了
        def enqueue(self, element):
            n = Node(element)
            self.tail.next = n
            self.tail = n
    
        # 取出head.next指向的元素,如果队列不是空的,就让head.next指向node.next,这样node就不在队列中了
        def dequeue(self):
            node = self.head.next
            if not self.empty():
                self.head.next = node.next
            return node
    
    
    # 测试函数
    def test():
        q = Myqueue()
    
        q.enqueue(1)
        q.enqueue(2)
        q.enqueue(3)
        q.enqueue(4)
    
        print(q.dequeue())
        print(q.dequeue())
        print(q.dequeue())
        print(q.dequeue())
    
    
    if __name__ == '__main__':
        # 运行测试函数
        test()
    Myqueue
    """
     数据结构的核心是,一个结构只有特定的操作,可以实现特定的功能
    
     栈的特点是「先进后出」,一般有这几个操作
    # push 将一个元素存入栈中
    # pop  将一个元素从栈中取出,并在栈中删除它
    
    # top  将一个元素从栈中取出
    # is_empty 查看栈是否是空的
    
    """
    
    
    # Node类是一个节点,有两个属性,一个存储元素,一个存储指向另一个节点的引用
    class Node():
        def __init__(self, element=None, next=None):
            self.element = element
            self.next = next
    
        # 这个函数是会在print的时候被自动调用,就是把这个Node显示出来
        def __repr__(self):
            return str(self.element)
    
    
    class Mystack():
        # 初始化函数,自动被调用
        # 初始化Stack()类的时候,它有一个head属性,值是一个空的Node
        def __init__(self):
            self.head = Node()
        
        # 如果head的next属性为空,则说明栈是空的
        def is_empty(self):
            return self.head.next is None
    
        # 创建一个node,并让它指向当前head.next指向的元素,再把head.next指向它
        def push(self, element):
            self.head.next = Node(element, self.head.next)
    
        # 取出head.next指向的元素,如果栈不是空的,就让head.next指向node.next,这样node就不在栈中了
        def pop(self):
            node = self.head.next
            if not self.is_empty():
                self.head.next = node.next
            return node
    
        # head.next就是栈里面第一个元素
        def top(self):
            return self.head.next
    
    
    # 测试函数
    def test():
        s = Mystack()
    
        s.push(1)
        s.push(2)
        s.push(3)
        s.push(4)
    
        print(s.pop())
        print(s.pop())
        print(s.pop())
        print(s.pop())
    
    
    if __name__ == '__main__':
        # 运行测试函数
        test()
    Mystack
    3.字典(哈希表 对象 Map)

      把字符串转为数字作为下标存储到数组中

      字符串转化为数字的算法是 O(1)

      所以字典的存取操作都是 O(1)

      除非对数据有顺序要求,否则字典永远是最佳选择

      字符串转化为数字的算法

        1,确定数据规模,这样可以确定容器数组的大小 Size

        2,把字符当作 N 进制数字得到结果

          'qwe' 被视为 q* 1 + w * 10 + e* 100 得到结果 n

          n % Size 作为字符串在数组中的下标

          通常 Size 会选一个 素数

        3, 当下标冲突(冲突属于叫做碰撞)的时候, 我们有标准解决碰撞的办法

          链接法

    class HashTable(object):
        def __init__(self):
            # table 是用来存储数据的数组
            # 先让它有 10007 个格子好了
            # 上课的时候说过, 这个尺寸最好选素数
            # 这样可以得到更为合理的下标分布
            self.table_size = 10007
            self.table = [0] * self.table_size
    
        # 这个魔法方法是用来实现 in  not in 语法的
        def __contains__(self, item):
            return self.has_key(item)
            
        def has_key(self, key):
            """
            检查一个 key 是否存在, 时间很短, 是 O(1)
            如果用 list 来存储, 需要遍历, 时间是 O(n)
            """
            index = self._index(key)
            # 取元素
            v = self.table[index]
            if isinstance(v, list):
                # 检查是否包含我们要找的 key
                for kv in v:
                    if kv[0] == key:
                        return True
            # 如果得到的是 int 0 说明没找到, 返回 False
            # 如果得到的是 list 但是遍历结果没有我们要找的 key 也是没找到
            return False
    
        def _insert_at_index(self, index, key, value):
            # 检查下标处是否是第一次插入数据
            v = self.table[index]
            data = [key, value]
            # 也可以用这个判断 if v == 0:
            if isinstance(v, int):
                # 如果是第一次, 得到的是 int 0
                # 那么就插入一个 list 来存, 以后相同 key 的元素都放这里面
                # 注意我们把 key value 作为一个数组保存进去了, 这是因为
                # 会出现相同 hash 值的 key
                # 这时候就需要比较原始信息来找到相应的数据
                self.table[index] = [data]
            else:
                # 如果不是, 得到的会是 list, 直接 append
                self.table[index].append(data)
    
        def add(self, key, value):
            """
            add 函数往 hashtable 中加入一对元素
            我们先只支持字符串当 key; value是uuid生成的随机字符串
            """
            # 先计算出下标
            index = self._index(key)
            # 在下标处插入元素
            self._insert_at_index(index, key, value)
    
        def get(self, key, default_value=None):
            """
            这个和 dict 的 get 函数一样
            """
            index = self._index(key)
            # 取元素
            v = self.table[index]
            if isinstance(v, list):
                # 检查是否包含我们要找的 key
                for kv in v:
                    if kv[0] == key:
                        return kv[1]
            # 如果得到的是 int 0 说明没找到, 返回 default_value
            # 如果得到的是 list 但是遍历结果没有我们要找的 key 也是没找到
            return default_value
    
        def _index(self, key):
            # 先计算出下标
            return self._hash(key) % self.table_size
    
        def _hash(self, s):
            """
            # 输入一个字符串,生成一串对应的数字
            下划线开始的函数被我们视为私有函数
            但实际上还是可以在外部调用, 这只是一个给自己看的标记
            """
            n = 1
            f = 1
            for i in s:
                n += ord(i) * f
                f *= 10
            return n
    
    
    def test():
        import uuid
        names = [
            'gua',
            'xiao',
            'name',
            'web',
            'python',
        ]
        ht = HashTable()
        for key in names:
            value = uuid.uuid4()
            ht.add(key, value)
            print('add 元素', key, value)
        for key in names:
            v = ht.get(key)
            print('get 元素', key, v)
        print('魔法方法', 'gua' in ht)
    
    
    if __name__ == '__main__':
        test()
    hash
    4.搜索树
    class Tree(object):
        def __init__(self, element=None):
            self.element = element
            self.left = None
            self.right = None
    
        def traversal(self):
            """
            树的遍历, 是一个递归操作
            """
            print(self.element)
            if self.left is not None:
                self.left.traversal()
            if self.right is not None:
                self.right.traversal()
    
        def reverse(self):
            self.left, self.right = self.right, self.left
            if self.left is not None:
                self.left.reverse()
            if self.right is not None:
                self.right.reverse()
    
    
    def test():
        # 手动构建二叉树
        # 为什么手动这么麻烦呢, 因为一般都是自动生成的
        # 这里只需要掌握性质就好
        t = Tree(0)
        left = Tree(1)
        right = Tree(2)
        t.left = left
        t.right = right
        # 遍历
        t.traversal()
    
    
    if __name__ == '__main__':
        test()
    tree
  • 相关阅读:
    cmd中编译java
    java出错
    去掉开始菜单中新装程序的红色标记【Windows】
    Windows安装java
    看视频缓冲好还一卡一卡【解决】
    python图像卷积
    电脑硬盘、内存
    python 查询Neo4j多节点的多层关系
    python 快速排序实现
    python 二分法实现
  • 原文地址:https://www.cnblogs.com/guotianbao/p/8902673.html
Copyright © 2020-2023  润新知