- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试向二叉搜索树添加值,为此我编写了两个函数,一个创建新节点,另一个将这些值插入树所以这是代码
typedef struct Node {
void *data;
struct Node *left;
struct Node *right;
} Node;
Node *createNode(void *data,size_t s){
Node *newNode = (Node *)malloc(s * sizeof(Node));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
void addValueBt(Node ** node, void *data, size_t s,int (* compar)(const void *, const void *)){
if (*node == NULL)
*node = createNode(data,s);
else if (compar((*node)->data,data) > 0)
addValueBt(&(*node)->left, data, s, compar);
else
addValueBt(&(*node)->right, data, s, compar);
}
当我用两种不同的方式在 main 中调用 addValueBt 时(但通常看起来是一样的)我没有得到相同的结果
第一种方式:
int main(){
Node *root = NULL;
for(int i = 0; i < 10; i++)
addValueBt(&root, &i, 1, myCompar);
printBt(root,print);//print the whole tree
}
显示器给出了这个:
10
10
10
10
10
10
10
10
10
10
第二种方式:
int main(){
int a = 8, b = 9, c = 5, d = 1;
addValueBt(&root, &a, 1, myCompar);
addValueBt(&root, &b, 1, myCompar);
addValueBt(&root, &c, 1, myCompar);
addValueBt(&root, &d, 1, myCompar);
printBt(root,print);
}
显示:
1
5
8
9
"myCompar"比较两个整数
我的问题是:为什么当我们在循环中使用函数“addValueBt”时它不起作用?(通常它应该给出与另一个相同的结果)
最佳答案
作为user3386109在 comment 中正确指出,主要问题是您存储的是指向树中数据的指针,而不是数据的副本。所有节点最终都指向同一位置,因此当该位置发生变化时,树指向的值会同时在所有节点中发生变化。
在一组评论中(其中一些是对 Yasmine 的评论问题的回复,OP),我注意到:
This is tangential to your main problem, but the parameter
s
is initially confusing. It's always 1 because you only allocate one node at a time. If it was larger, you'd waste the extra space thatcreateNode()
does allocate but does not initialize. You could stop usings
(assume 1) without causing problems in the visible code.I mean that in
Node *createNode(void *data, size_t s) { Node *newNode = (Node *)malloc(s * sizeof(Node));
, you allocate an array ofNode
structures of the size given bys
. However, you initialize only the first structure allocated, and you don't record the size of the array anywhere. This doesn't matter because if you track the calls frommain()
toaddValueBt()
tocreateNode()
, the value ofs
is always1
. But because it is always 1, you really have no need to pass it, so you can simplify the calls all the way down the calling chain.Note that @user3386109: has identified the problem. You store the same pointer in each element of the tree, so when the pointed at value changes, all the elements of the tree change at the same time. And if the pointed at value goes out of scope, you get undefined behaviour. You either need to make a copy of the data in the
createNode()
function, which means that you need to know how big the data is so that you can allocate new space to store it, or you need to ensure that a different pointer is passed to the code each time, and those pointers remain valid until the tree is freed.You can see with your working code, you pass a different pointer each time (a pointer to a different variable). When you repeatedly pass a pointer to the same variable, the same address is stored, so the current value stored at that address is used by all the nodes; they all have the same value.
Part of the problem comes from using
void *data;
instead of a simpleint data;
. For the immediate purposes, life would be much, much simpler if your data structure usedint data;
. You'd have:Node *createNode(int key)
{
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = data; newNode->left = NULL;
newNode->right = NULL;
return newNode;
}If you store
void *
, you really need to know how long the data you're storing pointers to is, so that you can make copies, etc. Your comparator can make assumptions about the length (and you'll be OK as long as those assumptions are correct). So, you'd need:Node *createNode(void *data, size_t len)
{
void *copy = malloc(len);
memmove(copy, data, len);
Node *newNode = (Node *)malloc(s * sizeof(Node));
newNode->data = copy;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
Yasmine 评论道:
I know but I have to do that so that it can be used with other types like
float
orchar
.
那时,我开始生成下面的代码。
这是一个处理我评论中提出的要点的产品。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct Node
{
void *data;
size_t datalen;
struct Node *left;
struct Node *right;
} Node;
static int cmp_dbl(const void *p1, const void *p2);
static int cmp_int(const void *p1, const void *p2);
static void freeBt(Node *node);
static void printBt(Node *node, void (*print)(const void *));
static void print_dbl(const void *p);
static void print_int(const void *p);
extern Node *createNode(void *data, size_t len);
extern void addValueBt(Node **node, void *data, size_t len, int (*compar)(const void *, const void *));
Node *createNode(void *data, size_t len)
{
void *copy = malloc(len);
// Error check allocation!
memmove(copy, data, len);
Node *newNode = (Node *)malloc(sizeof(*newNode));
// Error check allocation!
newNode->data = copy;
newNode->left = NULL;
newNode->right = NULL;
newNode->datalen = len;
return newNode;
}
void addValueBt(Node **node, void *data, size_t len, int (*compar)(const void *, const void *))
{
if (*node == NULL)
*node = createNode(data, len);
else if (compar((*node)->data, data) > 0)
addValueBt(&(*node)->left, data, len, compar);
else
addValueBt(&(*node)->right, data, len, compar);
}
int main(int argc, char **argv)
{
unsigned seed = time(0);
if (argc == 2)
seed = atoi(argv[1]);
srand(seed);
printf("Seed: %u\n", seed);
Node *root = NULL;
for (int i = 0; i < 10; i++)
addValueBt(&root, &i, sizeof(i), cmp_int);
printBt(root, print_int);
freeBt(root);
root = NULL;
for (int i = 0; i < 10; i++)
{
double d = (double)rand() / RAND_MAX * 1000.0 + (double)rand() / RAND_MAX;
addValueBt(&root, &d, sizeof(d), cmp_dbl);
}
printBt(root, print_dbl);
freeBt(root);
return 0;
}
static int cmp_int(const void *p1, const void *p2)
{
int v1 = *(int *)p1;
int v2 = *(int *)p2;
return (v1 > v2) - (v1 < v2);
}
static int cmp_dbl(const void *p1, const void *p2)
{
double v1 = *(double *)p1;
double v2 = *(double *)p2;
return (v1 > v2) - (v1 < v2);
}
static void print_int(const void *p)
{
printf("%d\n", *(int *)p);
}
static void print_dbl(const void *p)
{
printf("%8.3f\n", *(double *)p);
}
static void printBt(Node *node, void (*print)(const void *))
{
if (node != 0)
{
printBt(node->left, print);
print(node->data);
printBt(node->right, print);
}
}
static void freeBt(Node *node)
{
if (node != 0)
{
freeBt(node->left);
freeBt(node->right);
free(node);
}
}
这是一个运行示例:
Seed: 1511037421
0
1
2
3
4
5
6
7
8
9
87.907
118.694
140.163
170.833
343.940
412.792
422.254
530.731
557.656
936.981
我将参数 s
丢给了 addValueBt()
和 createNode()
,但添加了一个参数 size_t len
。这定义了数据的长度。长度存储在Node
结构中的size_t datalen
中。
createNode()
中的代码复制了传递给它的数据,并将该副本存储在树中。这确保每个节点都不会受到调用代码中值更改的影响。
我为 int
比较器添加了代码(你叫你的 myCompar()
;我叫我的 cmp_int()
因为我也想使用 double
) 和 double
比较器 cmp_dbl()
。我添加了打印函数 print_int()
和 printf_dbl()
— 将您的 print
重命名为 print_int
。我添加了内存释放函数freeBt()
。我为 printBt()
添加了代码。
使用命令行参数的代码允许您指定您选择的种子。这有点草率;我可能应该使用 strtoul()
而不是 atoi()
。但它允许您在命令行上指定一个数字作为种子,这将在您需要时为您提供可重复性。种子已报告,因此您可以重现结果。这在玩随机数时很有用——有时您想要随机性,有时您想要可重复性。
我没有修复打印代码,以便树的结构可见。您只会得到一个数字列表。
代码并没有真正使用结构的datalen
成员,但是检查一致性或记录字符串的长度可能很有用。
关于c - 为树添加值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47370114/
关于 B 树与 B+ 树,网上有一个比较经典的问题:为什么 MongoDb 使用 B 树,而 MySQL 索引使用 B+ 树? 但实际上 MongoDb 真的用的是 B 树吗?
如何将 R* Tree 实现为持久(基于磁盘)树?保存 R* 树索引或保存叶值的文件的体系结构是什么? 注意:此外,如何在这种持久性 R* 树中执行插入、更新和删除操作? 注意事项二:我已经实现了一个
目前,我正在努力用 Java 表示我用 SML 编写的 AST 树,这样我就可以随时用 Java 遍历它。 我想知道是否应该在 Java 中创建一个 Node 类,其中包含我想要表示的数据,以及一个数
我之前用过这个库http://www.cs.umd.edu/~mount/ANN/ .但是,它们不提供范围查询实现。我猜是否有一个 C++ 范围查询实现(圆形或矩形),用于查询二维数据。 谢谢。 最佳
在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择
书接上回,今天和大家一起动手来自己实现树。 相信通过前面的章节学习,大家已经明白树是什么了,今天我们主要针对二叉树,分别使用顺序存储和链式存储来实现树。 01、数组实现 我们在上一节中说过,
书节上回,我们接着聊二叉树,N叉树,以及树的存储。 01、满二叉树 如果一个二叉树,除最后一层节点外,每一层的节点数都达到最大值,即每个节点都有两个子节点,同时所有叶子节点都在最后一层,则这个
树是一种非线性数据结构,是以分支关系定义的层次结构,因此形态上和自然界中的倒挂的树很像,而数据结构中树根向上树叶向下。 什么是树? 01、定义 树是由n(n>=0)个元素节点组成的
操作系统的那棵“树” 今天从一颗 开始,我们看看如何从小树苗长成一颗苍天大树。 运转CPU CPU运转起来很简单,就是不断的从内存取值执行。 CPU没有好好运转 IO是个耗费时间的活,如果CPU在取值
我想为海洋生物学类(class)制作一个简单的系统发育树作为教育示例。我有一个具有分类等级的物种列表: Group <- c("Benthos","Benthos","Benthos","Be
我从这段代码中删除节点时遇到问题,如果我插入数字 12 并尝试删除它,它不会删除它,我尝试调试,似乎当它尝试删除时,它出错了树的。但是,如果我尝试删除它已经插入主节点的节点,它将删除它,或者我插入数字
B+ 树的叶节点链接在一起。将 B+ 树的指针结构视为有向图,它不是循环的。但是忽略指针的方向并将其视为链接在一起的无向叶节点会在图中创建循环。 在 Haskell 中,如何将叶子构造为父内部节点的子
我在 GWT 中使用树控件。我有一个自定义小部件,我将其添加为 TreeItem: Tree testTree = new Tree(); testTree.addItem(myWidget); 我想
它有点像混合树/链表结构。这是我定义结构的方式 struct node { nodeP sibling; nodeP child; nodeP parent; char
我编写了使用队列遍历树的代码,但是下面的出队函数生成错误,head = p->next 是否有问题?我不明白为什么这部分是错误的。 void Levelorder(void) { node *tmp,
例如,我想解析以下数组: var array1 = ["a.b.c.d", "a.e.f.g", "a.h", "a.i.j", "a.b.k"] 进入: var json1 = { "nod
问题 -> 给定一棵二叉树和一个和,确定该树是否具有从根到叶的路径,使得沿路径的所有值相加等于给定的和。 我的解决方案 -> public class Solution { public bo
我有一个创建 java 树的任务,它包含三列:运动名称、运动类别中的运动计数和上次更新。类似的东西显示在下面的图像上: 如您所见,有 4 种运动:水上运动、球类运动、跳伞运动和舞蹈运动。当我展开 sk
我想在 H2 数据库中实现 B+ Tree,但我想知道,B+ Tree 功能在 H2 数据库中可用吗? 最佳答案 H2 已经使用了 B+ 树(PageBtree 类)。 关于mysql - H2数据库
假设我们有 5 个字符串数组: String[] array1 = {"hello", "i", "cat"}; String[] array2 = {"hello", "i", "am"}; Str
我是一名优秀的程序员,十分优秀!