【数据结构&C++】超详细一文带小白轻松全面理解 [ 二叉平衡搜索树-AVL树 ]—— [从零实现&逐过程分析&代码演示简练易懂]

前言

大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:
在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

目录

  • 一.AVL树的概念
  • 二.AVL树节点的定义(代码演示)
  • 三.AVL树的基本操作:插入
  • 四.AVL树的核心操作:旋转
    • 【1】新节点插入较高右子树的右侧---右右:左单旋
    • 【2】新节点插入较高左子树的左侧—左左:右单旋
    • 【3】新节点插入较高左子树的右侧---左右:先左单旋再右单旋【双旋】
    • 【4】新节点插入较高右子树的左侧---右左:先右单旋再左单旋【双旋】
  • 五.AVL树的验证
      • 1. 验证其为二叉搜索树
      • 2. 验证其为平衡树
  • 六.AVL树的性能&引入红黑树
  • 七.AVL树的完整代码

一.AVL树的概念

  • 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证 每个结点的左右子树高度之差的绝对值不超过1 (需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
  • 平衡因子是-1,左比右高1;平衡因子是1,右比左高1;平衡因子是0,左右一样高
  • 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
    1. 它的左右子树都是AVL树
    2. 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
  • 如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
    O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)。

二.AVL树节点的定义(代码演示)

  • 除了基本的左右孩子节点与数据外,还需要引入平衡因子
  • 由于平衡因子取决于左右子树相对高度,所以节点本身 要能够返回父亲节点 ——> 要设置指向父亲节点的指针
  • 注意AVL树节点是三叉链
template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _pLeft;   // 该节点的左孩子AVLTreeNode<T>* _pRight;  // 该节点的右孩子AVLTreeNode<T>* _pParent; // 该节点的父亲节点T _data;int _bf;                  // 该节点的平衡因子
};

三.AVL树的基本操作:插入

  • AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么 AVL树的插入过程可以分为两步:
    1. 按照二叉搜索树的方式插入新节点
    2. 调整节点的平衡因子
  • AVL树的插入过程:
  • 与二叉搜索树同理,二叉搜索树博客传送门:https://blog.csdn.net/YYDsis/article/details/134374001?spm=1001.2014.3001.5501
  • 平衡因子的变化步骤:
  1. 新增在左,parent平衡因子减减
  2. 新增在右,parent平衡因子加加
  3. 平衡因子==0,高度不变,直接break
  4. 平衡因子==1/-1,高度改变-> 会影响祖先 -> 需要继续沿着到根节点root的路径向上更新
  5. 平衡因子==2/-2,高度改变& 树不再平衡 ->会影响祖先->需要对parent所在子树进行 旋转 操作,让其平衡 (旋转部分放在part4中详解)
  6. 向上更新,直到根节点(根节点parent==0)
template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}//1. 按照二叉搜索树的方式插入新节点Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//2. 调整节点的平衡因子while (parent)//向上更新,直到根节点(根节点parent==0){if (cur == parent->_left)// 1.新增在左,parent平衡因子减减{parent->_bf--;}else // if (cur == parent->_right){parent->_bf++;//2.新增在右,parent平衡因子加加}if (parent->_bf == 0)//3.平衡因子==0,高度不变,直接break{// 更新结束break;}//4.平衡因子==1/-1,高度改变-> 会影响祖先 -> 需要继续沿着到根节点root的路径向上更新else if (parent->_bf == 1 || parent->_bf == -1){// 继续往上更新cur = parent;parent = parent->_parent;}//平衡因子==2/-2,高度改变& 树不再平衡 ->会影响祖先->//需要对parent所在子树进行 旋转 操作,让其平衡else if (parent->_bf == 2 || parent->_bf == -2){// 子树不平衡了,需要旋转     (旋转部分为何这么设计放在part4中详解)if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}break;}else{assert(false);}}return true;}

四.AVL树的核心操作:旋转

  • 根据part3中avl树的基本操作"插入",以下情况会出现旋转
  • 平衡因子==2/-2,高度改变& 树不再平衡 ->会影响祖先->需要对parent所在子树进行 旋转 操作,让其平衡 (旋转部分放在part4中详解)
  • 所以一共有四种情况分别如下图所示:
  • 旋转要注意以下三点:
    1. 保持这颗树还是搜索树
    2. 变成平衡树&降低其高度
    3. 节点是三叉链的形式,旋转后要注意节点链接

【1】新节点插入较高右子树的右侧—右右:左单旋

  • 分析:
  • 如下图所示,新节点插入较高右子树的右侧时候,整体会发生“向左的单旋”

在这里插入图片描述
在这里插入图片描述

  • 核心操作:
  • cur的左给parent的右,parent再成为cur的左
    cur->_right = parent;
    parent->_parent = cur;
  • 注意:节点是三叉链的形式,旋转后要注意节点链接
  • 要设置 祖父节点pparent指向parent,parent的_parent节点指向parent
  • 情况1:特殊情况,父母节点为根节点,空指向cur
  • 情况2:正常情况,祖父节点指向cur
  • 注意:平衡因子变化
  • 观察以上图中变化可知,我们只需要 在最后将cur和parent都调整为0就行
  • parent->_bf = cur->_bf = 0;
  • 代码展示:
void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;//标记出祖父节点parent->_parent = cur;if (parent == _root)//特殊情况,父母节点为根节点,空指向cur{_root = cur;cur->_parent = nullptr;}else//正常情况,祖父节点指向cur{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}

【2】新节点插入较高左子树的左侧—左左:右单旋

  • 相关细节与上面【1】中 “右右:左单旋” 一致,下面展示代码
  • 代码展示:
void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}

【3】新节点插入较高左子树的右侧—左右:先左单旋再右单旋【双旋】

引入:

  • 我们观察【1】【2】情况,其都是左左/右右,发生逆向的旋转(左左->往右转)(右右->往左转)
  • 而【3】【4】这种双旋的情况呢?如下图所示,观察可以发现,当其是 新节点插入较高左子树的右侧时 ,形成了一个折线
    在这里插入图片描述
  • 进一步观察后,我们发现有 三种情况 会触发双旋,可以观察 curright的平衡因子 判断,分别是:
    1. curright就是新增节点 bf == 0
    2. b处新增节点 bf == -1
    3. c处新增节点 bf == 1在这里插入图片描述
  • 注意:平衡因子变化
  • 我们根据 三种触发双旋的情况 ,对他们平衡因子变化进行分析
  • 1. curright就是新增节点
    parent->_bf = 0;
    cur->_bf = 0;
    curright->_bf = 0;

    在这里插入图片描述
  • 2. b处新增节点
    parent->_bf = 0;
    cur->_bf = -1;
    curright->_bf = 0;

    在这里插入图片描述
    3. c处新增节点
    parent->_bf = 1;
    cur->_bf = 0;
    curright->_bf = 0;

    在这里插入图片描述
void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0)//1. curright就是新增节点 bf == 0{parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == -1)//2. b处新增节点 bf == -1{parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1)//3. c处新增节点 bf == 1{parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}}
  • 注意:双旋的本质(表现)
    1. 60的左边给了30的右边
    1. 60的右边给了90的左边
    1. 60成了这棵树的根
      在这里插入图片描述

【4】新节点插入较高右子树的左侧—右左:先右单旋再左单旋【双旋】

  • 相关细节与上面【2】中 “左右:先左单旋再右单旋【双旋】” 一致,下面展示代码
  • 代码展示:
void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){cur->_bf = 0;curleft->_bf = 0;parent->_bf = 0;}else if (bf == 1){cur->_bf = 0;curleft->_bf = 0;parent->_bf = -1;}else if (bf == -1){cur->_bf = 1;curleft->_bf = 0;parent->_bf = 0;}else{assert(false);}}

五.AVL树的验证

1. 验证其为二叉搜索树

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

2. 验证其为平衡树

  • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
  • 节点的平衡因子是否计算正确
int Height(){return Height(_root);}
int Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr)return true;int leftHight = Height(root->_left);int rightHight = Height(root->_right);if (rightHight - leftHight != root->_bf){cout << "平衡因子异常:" <<root->_kv.first<<"->"<< root->_bf << endl;return false;}return abs(rightHight - leftHight) < 2&& IsBalance(root->_left)&& IsBalance(root->_right);}

六.AVL树的性能&引入红黑树

  • AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这
    样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操
    作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,
    有可能一直要让旋转持续到根的位置。
    因此:如果需要一种查询高效且有序的数据结构,而且数
    据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。 因此需要
    引入红黑树,传送门如下所示:

  • 红黑树博客传送门:

七.AVL树的完整代码

#pragma once#include<iostream>
#include<assert.h>
using namespace std;template<class K, class V>
struct AVLTreeNode
{pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;  // balance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// ... 控制平衡// 更新平衡因子while (parent){if (cur == parent->_left){parent->_bf--;}else // if (cur == parent->_right){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 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}break;}else{assert(false);}}return true;}void RotateL(Node* parent){++_rotateCount;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}void RotateR(Node* parent){++_rotateCount;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){cur->_bf = 0;curleft->_bf = 0;parent->_bf = 0;}else if (bf == 1){cur->_bf = 0;curleft->_bf = 0;parent->_bf = -1;}else if (bf == -1){cur->_bf = 1;curleft->_bf = 0;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}}int Height(){return Height(_root);}int Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr)return true;int leftHight = Height(root->_left);int rightHight = Height(root->_right);if (rightHight - leftHight != root->_bf){cout << "平衡因子异常:" <<root->_kv.first<<"->"<< root->_bf << endl;return false;}return abs(rightHight - leftHight) < 2&& IsBalance(root->_left)&& IsBalance(root->_right);}private:Node* _root = nullptr;public:int _rotateCount = 0;
};

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

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

相关文章

Android自动化测试,5个必备的测试框架

Appium Appium是一个开源的移动测试工具&#xff0c;支持iOS和Android&#xff0c;它可以用来测试任何类型的移动应用&#xff08;原生、网络和混合&#xff09;。作为一个跨平台的工具&#xff0c;你可以在不同的平台上运行相同的测试。为了实现跨平台的功能&#xff0c;Appi…

linux高级篇基础理论五(用户安全,口令设置,JR暴力破解用户密码,NMAP端口扫描)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️不能因为人生的道路坎坷,就使自己的身躯变得弯曲;不能因为生活的历程漫长,就使求索的 脚步迟缓。 ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xff1a;云计算技…

linux中利用fork复制进程,printf隐藏的缓冲区,写时拷贝技术,进程的逻辑地址与物理地址

1.prinf隐藏的缓冲区 1.思考:为什么会有缓冲区的存在? 2.演示及思考? 1).演示缓存区没有存在感 那为什么我们感觉不到缓冲区的存在呢?我们要打印东西直接就打印了呢? 我们用代码演示一下: 比如打开一个main.c,输入内容如下: #include <stdio.h>int main(){printf…

知云文献翻译——外语论文你get了吗?

今天博主分享一款实用的翻译软件&#xff0c;希望对大家日后的学习有所帮助。这个翻译网站&#xff0c;主要做文档翻译&#xff0c;可以上传PDF、Word、Excel这些格式&#xff0c;翻译语言也比较齐全。操作简单&#xff0c;功能多样的翻译软件;知云文献翻译最新版可以直接对PDF…

Linux:详解(yum的使用、vim编辑器命令集合以及gcc/g++编译器的使用)

Linux 软件包管理器 yum 什么是软件包&#xff1a; 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在一个服务器上, 通…

[解决] 问题:ImportError: cannot import name ‘Callable‘ from ‘collections‘

问题 我在运行yolov8的代码时&#xff0c;出现了ImportError: cannot import name Callable from collections的错误 原因 版本问题:以下collections的方法都在Python3.10版本后被取消了 ["Awaitable", "Coroutine", "AsyncIterable", "A…

Web(5)Burpsuite之文件上传漏洞

1.搭建网站&#xff1a;为网站设置没有用过的端口号 2.中国蚁剑软件的使用 通过一句话木马获得权限 3.形象的比喻&#xff08;风筝&#xff09; 4.实验操作 参考文章&#xff1a; 文件上传之黑名单绕过_文件上传黑名单绕过_pigzlfa的博客-CSDN博客 后端验证特性 与 Window…

一起学docker系列之四docker的常用命令--系统操作docker命令及镜像命令

目录 前言1 操作 Docker 的命令1.1 启动 Docker1.2 停止 Docker1.3 重启 Docker1.4 查看 Docker 状态1.5 查看 Docker 所有命令的信息1.6 查看某个命令的帮助信息 2 操作镜像的命令2.1 查看所有镜像2.2 搜索某个镜像2.3 下载某个镜像2.4 查看镜像所占空间2.5 删除镜像2.6 强制删…

Threejs_04 gui调试开发

threejs的快捷调试工具就是这玩意&#xff0c;那么如何使用呢&#xff1f;&#xff1f; 使用gui调试开发 引入gui实例 //导入lil.gui // import * as dat from "dat.gui"; // 旧 import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";…

解决Python requests库中的重定向问题

目录 一、默认情况下&#xff0c;requests库如何处理重定向 二、手动处理重定向 三、处理多个重定向 四、注意事项 总结 在Python requests库中&#xff0c;处理重定向是一个常见的问题。默认情况下&#xff0c;requests库会自动处理重定向&#xff0c;并将最终的响应返回…

leetcode算法之分治-快排

目录 1.颜色分类2.排序数组3.数组中的第k个最大元素4.最小的k个数 1.颜色分类 颜色分类 class Solution { public:void sortColors(vector<int>& nums) {int n nums.size();int left -1,rightn,i0;while(i<right){if(nums[i] 0) swap(nums[left],nums[i]);e…

(四)、基于 LangChain 实现大模型应用程序开发 | 基于知识库的个性化问答 (基本功能介绍)

⭐ 使用大语言模型构建一个能够回答关于给定文档和文档集合的问答系统是一种非常实用和有效的应用场景。与仅依赖模型预训练知识不同&#xff0c;这种方法可以进一步整合用户自有数据&#xff0c;实现更加个性化和专业的问答服务。 例如,我们可以收集某公司的内部文档、产品说明…

Java JSON字符串替换其中对应的值

代码&#xff1a; public static void main(String[] args) { // String theData crmScene.getData();String theData "[{\"type\":1,\"values\":[\"审批中\",\"未交付\"],\"name\":\"status\"}]"…

实战项目:VB龟兔赛跑游戏+猜数字游戏

文章目录&#xff1a; 一&#xff1a;效果演示 二&#xff1a;实现思路 三&#xff1a;代码实现 form1 效果图 代码 form2 效果图 代码 form3 效果图 代码 一&#xff1a;效果演示 效果图◕‿◕✌✌✌ 代码下载 二&#xff1a;实现思路 窗口1&#xff1a;龟兔赛…

Mybatis-Plus 自定义SQL注入器,实现真正的批量插入![MyBatis-Plus系列]

导读 Hi,大家好,我是悟纤。过着爱谁谁的生活,活出不设限的人生。 在使用MyBatis-Plus时,dao层都会去继承BaseMapper接口,这样就可以用BaseMapper接口所有的方法CRUD。 在Mybatis-Plus中调用updateById方法进行数据更新默认情况下是不能更新空值字段的。

使用契约的链上限价订单

我们开发了链上限价订单。 它基于一种称为契约的智能合约&#xff0c;只有在花费输出的交易满足特定条件时才可以花费输出。 为了演示其工作原理&#xff0c;我们实施了以比特币支付的 Ordinals 代币买卖限价订单&#xff0c;无需托管人。 它可以运行在任何比特币协议链上&…

网络层——IP协议

文章目录 一.IP协议二.基本概念三.IP协议格式四.分片与组装五.网段划分六.特殊的IP地址七.IP地址的数量限制八.私网IP地址和公网IP地址九.路由十.路由表生成算法 一.IP协议 IP协议全称为“网际互连协议&#xff08;Internet Protocol&#xff09;”&#xff0c;IP协议是TCP/IP…

[uni-app]记录APP端跳转页面自动滚动到底部的bug

文章目录 bug描述原因分析: 处理方案 bug描述 1.点击的A页面, 跳转到了B页面, 第一次页面正常显示 2.从B页面返回A页面 3.A页面不进行任何操作,再次点击A页面进入B页面 4.B页面自动滚动到底部. 原因 看一段A页面代码 let that thisthis.defaultScrollTop uni.getStorageSy…

Zookeeper中的Watch机制的原理?

前言 Zookeeper是一个分布式协调组件&#xff0c;为分布式架构下的多个应用组件提供了顺序访问控制能力。它的数据存储采用了类似于文件系统的树形结构&#xff0c;以节点的方式来管理存储在Zookeeper上的数据 Zookeeper提供了一个Watch机制&#xff0c;可以让客户端感知到Zook…

启动dubbo消费端过程提示No provider available for the service的问题定位与解决

文/朱季谦 某次在启动dubbo消费端时&#xff0c;发现无法从zookeeper注册中心获取到所依赖的消费者API&#xff0c;启动日志一直出现这样的异常提示 Failed to check the status of the service com.fte.zhu.api.testService. No provider available for the service com.fte…