【6】算法基础:变治

三种类似:变换为同样问题的一个更简单或者更方便的实例(实例化简);变换为同样实例的不同表现(改变表现);变换为另一个问题的实例,这种问题的算法是已知的(问题化简)。

示例1:高斯消去法

(待更新)

 

示例2:二叉查找树

二叉树节点所包含的元素来自可排序项的集合,每个节点一个元素,使得所有左子树中的元素都小于等于子树根节点的元素,而所有右子树中的元素都大于等于它,是实现字典的数据结构。

参考:https://blog.youkuaiyun.com/qq_21396469/article/details/78419609

class Node:
    def __init__(self,value):
        self.value=value
        self.parent=None
        self.left=None
        self.right=None
    def add_parent(self,parent):
        self.parent=parent
    def add_left(self,left):
        self.left=left
    def add_right(self,right):
        self.right=right
    def __str__(self):
        if self.left is not None and self.right is not None:
            return str(self.value)+" l:"+str(self.left)+" r:"+str(self.right)
        elif self.left is not None and self.right is None:
            return str(self.value)  + " l:" + str(self.left)
        elif self.left is None and self.right is not None:
            return str(self.value)  + " r:" + str(self.right)
        else:
            return str(self.value)

class BST:
    def __init__(self):
        self.root=None
    def __str__(self):
        if self.root is not None:
            return str(self.root)
        else:
            return None
    def insert(self, value):
        new_node = Node(value)
        root_node = self.root
        if root_node is None:
            self.root = new_node
        else:
            while root_node is not None:
                new_node.parent = root_node
                if new_node.value < root_node.value:
                    root_node = root_node.left
                else:
                    root_node = root_node.right
            if new_node.value < new_node.parent.value:
                new_node.parent.left = new_node
            else:
                new_node.parent.right = new_node
    def mid_through(self,node):
        if node is not None:
            if node.left is not None:
                self.mid_through(node.left)
            print(node.value)
            if node.right is not None:
                self.mid_through(node.right)
    def search_recurrent(self,start_node,value):
        if start_node.value == value:
            return start_node
        elif value<start_node.value:
            if start_node.left is not None:
                return self.search_recurrent(start_node.left,value)
            else:
                return None
        else:
            if start_node.right is not None:
                return self.search_recurrent(start_node.right, value)
            else:
                return None
    def search_cycle(self,value):
        start_node=self.root
        while start_node is not None and start_node.value!=value:
            if value<start_node.value:
                start_node=start_node.left
            else:
                start_node=start_node.right
        return start_node
    def min_node(self,node):
        while node.left is not None:
            node=node.left
        return node
    def max_node(self,node):
        while node.right is not None:
            node=node.right
        return node
    def success(self,node):
        if node.right is not None:
            return self.min_node(node.right)
        else:
            parent=node.parent
            child=node
            while parent is not None and child.value==parent.right.value:
                child=parent
                parent=parent.parent
            return parent
    def predecess(self,node):
        if node.left is not None:
            return self.max_node(node.left)
        else:
            parent = node.parent
            child = node
            while parent is not None and child.value == parent.left.value:
                child = parent
                parent = parent.parent
            return parent
    def delete(self,node):
        if node.left is None and node.right is None:
            if node.value==node.parent.left.value:
                node.parent.left=None
            elif node.value==node.parent.right.value:
                node.parent.right=None
        elif node.left is not None and node.right is None:
            if node.value==node.parent.left.value:
                node.parent.left=node.left
            elif node.value==node.parent.right.value:
                node.parent.right=node.left
        elif node.left is None and node.right is not None:
            if node.value==node.parent.left.value:
                node.parent.left=node.right
            elif node.value==node.parent.right.value:
                node.parent.right=node.right
        else:
            successor=self.success(node)
            if successor.value==node.right.value:
                successor.left=node.left
                if node.value == node.parent.left.value:
                    node.parent.left = successor
                elif node.value == node.parent.right.value:
                    node.parent.right = successor
            else:
                if successor.value == successor.parent.left.value:
                    successor.parent.left = successor.right
                elif successor.value == successor.parent.right.value:
                    successor.parent.right = successor.right
                node.value=successor.value

if __name__=="__main__":
   bst=BST()
   bst.insert(0)
   bst.insert(4)
   bst.insert(2)
   bst.insert(1)
   bst.insert(7)
   bst.insert(5)
   bst.insert(6)
   bst.insert(3)
   bst.insert(8)
   bst.mid_through(bst.root)
   print(bst.min_node(bst.root))
   print(bst.max_node(bst.root))
   node4=bst.search_recurrent(bst.root,4)
   print(node4)
   print(bst.success(node4))
   print(bst.predecess(node4))
   bst.delete(node4)
   print(bst)

示例3:平衡查找树

平均情况下二叉查找树的效率为O(logn),但是在最差情况下会退化为一种严重不平很的树,效率为O(n)。平衡查找树就是把一棵不平衡的二叉查找树转变为平衡的形式,如果一个节点的插入或删除产生了一棵未被平衡要求的树,就用一系列称为旋转的特定变换中选择一种,重新构造这棵树,使得这棵树重新满足平衡要求。

类型1:AVL树

一棵AVL树要求它的每个节点的左右子树的高度差不能超过1。一棵红黑树能够容忍同一节点的一棵子树的高度是另一棵子树的两倍。这里只讨论AVL树。如果插入的一个新节点使得一棵AVL树失去了平衡,用旋转对这棵树做一个变换。AVL树的旋转,是以某节点为根的子树的一个本地变换,该节点的平衡要么变成了+2要么变成了-2。如果有若干个这样的节点,先找出最靠近新插入的叶子的不平衡节点,然后旋转以该节点为根的子树。(只有4类旋转,其中两种是另外两种的镜像)

1.向右单向旋转(右单转),在一个新的键插入树的左子女的左子树后发生

2.双向左右旋转(左右双转),对根的左子树左旋,再对新树右旋,在一个新的键插入树的左子女的右子树后发生

3.双向右左旋转(右左双转),对根的右子树左旋,再对新树左旋,在一个新的键插入树的右子女的左子树后发生

4.左单转,在一个新的键插入树的右子女的右子树后发生

旋转能够保证结果树是平衡的,而且保留了一棵二叉查找树的基本要求,在最差情况下AVL的效率为O(logn)。

https://www.cnblogs.com/skywang12345/p/3576969.html

class Node:
    def __init__(self,value):
        self.value=value
        self.parent=None
        self.left=None
        self.right=None
    def add_parent(self,parent):
        self.parent=parent
    def add_left(self,left):
        self.left=left
    def add_right(self,right):
        self.right=right
    def __str__(self):
        if self.left is not None and self.right is not None:
            return str(self.value)+" l:"+str(self.left)+" r:"+str(self.right)
        elif self.left is not None and self.right is None:
            return str(self.value)  + " l:" + str(self.left)
        elif self.left is None and self.right is not None:
            return str(self.value)  + " r:" + str(self.right)
        else:
            return str(self.value)

class AVLTree:
    def __init__(self):
        self.root=None
    def __str__(self):
        return str(self.root)
    def compute_height(self,root_node):
        if root_node is None:
            return 0
        height_left = 0
        height_right = 0
        if root_node.left is not None:
            height_left = self.compute_height(root_node.left)
        if root_node.right is not None:
            height_right = self.compute_height(root_node.right)
        height=max(height_left,height_right)+1
        return height
    def left_rotate(self,parent): # insert to right's right
        sub_right=parent.right
        sub_right_left=parent.right.left
        parent.right=sub_right_left
        if sub_right_left is not None:
            sub_right_left.parent=parent
        sub_right.left=parent
        sub_right.parent=parent.parent
        return sub_right
    def right_rotate(self,parent): # insert to left's left
        sub_left = parent.left
        sub_left_right = parent.left.right
        parent.left = sub_left_right
        if sub_left_right is not None:
            sub_left_right.parent = parent
        sub_left.right = parent
        sub_left.parent = parent.parent
        return sub_left
    def right_left_rotate(self,parent): # insert to right's left
        sub_right=parent.right
        sub_right_left=parent.right.left
        sub_right=self.right_rotate(sub_right)
        parent.right=sub_right
        parent=self.left_rotate(parent)
        return parent
    def left_right_rotate(self,parent): # insert to left's right
        sub_left=parent.left
        sub_left_right=parent.left.right
        sub_left=self.left_rotate(sub_left)
        parent.left = sub_left
        parent=self.right_rotate(parent)
        return parent
    def insert(self,parent,root_node,new_node):
        if root_node is None:
            root_node=new_node
            new_node.parent=parent
        elif new_node.value == root_node.value:
            print("ERROR")
            exit()
        else:
            if new_node.value<root_node.value:
                root_node.left=self.insert(root_node,root_node.left,new_node)
                if self.compute_height(root_node.left)-self.compute_height(root_node.right)>1:
                    if new_node.value < root_node.left.value: # insert to left's left
                        root_node=self.right_rotate(root_node)
                    else: # insert to left's right
                        root_node=self.left_right_rotate(root_node)
            else:
                root_node.right=self.insert(root_node,root_node.right, new_node)
                if self.compute_height(root_node.right)-self.compute_height(root_node.left)>1:
                    if new_node.value > root_node.right.value: # insert to right's right
                        root_node=self.left_rotate(root_node)
                    else: # insert to right's left
                        root_node=self.right_left_rotate(root_node)
        if self.root is None:
            self.root=new_node
        if parent is None:
            self.root=root_node
        return root_node
    def min_node(self,root_node):
        while root_node.left is not None:
            root_node=root_node.left
        return root_node
    def max_node(self,root_node):
        while root_node.right is not None:
            root_node=root_node.right
        return root_node
    def delete(self,root_node,node):
        if root_node is None or node is None:
            print("ERROR")
            exit()
        if node.value<root_node.value:
            root_node.left=self.delete(root_node.left,node)
            if self.compute_height(root_node.right)-self.compute_height(root_node.left)>1:
                if self.compute_height(root_node.right.left)>self.compute_height(root_node.right.right):  # insert to right's left
                    root_node = self.right_left_rotate(root_node)
                else:  # insert to right's right
                    root_node = self.left_rotate(root_node)
        elif node.value>root_node.value:
            root_node.right = self.delete(root_node.right, node)
            if self.compute_height(root_node.left) - self.compute_height(root_node.right) > 1:
                if self.compute_height(root_node.left.left)>self.compute_height(root_node.left.right): # insert to left's left
                    root_node = self.right_rotate(root_node)
                else: # insert ot left's right
                    root_node = self.left_right_rotate(root_node)
        else:
            if root_node.left is not None and root_node.right is not None:
                if self.compute_height(root_node.left)>self.compute_height(root_node.right):
                    max_node=self.max_node(root_node.left)
                    root_node.value=max_node.value
                    root_node.left=self.delete(root_node.left,max_node)
                else:
                    min_node=self.min_node(root_node.right)
                    root_node.value = min_node.value
                    root_node.right = self.delete(root_node.right, min_node)
            else:
                if root_node.left is not None:
                    root_node=root_node.left
                elif root_node.right is not None:
                    root_node = root_node.right
                else:
                    root_node=None
        return root_node
if __name__=="__main__":
    avlt=AVLTree()
    node0 = Node(0)
    avlt.insert(None,avlt.root,node0)
    node4 = Node(4)
    avlt.insert(None,avlt.root, node4)
    node2 = Node(2)
    avlt.insert(None,avlt.root, node2)
    node1 = Node(1)
    avlt.insert(None,avlt.root, node1)
    node7 = Node(7)
    avlt.insert(None,avlt.root, node7)
    node5 = Node(5)
    avlt.insert(None,avlt.root, node5)
    node6 = Node(6)
    avlt.insert(None,avlt.root, node6)
    node3 = Node(3)
    avlt.insert(None,avlt.root, node3)
    node8 = Node(8)
    avlt.insert(None,avlt.root, node8)
    print(avlt)
    avlt.delete(avlt.root,node4)
    print(avlt.delete(avlt.root,node3))
    

类型2:2-3树

2-3树是一种可以包含两种类型节点的树:2节点和3节点,一个2节点只包含一个键K和两个子女,一个3节点包含两个有序的键K1和K2(K1<K2)并且有3个子女,最左边的子女作为键值小于K1的子树的根,中间的子女作为键值位于K1和K2之间的子树的根,最右边的子女作为键值大于K2的子树的根。并且2-3树要求树中所有叶子必须位于同一层,也就是说2-3树总是高度平衡的:对于每个叶子来说,从树的根到叶子的路径长度都是相同的。为了这个特性,允许查找树的一个节点包含不止一个键。

http://www.cnblogs.com/yangecnu/p/Introduce-2-3-Search-Tree.html

(待更新)

 

类型3:B树

m叉搜索树在对应的扩充搜索树中,每个内部节点最多可以有m个子女即m-1个元素,每个含有p个元素的节点有p+1个子女,考察含有p个元素的节点,设k1,……,kp是这些元素的关键值,k1<……<kp,c0,……,cp是节点的p+1个孩子,以c0为根的子树中的元素关键值均小于k1,以cp为根的子树中的元素关键值均大于kp,以ci为根的子树中的元素关键值均大于k i小于k i+1 。一棵高度为h的m叉搜索树至少有h个元素,即每层一个节点,每个节点一个元素,最多时从1到h-1层的每个节点都含有m个孩子并且第h层的节点没有孩子只有外部节点,对每层的节点数求和,最大节点数为\sum_{i=0}^{h-1}m^{i}=\frac{m^{h}-1}{m-1},每个节点最多有m-1个元素,所以高度为h的m叉搜索树最多有m^{h}-1个元素。

B树也叫平衡多路查找树,一棵m阶B树的根节点至少有两个孩子,所有内部节点最多有m个子女,除根和叶子节点外的节点最少有m/2向上取整个子女,有j个子女的节点有j-1个键。所有的叶子节点出现在同一层。B-树的高度h就是从树最下面的非叶子节点到树的根节点所经过的路径节数,所有外部节点均在h+1层。n为树中元素的个数,d为m/2上取整。根节点至少有两个孩子,所以每层节点最小数目为1,2,2d,2d^{2},……,2d^{h-1},所以外部节点的最小数目为2d^{h-1},而外部节点的数量始终比元素的个数多1,则2d^{h-1}-1\leqslant n\leqslant m^{h}-1log_{m}(n+1)\leqslant h\leqslant log_{d}(\frac{n+1}{2})+1

https://blog.youkuaiyun.com/pleasecallmewhy/article/details/8451889

https://blog.youkuaiyun.com/v_JULY_v/article/details/6530142

(待更新)

 

 示例3:堆排序

(待更新)

 

示例4:霍纳法则和二进制幂

(待更新)

 

示例5:线性规划

许多决策最优化的问题都可以化简为线性规划问题的一个实例,线性规划问题是一个多变量线性函数的最优化问题,这些变量所要满足的一些约束是以线性等式或线性不等式的形式出现的。

类型1:背包问题:背包承重W,n个重量为w1~wn,价值为v1~vn的物品

将背包问题的连续/小数版本化简为线性规划问题,设xj,j=1~n,n是一个变量,代表物品j放在背包中的比例,0<=xj<=1。所选物品总重sum(wj*xj),总价值sum(vj*xj),所以背包问题的连续版本可以表示为下面这个线性规划问题:使sum(vj*xj)最大化,约束:sum(wj*xj)《=W,0<=xj<=1,j=1,……,n。

背包问题的离散/0-1版本要么拿走一个物品的全部,要么一点都不拿,化简为一个整数线性规划问题:使sum(vj*xj)最大化,约束:sum(wj*xj)《=W,xj属于{0,1},j=1,……,n。

 

示例6:图问题

许多问题可以化简为标准的图问题,图的顶点表示所讨论问题的可能状态,边表示这些状态之间的可能转变。这种变换把问题化简为一个求初始状态顶点到目标状态顶点之间路径的问题。

类型1:农夫带有一只狼,一只羊和一筐白菜需要过河,船只能容纳农夫本人和另外一样东西,如果农夫不在,狼会吃羊,羊会吃菜。

用P,w,g,c分别表示农夫、狼、羊、白菜,||表示河,有以下的状态转换:

可以看到在初始状态和结束状态之间存在着两条不同的简单路径。如果用广度优先查找来求解,可以证明这些路径包含了最少的边。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值