前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++AVL树

C++AVL树

作者头像
用户9645905
发布于 2022-11-30 05:02:03
发布于 2022-11-30 05:02:03
44800
代码可运行
举报
文章被收录于专栏:Linux学习~Linux学习~
运行总次数:0
代码可运行

AVL树

零、前言

本章主要讲解map和set的底层结构平衡二叉搜索树的一种-AVL树的特性及其实现

一、AVL树的概念

  • 引入:

  1. map/multimap/set/multiset其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷
  2. 假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N)
  3. 因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现
  • 概念:

对于数据有序或接近有序二叉搜索树将退化为单支树的情况,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

  • 一棵AVL树或者是空树或者是具有以下性质的二叉搜索树:

  1. 它的左右子树都是AVL
  2. 树左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
  • 示图:

注:如果一棵二叉搜索树是高度可保持在1(-1/0/1),则非常接近完全二叉树 ,搜索时间复杂度O(logN)

二、AVL树结点定义

为了方便找到子树对应的父亲节点,这里我们选择使用三叉链结构

  • 代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
struct AVLTreeNode
{
	//三叉链
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;
	
	int _bf;//平衡因子
	T _kv;//T可以是key也可以是pair<K,V>类型便于封装成map或set

	AVLTreeNode(const T& t)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
		,_kv(t)
	{}
};

三、AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树

  • 那么AVL树的插入过程:
  1. 首先按照二叉搜索树的方式插入新节点

  1. 待插入结点的key值比当前结点小就插入到该结点的左子树
  2. 待插入结点的key值比当前结点大就插入到该结点的右子树
  3. 待插入结点的key值与当前结点的key值相等就插入失败
  • 示例:插入12
  1. 插入后则向上调整当前结点到根路径上祖先结点的平衡因子
  • 示图:

平衡因子数值=右子树高度-左子树的高度

  • 平衡因子更新规则:

1.如果新增结点在祖父节点的左子树中,则父节点平衡因子数值减1 2.如果新增结点在祖父节点的右子树中,则父节点平衡因子数值加1

  • 平衡因子更新后处理规则:

1.更新后平衡因子为1或-1,那么说明该平衡因子更新前为0,子树的高度发生变化,则也会影响父亲结点的平衡因子,继续向上更新平衡因子 2.更新后平衡因子为0,那么说明该平衡因子更新前为1或-1,子树的高度未发生变化,则也不会影响父亲结点的平衡因子,停止向上更新平衡因子 3.更新后平衡因子为2或-2,那么当前子树不符合AVL树的规则,需要进行旋转子树进行调节高度

注:更新平衡因子之前,该树本身就满足AVL树的规则,平衡因子为1或0或-1

  • 示图:

实现代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pair<Node*, bool> Insert(const pair<K,V>& kv)
{
    if (_root == nullptr)//空树的情况
    {
        _root = new Node(kv);
        return make_pair(_root, true);
    }
    //插入需要链接父子节点,但是插入的位置是空节点,需要另一个指针记录父结点
    Node* cur = _root, *parent = _root;
    while (cur)
    {
        if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left;
        }
        else if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else//找到了相同的
        {
            return make_pair(cur,false);
        }
    }
    //找到插入位置,创建链接节点
    cur = new Node(kv);
    Node* newnode = cur;//记录新结点地址,便于返回
    if (parent->_kv.first > kv.first)
    {
        parent->_left = cur;
    }
    else
    {
        parent->_right = cur;
    }
    cur->_parent = parent;

    //向上更新平衡因子
    while (cur != _root)
    {
        if (parent->_right == cur)
        {
            parent->_bf++;
        }
        else
        {
            parent->_bf--;
        }

        if (parent->_bf == 0)//子树高度不变,不影响上面路径的结点平衡因子
        {
            break;
        }
        else if (parent->_bf == 1 || parent->_bf == -1)//子树高度改变,继续向上更新节点平衡因子
        {
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)//子树已经不平衡了,需要进行旋转处理
        {
            if (parent->_bf == -2)
            {
                if (cur->_bf == -1)//右单旋
                {
                    RotateR(parent);
                }
                else//左右双旋
                {
                    RotateLR(parent);
                }
            }
            else
            {
                if (cur->_bf == 1)//右单旋
                {
                    RotateL(parent);
                }
                else//右左双旋
                {
                    RotateRL(parent);
                }
            }
            break;
        }
        else//已经出错了
        {
            assert(false);
        }
    }
    return make_pair(newnode, true);
}

四、AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡

  • 根据节点插入位置的不同,AVL树的旋转分为四种:
  1. 新节点插入较高右子树的右侧—右右:左单旋

1、左单旋

  • 抽象示图:
  • 注意:

  1. 上图在插入前AVL树是平衡的,新节点插入到60的右子树(注意:此处不是有孩子)中,60右子树增加了一层,导致以30为根的二叉树不平衡
  2. 要让30平衡,只能将30右子树的高度减少一层,左子树增加一层,即将右子树往上提,这样30转下来,因为30比60大=小,只能将其放在60的左子树,而如果60有左子树,左子树根的值一定大于30,小于60,只能将其放在30的右子树,旋转完成后,更新节点的平衡因子即可
  3. 对于a,b,c都是符合AVL树且高度为h的树的一种,这里不具体表示,以抽象表示各种情况,对于以下抽象图示也是如此
  • 在旋转过程中,有以下几种情况需要考虑:

  1. 60节点的左孩子可能存在,也可能不存在
  2. 30可能是根节点,也可能是子树:如果是根节点,旋转完成后,要更新根节点;如果是子树,可能是某个节点的左子树,也可能是右子树,需要更新父结点的左右结点地址
  3. 旋转后,子树得到平衡,两个旋转结点的平衡因子更新为0,而高度没有改变,不用再向上更新结点平衡因子
  • 实例示图:
  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 左单旋
void RotateL(Node* parent)
{
    //记录节点信息
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    Node* parentP = parent->_parent;

    //修改链接关系
    parent->_right = subRL;
    if (subRL)//避免为空节点的情况
        subRL->_parent = parent;

    subR->_left = parent;
    parent->_parent = subR;

    //讨论父节点的情况
    if (parent == _root)
    {
        _root = subR;
        subR->_parent = nullptr;
    }
    else
    {
        if (parentP->_left == parent)
        {
            parentP->_left = subR;
        }
        else
        {
            parentP->_right = subR;
        }
        subR->_parent = parentP;
    }

    //更新平衡因子
    subR->_bf = parent->_bf = 0;
}
  1. 新节点插入较高左子树的左侧—左左:右单旋

2、右单旋

注:具体思路和步骤可以参考左单旋

  • 抽象示图:
  • 实例示图:
  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 右单旋
void RotateR(Node* parent)
{
    //记录节点信息
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    Node* parentP = parent->_parent;

    //修改链接关系
    parent->_left = subLR;
    if (subLR)//避免为空节点的情况
        subLR->_parent = parent;

    subL->_right = parent;
    parent->_parent = subL;

    //讨论父节点的情况
    if (parent == _root)
    {
        _root = subL;
        subL->_parent = nullptr;
    }
    else
    {
        if (parentP->_left == parent)
        {
            parentP->_left = subL;
        }
        else
        {
            parentP->_right = subL;
        }
        subL->_parent = parentP;
    }

    //更新平衡因子
    subL->_bf = parent->_bf = 0;
}
  1. 新节点插入较高左子树的右侧**—**左右:先左单旋再右单旋

3、左右双旋

  • 抽象示图:
  • 注意:

  1. 将双旋变成单旋后再旋转,即先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新(并不都为0,具体情况具体分析)
  2. 复用单旋会把其他情况都给处理,例如子树是否为空,当前不平衡结点为根结点还是子树结点
  3. 对于h高度的子树,h满足大于等于0,当h=0时,插入新节点就是60
  4. 左右双旋可以看做是60做当前树的根结点,并将左子树给给30结点,将右子树给给90结点
  • 旋转后更新平衡因子具有三种情况:

  1. 插入结点就是不平衡结点的subLR(左子结点的右子结点),30,60,90都更新为0
  2. 插入结点为不平衡结点的subLR的左子结点,30,60更新为0,90更新为1
  3. 插入结点就是不平衡结点的subLR的右子节点,90,60更新为0,30更新为-1
  • 实例示图:h为0的情况
  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 左右双旋
void RotateLR(Node* parent)
{
    //记录节点地址和平衡因子
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

    int bf = subLR->_bf;
    //旋转
    RotateL(subL);
    RotateR(parent);

    //处理平衡因子
    if (bf == -1)//新增节点为subLR的左子树
    {
        subLR->_bf = 0;
        parent->_bf = 1;
        subL->_bf = 0;
    }
    else if (bf == 1)//新增节点为subRL的右子树
    {
        subL->_bf = -1;
        parent->_bf = 0;
        subLR->_bf = 0;
    }
    else if (bf == 0)//新增节点就是subRL
    {
        subLR->_bf = 0;
        parent->_bf = 0;
        subL->_bf = 0;
    }
    else //旋转之前就已经存在错误了
    {
        assert(false);
    }
}
  1. 新节点插入较高右子树的左侧**—**右左:先右单旋再左单旋

4、右左双旋

  • 抽象示图:

注:具体情况可以参考左右双旋

  • 实例示图:
  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 右左双旋
void RotateRL(Node* parent)
{
    //记录节点地址和平衡因子
    Node* subR = parent->_right;
    Node* subRL = subR->_left;

    int bf = subRL->_bf;
    //旋转
    RotateR(subR);
    RotateL(parent);

    //处理平衡因子
    if (bf == -1)//新增节点为subLR的左子树
    {
        subRL->_bf = 0;
        parent->_bf = 0;
        subR->_bf = 1;
    }
    else if (bf == 1)//新增节点为subLR的右子树
    {
        subRL->_bf = 0;
        parent->_bf = -1;
        subR->_bf = 0;
    }
    else if (bf == 0)//新增节点就是subLR
    {
        subRL->_bf = 0;
        parent->_bf = 0;
        subR->_bf = 0;
    }
    else //旋转之前就已经存在错误了
    {
        assert(false);
    }
}

5、总结

  • 假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑:
  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR

  1. 当SubR的平衡因子为1时,执行左单旋
  2. 当SubR的平衡因子为-1时,执行右左双旋
  3. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL

  1. 当SubL的平衡因子为-1是,执行右单旋
  2. 当SubL的平衡因子为1时,执行左右双旋

从视角上来看,当旋转相关结点成直线,则进行单旋;当旋转相关结点成折线,则进行双旋

旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新

五、AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制

  • 要验证AVL树可以分两步:
  1. 验证其为二叉搜索树

如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void _InOrder(Node* root)
{
    if (root == nullptr)
        return;

    _InOrder(root->_left);
    cout << root->_kv.first << " : " << root->_kv.second << endl;
    _InOrder(root->_right);
}
  1. 验证其为平衡树

每个结点子树高度差的绝对值不超过1(注意结点中如果没有平衡因子)以及结点的平衡因子是否计算正确

  • 实现代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//验证pRoot是否为有效的AVL树
bool _IsAVLTree(Node* root)
{
    //空树
    if (root == nullptr)
        return true;
	//比较高度
    int heightL = _Height(root->_left);
    int heightR = _Height(root->_right);
    //检查平衡因子是否有误
    if (heightR - heightL != root->_bf)
    {
        cout << "平衡因子错误:" << root->_kv.first << endl;
        return false;
    }

    return abs(heightR - heightL) < 2
        && _IsAVLTree(root->_left)
        && _IsAVLTree(root->_right);//递归检查左右子树
}
//高度
size_t _Height(Node* root)
{
    //空节点
    if (root == nullptr)
        return 0;

    size_t left = _Height(root->_left);
    size_t right = _Height(root->_right);

    return left > right ? left + 1 : right + 1;
}

六、AVL树的性能

  • 分析:
  1. AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度logN
  2. 但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置
  • 总结:

如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Ubuntu 18.04+RTX2080Ti+CUDA10.1+CUDNN7.6.5+Pytorch1.3环境部署(详细教程)
修正:Ubuntu 18.04+RTX2080Ti建议安装cuda10.0,cudnn7.5.1,pytorch1.4.0+cu100 / torchvision0.5.0+cu100,tensorflow-gpu1.14.0 修正日期:20200611
全栈程序员站长
2021/04/07
1K0
解决Ubuntu循环登陆问题 + Nvidia驱动、Cuda、Cudnn安装
ubuntu系统显卡驱动偶尔会出现奇怪的问题,造成图形用户界面循环登陆,本文记录相关问题的解决方案。 卸载旧驱动 在登陆界面 ctrl+alt+F2 进入非图形界面控制台,登陆后输入命令: sudo apt-get remove nvidia-* sudo apt-get autoremove sudo apt-get purge nvidia* 安装驱动 加入PPA,然后更新库 sudo add-apt-repository ppa:graphics-drivers sudo apt-get
为为为什么
2022/08/05
1K0
Ubuntu 20.04 CUDA&cuDNN安装方法[通俗易懂]
如果无法查看,则说明尚未安装nvidia驱动,点击附加驱动,选择对应版本的驱动即可自动下载。
全栈程序员站长
2022/07/01
1K0
Ubuntu 20.04 CUDA&cuDNN安装方法[通俗易懂]
手把手教你在 Ubuntu16.04 安装 GPU 驱动 + CUDA9.0 + cuDNN7
如果你的电脑安装了 Ubuntu16.04,而且电脑自带一块 NVIDIA GeForce 的 GPU 显卡,那么不用来跑深度学习模型就太可惜了!关于这方面的网上教程很多,但大都良莠不齐。这篇文章将手把手教你如何安装 GPU 显卡驱动、CUDA9.0 和 cuDNN7。值得一试!
红色石头
2022/01/12
6.9K0
手把手教你在 Ubuntu16.04 安装 GPU 驱动 + CUDA9.0 + cuDNN7
Ubunt16.04 搭建 GPU 显卡驱动 + CUDA9.0 + cuDNN7 详细教程
如果你的电脑安装了 Ubuntu16.04,而且电脑自带一块 NVIDIA GeForce 的 GPU 显卡,那么不用来跑深度学习模型就太可惜了!关于这方面的网上教程很多,但大都良莠不齐。这篇文章将手把手教你如何安装 GPU 显卡驱动、CUDA9.0 和 cuDNN7。值得一试!
红色石头
2022/01/12
7890
Ubunt16.04 搭建 GPU 显卡驱动 + CUDA9.0 + cuDNN7 详细教程
深度学习GPU环境安装教程:Ubuntu16.04+1080(Ti)显卡驱动+CUDA+cuDNN(已n次完美安装)
当前只装了ubuntu16.04单系统,亲测可用,之前ubuntu16.04+win10双系统下也是这种方法装的,只是需要切换视频线的接口,可参考这篇
对角巷法师
2022/05/07
1.5K0
深度学习GPU环境安装教程:Ubuntu16.04+1080(Ti)显卡驱动+CUDA+cuDNN(已n次完美安装)
Ubuntu下安装cuda_Ubuntu下KDE的安装删除
若没有安装,则查看是否有N卡驱动,若无N卡驱动,则到软件与更新 -> 附加驱动中安装驱动
全栈程序员站长
2022/11/09
9420
Ubuntu下安装cuda_Ubuntu下KDE的安装删除
点云深度学习环境配置指南(一)Ubuntu16.04+RTX2080ti
【今日导读】想做点云深度学习?先把环境配置好吧。本期为初学者带来环境配置指南,有需求的同学赶快上手吧。配置为:
点云乐课堂
2020/05/18
1.3K0
Ubuntu 16.04下为TITAN 1080 显卡安装驱动及Gpu版TensorFlow|深度学习
近来入坑了TITAN 1080显卡,在Ubuntu 16.04下为装好驱动以使用Gpu版TensorFlow可不简单,踩了许多坑之后写下此篇为记录。 下载Cuda 按装官方教程,我们可以应该安装Cu
陆勤_数据人网
2018/02/28
1.5K0
Ubuntu 16.04下为TITAN 1080 显卡安装驱动及Gpu版TensorFlow|深度学习
Ubuntu18.04安装 NVIDIA驱动+CUDA10.2+cuDNN+TensorRT
之后,按照提示安装,成功后重启即可。 如果提示安装失败,不要着急重启;可重复上述步骤,多试几次。
全栈程序员站长
2022/08/19
2K0
深度学习GPU环境Ubuntu16.04+GTX1080+CUDA9+cuDNN7+TensorFlow1.6环境配置
本节详细说明一下深度学习环境配置,Ubuntu 16.04 + Nvidia GTX 1080 + Python 3.6 + CUDA 9.0 + cuDNN 7.1 + TensorFlow 1.6。 Python 3.6 首先安装 Python 3.6,这里使用 Anaconda 3 来安装,下载地址:https://www.anaconda.com/download/#linux,点击 Download 按钮下载即可,这里下载的是 Anaconda 3-5.1 版本,如果下载速度过慢可以选择使用清华
崔庆才
2018/04/08
2.1K0
深度学习GPU环境Ubuntu16.04+GTX1080+CUDA9+cuDNN7+TensorFlow1.6环境配置
Ubuntu安装和卸载CUDA和CUDNN
最近在学习PaddlePaddle在各个显卡驱动版本的安装和使用,所以同时也学习如何在Ubuntu安装和卸载CUDA和CUDNN,在学习过程中,顺便记录学习过程。在供大家学习的同时,也在加强自己的记忆。本文章以卸载CUDA 8.0 和 CUDNN 7.05 为例,以安装CUDA 10.0 和 CUDNN 7.4.2 为例。
夜雨飘零
2020/05/06
10.3K0
ubuntu16.04安装cuda9.0(ubuntu18安装nvidia驱动)
Ubuntu 下安装CUDA需要装NVIDIA驱动,首先进入NVIDIA官网,然后查询对应NVIDIA驱动是否支持你电脑的型号。
全栈程序员站长
2022/07/29
7630
ubuntu16.04安装cuda9.0(ubuntu18安装nvidia驱动)
深度学习之CUDA + cudnn
CUDA官网: https://developer.nvidia.com/cuda-downloads
数据科学工厂
2023/02/27
4100
深度学习之CUDA + cudnn
Ubuntu18.04LTS下cuda10.0+cudnn7.5+TensorFlow1.13环境搭建
前言 之前写过cuda环境的搭建文章, 这次干脆补全整个深度学习环境的搭建. ---- 开发环境一览 CPU: Intel core i7 4700MQ GPU: NVIDIA GT 750M
sean_yang
2019/03/15
2K0
Ubuntu18.04LTS下cuda10.0+cudnn7.5+TensorFlow1.13环境搭建
真实机下 ubuntu 18.04 安装GPU +CUDA+cuDNN 以及其版本选择(亲测非常实用)
目前,大多情况下,能搜到的基本上都ubuntu 14.04.或者是ubuntu 16.04的操作系统安装以及GPU 环境搭建过程,博主就目前自身实验室环境进行分析,总结一下安装过程。
全栈程序员站长
2022/08/20
2K0
真实机下 ubuntu 18.04 安装GPU +CUDA+cuDNN 以及其版本选择(亲测非常实用)
纯净Ubuntu16安装CUDA(9.1)和cuDNN
本篇概览 自己有一台2015年的联想笔记本,显卡是GTX950M,已安装ubuntu 16.04 LTS桌面版,为了使用其GPU完成deeplearning4j的训练工作,自己动手安装了CUDA和cuDNN,在此将整个过程记录下来,以备将来参考,整个安装过程分为以下几步: 准备工作 安装Nvidia驱动 安装CUDA 安装cuDNN 特别问题说明 按照一般步骤,在安装完Nvidia显卡驱动后,会提示对应的CUDA版本,接下来按照提示的版本安装CUDA,例如我这里提示的是11.2,正常情况下,我应该安装11.
程序员欣宸
2021/12/07
6750
纯净Ubuntu16安装CUDA(9.1)和cuDNN
Ubuntu 16.04 上 CUDA_10.0及cuDNN的安装
GPU:Geforce GTX1060 驱动版本:418.56 最开始打算装CUDA_10.1( nvidia与cuda需相匹配),但是在运行cuda.run后出现的用户许可证信息有问题,如图
全栈程序员站长
2022/08/14
1.7K0
Ubuntu 16.04 上 CUDA_10.0及cuDNN的安装
Ubuntu20.04安装cuda10.1「建议收藏」
CUDA的主要用途是深度学习,而目前主流的深度学习框架Tensorflow2最高支持CUDA 10.1,因此本文讲解在Ubuntu 20.04系统上安装CUDA 10.1的主要过程。
全栈程序员站长
2022/09/29
1.6K0
Ubuntu20.04安装cuda10.1「建议收藏」
Ubuntu 安装 tensorflow-gpu 1.4 +CUDA 8.0 +cuDNN详细教程
作者 | fendouai 编辑 | 磐石 出品 | 磐创AI技术团队 【磐创AI导读】:本文详细介绍了tensorflow-gpu在Ubuntu下的安装步骤。欢迎大家点击上方蓝字关注我们的公众号:磐创AI。 硬件环境:NVIDIA GTX 980 Ti 系统环境:Ubuntu 16.04 64位 一.安装 NVIDIA驱动 1. 关闭 Secure Boot 具体如何禁用 BIOS 中的 Secure Boot 要根据主板的情况。 以华硕主板的禁用方法为例: 首先进入 BIOS,然后选择 Boot ,
磐创AI
2018/07/03
1.5K0
推荐阅读
相关推荐
Ubuntu 18.04+RTX2080Ti+CUDA10.1+CUDNN7.6.5+Pytorch1.3环境部署(详细教程)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档