mirror of
https://github.com/lifegpc/c-utils.git
synced 2026-06-06 05:08:45 +08:00
Add binary tree and binary search tree
This commit is contained in:
@@ -146,6 +146,8 @@ set(SOURCE_FILE_HEADERS
|
||||
stack.h
|
||||
linked_stack.h
|
||||
circular_queue.h
|
||||
binary_tree.h
|
||||
binary_search_tree.h
|
||||
)
|
||||
|
||||
if (NOT HAVE_STRPTIME)
|
||||
@@ -195,7 +197,7 @@ if (ENABLE_UTILS_TESTING)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(googletest)
|
||||
enable_testing()
|
||||
add_executable(unittest test/stack_test.cpp test/queue_test.cpp)
|
||||
add_executable(unittest test/stack_test.cpp test/queue_test.cpp test/binary_tree_test.cpp)
|
||||
target_link_libraries(unittest GTest::gtest_main utils)
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(unittest)
|
||||
|
||||
96
binary_search_tree.h
Normal file
96
binary_search_tree.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef _UTIL_BINARY_SEARCH_TREE_H
|
||||
#define _UTIL_BINARY_SEARCH_TREE_H
|
||||
#include "binary_tree.h"
|
||||
|
||||
template <typename K, typename V>
|
||||
struct BinarySearchTreePair {
|
||||
K key;
|
||||
V value;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
using BinarySearchTree = BinaryTree<BinarySearchTreePair<K, V>>;
|
||||
|
||||
template <typename K, typename V>
|
||||
void binary_search_tree_clear(BinarySearchTree<K, V>*& root, std::function<void(K)> free_key = std::function<void(K)>(), std::function<void(V)> free_value = std::function<void(V)>()) {
|
||||
if (free_key || free_value) {
|
||||
binary_tree_clear(root, [&free_key, &free_value](BinarySearchTreePair<K, V> e) {
|
||||
if (free_key) free_key(e.key);
|
||||
if (free_value) free_value(e.value);
|
||||
});
|
||||
} else {
|
||||
binary_tree_clear(root);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V, class F, class G>
|
||||
inline void binary_search_tree_clear(BinarySearchTree<K, V>*& root, F free_key, G free_value = nullptr) {
|
||||
binary_search_tree_clear(root, std::function<void(K)>(free_key), std::function<void(V)>(free_value));
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename... Args>
|
||||
void binary_search_tree_iter(BinarySearchTree<K, V>* root, std::function<void(K, V, Args...)> callback, Args... args) {
|
||||
binary_tree_lnr(root, [&callback](BinarySearchTree<K, V>* e, Args... args) {
|
||||
callback(e->data.key, e->data.value, args...);
|
||||
}, args...);
|
||||
}
|
||||
|
||||
template <typename K, typename V, class F, typename... Args>
|
||||
inline void binary_search_tree_iter(BinarySearchTree<K, V>* root, F callback, Args... args) {
|
||||
binary_search_tree_iter(root, std::function<void(K, V, Args...)>(callback), args...);
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool binary_search_tree_insert(BinarySearchTree<K, V>*& root, K key, V value, std::function<int(K, K)> comp, std::function<void(K)> free_key = std::function<void(K)>(), std::function<void(V)> free_value = std::function<void(V)>()) {
|
||||
if (!root) {
|
||||
root = binary_tree_new<BinarySearchTreePair<K, V>>({ key, value });
|
||||
if (!root) {
|
||||
if (free_key) free_key(key);
|
||||
if (free_value) free_value(value);
|
||||
}
|
||||
return root != nullptr;
|
||||
}
|
||||
BinarySearchTree<K, V>* cur = root;
|
||||
int re = comp(key, cur->data.key);
|
||||
while (!binary_tree_is_leaf(cur)) {
|
||||
if (re == 0) {
|
||||
break;
|
||||
}
|
||||
if (re < 0 && !cur->left) break;
|
||||
if (re > 0 && !cur->right) break;
|
||||
cur = re < 0 ? cur->left : cur->right;
|
||||
re = comp(key, cur->data.key);
|
||||
}
|
||||
if (re == 0) {
|
||||
if (free_value) free_value(cur->data.value);
|
||||
if (free_key) free_key(key);
|
||||
cur->data.value = value;
|
||||
return true;
|
||||
}
|
||||
BinarySearchTree<K, V>* node = binary_tree_new<BinarySearchTreePair<K, V>>({key, value});
|
||||
if (!node) {
|
||||
if (free_key) free_key(key);
|
||||
if (free_value) free_value(value);
|
||||
return false;
|
||||
}
|
||||
if (re == -1) {
|
||||
cur->left = node;
|
||||
} else {
|
||||
cur->right = node;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V, class F>
|
||||
inline bool binary_search_tree_insert(BinarySearchTree<K, V>*& root, K key, V value, F comp) {
|
||||
return binary_search_tree_insert(root, key, value, std::function<int(K, K)>(comp));
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool binary_search_tree_insert(BinarySearchTree<K, V>*& root, K key, V value) {
|
||||
return binary_search_tree_insert(root, key, value, [](K k1, K k2) {
|
||||
return k1 == k2 ? 0 : k1 < k2 ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
115
binary_tree.h
Normal file
115
binary_tree.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef _UTIL_BINARY_TREE_H
|
||||
#define _UTIL_BINARY_TREE_H
|
||||
#include <functional>
|
||||
#include <stddef.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "linked_queue.h"
|
||||
|
||||
template <typename T>
|
||||
struct BinaryTree {
|
||||
T data;
|
||||
struct BinaryTree* left;
|
||||
struct BinaryTree* right;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void binary_tree_clear(struct BinaryTree<T>*& top, std::function<void(T)> free_func = std::function<void(T)>()) {
|
||||
if (!top) return;
|
||||
binary_tree_dfs(top, [&free_func](struct BinaryTree<T>* node) {
|
||||
if (free_func) {
|
||||
free_func(node->data);
|
||||
}
|
||||
node->left = nullptr;
|
||||
node->right = nullptr;
|
||||
free(node);
|
||||
});
|
||||
top = nullptr;
|
||||
}
|
||||
|
||||
template <typename T, class F>
|
||||
inline void binary_tree_clear(struct BinaryTree<T>*& top, F free_func) {
|
||||
binary_tree_clear(top, std::function<void(T)>(free_func));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void binary_tree_dfs(struct BinaryTree<T>* top, std::function<void(struct BinaryTree<T>*, Args...)> callback, Args... args) {
|
||||
if (!top) return;
|
||||
if (top->left) binary_tree_dfs(top->left, callback, args...);
|
||||
if (top->right) binary_tree_dfs(top->right, callback, args...);
|
||||
callback(top, args...);
|
||||
}
|
||||
|
||||
template <typename T, class F, typename... Args>
|
||||
inline void binary_tree_dfs(struct BinaryTree<T>* top, F callback, Args... args) {
|
||||
binary_tree_dfs(top, std::function<void(struct BinaryTree<T>*, Args...)>(callback), args...);
|
||||
}
|
||||
|
||||
#define binary_tree_lrn binary_tree_dfs
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void binary_tree_lnr(struct BinaryTree<T>* top, std::function<void(struct BinaryTree<T>*, Args...)> callback, Args... args) {
|
||||
if (!top) return;
|
||||
if (top->left) binary_tree_lnr(top->left, callback, args...);
|
||||
callback(top, args...);
|
||||
if (top->right) binary_tree_lnr(top->right, callback, args...);
|
||||
}
|
||||
|
||||
template <typename T, class F, typename... Args>
|
||||
inline void binary_tree_lnr(struct BinaryTree<T>* top, F callback, Args... args) {
|
||||
binary_tree_lnr(top, std::function<void(struct BinaryTree<T>*, Args...)>(callback), args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void binary_tree_nlr(struct BinaryTree<T>* top, std::function<void(struct BinaryTree<T>*, Args...)> callback, Args... args) {
|
||||
if (!top) return;
|
||||
callback(top, args...);
|
||||
if (top->left) binary_tree_nlr(top->left, callback, args...);
|
||||
if (top->right) binary_tree_nlr(top->right, callback, args...);
|
||||
}
|
||||
|
||||
template <typename T, class F, typename... Args>
|
||||
inline void binary_tree_nlr(struct BinaryTree<T>* top, F callback, Args... args) {
|
||||
binary_tree_nlr(top, std::function<void(struct BinaryTree<T>*, Args...)>(callback), args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void binary_tree_bfs(struct BinaryTree<T>* top, std::function<void(struct BinaryTree<T>*, Args...)> callback, Args... args) {
|
||||
if (!top) return;
|
||||
struct LinkedQueue<struct BinaryTree<T>*> queue;
|
||||
linked_queue_init(queue);
|
||||
linked_queue_push(queue, top);
|
||||
struct BinaryTree<T>* tmp;
|
||||
while (linked_queue_pop(queue, tmp)) {
|
||||
if (tmp->left) linked_queue_push(queue, tmp->left);
|
||||
if (tmp->right) linked_queue_push(queue, tmp->right);
|
||||
callback(tmp, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class F, typename... Args>
|
||||
inline void binary_tree_bfs(struct BinaryTree<T>* top, F callback, Args... args) {
|
||||
binary_tree_bfs(top, std::function<void(struct BinaryTree<T>*, Args...)>(callback), args...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct BinaryTree<T>* binary_tree_new(T data) {
|
||||
struct BinaryTree<T>* node = (struct BinaryTree<T>*)malloc(sizeof(struct BinaryTree<T>));
|
||||
if (!node) return nullptr;
|
||||
node->data = data;
|
||||
node->left = nullptr;
|
||||
node->right = nullptr;
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool binary_tree_is_leaf(struct BinaryTree<T>* node) {
|
||||
return node->left == nullptr && node->right == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool binary_tree_node_is_full(struct BinaryTree<T>* node) {
|
||||
return node->left != nullptr && node->right != nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,6 +16,12 @@ struct LinkedQueue {
|
||||
struct LinkedQueueNode<T>* rear;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void linked_queue_init(struct LinkedQueue<T>& queue) {
|
||||
queue.front = nullptr;
|
||||
queue.rear = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t linked_queue_length(struct LinkedQueue<T>& queue) {
|
||||
if (!queue.front) return 0;
|
||||
|
||||
72
test/binary_tree_test.cpp
Normal file
72
test/binary_tree_test.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "binary_search_tree.h"
|
||||
#include <string>
|
||||
|
||||
TEST(BinaryTreeTest, BinaryTree1) {
|
||||
struct BinaryTree<int>* top = binary_tree_new(1);
|
||||
top->left = binary_tree_new(2);
|
||||
top->right = binary_tree_new(3);
|
||||
top->left->left = binary_tree_new(4);
|
||||
top->left->right = binary_tree_new(5);
|
||||
top->right->right = binary_tree_new(6);
|
||||
std::string text;
|
||||
binary_tree_dfs(top, [&text](struct BinaryTree<int>* ele) {
|
||||
if (!text.empty()) {
|
||||
text += ",";
|
||||
}
|
||||
text += std::to_string(ele->data);
|
||||
});
|
||||
GTEST_ASSERT_EQ(text, "4,5,2,6,3,1");
|
||||
text = "";
|
||||
binary_tree_lnr(top, [&text](struct BinaryTree<int>* ele) {
|
||||
if (!text.empty()) {
|
||||
text += ",";
|
||||
}
|
||||
text += std::to_string(ele->data);
|
||||
});
|
||||
GTEST_ASSERT_EQ(text, "4,2,5,1,3,6");
|
||||
text = "";
|
||||
binary_tree_nlr(top, [&text](struct BinaryTree<int>* ele) {
|
||||
if (!text.empty()) {
|
||||
text += ",";
|
||||
}
|
||||
text += std::to_string(ele->data);
|
||||
});
|
||||
GTEST_ASSERT_EQ(text, "1,2,4,5,3,6");
|
||||
text = "";
|
||||
binary_tree_bfs(top, [&text](struct BinaryTree<int>* ele) {
|
||||
if (!text.empty()) {
|
||||
text += ",";
|
||||
}
|
||||
text += std::to_string(ele->data);
|
||||
});
|
||||
GTEST_ASSERT_EQ(text, "1,2,3,4,5,6");
|
||||
binary_tree_clear(top);
|
||||
}
|
||||
|
||||
TEST(BinaryTreeTest, BinarySearchTree1) {
|
||||
BinarySearchTree<int, int>* tree = nullptr;
|
||||
binary_search_tree_insert(tree, 100, 3);
|
||||
GTEST_ASSERT_EQ(tree->data.key, 100);
|
||||
GTEST_ASSERT_EQ(tree->data.value, 3);
|
||||
binary_search_tree_insert(tree, 100, 20);
|
||||
GTEST_ASSERT_EQ(tree->data.value, 20);
|
||||
binary_search_tree_insert(tree, 20, 45);
|
||||
binary_search_tree_insert(tree, 40, 13);
|
||||
binary_search_tree_insert(tree, 33, 23);
|
||||
binary_search_tree_insert(tree, 77, 33);
|
||||
binary_search_tree_insert(tree, 120, 222);
|
||||
std::string keys;
|
||||
std::string values;
|
||||
binary_search_tree_iter(tree, [&keys, &values](int key, int value) {
|
||||
if (!keys.empty()) {
|
||||
keys += ",";
|
||||
values += ",";
|
||||
}
|
||||
keys += std::to_string(key);
|
||||
values += std::to_string(value);
|
||||
});
|
||||
GTEST_ASSERT_EQ(keys, "20,33,40,77,100,120");
|
||||
GTEST_ASSERT_EQ(values, "45,23,13,33,20,222");
|
||||
binary_search_tree_clear(tree);
|
||||
}
|
||||
@@ -85,8 +85,7 @@ TEST(Queue_Test, CircularQueue2) {
|
||||
|
||||
TEST(Queue_Test, LinkedQueue1) {
|
||||
struct LinkedQueue<int> queue;
|
||||
queue.front = nullptr;
|
||||
queue.rear = nullptr;
|
||||
linked_queue_init(queue);
|
||||
EXPECT_EQ(linked_queue_push(queue, 3), true);
|
||||
EXPECT_EQ(linked_queue_push(queue, 4), true);
|
||||
EXPECT_EQ(linked_queue_push(queue, 9), true);
|
||||
@@ -105,3 +104,20 @@ TEST(Queue_Test, LinkedQueue1) {
|
||||
EXPECT_EQ(text, "4,9,7");
|
||||
free_linked_queue(queue);
|
||||
}
|
||||
|
||||
TEST(Queue_Test, LinkedQueue2) {
|
||||
struct LinkedQueue<int> queue;
|
||||
linked_queue_init(queue);
|
||||
EXPECT_EQ(linked_queue_push(queue, 3), true);
|
||||
EXPECT_EQ(linked_queue_push(queue, 4), true);
|
||||
EXPECT_EQ(linked_queue_push(queue, 9), true);
|
||||
int v;
|
||||
EXPECT_EQ(linked_queue_pop(queue, v), true);
|
||||
EXPECT_EQ(v, 3);
|
||||
EXPECT_EQ(linked_queue_pop(queue, v), true);
|
||||
EXPECT_EQ(linked_queue_pop(queue, v), true);
|
||||
EXPECT_EQ(linked_queue_pop(queue, v), false);
|
||||
EXPECT_EQ(v, 9);
|
||||
EXPECT_EQ(queue.front, nullptr);
|
||||
EXPECT_EQ(queue.rear, nullptr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user