AVL的单旋和双旋—附图超详细

news2024/12/24 2:37:58

文章目录

  • 前言:
  • AVL 的插入
    • 寻找插入位置
    • 更新平衡因子
    • 调整AVL
      • 右旋
      • 左旋
      • 左右双旋
      • 右左双旋
  • 完整代码

前言:

我们知道二叉排序树的搜索效率很高,能达到(logn)的时间复杂度,但是当元素有序导致二叉搜索树变成了一个长条(图左)的时候,就会导致搜索速率大大降低,所以我们需要对二叉搜索树做出改进:让二叉搜索树能够在插入元素的时候及时调整自己的结构(右图),这就是 AVL 树(高度平衡的二叉搜索树)

在这里插入图片描述

AVL 的插入

所以今天我们的重点就是 AVL 树的插入,并且每一次插入结点,都要调整树的高度,保持树的高度平衡

而不同于普通的二叉搜索树,AVL 树的结点还优点不同,我们不仅需要左右孩子节点,还需要一个指向父亲节点的引用,除此之外,还有 bf 字段——balanced factor,也就是平衡因子,定义为右子树高度 - 左子树高度,于是对于 AVL 树的节点就有了以下定义:

static class TreeNode {
    public TreeNode parent ; // 父亲结点
    public TreeNode left;    
    public TreeNode right;
    // 平衡因子, 右子树增加则 bf ++, 左子树增加则 bf --
    public int bf; // 平衡因子
    public int val;
    public TreeNode(int val) {
        this.val = val;
    }
}

寻找插入位置

对于一个要插入的节点,第一步肯定就是寻找插入位置
如果根节点为空,那么这个新节点就是根节点,否则,寻找插入位置
这个比较简单,不是本文重点,直接上这部分的代码

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return true;
        }

        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                return true; // 重复节点
            }
        }
        // 至此 cur 为空,parent 为 cur 的父节点
        // 完成节点的插入
        if (parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent; // 双向连接

	}

等到循环终止的时候,parent 的左孩子或者右孩子就是待插入节点,这时在分别判断一下,更新一下 parent 的孩子引用和新节点的 parent 引用即可

更新平衡因子

这个新插入的节点我们称为「newNode」, 每次插入一个新节点必然需要调整「从该节点到根节点的平衡因子」
所以我们让 cur = newNode, parent = cur.parent,从 newNode 开始往根节点开始更新平衡因子

如果 cur 为 parent 的左孩子, 那么 parent 的 bf 就需要 -1 (左子树高度增加), 相反, 如果 cur 为 parent 的右孩子, 那么 parent 的 bf 就需要 +1 (右子树高度增加), 于是我们就有了这个代码, 先别急, 慢慢来

// parent = cur.parent;
 cur = node;
 // 开始向上搜索, 重新计算平衡因子, 平衡这棵树
 while (parent != null) {  // 因为是「向上更新 bf」, 所以 parent 自然到根节点的parent就结束了
     if (cur == parent.left) {
         parent.bf --;
     } else {
         parent.bf ++;
     }

在向上更新的过程中, 如果更新完之后, parent 的平衡因子为 1 或者 - 1, 那么我们还是要继续向上更新的, 如下图, 插入 cur 结点后, 「parent 的 bf 变为了 -1, 而 pParent.bf 明显不是原来的 0 」了, pParent.bf 是需要重新更新的

所以当更新完 parent 结点的 bf 的时候, parent.bf 还是 1 或 -1的时候, 我们还需要向上更新

在这里插入图片描述
因此:

// 更新完parent.bf后, bf 还是 if 中的情况
if (parent.bf == 1 || parent.bf == -1) {
   // 继续向上调整
    cur = parent;
    parent = cur.parent;
}

而如果新增 newNode 之后, parent.bf 更新完后 parent.bf = 0是什么情况 ? 如下图, 如果某个 parent 更新完后 parent.bf = 0, 那么就不需要再向上更新了

在这里插入图片描述
于是就有:

// 更新完 parent.bf 后, 还出现 if 中的情况
if (parent.bf == 0) {
     break;
}

那么当更新完后, parent.bf == 2 || parent.bf == -2 的时候, 那就是这个树此时不是高度平衡的状态了, 这时候就需要进行旋转了 !!! 正文开始 !

调整AVL

右旋

首先第一种情况, 如果parent.bf == -2 && cur.bf == -1, 从这两个 bf 变量的情况上来看, 就是这个树左边比较长。插入结点前后 如下👇

在这里插入图片描述

那么这时候, 我们就需要对这个树进行右旋, 如图, 把 lson 提起来, 然后把它的右孩子交给 parent 的左孩子, 于是这个左旋就完成了

在这里插入图片描述
对于当前子树左旋后, 我们还需要更新原 parent.parent的孩子指向, 说的有点复杂, 给个图就懂了

如果 parent == root, 那么旋转后, 根节点就是 lson, 让root = lson 即可 , 这个情况跟上面这个图一样👆
如果 pParent.left == parent || pParent.right == parent 那么就重新连接一下 「旋转后的子树」, 这个应该不难理解, 如下图, 旋转后, pParent 的孩子结点应该更新为 lson, 同理 lson 的父亲结点也要更新

在这里插入图片描述
随后, 再更新相应结点的平衡因子, 这种情况下 lson.bf 和 parent.bf 都会变成 0

而另一种情况, 也就是 lsonRight = null 的情况,如下 👇,这种情况 lson 和 parent 的 bf 也都会变成 0

在这里插入图片描述

所以右旋之后, parent 和 lson 的平衡因子都会变成 0

最后, 再给出上述操作的代码

    // 右旋, 把 parent 的 左孩子提起来
    private void rotateRight(TreeNode parent) {
        // parent 的左孩子
        TreeNode lson = parent.left;
        // lson 的右孩子
        TreeNode lsonRight = lson.right;
        // parent 的父节点
        TreeNode pParent = parent.parent;


        lson.right = parent;
        parent.parent = lson;
        // 这里特殊处理一下, 有可能 lsonRight 为空
        if (lsonRight != null) { 
            lsonRight.parent = parent;
        }
        parent.left = lsonRight;

        // 再讨论根节点, 如果 parent 本来是根节点
        if (root == parent) {
            root = lson;
            lson.parent = null;
        } else {
            if (pParent.left == parent) {
                pParent.left = lson;
            } else {
                pParent.right = lson;
            }
            lson.parent = pParent;
        }
        lson.bf = parent.bf = 0;
    }

左旋

第二种情况,当parent.bf == 2 && cur.bf == 1 的时候,从这两个变量上看,就是右子树比较长,插入元素前后 如下👇

在这里插入图片描述

这时候右树比较长,所以需要进行左旋,操作图如下👇,rson 的左孩子(即rsonLeft)交给 parent 的右孩子

在这里插入图片描述
左旋和右旋很相似,也需要重新判断旋转后的子树的父亲节点pParent,同时无论 rsonLeft 存不存在,最终 rson.bf 和 parent.bf 都会变成 0,这里就不赘述了

所以啊, 单旋之后 parent 和 lson(或者rson) 的平衡因子都置为 0 即可

    // 右子树太高, 左旋
    private void rotateLeft(TreeNode parent) {
        TreeNode rson = parent.right;
        TreeNode rsonLeft = rson.left;
        TreeNode pParent = parent.parent;

        rson.left = parent;
        parent.parent = rson;
        if (rsonLeft != null) { // 再 rsonLeft 存在的情况下才更新 rsonLeft 的父亲节点
            rsonLeft.parent = parent;
        }
        parent.right = rsonLeft;

        if (parent == root) {
            root = rson;
            rson.parent = null;
        } else {
            if (pParent.left == parent) {
                pParent.left = rson;
            } else {
                pParent.right = rson;
            }
            rson.parent = pParent; // 双向连接
        }
        // 再更新平衡因子
        rson.bf = parent.bf = 0;
    }

左右双旋

接下来还有两种情况,我们再来讨论其中的第一种:
如果parent.bf = -2, cur.bf = 1, 从这两个变量和图形来说,有种中间比较长的感觉
在这里插入图片描述
这种情况下,不论是一次左旋还是一次右旋都无法达成平衡,左旋后高度差还是能能达到 2,左旋后如下图👇。
只进行一次右旋也是一样,笔者就偷懒不画了

在这里插入图片描述这种情况下就只能够通过两次旋转来重新调整为平衡——先左旋再右旋, 最终让 lsonRight 变成旋转后的根节点, 我们再画个图看看这个过程

在这里插入图片描述以上就是左右双旋的旋转过程, 然后我们再讨论一下平衡因子的变化情况
双旋的平衡因子调整分为三种情况: lsonRight.bf = 1 || lsonRight.bf == -1 || lsonRight.bf == 0

  • 上面我这种画法 lsonRight.bf = 1, 最终 parent, lson, lsonRight 的因子最终变化:lson.bf = -1, lsonRight.bf = 0, parent.bf = 0
  • 而如果 lsonRight.bf == 0 的时候,这种情况下这三个节点的 bf 值是不会发生变化的,不需要做处理
  • 如果lsonRight.bf = -1,这种情况最终变化:lson.bf = 0, lsonRight.bf = 0, parent.bf = 1,我们画个图看看:

在这里插入图片描述到这里左右双旋就完成了,稍微有点复杂,但是理解了左旋和右旋应该是能够接受的,加油,这里上代码

    private void rotateLR(TreeNode parent) {
        TreeNode lson = parent.left;
        TreeNode lsonRight  = lson.right;
        int bf = lsonRight.bf;

        rotateLeft(lson);
        rotateRight(parent);

        // 再分情况调整 平衡因子
        if (bf == 1) {
            lson.bf = -1;
            lsonRight.bf = 0;
            parent.bf = 0;
        } else if (bf == -1) { // bf == -1
            lson.bf = 0;
            lsonRight.bf = 0 ;
            parent.bf = 1;
        } // 至此 bf == 0,不做任何处理
    }

右左双旋

最后一个就是右左双旋了, 这个和左右…是类似的, 我们画一次熟悉熟悉, 如果是parent.bf = 2, cur.bf = -1, 插入结点后如下图, 有种中间比较长的感觉

在这里插入图片描述
这情况和左右双旋的情况一样, 单次右旋或者左旋都是无法调整为平衡的
我们需要先右旋再左旋, 示意图如下, 还是一样定义三个关键结点 parent, rson, rsonLeft, 并且最终旋转后的子树根节点为 rsonLeft

在这里插入图片描述然后是平衡因子的调整, 还是一样分为三种情况: rsonLeft.bf == -1 || rsonLeft == 0 || rsonLeft == 1

  • 如果rsonLeft.bf == -1, 那么这种情况就和上面这张图一样, 最终因子变成parent.bf = 0, rsonLeft.bf = 0, rson = 1
  • 如果rsonLeft.bf == 0, 这种情况不用做任何操作
  • 如果rsonLeft.bf == 1, 最终因子变成parent.bf = -1, rsonLeft.bf = 0, rson.bf = 0, 我们再画个图看看

在这里插入图片描述到这里就完成了, 上代码

    private void rotateRL(TreeNode parent) {
        TreeNode rson = parent.right;
        TreeNode rsonLeft = rson.left;
        int bf = rsonLeft.bf;

        rotateRight(rson); // 复用之前的左旋右旋即可
        rotateLeft(parent);


        if (bf == 1) {
            parent.bf = -1;
            rsonLeft.bf = 0;
            rson.bf = 0;
        } else if (bf == -1) { // bf = -1
            parent.bf = 0;
            rsonLeft.bf = 0;
            rson.bf = 1;
        }
    }

完整代码

/**
 * 模拟实现:高度平衡的二叉搜索树
 */
public class AVL {
    static class TreeNode {
        public TreeNode parent ;
        public TreeNode left;
        public TreeNode right;
        // 平衡因子, 右子树增加则 bf ++, 左子树增加则 bf --
        public int bf;
        public int val;
        public TreeNode(int val) {
            this.val = val;
        }
    }

    public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return true;
        }

        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                return true; // 重复节点
            }
        }
        // 至此 cur 为空,parent 为 cur 的父节点
        // 完成节点的插入
        if (parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent; // 双向连接

        // parent = cur.parent;
        cur = node;
        // 开始向上搜索, 重新计算平衡因子, 平衡这棵树
        while (parent != null) {
            if (cur == parent.left) {
                parent.bf --;
            } else {
                parent.bf ++;
            }

            // 如果说 parent 的结点重新变为 0, 则说明该树平衡, 不需要再调整
            if (parent.bf == 0) {
                break;
            } else if (parent.bf == 1 || parent.bf == -1) {
                // 还算正常, 继续向上结算
                cur = parent;
                parent = cur.parent;
            } else {
                // 否则 parent 的 bf 为 2, 或者 parent 的 bf 为 -2
                if (parent.bf == -2) {
                    if (cur.bf == -1) {
                        // 左子树太高了, 需要右旋
                        rotateRight(parent);
                    } else {
                        // parent.bf == -2 && cur.bf == 1
                        // 这时候就需要 左旋 再 右旋
                        rotateLR(parent);
                    }
                } else {
                    // parent.bf == 2
                    if (cur.bf == 1) {
                        rotateLeft(parent);
                    } else { // cur.bf == -1
                        // 这时候需要先 左旋 和 右旋
                        rotateRL(parent);
                    }
                }
                // 完成了一次旋转就能够达成平衡
                break;
            }
        }
        return true;
    }

    private void rotateRL(TreeNode parent) {
        TreeNode rson = parent.right;
        TreeNode rsonLeft = rson.left;
        int bf = rsonLeft.bf;

        rotateRight(rson);
        rotateLeft(parent);


        if (bf == 1) {
            parent.bf = -1;
            rsonLeft.bf = 0;
            rson.bf = 0;
        } else if (bf == -1) { // bf = -1
            parent.bf = 0;
            rsonLeft.bf = 0;
            rson.bf = 1;
        }
    }

    private void rotateLR(TreeNode parent) {
        TreeNode lson = parent.left;
        TreeNode lsonRight  = lson.right;
        int bf = lsonRight.bf;

        rotateLeft(lson);
        rotateRight(parent);

        // 再分情况调整 平衡因子
        if (bf == 1) {
            lson.bf = -1;
            lsonRight.bf = 0;
            parent.bf = 0;
        } else if (bf == -1) { // bf == -1
            lson.bf = 0;
            lsonRight.bf = 0 ;
            parent.bf = 1;
        }
    }

    // 右子树太高, 左旋
    private void rotateLeft(TreeNode parent) {
        TreeNode rson = parent.right;
        TreeNode rsonLeft = rson.left;
        TreeNode pParent = parent.parent;

        rson.left = parent;
        parent.parent = rson;
        if (rsonLeft != null) {
            rsonLeft.parent = parent;
        }
        parent.right = rsonLeft;

        if (parent == root) {
            root = rson;
            rson.parent = null;
        } else {
            if (pParent.left == parent) {
                pParent.left = rson;
            } else {
                pParent.right = rson;
            }
            rson.parent = pParent; // 双向连接
        }
        // 再更新平衡因子
        rson.bf = parent.bf = 0;
    }

    // 右旋, 把 parent 的 左孩子提起来
    private void rotateRight(TreeNode parent) {
        // parent 的左孩子
        TreeNode lson = parent.left;
        // lson 的右孩子
        TreeNode lsonRight = lson.right;
        // parent 的父节点
        TreeNode pParent = parent.parent;


        lson.right = parent;
        parent.parent = lson;
        if (lsonRight != null) {
            lsonRight.parent = parent;
        }
        parent.left = lsonRight;

        // 再讨论根节点, 如果 parent 本来是根节点
        if (root == parent) {
            root = lson;
            lson.parent = null;
        } else {
            if (pParent.left == parent) {
                pParent.left = lson;
            } else {
                pParent.right = lson;
            }
            lson.parent = pParent;
        }
        lson.bf = parent.bf = 0;
    }


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

游戏优化之空间划分

使用的动机 定义&#xff1a;将对象根据它们的位置存储在数据结构中&#xff0c;来高效地定位对象。 在游戏中&#xff0c;AI向最近的敌人攻击是很常见的&#xff0c;但如果有很多单位的话&#xff0c;他们AI需要频繁的查找单位&#xff0c;然后在检测是不是距离最近的单位&a…

Git工具使用全解

Git工具使用全解 文章目录Git工具使用全解1.企业开发中的版本控制器2.Git工具的使用场景3.Git工具操作流程(三板斧操作)4.Git工具的安装与常用命令4.1 Git的安装4.2 Git基本操作指令5.Git工具常见问题解决5.1 常见问题&#xff1a;分支冲突解决办法5.2 常见问题&#xff1a;合并…

Linux | 动静态库 | 动静态链接 | makefile库打包 | 第三方库使用

文章目录何为动静态库库文件的链接静态链接静态库打包动态链接动态库打包第三方库的使用静态库的使用动态库的使用在系统层面上的动态链接理解何为动静态库 静态库(.a)&#xff1a;在程序编译链接时将静态库二进制码拷贝到程序代码中&#xff0c;程序运行时不再需要外部的静态库…

面试:类相关---Java、Android有哪些类加载器

Android类加载器与Java类加载器的对比 - 掘金 Android | 类加载器与插件化 - 简书 相关复习&#xff1a; 面试&#xff1a;热修复原理_沙漠一只雕得儿得儿的博客-CSDN博客_android 热修复原理面试 面试&#xff1a;类的初始化过程_沙漠一只雕得儿得儿的博客-CSDN博客 什么…

内置 230+ 工具,它值得被官方纳入标准库

经过了几十年的发展&#xff0c;Python 的轮子生态越来越丰富&#xff0c;例如针对网络请求有 requests&#xff0c;针对命令行开发有 typer 等等&#xff0c;这些第三方库给我们的日常开发带来了极大的便利。 今天我推荐另一个第三方库 – Boltons&#xff0c;和大多数第三方…

go语言之不必要的拷贝

其实我也是个golang开发者~~ Go语言本来就以轻量快速著称&#xff0c;一位GitHub员工却偶然发现&#xff1a; 只改变一个字符的位置&#xff0c;能把一段代码运行速度提高足足42%。 简直就像是…… 这个简单有效的技巧一经发布&#xff0c;就引来众多程序员围观。 原作者自己…

论文中常见的拟合散点验证图(R语言版)

论文中常见的拟合散点验证图&#xff08;R语言版&#xff09; 如上图所示&#xff0c;是论文中常见的validation图&#xff0c;python也能实现相似的图绘。 今天先介绍R语言版&#xff0c;python改期再介绍吧 这张图需要依次实现下列功能&#xff1a; data实测与data模拟的散…

RabbitMQ系列【11】延迟队列

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录前言1. 过期消息实现延迟队列2. 过期队列实现延迟队列3. 插件实现延迟队列3.1 安装插件3.2 代码实现3.3 测试前言 延迟队列&#xff1a;即消息进入队列后不会立即被消费&#xff0c;只有到达指…

MySQL事务隔离机制 -- 必须说透

文章目录前言一、什么是数据库事务二、事务并发带来的4类问题三、事务4种隔离级别四、Mysql演示4种隔离级别总结前言 如何控制并发是数据库领域中非常重要的问题之一&#xff0c;MySQL为了解决并发带来的问题&#xff0c;设计了事务隔离机制、锁机制、MVCC机制&#xff0c;用一…

c# 实验七 图像列表框及树形视图控件的使用

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;《项目专栏》 &#x1f4e7;如果文章知识点有错误的地方&#xf…

[附源码]java毕业设计四六级考试管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

m基于matlab的wcdma软切换算法的研究分析和仿真

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 软切换是WCDMA系统的关键技术之一&#xff0c;软切换算法和相关参数的设置直接影响着系统的容量和服务质量。通过WCDMA系统的软切换技术可以提高小区覆盖率和系统容量。所以软切换技术是…

【ASM】字节码操作 工具类与常用类 LocalVariablesSorter 类 简单介绍与使用

文章目录 1.概述2. LocalVariablesSorter#2.1 class info2.2 fields3.案例3.1 编码实现3.2 编码实现v21.概述 在上一节:【ASM】字节码操作 工具类与常用类 GeneratorAdapter 介绍 我们知道了对于GeneratorAdapter 类来说,它非常重要的一个特点:将一些visitXxx()方法封装成一…

Java面向对象详解(上)

Java面向对象详解&#xff08;上&#xff09;&#x1fa85;面向对象与面向过程的区分✨面向过程&#xff1a;✨面向对象&#xff1a;&#x1fa85;类是什么&#xff1f;&#x1fa85;对象是什么&#xff1f;&#x1fa85;类的结构&#x1fa85;类中方法&#xff1a;✨成员方法与…

实战讲解SpringBoot启动时自动加载数据库数据到内存:通过回调方法自动运行Bean(图+文+源码)

1 缘起 在补充SpringCloud网关&#xff08;Gateway&#xff09;配置白名单相关知识过程中&#xff0c; 有两种实现方案&#xff1a; &#xff08;1&#xff09;SpringBoot的启动配置文件application.yml进行配置&#xff1b; &#xff08;2&#xff09;自动加载MySQL数据库中的…

【人工智能】Mindspore框架中保存加载模型

前言 MindSpore着重提升易用性并降低AI开发者的开发门槛&#xff0c;MindSpore原生适应每个场景包括端、边缘和云&#xff0c;并能够在按需协同的基础上&#xff0c;通过实现AI算法即代码&#xff0c;使开发态变得更加友好&#xff0c;显著减少模型开发时间&#xff0c;降低模…

深度学习在图像处理中的应用学习笔记

这篇学习笔记用于记录本人在读研期间的学习内容 在刚入学不久&#xff0c;发现一个B站up主对这方面进行了一系列的整理总结&#xff0c;并上传了代码&#xff0c;并且非常成体系&#xff0c;因此本人打算跟着这位up主的步骤&#xff0c;对这方面进行学习并且做一个记录&#xf…

Vue安装并使用axios发送请求

前言 本文主要介绍的是使用在Vue项目中安装并使用axios发送请求 axios介绍 axios是一种Web数据交互方式 它是一个基于promise的网络请求库&#xff0c;作用于node.js和浏览器中&#xff0c;它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中) 本质是对原生XHRX…

NAND Flash原理

Flash 简介 Flash全名叫做Flash Memory&#xff0c;属于非易失性存储设备(Non-volatile Memory Device)&#xff0c;与此相对应的是易失性存储设备(Volatile Memory Device)。关于什么是非易失性/易失性&#xff0c;从名字中就可以看出&#xff0c;非易失性就是不容易丢失&…

BGP→→

BGP-4 提供了一套新的机制以支持无类域间路由。这些机制包括支持网络前缀的通告、取消 BGP 网络中 “ 类 ” 的概念。 BGP-4 也引入机制支持路由聚合&#xff0c;包括 AS 路径的集合。 特点 BGP属于外部或域间路由协议。BGP的主要目标是为处于不同AS中的路由器之间进行路由信…