数据结构与算法之 B 树 嵌入式系统 资源受限 / 轻量级实现 实践

数据结构与算法阿木 发布于 2025-07-12 11 次阅读


B树:嵌入式系统中的数据结构与算法实践

在嵌入式系统中,由于资源受限和性能要求高的特点,选择合适的数据结构和算法至关重要。B树作为一种平衡的多路查找树,因其能够有效减少磁盘I/O操作,提高数据检索效率,在嵌入式系统中得到了广泛应用。本文将围绕B树的数据结构与算法,探讨其在嵌入式系统中的实践应用。

B树概述

B树是一种自平衡的树,它能够将数据均匀地分布在多个节点中,从而减少磁盘I/O操作。B树的特点如下:

1. 每个节点包含多个键值和子节点指针。

2. 树的高度较小,通常为3或4。

3. 每个节点可以存储多个键值,但数量有限。

4. 树的每个节点都满足以下条件:

- 根节点至少有两个子节点。

- 非根节点至少有t个子节点,其中t是B树的阶数。

- 所有叶子节点都在同一层。

B树的数据结构

以下是一个简单的B树数据结构实现:

c

define MAX_KEYS 4 // 每个节点最多存储的键值数量


define MIN_KEYS 2 // 每个节点最少存储的键值数量

typedef struct BTreeNode {


int keyCount; // 当前节点存储的键值数量


int keys[MAX_KEYS]; // 存储键值


struct BTreeNode children[MAX_KEYS + 1]; // 存储子节点指针


} BTreeNode;

typedef struct BTree {


int t; // B树的阶数


BTreeNode root; // 根节点指针


} BTree;


B树的基本操作

B树的基本操作包括:

1. 插入

2. 删除

3. 搜索

以下分别介绍这些操作的具体实现。

插入操作

插入操作分为以下步骤:

1. 如果根节点为空,则创建一个新的根节点。

2. 如果根节点不为空,则递归地在相应的节点中插入键值。

3. 如果节点已满,则需要分裂节点。

以下是一个简单的插入操作实现:

c

void insert(BTree tree, int key) {


BTreeNode node = tree->root;


if (node == NULL) {


// 创建新的根节点


node = createNode();


node->keys[0] = key;


node->keyCount = 1;


tree->root = node;


return;


}

// 递归插入键值


insertNode(node, key);

// 如果根节点已满,则分裂根节点


if (node->keyCount == MAX_KEYS) {


BTreeNode newRoot = createNode();


newRoot->children[0] = node;


node = splitNode(newRoot, node, 0);


tree->root = newRoot;


}


}

// 创建新节点的函数


BTreeNode createNode() {


BTreeNode node = (BTreeNode )malloc(sizeof(BTreeNode));


node->keyCount = 0;


for (int i = 0; i < MAX_KEYS + 1; i++) {


node->children[i] = NULL;


}


return node;


}

// 插入键值的函数


void insertNode(BTreeNode node, int key) {


int i = node->keyCount - 1;


while (i >= 0 && node->keys[i] > key) {


node->keys[i + 1] = node->keys[i];


i--;


}


node->keys[i + 1] = key;


node->keyCount++;


}

// 分裂节点的函数


BTreeNode splitNode(BTreeNode parent, BTreeNode node, int childIndex) {


BTreeNode newNode = createNode();


int mid = node->keyCount / 2;


for (int i = 0; i < mid; i++) {


newNode->keys[i] = node->keys[mid + i + 1];


}


newNode->keyCount = mid;

for (int i = 0; i <= MAX_KEYS; i++) {


newNode->children[i] = node->children[mid + i + 1];


node->children[mid + i + 1] = NULL;


}

parent->children[childIndex + 1] = newNode;


parent->keys[childIndex] = newNode->keys[0];


parent->keyCount++;

return parent;


}


删除操作

删除操作分为以下步骤:

1. 如果要删除的键值不存在,则返回。

2. 如果要删除的键值存在,则递归地在相应的节点中删除键值。

3. 如果节点变为不满,则需要合并节点。

以下是一个简单的删除操作实现:

c

void delete(BTree tree, int key) {


BTreeNode node = tree->root;


if (node == NULL) {


return;


}

// 递归删除键值


deleteNode(node, key);

// 如果根节点为空,则创建新的根节点


if (node->keyCount == 0) {


tree->root = node->children[0];


free(node);


}


}

// 删除键值的函数


void deleteNode(BTreeNode node, int key) {


int i = 0;


while (i < node->keyCount && node->keys[i] < key) {


i++;


}

if (node->keys[i] == key) {


// 找到要删除的键值


if (i < node->keyCount - 1) {


// 从右子节点复制最后一个键值


node->keys[i] = node->keys[node->keyCount - 1];


} else {


// 如果是最后一个键值,则删除它


node->keyCount--;


}

// 如果节点不满,则递归地调整节点


if (node->keyCount < MIN_KEYS) {


if (i > 0 && node->children[i - 1]->keyCount >= MIN_KEYS) {


// 从左子节点借一个键值


borrowKey(node, i);


} else if (i < node->keyCount && node->children[i + 1]->keyCount >= MIN_KEYS) {


// 从右子节点借一个键值


borrowKey(node, i + 1);


} else {


// 合并节点


mergeNodes(node, i);


}


}


} else {


// 递归删除键值


deleteNode(node->children[i], key);


}


}

// 借取键值的函数


void borrowKey(BTreeNode node, int childIndex) {


BTreeNode child = node->children[childIndex];


BTreeNode sibling = node->children[childIndex + 1];

// 从右子节点借一个键值


child->keys[child->keyCount] = node->keys[childIndex];


child->keyCount++;

// 将中间的键值移动到父节点


node->keys[childIndex] = sibling->keys[0];


sibling->keys[0] = 0;

// 调整子节点的键值


for (int i = 1; i < sibling->keyCount; i++) {


sibling->keys[i - 1] = sibling->keys[i];


}


sibling->keyCount--;


}

// 合并节点的函数


void mergeNodes(BTreeNode node, int childIndex) {


BTreeNode child = node->children[childIndex];


BTreeNode sibling = node->children[childIndex + 1];

// 将子节点的键值移动到父节点


for (int i = 0; i < sibling->keyCount; i++) {


child->keys[child->keyCount + i] = sibling->keys[i];


}


child->keyCount += sibling->keyCount;

// 删除子节点


for (int i = 0; i <= MAX_KEYS; i++) {


child->children[i + sibling->keyCount] = sibling->children[i];


sibling->children[i] = NULL;


}

// 删除子节点


free(sibling);


node->children[childIndex + 1] = NULL;


}


搜索操作

搜索操作相对简单,只需递归地在节点中查找键值即可。

c

int search(BTreeNode node, int key) {


if (node == NULL) {


return -1; // 未找到


}

int i = 0;


while (i < node->keyCount && node->keys[i] < key) {


i++;


}

if (node->keys[i] == key) {


return i; // 找到


} else {


return search(node->children[i], key);


}


}


总结

B树作为一种高效的数据结构,在嵌入式系统中具有广泛的应用。本文介绍了B树的数据结构、基本操作以及在实际应用中的实现方法。通过合理地选择B树的阶数和优化算法,可以进一步提高B树在嵌入式系统中的性能。在实际应用中,可以根据具体需求对B树进行改进和优化,以满足嵌入式系统的资源限制和性能要求。