首页 > 编程语言 >【算法】平衡树

【算法】平衡树

时间:2024-07-09 23:55:39浏览次数:12  
标签:return val int tree 二叉 算法 ls 平衡

1. 二叉搜索树

1.1 简介

二叉搜索树是一种二叉树的树形数据结构,能将数据存储在一个树形结构上。

其满足的性质:

  1. 二叉搜索树为一棵二叉树,每个节点至多有 \(2\) 个子节点;
  2. 二叉搜索树中任意一个节点的左儿子值小于本节点值,右儿子值大于本节点值(左右取等亦可);
  3. 二叉搜索树的左右子树均为二叉搜索树。

以上的引申结论有:

  1. 若二叉搜索树的左子树不为空,则其左子树上所有点的值均小于其根节点的值;
  2. 若二叉搜索树的右子树不为空,则其右子树上所有点的值均大于其根节点的值。

基本的二叉搜索树可以完成插入,删除,按值查排名,按排名查值,前驱、后继等等。

1.2 理论

想象如何在二叉搜索树上插入一个数。

考虑在搜索树上二分,每次比较当前插入值与经过节点的值来判断插入的位置。删除亦是如此。

而按值查排名,按排名查值等可以通过维护每个节点子树大小 \(siz\) 来求解。

  1. 按值查排名:类似于插入操作来找到值的位置,若走右儿子,将排名加上左儿子的子树大小加一。

  2. 按排名查值:若左儿子的子树大小大于等于当前排名,则值在左子树;#若右儿子的子树大小小于当前排名,则值在右子树,排名更新为当前排名-左子树-1;

前驱为小于当前值的最大值,后继为大于当前值的最小值。

所以前驱为当前值的排名+1的值,后继为当前值+1的排名的值。

1.3 实现

1.3.1 插入

void add(int x, int v) {
  tree[x].siz++;
  if(tree[x].val == v) {
    tree[x].cnt++;
    return ;
  }
  if(v < tree[x].val) {//左子树
    if(tree[x].ls) {//有左子树
      add(tree[x].ls, v);
    } else {//无左子树
      tree[++num].val = v;
      tree[num].cnt = tree[num].siz = 1;
      tree[x].ls = num;
    }
  }
  else {//右子树
    if(tree[x].rs) {
      add(tree[x].rs, v);
    } else {
      tree[++num].val = v;
      tree[num].cnt = tree[num].siz = 1;
      tree[x].rs = num;
    }
  }
}

删除类似于插入,不过多赘述。

1.3.2 按值查排名

int lrank(int x, int v) {
  if(x == 0) return 0;
  if(v == tree[x].val) {
    return tree[tree[x].ls].siz;
  }
  if(v < tree[x].val) {
    return lrank(tree[x].ls, v);
  }
  return lrank(tree[x].rs, v) + tree[tree[x].ls].siz + tree[x].cnt;
}

1.3.3 按排名查值

int kth(int x, int rk) {
  if(x == 0) return INF;
  if(rk <= tree[tree[x].ls].siz) {
    return kth(tree[x].ls, rk);
  }
  if(rk <= tree[tree[x].ls].siz + tree[x].cnt) {
    return tree[x].val;
  }
  return kth(tree[x].rs, rk - tree[tree[x].ls].siz - tree[x].cnt);
}

1.3.4 前驱

int pre(int x, int v, int ans) {
  if(v <= tree[x].val) {
    if(tree[x].ls) {
      return pre(tree[x].ls, v, ans);
    } else return ans;
  } else {
    if(!tree[x].rs) {
      return (tree[x].val < v) ? tree[x].val : ans;
    }
    if(tree[x].cnt) {
      return pre(tree[x].rs, v, tree[x].val);
    } else return pre(tree[x].rs, v, ans);
  }
}

1.3.5 后继

int nxt(int x, int v, int ans) {
  if(v >= tree[x].val) {
    if(tree[x].rs) {
      return nxt(tree[x].rs, v, ans);
    } else return ans;
  } else {
    if(!tree[x].ls) {
      return (tree[x].val > v) ? tree[x].val : ans;
    }
    if(tree[x].cnt) {
      return nxt(tree[x].ls, v, tree[x].val);
    } else return nxt(tree[x].ls, v, ans);
  }
}

以上为远古代码,码风奇丑轻喷

不难发现,如果数据为顺序插入一条链,那么搜索树也是一条链,这样单次操作的复杂度会升到 \(O(n)\) 级别。所以,平衡树诞生了!平衡树便是用来控制树高在 \(\log n\) 级别左右并且本身为一棵二叉搜索树的数据结构。

2. fhp-treap

2.1 简介

由范浩强巨佬发明的一棵无旋 treap。

2.1.1 何为 treap

考虑如何将一棵普通二叉搜索树维护平衡。

很显然,如果每次数据的插入数据随机,那么时间复杂度就能获得均摊的机会。比如按顺序插入 1 2 3 4 5,同样的数据按 3 1 5 2 4 随机插入,树高就变底了,时间复杂度更为优秀。

但是我们无法做到将数据离线下来打乱随机插入,因为可能在期间会涉及到其他的一系列操作,使得最后得到的平衡树和某一时间戳内的不一致。

标签:return,val,int,tree,二叉,算法,ls,平衡
From: https://www.cnblogs.com/Daniel-yao/p/18292207

相关文章

  • 代码随想录算法训练营第57天 | 99.岛屿数量 深搜 、99.岛屿数量 广搜 、100.岛屿的最
    99.岛屿数量深搜注意深搜的两种写法,熟练掌握这两种写法以及知道区别在哪里,才算掌握的深搜。https://www.programmercarl.com/kamacoder/0099.岛屿的数量深搜.html/***@param{character[][]}grid*@return{number}*/varnumIslands=function(grid){letre......
  • 斜率优化(不是算法介绍)
    翻省选游记的时候翻到马同学的游记。因为我很喜欢划水,我顺手看了马同学的个人主页,我发现一段话:你这一辈子就是被加训害了,没办法跟正经妹子处事,跟妹子吃饭的时候,总是在想,她要是加训就好了,会在桌子底下打开电脑打vp的那种卷姐,送她回家的时候,总是在想,她要是个会问我要不要一起打ACM......
  • 代码随想录算法训练营第56天 | 图论理论基础 、深搜理论基础、98. 所有可达路径、广
    图论理论基础今天主要是理论大家可以在看图论理论基础的时候,很多内容看不懂,例如也不知道看完之后还是不知道邻接矩阵,邻接表怎么用,别着急。理论基础大家先对各个概念有个印象就好,后面在刷题的过程中,每个知识点都会得到巩固。https://www.programmercarl.com/kamacoder/图......
  • 代码随想录算法训练营第七天 | 454.四数相加
    1、四数相加不需要考虑去重四个数组采两个数组一起相加的遍历方式,为了缩短时间复杂度。classSolution{public:intfourSumCount(vector<int>&nums1,vector<int>&nums2,vector<int>&nums3,vector<int>&nums4){unordered_map<int,int>......
  • SPFA算法模板和判断负环
    851.spfa求最短路-AcWing题库852.spfa判断负环-AcWing题库#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intn,m,k;inth[N],e[N],idx,w[N],ne[N];intq[N],tt=-1,hh=0;voidadd(inta,intb,intc){ e[idx]=b; ne[idx]=h[a]; w[idx]=c;......
  • 【matlab】层次分析算法
    目录一、层次分析算法实现的主要步骤1.1 建立层次结构模型1.2构造成对比较矩阵(判断矩阵)1.3计算权向量并做一致性检验1.4计算组合权向量并做组合一致性检验二、层次分析算法的应用三、MATLAB代码实现        MATLAB中的层次分析算法(AnalyticHierarchyPr......
  • Day65 代码随想录打卡|回溯算法篇---组合总和II
    题目(leecodeT40):给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用 一次 。注意:解集不能包含重复的组合。 方法:本题的要求是每个元素在组合中只能......
  • 常用的排序算法(C语言)
    1、冒泡排序(BubbleSort)冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮......
  • 学习老算法,争做老东西
    目录DancingLinksDancingLinks考虑这样一个问题:给定一个\(N\)行\(M\)列的矩阵,矩阵中每个元素要么是\(1\),要么是\(0\)。你需要在矩阵中挑选出若干行,使得对于矩阵的每一列\(j\),在你挑选的这些行中,有且仅有一行的第\(j\)个元素为\(1\)。这类问题统称为精确覆盖问......
  • 基于秃鹰算法的投影寻踪模型 - 附代码
    基于秃鹰算法的投影寻踪模型-附代码文章目录基于秃鹰算法的投影寻踪模型-附代码1.秃鹰算法2.投影寻踪模型3.秃鹰算法结合投影寻踪4.测试结果5.参考文献6.Matlab代码摘要:投影寻踪(projectionpursuit,PP)是处理和分析高维数据的一类新兴统计方法,其基本思想是将高......