算法集锦(3)

查询

  • 顺序查询

过程:从头到为遍历数组,直到查询到所要查询的值未止

时间复杂度:O(n)

实现:for each循环遍历即可

  • 折半查询

目的:顺序查询效率慢,对顺序查询进行优化

前提:序列S已有序;存储采用数组

过程:

  1. 取数组中间值a,中间索引half,开始索引start,结尾索引end
  2. 若end = start + 1,跳出3循环,返回-1——未找到
  3. 若a满足要求,返回a的索引;若a<目的值,序列S缩短为S[start:end],重复1操作;若a>目的值,S缩短未S[half:end],重复1操作

代码实现:

class Search:

    @classmethod

    def half_search(cls, array, value):

        start = 0

        end = len(array)

        while start+1 < end:

            half = (start+end)>>1

            if array[half] == value:

                return half

            elif array[half] < value:

                start = half

            else:

                end = half

                pass

        if value == array[start]:

            return start           

        return -1
  • 插值查询

目的:对折半查找优化,当数据均匀分布时,查询效率高于折半查询

基础:序列S采用数组存储;序列已有序

思想:一本500页的书(数组,已排序),若想找到第10页内容(查询数据),我们第10页定位在前面一部分范围内

区别:折半查找每次选取数组中位数作为分割点进行比较,插值法则是自适应的选择分割点。

代码实现:

     

import random

from collections import Iterable



array = [x for x in range(0, 100)]

random.shuffle(array)

class Search:



    @classmethod

    def insert_search(cls, array, key):

        start = 0

        end = len(array)

        # 自适应计算中间点,而不是傻瓜式取最中间

        while start+1 < end:

            half = start + int((key-array[start])/(array[end-1] - array[start]) * (end-start))

            if half >= end or half < start:

                return -1

            if array[half] == key:

                return half

            elif array[half] < key:

                # 问题补丁(若key不存在,且key位于array[start: end]区间内,

                #  

                if start == half:

                    return -1

                start = half

            else:

                end = half

                pass

        if key == array[start]:

            return start          

                if start == half:

                    return -1

                start = half

            else:

                end = half

                pass

        if key == array[start]:

            return start           
  • 斐波那契查询
  1. 类似折半查找和插值查找,确定mid方式不同
  2. 斐波那契序列a0 = 1, a1 = 1, an = an-1 + an-2, 当n-->无穷时,an-1/an趋近于黄金比例。
  3. 将序列S扩增到长度未an - 1(a为i斐波那契序列),mid取an-1 - 1
  1. 树查询

采用排序树数据结构,存储数据,加快查询性能(最大查询次数即为树的深度)。最基本的数据结构为二叉排序树。基于此优化,又有2-3查找树,红黑树,B/B+树,解决二叉排序树可能出现时间性能退化为O(n)的问题

二叉排序树

数据结构:二叉排序树

特点:任意节点值>其左子树所有值,且<右子树所有值

时间复杂度:二叉树为完全二叉树甚至时,查询性能最佳O(log2n)。但是,当树“歪”左或者“歪”右时,时间性能下降,最差退化为O(n)【所有数据都偏向一边,如同链表】。

空间复杂度:维护二叉树基本结构即可。

代码实现:

class BinaryTree:
    def __init__(self, val, **kargs):
        try:
            left = kargs['left']
            right = kargs['right']
        except KeyError:
            self.val = val
            self.left = None
            self.right = None
            return
        if not isinstance(left, BinaryTree) or not isinstance(right, BinaryTree):
            raise Exception('left or right must be the object of BinaryTree')
        self.left = left
        self.right = right
        self.val = val
    pass
    
    # 中序遍历
    def b_tree(self):
        if self.left is not None:
            self.left.b_tree()
        print(self.val)
        if self.right is not None:
            self.right.b_tree()

    def insert(self, val):
        if val < self.val:
            if self.left is None:
                left = BinaryTree(val)
                self.left = left
                return
            self.left.insert(val)
        elif val > self.val:
            if self.right is None:
                right = BinaryTree(val)
                self.right = right
                return
            self.right.insert(val)
        else:
            return
    
    def get(self, key):
        if key==self.val:
            return self
        elif key<self.val:
            if self.left is not None:
                return self.left.get(key)
        elif key>self.val:
            if self.right is not None:
                return self.right.get(key)
        return None
  1. 树查询

采用排序树数据结构,存储数据,加快查询性能(最大查询次数即为树的深度)。最基本的数据结构为二叉排序树。基于此优化,又有2-3查找树,红黑树,B/B+树,解决二叉排序树可能出现时间性能退化为O(n)的问题

二叉排序树

数据结构:二叉排序树

特点:任意节点值>其左子树所有值,且<右子树所有值

时间复杂度:二叉树为完全二叉树甚至时,查询性能最佳O(log2n)。但是,当树“歪”左或者“歪”右时,时间性能下降,最差退化为O(n)【所有数据都偏向一边,如同链表】。

空间复杂度:维护二叉树基本结构即可。

代码实现:

class BinaryTree:
    def __init__(self, val, **kargs):
        try:
            left = kargs['left']
            right = kargs['right']
        except KeyError:
            self.val = val
            self.left = None
            self.right = None
            return
        if not isinstance(left, BinaryTree) or not isinstance(right, BinaryTree):
            raise Exception('left or right must be the object of BinaryTree')
        self.left = left
        self.right = right
        self.val = val
    pass

    def b_tree(self):
        if self.left is not None:
            self.left.b_tree()
        if self.val is not None:
            print(self.val)
        if self.right is not None:
            self.right.b_tree()

    def insert(self, val):
        if val < self.val:
            if self.left is None:
                left = BinaryTree(val)
                self.left = left
                return
            self.left.insert(val)
        elif val > self.val:
            if self.right is None:
                right = BinaryTree(val)
                self.right = right
                return
            self.right.insert(val)
        else:
            return
    
    def get(self, key):
        if key==self.val:
            return self
        elif key<self.val:
            if self.left is not None:
                return self.left.get(key)
        elif key>self.val:
            if self.right is not None:
                return self.right.get(key)
        return None

二三树

数据结构:二三树。每个节点拥有哦2/3个字节点。

特色:

  1. 2节点:key1,左子树,右子树。Key1>左子树上所有key且<右子树上所有key,同普通二叉树。
  2. 3节点:key1,key2,左子树,中子树,右子树。Key1>左子树上所有key,key1>中子树上所有key>key2,key2<右子树上所有key
  3. 自平衡树(插入规则保证树平衡)
  4. 插入数据时,查询二叉树为向下生长,二三树为向上生长

时间复杂度:最坏情况,所有节点均为2节点,查询时间复杂度O(log2n),最佳情况为所有节点均为3节点,查询时间复杂度O(log3n)

插入(val)(叶子节点操作)(过程逻辑较为复杂,此描述暂时有问题):

            a.  

            b. 

            c.  

            d.  

  1. 若根结点为None,实例化root(2节点)
  2. 若节点为2节点,则变为3节点(key1 not None, key2 None)
  3. 若节点为3节点:
  • 短暂变为4节点(key1,key2,key_tmp)
  • 提取mid(key1, key2, key_tmp),插入到parent,key1与key2分裂为2个2节点(key1_node, key2_node)
  • 若parent为2-->3节点,mid(key1, key2, key_tmp),key2_node(分裂前结点为parent的left,反之亦然)节点挂载到parent的mid节点,key1_node保持为parent的left,插入完成,跳到4;
  • 若parent为3-->4节点,(假设此时节点为parent的right节点),将parent分裂为parent_left_node,parent_right_node,将mid(parent_key1,parent_key2, key_tmp【子孙插入】)继续上抛,跳到c;原parent的mid节点和left节点归为parent_left_node子孙; 原left,right归入到parent_right_node;parent_left_node与parent_right_node

    4. end

红黑树

特性:

  1. 二叉树
  2. 节点分为红色/黑色 (默认插入为红色)
  3. Root节点为黑色
  4. 红色节点子节点为黑色【无两个连续红点】
  5. 任意节点到其叶子节点所经过黑色节点数目相同哦【保证其平衡】

自平衡(大致平衡):

当插入或者删除其中某个节点后(红),可能出现不符合红黑树定义。通过变色,左旋转,右旋转,重新平衡红黑树

与平衡二叉树区别:

放弃完全平衡,追求大致平衡。完全平衡条件苛刻,每次修改后旋转次数未知。红黑树放弃了完全平衡,每次插入最多3次旋转(逻辑简化),而性能保持在对数级

时间复杂度:O(log2n),最差不超过O(2log2n)

应用:当Java中HashMap某个桶的数据(哈希分桶分到同一个桶)超过了门槛,则将桶由链表重构为红黑树,提高时间性能

实现:

Ps:参考Java中HashMap对红黑树的实现(python其实现逻辑)【经过测试:插入10,11,12,13,14,15,16,17,18,19,20,21后,根右子树深度比左子树+2】:

class Red_Black_Tree:
    class Node:
        def __init__(self, val):
            self.parent = None
            self.left = None
            self.right = None
            self.red = True
            self.key = val
            pass

        def root(self):
            r = self
            p = r.parent
            while p is not None:
                r = p
                p = r.parent
            return r
        
        def putNode(self, node):
            if self.parent==None:
                r = self
                root = self
            else:
                r = self.root()
                root = r
            node.red = True
            dir = 0
            while True:
                if r.key > node.key:
                    dir = -1
                    if r.left is not None:
                        r = r.left
                    else:
                        break
                elif r.key == node.key and node is not None:
                    return node
                else:
                    dir = 1
                    if r.right is not None:
                        r = r.right
                    else:
                        break
                pass
            xp = r
            if dir <= 0:
                xp.left = node
                pass
            else:
                xp.right = node
                pass
            node.parent = r
            self.balance_insert(node, root)
            pass
        
        def balance_insert(self, node, root):
            node.red = True
            while True:
                if node.parent is None:
                    node.red = False
                    return node
                elif not node.parent.red or node.parent.parent is None:
                    return root
                xppl = node.parent.parent.left
                xppr = node.parent.parent.right
                if node.parent == xppl:
                    if xppr is not None and xppr.red:
                        xppr.red = False
                        node.parent.red = False
                        xppr.parent.red = True
                        node = xppr.parent
                        pass
                    else:
                        if node == node.parent.right:
                            node = node.parent
                            # xpp = (xp = x.parent) == null ? null : xp.parent;
                            self.rotateLeft(root, node)
                        if node.parent != None:
                            node.parent.red = False
                            if node.parent.parent != None:
                                node.parent.parent.red = True
                                self.rotateRight(root, node.parent.parent)
                                pass
                            pass
                        pass
                    pass
                else:
                    if xppl is not None and xppl.red:
                        xppl.red = False
                        xppl.parent.red = True
                        node.parent.red = False
                        node = xppl.parent
                        pass
                    else:
                        if node == node.parent.left:
                            node = node.parent
                            self.rotateRight(root, node)
                            pass
                        if node.parent is not None:
                            node.parent.red = False
                            if node.parent.parent is not None:
                                node.parent.parent.red = True
                                root = self.rotateLeft(root, node.parent.parent)
                            pass
                        pass
                    pass
                pass
            pass
        
        def rotateRight(self, root, node):
            l = node.left
            if node is not None and l is not None:
                node.left = l.right
                lr = node.left
                if lr is not None:
                    lr.parent = node
                l.parent = node.parent
                pp = l.parent
                if pp is None:
                    root = l
                    root.red = False
                elif pp.right==node:
                    pp.right = l
                else:
                    pp.left = l
                l.right = node
                node.parent = l
            return root

        def rotateLeft(self, root, node):
            r = node.right
            if node is not None and r is not None:
                node.right = r.left
                rl = node.right
                if rl is not None:
                    rl.parent = node
                    pass
                r.parent = node.parent
                pp = r.parent
                if pp is None:
                    root = r
                    root.red = False
                    pass
                elif pp.left == node:
                    pp.left = r
                    pass
                else:
                    pp.right = r
                    pass
                r.left = node
                node.parent = r
            return root
    
    def __init__(self, val):
        self.root = self.Node(val)
        self.root.red = False
        pass

    def insert(self, val):
        self.root.putNode(self.Node(val))
        self.root = self.root.root()
        pass
    pass

B-Tree

二三树扩展。二三树每个结点至多3个子节点。B-Tree每个节点至多M个节点,M-1个key,其他规则与二三树相同(包括插入,查询)。

有点复杂:不会实现(插入过程见图1)

 

                                                                                                 图1

B+Tree

对于B-Tree的变形,区别:

  1. 值保存在叶子节点中,非叶子节点中的key只起到索引作用【B-Tree中节点的key值,既是索引,也是值】
  2. 所有叶子节点由指针,连接成一条有序链表

插入过程见图2 

                                                                                  图2

  • 分块查询

基本思想:水洼找鱼总比海底捞针容易

做法:将数据(水)倒入几个有序排列桶中,每次查询只需要在某一个桶进行查找就好(桶有序,而桶中数据无序)

自我理解:只是通过分桶,将数据量减少,从而提高查询效率

@classmethod
def bult_search(cls, array, val):
    pool = list()
    for i in range(7):
        pool.append(list())
        # 分块,我此处分为7个有序的桶
        for i in range(0, len(array)):
            index = array[i]//7
            if index > 6:
                index = 6
            elif index < 0:
                index = 0
                pool[index].append(array[i])
                index = val//7
                if index > 6:
                    index = 6
                elif index < 0:
    index = 0
    length = len(pool[index])
    for i in range(length):    
        if pool[index][i]==val:
            return (index, i)
    return (-1, -1)
  • Hash查询

原理:若在获取查询值的时候,直接计算出所应该在的位置,则查询速度将会大大提高

行为:利用hash函数,传入查询值val,直接计算出位置index。

理解:冲突问题,若两个不同的值映射到同一个位置(产生冲突),如何解决【后移一位,或者链表】;效率问题:理论上,一个合理的hash函数能将时将性能提升到O(1),但当hash函数不合理,导致频频冲突,会降低查询性能【原因:每次冲突,都需要去访问下一个节点】。

Java 中HashMap<K, V>便是对该算法的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值