Coursera - Algorithm (Princeton) - 课程笔记 - Week 5

Week 5

2-3搜索树 2-3 Search Trees

  • 寻找一个目标,能够保证最坏情况下的增删查效率在 log ⁡ N \log N logN(二分查找树只能在随机时达到,但是实际情况,键的数据往往不是随机的,因此BST的最坏情况是 N N N
  • 2-3树:每个阶段允许包含1~2个键
    • 2-节点:拥有一个键,两个子节点(一大一小)
    • 3-节点:拥有两个键,三个子节点(一大一小一中间)
  • 完美的平衡性:从根节点到空树的任一条路径的长度都是相同的
  • 对称有序的树:对2-3树的中序遍历将按照升序进行(左连接-左键-中间连接-右键-右连接)
  • 查找:
    • 将目标键与当前节点的键进行比较
    • 确定目标键所在的区间
    • 在这个区间上进入对应的链接(递归地进行,找到空树即目标不存在)
  • 插入:
    • 对于底部是一个2-节点的情况
      • 正常地搜索目标键
      • 找到对应的节点,将其变成一个3-节点
    • 对于底部是一个3-节点的情况
      • 将这个键插入3-节点,将其变成一个临时的4-节点
      • 将这个4-节点的中间一个键向上移动到父结点中,其左右链接分别指向另外两个2节点(另外两个键分裂成了两个2-节点)
      • 如果其父节点本身是一个3-节点,将递归地进行上述过程
      • 如果一次插入使得根节点变成了一个4-节点,那么就继续分裂,形成三个2-节点(注意,根节点可以是一个3-节点)
  • 4-节点的分裂只是一个局部转换,因此只涉及常数个数的操作
  • 性质:2-3树能够很好地维持对称有序并且具有完美的平衡性
  • 树的高度:
    • 最坏情况(全都是2-节点): log ⁡ N \log N logN
    • 最好情况(全都是3-节点): log ⁡ 3 N ≈ 0.631 l o g N \log_3 N \approx 0.631 log N log3N0.631logN
    • 百万数量级节点,上述值在 12 12 12 20 20 20之间
    • 十亿数量级节点,上述值在 18 18 18 30 30 30之间
    • 可以保证在搜索和插入上的对数级复杂度

红黑二分查找树 Red-Black BSTs

  • 一种很简单的数据结构,只在基本BST代码上增加一点点代码就能实现一个2-3树
  • 实现的版本是左倾红黑树(left-leaning red-black tree, LLRB)
    • 将2-3树表示成一个BST
    • 使用内部的左倾链接作为3-节点的“胶水”,即更大的键将会是另外一个的根节点
    • 这个左连接将被称为红连接,以区分本身的树的黑连接
  • 任何2-3树都能够被转换成BST实现的红黑树
  • 红黑BST有如下性质:
    • 没有任何一个节点有两个红连接(对应的将会是一个4-节点)
    • 从根节点到空节点的每一条路径上黑连接的数量都是相等的(相当于原有2-3树的完美平衡)
    • 红色链接都是左倾的(定义)
  • 查找:与元素级BST相同,并忽略边的颜色(代码无需任何改动,事实上其他的方法实现也基本没有改动)
public Val get(Key key)
{
    Node x = root;
    while (x != null)
    {
        int cmp = key.compareTo(x.key);
        if (cmp < 0) x = x.left;
        else if (cmp > 0) x = x.right;
        else if (cmp == 0) return x.val;
    }
    return null;
}
  • 红黑树的表示

    • 因为每一个节点只会被一个连接指向(其父结点),因此只需要将被指向的连接的颜色进行编码即可
    private static final boolean RED = true;
    private static final boolean BLACK = false;
    
    private class Node
    {
        Key key;
        Value val;
        Node left, right;
        boolean color; // color of parent link
    }
    
    private boolean isRed(Node x)
    {
        if (x == null) return false; // 空节点的连接正好是黑连接
        return x.color == RED;
    }
    
  • 左旋转:将一个可能出现的临时右倾连接转成左倾

    • 保持的对称有序和完全平衡(中间的子树还在中间,只是访问路径改变了,而没有改变路径访问中的黑连接个数)
    private Node rotateLeft(Node h)
    {
        assert isRed(h.right);
        Node x = h.right;
        h.right = x.left; // 把右倾红连接子节点的左子树变成旋转后的左倾红连接子节点的右子树
        x.left = h; // 把右倾红连接父结点变成左倾红连接子节点
        x.color = h.color; //更换保存顺序(因为父子关系倒置了)
        h.color = RED;
        return x;
    }
    
  • 右旋转:将一个左倾连接转成临时右倾

    • 实现基本是对称的
    private Node rotateRight(Node h)
    {
        assert isRed(h.left);
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }
    
  • 颜色反转:将一个临时的4-节点(一个节点的左右链接都是红连接)的两个连接变成黑色(相当于变成了独立的节点,实现了2-3树中4-节点的分裂),并把父结点与之的连接变成红色(2-3树中的向上分裂并合并到父结点)

    private void flipColors(Node h)
    {
        assert !isRed(h);
        assert isRed(h.left);
        assert isRed(h.right);
        h.color = RED;
        h.left.color = BLACK;
        h.right.color = BLACK;
    }
    
  • 基本策略:实现2-3树操作和元素级红黑树操作的一一对应

  • 在LLRB上的插入操作

    • 训练1:向只有一个节点的树中插入节点

      • 如果这个节点插在左分支,则结束
      • 如果这个节点插在右分支,则执行一次左旋转,结束
    • 情景1:插入一个树底部的2-节点

      • 完成标准BST的插入动作,将新连接标成红色(形成一个3-节点)
      • 如果这个红连接是右倾的,执行一次左旋转
    • 训练2:向只有两个节点的树插入节点(即一个3-节点)

      • 如果这个节点比根节点大,将会插入到根节点右侧,那么将会出现根节点两个连接都是红色,这个时候执行一次颜色翻转(根节点没有与父结点的连接,因此直接反转左右连接颜色变黑即可)
      • 如果这个节点比根节点的左字节点小,那么将会以红连接连接到左子节点的左侧,这是需要完成一次左子节点的右旋转(将中间节点转到根节点位置),然后进行一次颜色翻转(分裂)
      • 如果这个节点比根节点小但是比左节点大,那么将会以红连接连接到左子节点的右侧,就出现了两个连续的连接,但一左一右,这时先将左子节点为根节点的子树左旋转,然后整体右旋转,并进行颜色翻转
    • 情景2:插入一个树底部的3-节点

      • 完成标准BST的插入动作,将新连接标成红色(形成一个4-节点,准备调整和分裂)
      • 根据需要对这个4-节点进行旋转以达到平衡(训练2的后面两种情况)
      • 颜色反转,并向上一级传递一个红色连接(训练2中,顶级点为根节点不考虑传递,这里需要考虑,并可能会引起连锁反应)
      • 按照需要旋转这个树以确保左倾(如果向上传递的连接是右倾的)
      • 不断向上重复情景1或者情景2
    • 实现:同一份代码可以解决上面的所有情形,因为各种情况都是相互传递直到合法状态的

      • 右子红,左子黑,左旋转
      • 左子红,左子左子红,右旋转
      • 双子红,颜色翻转

    在这里插入图片描述

    private Node put(Node h, Key key, Value val)
    {
        // 标准BST插入操作,空就插,不空比大小往下插
        if (h == null) return new Node(key, val, RED); // 默认插入的是红连接(往节点里面插)
        int cmp = key.compareTo(h.key);
        if (cmp < 0) h.left = put(h.left, key, val);
        else if (cmp > 0) h.right = put(h.right, key, val);
        else if (cmp == 0) h.val = val;
        // 上面描述的三种情况,这里实际递归了,下面一层处理完再处理当前层
        if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
        if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left) && isRed(h.right)) flipColors(h);
        
    	return h;
    }
    
  • 红黑树的性质

    • 在最坏情况下,树的高度 ≤ 2 log ⁡ N \le 2 \log N 2logN
    • 常规应用中(随机顺序),树的高度 ∼ 1.00 l log ⁡ N \sim 1.00 l\log N 1.00llogN(接近完全平衡)
  • 标记表实现的性能比较整理

    实现最坏查找最坏插入最坏删除平均查找平均插入平均删除有序迭代键比较
    序列 N N N N N N N N N N 2 \frac N2 2N N N N N 2 \frac N2 2N不可以equals
    二分 log ⁡ n \log n logn N N N N N N log ⁡ n \log n logn N 2 \frac N2 2N N 2 \frac N2 2N可以compareTo
    BST N N N N N N N N N 1.39 log ⁡ N 1.39 \log N 1.39logN 1.39 log ⁡ N 1.39 \log N 1.39logN?可以compareTo
    2-3树 c log ⁡ N c \log N clogN c log ⁡ N c \log N clogN c log ⁡ N c \log N clogN c log ⁡ N c \log N clogN c log ⁡ N c \log N clogN c log ⁡ N c \log N clogN可以compareTo
    红黑树 2 log ⁡ N 2 \log N 2logN 2 log ⁡ N 2 \log N 2logN 2 log ⁡ N 2 \log N 2logN 1.00 log ⁡ N 1.00 \log N 1.00logN 1.00 log ⁡ N 1.00 \log N 1.00logN 1.00 log ⁡ N 1.00 \log N 1.00logN可以compareTo

B树 B-Trees

  • 一种红黑树的实际应用
  • 文件系统访问:寻找页指针的开销要远大于在页内访问数据的开销,因此需要实现最小寻找探查次数来访问数据
  • B树是对2-3树的一种泛化,其节点最多允许 M − 1 M-1 M1个键(M大概就是一个页中块的数量)
    • 根节点至少两个键
    • 其余节点至少 M 2 \frac M2 2M个键(相当于一个节点塞满了就分裂成两个)
    • 所有的数据(键)都在外部节点保存,每一个节点会有一位用于临时存储
    • 树的内部节点会将这些键复制到树中以引导搜索(可以认为是每一个外部节点的第一个键,节点间内容有序)
  • 在一个B树中查找
    • 从根节点开始
    • 在节点中找到正确的区间,并取对应的连接
    • 搜索在外部节点中终止
  • 在一个B树中插入
    • 为这个新键寻找合适位置
    • 在底部的外部节点插入
    • 如果节点满足了 M M M个,就需要不断地向上分裂
  • 性质
    • 对于有N个键的M阶(一个页M个块)B树的搜索和插入在 log ⁡ M − 1 N \log_{M-1}N logM1N log ⁡ M 2 N \log_{\frac M2}N log2MN次探查
    • 实际视线中,探查次数最多就是4( log ⁡ 1024 2 6200000 ≤ 4 \log_{\frac{1024}{2}}{6200000} \le 4 log2102462000004
    • 优化:将根页面(节点)永远保存在内存中

几何对象

  • 使用BST及其扩展解决几何对象的重叠或者搜素问题

1维范围搜索 1D Range Search

  • 有序的符号表的一个扩展

    • 范围搜索(Range Search):在区间 k 1 k_1 k1 k 2 k_2 k2之间的所有键
    • 范围计数(Range Count):在区间 k 1 k_1 k1 k 2 k_2 k2之间的所有键的个数
  • 1维指的是只有一个键

  • 几何解释

    • 键是一条线上的点
    • 在给定的1维区间上对点进行查找或者计数
  • 实现·未排序数组:快速插入,慢速区间查找和计数(遍历一遍)( 1 , N , N 1,N,N 1,N,N

  • 实现·有序数组:慢速插入,使用二分查找完成区间查找和计数( N , log ⁡ N , R + log ⁡ N N,\log N,R+\log N N,logN,R+logN)(R是符合要求需要返回的键)

  • 区间计数实现·BST:

    • rank函数,小于该键节点个数,可以在建树时维护
    • 实现:分别询问rank(k1)rank(k2),然后通过判断区间右端点是否为树上的键
    • 时间正比于 log ⁡ N \log N logN(检查的节点数量是查找最大和最小的路径和)
    public int size(Key lo, Key hi)
    {
        if (contains(hi)) return rank(hi) - rank(lo) + 1;
        else return rank(hi) - rank(lo);
    }
    
  • 区间搜索实现·BST:

    • 递归地查找所有左子树上的键
    • 检查当前节点上的键
    • 递归地查找所有右子树上的键
    • 时间正比于 R + log ⁡ N R+\log N R+logN(检查的节点数量是查找最大和最小的路径和,另外加上返回的数量)

线段交叉 Line Segment Intersection

  • 给定 N N N条水平和垂直的线段,找到所有的交点
  • 平方复杂度算法:检查所有的线段对
  • 假设在当前问题设置下,没有重合的坐标
  • 正交线段交叉搜索:扫线算法(sweep-line)
    • 假设一条从左到右扫过所有数据的垂直线
    • 我们利用每一个横坐标(x坐标)来定义事件
    • 扫到一条水平线的左端点:将其y坐标加入BST(该线段可能产生交叉)
    • 扫到一条水平线的右端点:将其y坐标移出BST(该线段已不再可能产生交叉)
    • 遇到垂直线段:在BST上完成其y坐标区间的BST区间搜索(BST上有几个点在区间内,就产生几个新的交点)
  • 性质:扫线算法时间复杂度正比于 N log ⁡ N + R N \log N + R NlogN+R,其中 R R R为交点信息个数(倍数 N N N是因为要完成对所有线段的遍历)
  • 我们使用基于BST的高效一维区间搜索算法完成了在对数线性时间内的二维正交交点查询

K维树 Kd-Trees

  • 一种BST的扩展应用,用于处理空间中大量的点,非常有效而灵活
  • 2维正交区间查找·将有序符号表扩展到二维键
    • 插入二维键
    • 删除二维键
    • 查找二维键
    • 范围查找:在一个二维区间内找到所有其中的点
    • 范围计数:在一个二维区间内计数所有其中的点
  • 几何解释
    • 键是平面上的点
    • 查找或计数出现在指定的平行于坐标轴的矩形区域内的点
  • 网格划分
    • 将整个空间划分成 M × M M \times M M×M的网格
    • 创建一个在每一个方格中的点的列表
    • 使用一个二维数组直接索引对应的方格
    • 插入:直接将点 ( x , y ) (x,y) (x,y)添加到对应方格的列表中
    • 范围查找:只检查与问询区间有交集的方格
    • 性能权衡
      • 空间复杂度: M 2 + N M^2+N M2+N
      • 时间复杂度:平均每个方格 1 + N M 2 1+\frac N {M^2} 1+M2N
    • 划分参数 M M M的选择
      • 太小,则浪费了大量空间
      • 太大,每一个方格含有太多的点
      • 常用规则: N × N \sqrt N \times \sqrt N N ×N
    • 在选择上面的规则划分时,假设这些点均匀分布
      • 初始化数据结构: N N N
      • 插入一个点: 1 1 1
      • 区间查找:每个点 1 1 1
    • 网格划分实现,是一个简单实现,但是受限制于点时均匀分布的
    • 对于聚类情况,这种方法并不能很高效地解决问题
  • 空间划分树:使用树表示二维空间下的递归子划分
    • 递归地将平面划分成两个半平面
    • 数据结构:实质上就是一个BST,但是交替使用x和y表作为键
    • 偶数层使用y坐标作为键,左侧在左,右侧在右;奇数层使用x坐标作为键,下方在左,上方在右
    • 查找结果将会给出包含目标点的矩形框
    • 插入将会插入到子划分中
  • 二维树实现在二维空间中进行范围查找
    • 检查当前点是否在区间的矩形中
    • 递归地检查左节点(左侧或者下方)(如果矩形部分出现在这些部分)
    • 递归地检查右节点(右侧或者上方)(如果矩形部分出现在这些部分)
    • 一般情况下的时间复杂度: R + log ⁡ N R+\log N R+logN
    • 假设树已经平衡的情况下,最坏情况为 R + N R+\sqrt N R+N
  • 2维树的另外一个应用:最近邻,给定一点找现有所有点中最近一点
    • 检查当前节点与查询点的距离
    • 递归地向左子节点(左侧或底部)查找(如果可能存在更近点的话,如果能证明不存在就剪枝)
    • 递归地向右子节点(右侧或上方)查找(如果可能存在更近点的话)
    • 以根节点与之距离为初始化值,然后不断更新最优结果
    • 这个算法十分高效,因为在不断靠近查询点的过程中,我们可以直接剪枝掉显然更远的子树
    • 实际应用中,这个算法的复杂度在 log ⁡ N \log N logN
    • 最快情况下(树已经平衡),复杂度在 N N N(比如所有点都在以查询点为圆心的圆上,这样就需要查找所有点)
  • 模拟应用:鸟群
    • 光圈(Boids)三个简单法则产生复杂的集群行为
      • 碰撞避免:将k个最近的光圈远离的点
      • 集群中心:k个最近光圈欸但中心
      • 速度匹配:更新到k个最近光圈的平均速度
    • k维树:递归地将k维空间划分为两个部分
      • 实现:BST,但是向像在2维空间中水平个垂直两个维度交替,遍历k个维度
      • 对第i节点p,将第i维坐标值小于p的置于左节点,大的置于右节点
      • 高效而简单,被广泛使用,很容易适配到高维集群数据
  • N体模拟问题
    • 目标:模拟互相受到引力作用的N个粒子的运动
    • 一个关键思路:当粒子离一个集群很远时,将集群当作一个中心粒子,整体力的作用使用中心质量考虑
    • 使用3D树保存各个子划分的中心质量
    • 通过遍历树以获得足够信息(中心质量)以计算力

区间查找 Interval Search

  • 搜索的目标不再是具体数值点,而是一组可能存在重叠的区间
  • 1维区间查找,用于保持一组可能重叠的区间的数据结构
    • 插入区间 ( l o , h i ) (lo,hi) (lo,hi)
    • 查找一个区间 ( l o , h i ) (lo,hi) (lo,hi)
    • 删除一个区间 ( l o , h i ) (lo,hi) (lo,hi)
    • 区间交集查询:给定一个区间 ( l o , h i ) (lo,hi) (lo,hi),找出所所有与之存在交集的区间
  • API
public class IntervalST<Key extends Comparable<Key>, Value>{
    IntervalST(); // create interval search tree
    void put(Key lo, Key hi, Value val); // put interval-value pair into ST
    Value get(Key lo, Key hi); // value paired with given interval
    void delete(Key lo, Key hi); // delete the given interval
    Iterable<Value> intersects(Key lo, Key hi); // all intervals that intersect the given interval
}
  • 非简并假设:不存在具有相同左端点的区间

  • 创建一个BST,每个节点存储一个区间

    • 使用其左端点作为键

    • 在该节点存储以这个节点为根的子树的最大端点值

    • 插入操作

      • 使用左端点作为键将其插入BST
      • 更新其路径上的每个节点的值(最大端点)
    • 交集存在查询:交集区间

      • 当前节点与查询区间有交集,返回之
      • 如果左子树为空,向右
      • 如果左子树最大端点比查询区间左端点小,向右
      • 否则向左
      Node x = root;
      while (x != null)
      {
          if (x.interval.intersects(lo, hi)) return x.interval;
          else if (x.left == null) x = x.right;
          else if (x.left.max < lo) x = x.right;
          else x = x.left;
      }
      return null;
      
      • 一些情况
        • 如果向右搜索,那么左侧一定没有交集
        • 如果向左,那么要么有一个交集要么一个都没有(如果左边没有交集,右边一定没有)
  • 区间查找算法的时间复杂度为 R log ⁡ N R \log N RlogN,高于理想值 R + log ⁡ N R+\log N R+logN(但是已经够用)

矩形交集 Rectangle Intersection

  • 目标:寻找一组正交的举行中所有的交集
  • 不退化假设:没有相关坐标是相同的
  • 扫线算法,从左到右扫过所有垂直线
    • 左右端点的x坐标定义了一个事件
    • 扫线经过的矩形维护为一个区间
    • 左端点,插入y坐标区间,同时使用y坐标区间进行查找
    • 右端点,移除y坐标区间
  • 扫线算法在 N N N个矩形中找到 R R R个交集的时间复杂度正比于 N log ⁡ N + R log ⁡ N N \log N + R \log N NlogN+RlogN
  • 最佳情况下可以将2维区间查找将维为1维区间查找
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Coursera-ml-andrewng-notes-master.zip是一个包含Andrew Ng的机器学习课程笔记和代码的压缩包。这门课程是由斯坦福大学提供的计算机科学和人工智能实验室(CSAIL)的教授Andrew Ng教授开设的,旨在通过深入浅出的方式介绍机器学习的基础概念,包括监督学习、无监督学习、逻辑回归、神经网络等等。 这个压缩包中的笔记和代码可以帮助机器学习初学者更好地理解和应用所学的知识。笔记中包含了课程中涉及到的各种公式、算法和概念的详细解释,同时也包括了编程作业的指导和解答。而代码部分包含了课程中使用的MATLAB代码,以及Python代码的实现。 这个压缩包对机器学习爱好者和学生来说是一个非常有用的资源,能够让他们深入了解机器学习的基础,并掌握如何运用这些知识去解决实际问题。此外,这个压缩包还可以作为教师和讲师的教学资源,帮助他们更好地传授机器学习的知识和技能。 ### 回答2: coursera-ml-andrewng-notes-master.zip 是一个 Coursera Machine Learning 课程笔记和教材的压缩包,由学生或者讲师编写。这个压缩包中包括了 Andrew Ng 教授在 Coursera 上发布的 Machine Learning 课程的全部讲义、练习题和答案等相关学习材料。 Machine Learning 课程是一个介绍机器学习的课程,它包括了许多重要的机器学习算法和理论,例如线性回归、神经网络、决策树、支持向量机等。这个课程的目标是让学生了解机器学习的方法,学习如何使用机器学习来解决实际问题,并最终构建自己的机器学习系统。 这个压缩包中包含的所有学习材料都是免费的,每个人都可以从 Coursera 的网站上免费获取。通过学习这个课程,你将学习到机器学习的基础知识和核心算法,掌握机器学习的实际应用技巧,以及学会如何处理不同种类的数据和问题。 总之,coursera-ml-andrewng-notes-master.zip 是一个非常有用的学习资源,它可以帮助人们更好地学习、理解和掌握机器学习的知识和技能。无论你是机器学习初学者还是资深的机器学习专家,它都将是一个重要的参考工具。 ### 回答3: coursera-ml-andrewng-notes-master.zip是一份具有高价值的文件,其中包含了Andrew Ng在Coursera上开授的机器学习课程笔记。这份课程笔记可以帮助学习者更好地理解掌握机器学习技术和方法,提高在机器学习领域的实践能力。通过这份文件,学习者可以学习到机器学习的算法、原理和应用,其中包括线性回归、逻辑回归、神经网络、支持向量机、聚类、降维等多个内容。同时,这份笔记还提供了很多代码实现和模板,学习者可以通过这些实例来理解、运用和进一步深入研究机器学习技术。 总的来说,coursera-ml-andrewng-notes-master.zip对于想要深入学习和掌握机器学习技术和方法的学习者来说是一份不可多得的资料,对于企业中从事机器学习相关工作的从业人员来说也是进行技能提升或者知识更新的重要资料。因此,对于机器学习领域的学习者和从业人员来说,学习并掌握coursera-ml-andrewng-notes-master.zip所提供的知识和技能是非常有价值的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值