AVL树是平衡二叉搜索树中的一种,在渐进意义下,AVL树可以将高度始终控制在O(log n) 以内,以保证每次查找、插入和删除操作均可以在O(log n)的时间内完成。

平衡因子

定义任一结点v的平衡因子(balance factor)为其左右子树的高度差

balfac(v) = height(v->lc) - height(v->rc)

AVL树即平衡因子受限的二叉搜索树—————各结点平衡因子的绝对值不超过1


template<typename T>
inline bool AVLTree<T>::balanced(AVLNodePos(T) &cur) {
int lh = 0, rh = 0;
if (HasLChild(cur))
lh = cur->lc->height;
if (HasRChild(cur))
rh = cur->rc->height;
if ((lh - rh)*(lh - rh) > 1)
return false;
return true;
}

AVL树节点和AVL树的定义

  1. AVL节点AVLNode的定义
template<typename T>
struct AVLNode {
public:
T key;
int height;
AVLNodePos(T) pa;//pa -- parent
AVLNodePos(T) lc;//lc -- left child
AVLNodePos(T) rc;//rc -- right child
//构造函数
AVLNode(): height(0), pa(NULL), lc(NULL), rc(NULL) {}
AVLNode(T elem, int height = 0, AVLNodePos(T) pa = NULL, AVLNodePos(T) lc = NULL, AVLNodePos(T) rc = NULL) :
key(elem), height(height), pa(pa), lc(lc), rc(rc) { }
};
  1. AVL树AVLTree的定义如下

template<typename T>
class AVLTree {
private:
AVLNodePos(T) root; //树根
public:
//构造函数和析构函数
AVLTree():root(NULL){}
~AVLTree() {} //只读函数
int height() { return Height(root); }
//遍历函数
void preOrder(); //前序遍历
void inOrder(); //中序遍历
void postOrder(); //后序遍历
//操作函数
AVLNodePos(T) search(const T &key); //查找函数,若存在值为key的节点返回相应节点,若不存在则返回NULL
AVLNodePos(T) insert(const T &key); //插入值为key的节点
bool remove(const T &key); //移除值为key的节点 private:
bool balanced(AVLNodePos(T) &cur); //判断cur节点是否平衡
void upDataHeight(AVLNodePos(T) &cur); //更新节点cur的高度
void preOrder(AVLNodePos(T) &cur); //以cur节点为root进行前序遍历
void inOrder(AVLNodePos(T) &cur); //以cur节点为root进行中序遍历
void postOrder(AVLNodePos(T) &cur); //以cur节点为root进行后序遍历
AVLNodePos(T) searchIn(const T & key, AVLNodePos(T) cur, AVLNodePos(T)& hot); //以cur节点为root查找值为key的节点,hot为返回节点的父节点
AVLNodePos(T) tallerChild(AVLNodePos(T) &g); //返回g高度更高的孩子
AVLNodePos(T) connect34(AVLNodePos(T) a, AVLNodePos(T) b, AVLNodePos(T) c, //根据 "3"+"4"法则对子树进行调整
AVLNodePos(T) T0, AVLNodePos(T) T1, AVLNodePos(T) T2, AVLNodePos(T) T3);
AVLNodePos(T) adjustNode(AVLNodePos(T)& cur); //调整失衡节点cur,返回调整后得到局部子树的root,并将调整后子树接入原树
};

失衡调整

AVL树和常规二叉搜索树一样也支持插入、删除等动态修改操作,但是经过这类操作之后节点的高度可能发生变化,以至于不再满足AVL树的条件。为此要定义相关使得搜索树平衡的调整算法。只需在每次动态操作后利用balanced()函数判断当前节点是否平衡,在找到第一个失衡节点g后,找到g高度更高的孩子p和p高度更高的孩子s这三个节点,根据相应g,p,s的关系能找到四棵子树T0,T1,T2,T3,利用 3 + 4 重构法则就能统一调整算法

//根据 "3"+"4"法则对子树进行调整
template<typename T>
AVLNodePos(T) AVLTree<T>::connect34(
AVLNodePos(T) a, AVLNodePos(T) b, AVLNodePos(T) c,
AVLNodePos(T) T0, AVLNodePos(T) T1, AVLNodePos(T) T2, AVLNodePos(T) T3)
{ //最终实现效果如下:
a->lc = T0; if (T0) T0->pa = a; // b
a->rc = T1; if (T1) T1->pa = a; // / \
c->lc = T2; if (T2) T2->pa = c; // a c
c->rc = T3; if (T3) T3->pa = c; // / \ / \
b->lc = a; b->rc = c; // T0 T1 T2 T3
a->pa = b; c->pa = b;
b->pa = NULL;
upDataHeight(a);
upDataHeight(c);
upDataHeight(b); //更新高度
return b; //返回b
} //调整失衡节点cur,返回调整后得到局部子树的root,并将调整后子树接入原树
template<typename T>
AVLNodePos(T) AVLTree<T>::adjustNode(AVLNodePos(T) &g) {
//一旦发现失衡 找到g,p,s 三个节点 //g --grandfa
AVLNodePos(T) p = tallerChild(g); //p --parent
AVLNodePos(T) s = tallerChild(p); //s --son
AVLNodePos(T) gg = g->pa; //gg -- g's parent
if (IsLChild(s) && IsLChild(p)) //left -- left
g = connect34(s, p, g, s->lc, s->rc, p->rc, g->rc);
else if (IsRChild(s) && IsLChild(p)) //right -- left
g = connect34(p, s, g, p->lc, s->lc, s->rc, g->rc);
else if (IsRChild(s) && IsRChild(p)) //right -- right
g = connect34(g, p, s, g->lc, p->lc, s->lc, s->rc);
else if (IsLChild(s) && IsRChild(p)) //left -- left
g = connect34(g, s, p, g->lc, s->lc, s->rc, p->rc); //将调整后子树接回原树
if (!gg) root = g;// gg为NULL说明g为root,更新root
else { //连接gg和 g
if (gg->key > g->key) {//作为左子树接入
gg->lc = g;
g->pa = gg;
}
else {//作为右子树接入
gg->rc = g;
g->pa = gg;
}
}
upDataHeight(gg);//g的高度已经在connect34时更新,故更新连接g后的gg高度
return g;
}

插入和删除操作

插入和删除操作同搜索二叉树一样,只是每一次都要节点检查是否平衡,如果失衡则进行调整

//AVLNodePos(T) searchIn(const T & key, AVLNodePos(T) cur, AVLNodePos(T)& hot);以cur节点为root查找值为key的节点,hot为返回节点的父节点
//插入值为key的节点
template<typename T>
AVLNodePos(T) AVLTree<T>::insert(const T & key)
{
AVLNodePos(T) cur_pa=NULL;
AVLNodePos(T) x = searchIn(key, root, cur_pa);
if (x) return x; //已经存在值为key的节点
//否则确认key不存在
if (!root)
x = root = new AVLNode<T>(key);
else {
//连接节点
x = new AVLNode<T>(key,0,cur_pa);
if (x->key > cur_pa->key)
cur_pa->rc = x;
else
cur_pa->lc = x;
upDataHeight(x); //更新高度
}
for (AVLNodePos(T) g = cur_pa; g; g = g->pa) { //从 插入节点的父节点开始向上检查
if (!balanced(g)) {//若g失衡
adjustNode(g);//调整以g为root的局部子树
break; //g复平衡后,局部子树高度必然复原,其祖先亦如此,故调整可到此结束
}else {//否则g平衡
upDataHeight(g); //更新高度
}
}
return x; //返回新插入节点位置
} //移除值为key的节点
template<typename T>
inline bool AVLTree<T>::remove(const T & key)
{
AVLNodePos(T) x = search(key);
if (!x)
return false;//不存在值为key的节点
AVLNodePos(T) w = x; //w是实际被摘除节点
if (!HasLChild(x)) {// 如果x没有左子树,用右子树代替x
if (IsRoot(x))
root = x->rc;
else {
if (IsLChild(x)) x->pa->lc = x->rc;
else x->pa->rc = x->rc;
}
}
else if (!HasRChild(x)) {//如果x没有右子树, 用左子树替代
if (IsRoot(x))
root = x->lc;
else {
if (IsLChild(x)) x->pa->lc = x->lc;
else x->pa->rc = x->lc;
}
}else { //否则有左右子树
//取w为x的直接后继
w = x->rc;
while (HasLChild(w))
w = w->lc;
//交换数据
T tmp = x->key;
x->key = w->key;
w->key = tmp;
if (HasRChild(w))
w->rc->pa = w->pa;
if (w->pa == x) // 注意 w为x的右子这种情况
x->rc = w->rc;
else
w->pa->lc = w->rc; }
upDataHeight(w->pa);
for (AVLNodePos(T) g = w->pa; g; g = g->pa) { //从succ的父节点开始向上检查
if (!balanced(g)) {//若g失衡
g=adjustNode(g);//调整以g为root的局部子树
//不同于插入操作,删除操作造成的失衡可能会传播
}
else {//否则g平衡
upDataHeight(g); //更新高度
}
}
delete w;//摘除w
return true;
}

完整源码

代码参考《数据结构(c++语言版)》--清华大学邓俊辉

"AVLTree_Declaration.h"

//#include"pch.h"
#include<iostream>
#ifndef _AVL_TREE_DECLARATION_H
#define _AVL_TREE_DECLARATION_H #define AVLNodePos(T) AVLNode<T> *
//宏定义
#define IsRoot(x) ( !((x)->pa) )
#define IsLChild(x) ( !(IsRoot(x) ) && (x)==(x)->pa->lc)
#define IsRChild(x) ( !(IsRoot(x) ) && (x)==(x)->pa->rc)
#define HasLChild(x) ((x)->lc )
#define HasRChild(x) ((x)->rc )
#define HasChild(x) (HasLChild(x) || HasRChild(x))
#define HasBothChild(x) (HasRChild(x) && HasLChild(x) )
#define IsLeaf(x) (! HasChild(x) )
#define Height(x) ( (x)==NULL? 0: (x)->height ) //x的高度
//AVLNode 定义
template<typename T>
struct AVLNode {
public:
T key;
int height;
AVLNodePos(T) pa;//pa -- parent
AVLNodePos(T) lc;//lc -- left child
AVLNodePos(T) rc;//rc -- right child
//构造函数
AVLNode(): height(0), pa(NULL), lc(NULL), rc(NULL) {}
AVLNode(T elem, int height = 0, AVLNodePos(T) pa = NULL, AVLNodePos(T) lc = NULL, AVLNodePos(T) rc = NULL) :
key(elem), height(height), pa(pa), lc(lc), rc(rc) { }
};
//AVLTree 定义
template<typename T>
class AVLTree {
private:
AVLNodePos(T) root; //树根
public:
//构造函数和析构函数
AVLTree():root(NULL){}
~AVLTree() {} //只读函数
int height() { return Height(root); }
//遍历函数
void preOrder(); //前序遍历
void inOrder(); //中序遍历
void postOrder(); //后序遍历
//操作函数
AVLNodePos(T) search(const T &key); //查找函数,若存在值为key的节点返回相应节点,若不存在则返回NULL
AVLNodePos(T) insert(const T &key); //插入值为key的节点
bool remove(const T &key); //移除值为key的节点 private:
bool balanced(AVLNodePos(T) &cur); //判断cur节点是否平衡
void upDataHeight(AVLNodePos(T) &cur); //更新节点cur的高度
void preOrder(AVLNodePos(T) &cur); //以cur节点为root进行前序遍历
void inOrder(AVLNodePos(T) &cur); //以cur节点为root进行中序遍历
void postOrder(AVLNodePos(T) &cur); //以cur节点为root进行后序遍历
AVLNodePos(T) searchIn(const T & key, AVLNodePos(T) cur, AVLNodePos(T)& hot); //以cur节点为root查找值为key的节点,hot为返回节点的父节点
AVLNodePos(T) tallerChild(AVLNodePos(T) &g); //返回g高度更高的孩子
AVLNodePos(T) connect34(AVLNodePos(T) a, AVLNodePos(T) b, AVLNodePos(T) c, //根据 "3"+"4"法则对子树进行调整
AVLNodePos(T) T0, AVLNodePos(T) T1, AVLNodePos(T) T2, AVLNodePos(T) T3);
AVLNodePos(T) adjustNode(AVLNodePos(T)& cur); //调整失衡节点cur,返回调整后得到局部子树的root,并将调整后子树接入原树
};
#endif

"AVLTree_Define.h"

//具体实现
//#include"pch.h"
#include "AVLTree_Declaration.h"
#include<iostream> #ifndef _AVL_TREE_DEFINE_H
#define _AVL_TREE_DEFINE_H template<typename T>
inline void AVLTree<T>::preOrder(AVLNodePos(T) & cur)
{
if (!cur) return;
std::cout << cur->key << " ";
preOrder(cur->lc);
preOrder(cur->rc);
} template<typename T>
inline void AVLTree<T>::inOrder(AVLNodePos(T) & cur)
{
if (!cur) return;
inOrder(cur->lc);
std::cout << cur->key << " ";
inOrder(cur->rc);
} template<typename T>
inline void AVLTree<T>::postOrder(AVLNodePos(T)& cur)
{
if (!cur) return;
postOrder(cur->lc);
postOrder(cur->rc);
std::cout << cur->key << " ";
} template<typename T>
inline void AVLTree<T>::preOrder()
{
preOrder(root);
} template<typename T>
inline void AVLTree<T>::inOrder()
{
inOrder(root);
} template<typename T>
inline void AVLTree<T>::postOrder()
{
postOrder(root);
}
//判断cur节点是否平衡
template<typename T>
inline bool AVLTree<T>::balanced(AVLNodePos(T) &cur) {
int lh = 0, rh = 0;
if (HasLChild(cur))
lh = cur->lc->height;
if (HasRChild(cur))
rh = cur->rc->height;
if ((lh - rh)*(lh - rh) > 1)
return false;
return true;
}
//返回g高度更高的孩子
template<typename T>
inline AVLNodePos(T) AVLTree<T>::tallerChild(AVLNodePos(T) &g) {
if (HasBothChild(g)) {
return (g->lc->height >= g->rc->height ? g->lc : g->rc);
}
else if (HasLChild(g))
return g->lc;
else if (HasRChild(g))
return g->rc;
return NULL;
}
//更新节点cur的高度
template<typename T>
inline void AVLTree<T>::upDataHeight(AVLNodePos(T) & p)
{
if (!p) return;
int tmp = p->height;
if (IsLeaf(p))
p->height = 1;
else if (HasBothChild(p))
p->height = (p->lc->height > p->rc->height) ? p->lc->height + 1 : p->rc->height + 1;
else if (HasLChild(p))
p->height = p->lc->height + 1;
else if (HasRChild(p))
p->height = p->rc->height + 1; return;
} //以cur节点为root查找值为key的节点,hot为返回节点的父节点
template<typename T>
inline AVLNodePos(T) AVLTree<T>::searchIn(const T & key, AVLNodePos(T) cur, AVLNodePos(T)& hot)
{
if (!cur || key == cur->key) return cur;
hot = cur;
return searchIn(key, (key < cur->key ? cur->lc : cur->rc), hot);
} //查找函数,若存在值为key的节点返回相应节点,若不存在则返回NULL
template<typename T>
inline AVLNodePos(T) AVLTree<T>::search(const T & key)
{
AVLNodePos(T) hot = NULL;
return searchIn(key, root, hot);
} //根据 "3"+"4"法则对子树进行调整
template<typename T>
AVLNodePos(T) AVLTree<T>::connect34(
AVLNodePos(T) a, AVLNodePos(T) b, AVLNodePos(T) c,
AVLNodePos(T) T0, AVLNodePos(T) T1, AVLNodePos(T) T2, AVLNodePos(T) T3)
{ //最终实现效果如下:
a->lc = T0; if (T0) T0->pa = a; // b
a->rc = T1; if (T1) T1->pa = a; // / \
c->lc = T2; if (T2) T2->pa = c; // a c
c->rc = T3; if (T3) T3->pa = c; // / \ / \
b->lc = a; b->rc = c; // T0 T1 T2 T3
a->pa = b; c->pa = b;
b->pa = NULL;
upDataHeight(a);
upDataHeight(c);
upDataHeight(b); //更新高度
return b; //返回b
} //调整失衡节点cur,返回调整后得到局部子树的root,并将调整后子树接入原树
template<typename T>
AVLNodePos(T) AVLTree<T>::adjustNode(AVLNodePos(T) &g) {
//一旦发现失衡 找到g,p,s 三个节点 //g --grandfa
AVLNodePos(T) p = tallerChild(g); //p --parent
AVLNodePos(T) s = tallerChild(p); //s --son
AVLNodePos(T) gg = g->pa; //gg -- g's parent
if (IsLChild(s) && IsLChild(p)) //left -- left
g = connect34(s, p, g, s->lc, s->rc, p->rc, g->rc);
else if (IsRChild(s) && IsLChild(p)) //right -- left
g = connect34(p, s, g, p->lc, s->lc, s->rc, g->rc);
else if (IsRChild(s) && IsRChild(p)) //right -- right
g = connect34(g, p, s, g->lc, p->lc, s->lc, s->rc);
else if (IsLChild(s) && IsRChild(p)) //left -- left
g = connect34(g, s, p, g->lc, s->lc, s->rc, p->rc); //将调整后子树接回原树
if (!gg) root = g;// gg为NULL说明g为root,更新root
else { //连接gg和 g
if (gg->key > g->key) {//作为左子树接入
gg->lc = g;
g->pa = gg;
}
else {//作为右子树接入
gg->rc = g;
g->pa = gg;
}
}
upDataHeight(gg);//g的高度已经在connect34时更新,故更新连接g后的gg高度
return g;
} //插入值为key的节点
template<typename T>
AVLNodePos(T) AVLTree<T>::insert(const T & key)
{
AVLNodePos(T) cur_pa=NULL;
AVLNodePos(T) x = searchIn(key, root, cur_pa);
if (x) return x; //已经存在值为key的节点
//否则确认key不存在
if (!root)
x = root = new AVLNode<T>(key);
else {
//连接节点
x = new AVLNode<T>(key,0,cur_pa);
if (x->key > cur_pa->key)
cur_pa->rc = x;
else
cur_pa->lc = x;
upDataHeight(x); //更新高度
}
for (AVLNodePos(T) g = cur_pa; g; g = g->pa) { //从 插入节点的父节点开始向上检查
if (!balanced(g)) {//若g失衡
adjustNode(g);//调整以g为root的局部子树
break; //g复平衡后,局部子树高度必然复原,其祖先亦如此,故调整可到此结束
}else {//否则g平衡
upDataHeight(g); //更新高度
}
}
return x; //返回新插入节点位置
} //删除值为key的节点
template<typename T>
inline bool AVLTree<T>::remove(const T & key)
{
AVLNodePos(T) x = search(key);
if (!x)
return false;//不存在值为key的节点
AVLNodePos(T) w = x; //w是实际被摘除节点
if (!HasLChild(x)) {// 如果x没有左子树,用右子树代替x
if (IsRoot(x))
root = x->rc;
else {
if (IsLChild(x)) x->pa->lc = x->rc;
else x->pa->rc = x->rc;
}
}
else if (!HasRChild(x)) {//如果x没有右子树, 用左子树替代
if (IsRoot(x))
root = x->lc;
else {
if (IsLChild(x)) x->pa->lc = x->lc;
else x->pa->rc = x->lc;
}
}else { //否则有左右子树
//取w为x的直接后继
w = x->rc;
while (HasLChild(w))
w = w->lc;
//交换数据
T tmp = x->key;
x->key = w->key;
w->key = tmp;
if (HasRChild(w))
w->rc->pa = w->pa;
if (w->pa == x) // 注意 w为x的右子这种情况
x->rc = w->rc;
else
w->pa->lc = w->rc; }
upDataHeight(w->pa);
for (AVLNodePos(T) g = w->pa; g; g = g->pa) { //从succ的父节点开始向上检查
if (!balanced(g)) {//若g失衡
g=adjustNode(g);//调整以g为root的局部子树
//不同于插入操作,删除操作造成的失衡可能会传播
}
else {//否则g平衡
upDataHeight(g); //更新高度
}
}
delete w;//摘除w
return true;
} #endif

"main.cpp"

// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include "pch.h"
#include <iostream>
#include"AVLTree_Define.h"
#include"AVLTree_Declaration.h"
using namespace std;
int main()
{
AVLTree<int> t;
int n;
cout << "insert number:";
cin >> n;
cout << "insert:" ;
for (int i = 0; i < n; i++) {
int tmp;
cin >> tmp;
t.insert(tmp);
}
cout << "PreOrder:";
t.preOrder();
cout << endl;
cout << "InOrder:";
t.inOrder();
cout << endl;
cout << "PostOrder:";
t.postOrder();
cout << endl;
cout << "remove number:";
cin >>n;
cout << "remove:";
for (int i = 0; i < n; i++) {
int tmp;
cin >> tmp;
t.remove(tmp);
}
cout << "PreOrder:";
t.preOrder();
cout << endl;
cout << "InOrder:";
t.inOrder();
cout << endl;
cout << "PostOrder:";
t.postOrder();
cout << endl;
return 0;
}

高级搜索树-AVL树的更多相关文章

  1. 树-二叉搜索树-AVL树

    树-二叉搜索树-AVL树 树 树的基本概念 节点的度:节点的儿子数 树的度:Max{节点的度} 节点的高度:节点到各叶节点的最大路径长度 树的高度:根节点的高度 节点的深度(层数):根节点到该节点的路 ...

  2. 高度平衡的二叉搜索树(AVL树)

    AVL树的基本概念 AVL树是一种高度平衡的(height balanced)二叉搜索树:对每一个结点x,x的左子树与右子树的高度差(平衡因子)至多为1. 有人也许要问:为什么要有AVL树呢?它有什么 ...

  3. 高级搜索树-伸展树(Splay Tree)

    目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...

  4. AVL树和平衡二叉树 平衡因子 右旋转LL 左旋转RR LR RL

    前言 今天要介绍几种高级数据结构AVL树,介绍之前AVL,会先说明平衡二叉树,并将树的学习路线进行总结,并介绍维持平衡的方法:右旋转.左旋转. 一.树学习路线 1.路线总结 总结了一下树的学习路线,如 ...

  5. 自已动手作图搞清楚AVL树

    @ 目录 一.背景 二.平衡二分搜索树---AVL树 2.1 AVL树的基本概念 结点 高度 平衡因子 2.2 AVL树的验证 三.旋转操作 3.1 L L--需要通过右旋操作 3.2 R R--需要 ...

  6. PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由

    03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...

  7. 平衡搜索树(一) AVL树

    AVL树 AVL树又称为高度平衡的二叉搜索树,是1962年有俄罗斯的数学家G.M.Adel'son-Vel'skii和E.M.Landis提出来的.它能保持二叉树的高度 平衡,尽量降低二叉树的高度,减 ...

  8. 二叉搜索树的平衡--AVL树和树的旋转(图解)

    二叉搜索树只有保持平衡时其查找效率才会高. 要保持二叉搜索树的平衡不是一件易事.不过还是有一些非常经典的办法可以做到,其中最好的方法就是将二叉搜索树实现为AVL树. AVL树得名于它的发明者 G.M. ...

  9. 算法二叉搜索树之AVL树

    最近学习了二叉搜索树中的AVL树,特在此写一篇博客小结. 1.引言 对于二叉搜索树而言,其插入查找删除等性能直接和树的高度有关,因此我们发明了平衡二叉搜索树.在计算机科学中,AVL树是最先发明的自平衡 ...

随机推荐

  1. 从连接器组件看Tomcat的线程模型——连接器简介

    Connector组件介绍 Connector(连接器)组件是Tomcat最核心的两个组件之一,主要的职责是负责接收客户端连接和客户端请求的处理加工.每个Connector都将指定一个端口进行监听,分 ...

  2. (1)为什么要使用webpack?

    1.在网页中有哪些常见的静态资源? Js: .js .jsx .coffee .ts Css: .css .less .sass .scss Images: .jpg .png .gif .bmp . ...

  3. P1330 封锁阳光大学(洛谷)

    题目描述 曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街.河蟹看到欢快的曹,感到不爽.河蟹决定封锁阳光大学,不让曹刷街. 阳光大学的校园是一张由n个点构成的无向图,n个点由m条道 ...

  4. 详解Vue大护法——组件

    1.什么是组件化 人面对复杂问题的处理方式: 任何一个人处理信息的逻辑能力都是有限的 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容. 但是,我们人有一种天生的能力,就是将问题进 ...

  5. PyQt5多线程和定时器

    多线程 一般情况单线程就可以很好的完成任务,但是对于GUI程序来说,单线程就不能完全满足需求.如果有耗时流程,在单线程的情况下,界面操作就会卡死,直到耗时操作完成,才会响应界面操作.为了解决这个问题, ...

  6. 题解 CF938G 【Shortest Path Queries】

    题目让我们维护一个连通无向图,边有边权,支持加边删边和询问从\(x\)到\(y\)的异或最短路. 考虑到有删边这样的撤销操作,那么用线段树分治来实现,用线段树来维护询问的时间轴. 将每一条边的出现时间 ...

  7. web自动化 -- 三种等待方式

    一.强制等待 二.隐式等待 注:隐式等待的作用域是全局,所以一般设置在整局代码的头几行. 如: 三.显示等待 元素存在: 元素可见: 元素可点击: 看到上图源码中有一个   element.is_en ...

  8. Python 正则表达式简单了解

    match 从字符串的开始匹配  如果开头不符合要求  就会报错 search  用字符串里的每一个元素  去匹配找的元素 1.匹配单个字符 \d 数字 \D 非数字 . 匹配任意字符 除了\n [] ...

  9. ajax快速入门

    一.ajax简单入门 1.Ajax的实现步骤 // 1.创建ajax对象var xhr = new XMLHttpRequest();// 2.高数ajax请求地址及请求方式//第一个参数就是请求方式 ...

  10. Pycharm远程解释器SFTP开发和调试

    转载:https://blog.csdn.net/ll641058431/article/details/53049453 使用PyCharm进行远程开发和调试 你是否经常要在Windows 7或MA ...