23树
平衡二叉树虽然查找很快,但是做插入和删除的时候却很麻烦
所以要学习B+树,而要了解B+树,首先要学B树,而学B树之前,首先要学23树
23树是如何在不影响查找效率的情况下,优化平衡二叉树的插入和删除
为什么叫23树:是因为它的树节点不一样,它的节点指针数量是不固定的(一般树是2个指针)
一般用的是2节点和3节点,如果遇到4节点(4节点只会临时存在)会变成3个2节点
准备几个数:
插入的时候一开始第一个数是2节点,当第二个数进来后变成3节点,当第三个数进来后变成4节点,然后4节点变成3个2节点。
然后第四个节点进来,首先将其沉底,第五个也是一样
然后继续插入:
然后因为4节点只是临时存在的,然后11升上去,而11的左边是5,右边是33,所以就会变成这个样子:
下面也是一样的,查找效率降低了一点点,但是增删效率增加的却不是那么一点点,再也不需要进行旋转:
但为什么不用23树,是因为23树的节点是固定的,就是2节点或3节点,数据多的时候也是很麻烦的,数据多的时候可能是希望4,5,6,7,8节点,这样层数不会更多(比较灵活)。
而一旦节点里面的指针数量是变化的就不叫23树,而是B树。
23树
- 有3种节点:2节点,3节点,4节点(临时)
- 4节点一旦出现进行拆分 从下往上
数据进入先往下沉,再往上升
以下是头文件:
#pragma once
#include<iostream>
using namespace std;
enum state{null_node,two_node,three_node,four_node}; //枚举表示空节点,2,3,4节点
template<class T>
struct TTTreeNode
{
int count; //标记当前是空,2,3,4节点
T data[3]; //数据是数组 可以用vector动态数组(这个反而比较好)
TTTreeNode* pArr[4]; //存储指针的数组
TTTreeNode()
{
count = null_node;
memset(data, 0, sizeof(T) * 3);
memset(pArr, 0, sizeof(TTTreeNode*) * 4);
}
};
template<class T>
class twothreeTree
{
public:
TTTreeNode<T>* pRoot;//指向根节点
public:
twothreeTree() { pRoot = NULL; }
~twothreeTree(){}
void insertNode(const T& data);
private:
void _insertNode(TTTreeNode<T>* node, TTTreeNode<T>* pParent,const T& data);
};
//public:
template<class T>
void twothreeTree<T>::insertNode(const T& data)
{
if (pRoot)
{
_insertNode(pRoot, NULL, data);//pRoot的父为空
}
else//当前树为空树
{
pRoot = new TTTreeNode<T>;
pRoot->data[0] = data;
pRoot->count = two_node;//2节点
}
}
//private
template<class T>
void twothreeTree<T>::_insertNode(TTTreeNode<T>* node, TTTreeNode<T>* pParent, const T& data)
{
if (node->count==null_node)//如果当前节点为空
{
node->data[0] = data;
node->count++;
return;
}
if (node->count == two_node)//如果当前节点为2
{
if (data>node->data[0])//判断新数据是不是比本来的数据大 往右插入
{
if (node->pArr[1])//判断有没有孩子
{
_insertNode(node->pArr[1], node, data);//插入当前节点的右孩子
}
else//没有孩子
{
node->data[1] = data;
node->count++;
}
}
else// 往左插入
{
if (node->pArr[0])//判断有没有孩子
{
_insertNode(node->pArr[0], node, data);//插入当前节点的右孩子
}
else//没有孩子
{
//原来的数据右移
node->data[1] = node->data[0];
//新数据进入
node->data[0] = data;
//节点变化
node->count++;
}
}
}
else//如果当前节点为3
{
if (data<node->data[0])//往最左边插入
{
if (node->pArr[0])//判断有没有孩子
{
_insertNode(node->pArr[0], node, data);//有孩子统一递归
}
else
{
//中间变右边
node->data[2] = node->data[1];
//左边变中间
node->data[1] = node->data[0];
//新数据进入
node->data[0] = data;
//节点加加
node->count++;
}
}
else if(data < node->data[1])//往中间插入
{
if (node->pArr[1])//判断有没有孩子
{
_insertNode(node->pArr[1], node, data);
}
else
{
//中间变右边
node->data[2] = node->data[1];
//新数据变中间
node->data[1] = data;
//节点加加
node->count++;
}
}
else//往右边插入
{
if (node->pArr[2])//判断有没有孩子
{
_insertNode(node->pArr[2], node, data);
}
else
{
//新数据变右边
node->data[2] = data;
//节点加加
node->count++;
}
}
}
if (node->count == four_node)//如果当前节点为4
{
//创建2个节点
TTTreeNode<T>* node1 = new TTTreeNode<T>;//左
TTTreeNode<T>* node2 = new TTTreeNode<T>;//右
// node1是左边的
node1->data[0] = node->data[0];
node1->pArr[0] = node->pArr[0];
node1->pArr[1] = node->pArr[1];
node1->count = two_node;
//node2是右边的
node2->data[0] = node->data[2];
node2->pArr[0] = node->pArr[2];
node2->pArr[1] = node->pArr[3];
node1->count = two_node;
//临时存储中间值
T temp = node->data[1];
if (pParent)//当前节点有没有父节点
{
//找位置插入
if (temp<pParent->data[0])//左边
{
if (pParent->pArr[2])//最右边有孩子
{
pParent->data[2] = pParent->data[1];
pParent->data[1] = pParent->data[0];
pParent->data[0] = temp;
pParent->pArr[3] = pParent->pArr[2];
pParent->pArr[2] = pParent->pArr[1];
pParent->pArr[1] = node2;
pParent->pArr[0] = node1;
}
else if(pParent->pArr[1])//最右边没孩子,中间有孩子
{
pParent->data[1] = pParent->data[0];
pParent->data[0] = temp;
pParent->pArr[2] = pParent->pArr[1];
pParent->pArr[1] = node2;
pParent->pArr[0] = node1;
}
}
else if(pParent->count==two_node || (pParent->count>1) && (temp < pParent->data[1]))//中间
{
if (pParent->pArr[2])//最右边有孩子
{
pParent->data[2] = pParent->data[1];
pParent->data[1] = temp;
pParent->pArr[3] = pParent->pArr[2];
pParent->pArr[2] = node2;
pParent->pArr[1] = node1;
}
else if (pParent->pArr[1])//最右边没孩子,中间有孩子
{
pParent->data[1] = temp;
pParent->pArr[2] = node2;
pParent->pArr[1] = node1;
}
}
else if(pParent->count == three_node || (pParent->count > 2) && (temp < pParent->data[2]))//右边
{
//父节点是3节点,直接向上升
if (pParent->pArr[2])
{
pParent->data[2] = temp;
pParent->pArr[3] = node2;
pParent->pArr[2] = node1;
}
}
pParent->count++;
//释放内存
delete node;
}
else
{
//当前节点变成二节点
memset(node->data, 0, sizeof(T) * 3);//清空数组
memset(node->pArr, 0, sizeof(TTTreeNode<T>) * 4);//清空指针
node->data[0] = temp;
node->count = two_node;
//node1成为左孩子 node2成为右孩子
node->pArr[0] = node1;
node->pArr[1] = node2;
}
}
}
源文件:
#include "twothreeTree.h"
int main()
{
int arr[10] = { 44,88,33,66,11,5,77,15,35,80 };
twothreeTree<int> Tree;
for (int i = 0; i < 10; i++)
{
Tree.insertNode(arr[i]);
}
while (1)
{
}
return 0;
}