我正在学习C并在我的C书中找到了一个基本的树实现:
#include <stdio.h>
#include <stdlib.h>
struct tree_node {
int data;
struct tree_node *left_p, *right_p;
};
struct tree_node *t_search(struct tree_node *root, int v) {
while (root) {
printf("Looking for %d, looking at %d\n", v, root->data);
if (root->data == v)
return root;
if (root->data > v)
root = root->left_p;
else
root = root->right_p;
}
return 0;
}
int t_insert(struct tree_node **root, int v) {
while (*root) {
if ((*root)->data == v)
return 1;
if ((*root)->data > v)
root = &((*root)->left_p);
else
root = &((*root)->right_p);
}
if ((*root = (struct tree_node *) malloc(sizeof(struct tree_node))) == 0)
return 2;
(*root)->data = v;
(*root)->left_p = 0;
(*root)->right_p = 0;
return 0;
}
int main(void) {
struct tree_node *tp, *root_p = 0;
int i;
t_insert(&root_p, 4);
t_insert(&root_p, 2);
t_insert(&root_p, 6);
t_insert(&root_p, 1);
t_insert(&root_p, 3);
t_insert(&root_p, 4);
t_insert(&root_p, 7);
for (i = 0; i < 9; i++) {
tp = t_search(root_p, i);
if (tp)
printf("%d found\n", i);
else
printf("%d not found\n", i);
}
return 0;
}
虽然代码似乎很直接,但我很难理解t_insert
函数。为什么选择struct tree_node **root
代替struct tree_node *root
? t_serach
实际上是相同的代码,但只使用指针而不是指针指针。也许另一个例子可以更好地解释这个问题。
它的价值:我来自Java背景。
注意:是的,我知道插入后树没有重新平衡。
答案 0 :(得分:3)
我们需要的一些东西:
#include <stdio.h>
struct thing {
struct thing *next;
int value;
};
struct thing otherthing ={ NULL, 42};
struct thing something ={ &otherthing, 13};
int forty_two = 42;
void do_the_int(int i);
void do_the_intp(int *ip);
void do_the_intpp(int **ipp);
void do_the_structp(struct thing *p);
void do_the_structpp(struct thing **pp);
函数可以更改其参数,但调用者不会看到结果,因此:
void do_the_int(int i) {
i = 42;
}
实际上是无操作。要实际更改某些内容,该函数需要一个指向它的指针,如:
void do_the_intp(int *ip) {
*ip = 42;
}
现在,ip
指向的值实际上已被函数更改。接下来,如果要更改指针,该函数将需要一个指向它的指针:指向指针的指针:
int forty_two = 42; // global (or static)
void do_the_intpp(int **ipp) {
*ipp = &forty_two;
}
对于其他数据类型(比int),情况并没有区别:如果要更改结构,该函数将需要一个指向struct 的指针,如果该函数需要更改指针要构造它需要一个指向struct 的指针的指针。所以
void do_the_structp(struct thing *p) {
p->value = 42;
}
实际上会在结构*p
和
void do_the_structpp(struct thing **pp) {
*pp = (*pp)->next;
}
实际上会更改位于*pp
的指针。
现在,我们打电话给他们:
int main(void) {
int zero=0
, one=1
, two=2;
int *the_p;
struct thing *tp, *cur;
the_p = &two;
tp = &something;
printf("Before: zero=%d, one=%d, two=%d the_p=%p *the_p=%d\n"
, zero, one, two, (void*) the_p,*the_p);
for(cur=tp; cur; cur = cur->next) {
printf("p=%p Next=%p Val=%d\n"
, (void*) cur, (void*) cur->next, cur->value );
}
do_the_int(zero);
do_the_intp(&one);
do_the_intpp(&the_p);
do_the_structp(tp);
do_the_structpp( &tp);
printf("After: zero=%d, one=%d, two=%d the_p=%p *the_p=%d\n"
, zero, one, two, (void*) the_p,*the_p);
for(cur=tp; cur; cur = cur->next) {
printf("p=%p Next=%p Val=%d\n"
, (void*) cur, (void*) cur->next, cur->value );
}
return 0;
}
输出:
Before: zero=0, one=1, two=2 the_p=0x7fff97a7db28 *the_p=2
p=0x601030 Next=0x601020 Val=13
p=0x601020 Next=(nil) Val=42
After: zero=0, one=42, two=2 the_p=0x601040 *the_p=42
p=0x601020 Next=(nil) Val=42
答案 1 :(得分:1)
它传递一个双指针,因为它需要在进程中更改指针。树的所有元素都可以被认为是在调用insert时需要更改的指针。如果你没有传入双指针,你实际上要做的是将指针作为局部变量复制到函数中,当插入函数退出时,更改不起作用。如果我错了,请忽略我。
答案 2 :(得分:0)
如果您只想读取指针指向的内容,则您的参数类型为foo*
。如果要更改指针指向的内容,则参数类型为foo *
。但在那里,你希望能够改变你指向的东西。因此,您需要一个指向指针的指针,以便能够更改其值。
在java中,任何具有TreeNode
参数的方法等同于具有tree_node*
参数的C函数;因此,如果你有一个带有该签名的方法:
// Java code
static void treeInsert (TreeNode t);
在该方法中,您可以从t中访问可以读取其当前状态的方法,甚至可以修改t
的状态,即使您离开该方法,该状态也会被更改。
但是,如果你正在做那样的事情
t = null;
离开方法后,无效。这意味着
// Java code
static void treeNull (TreeNode t) {
t = null; // Warning, local change only !
}
static void treeChange (TreeNode t) {
t.changeState();
}
static void foo () {
t = new TreeNode();
t.treeChange(); // Ok, t has now an update state
t.treeNull(); // Warning, t is not null here
}
你不能在java中拥有等价的双指针,所以你无法做到这一点。
在C中,你可以这样做:
// C code
void updateState(tree_node* t) {
data = data + 1; // This is nonsense, just to make the point
}
void localChangePointer(tree_node* t) {
t = NULL;
}
void changePointer(tree_node** t) {
free_and_clean(*t);
*t = NULL;
}
void sillyStuff() {
tree_node* t;
// Initialize t
...
updateState(t); // (*t).data changed
localChangePointer(t); // Warning : t is not null yet !
changePointer(&t); // This time, we gave the address of t, not t itself
// Now t == NULL;
}
答案 3 :(得分:0)
也许你会喜欢这样的变体:
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
typedef struct btree_node{
int data;
struct btree_node *left, *right;
} BTree;
// get tree leaf nearest to v
BTree *bt_get(BTree *root, int v){
if(!root) return NULL;
int D = root->data;
if(D == v) return root;
if(root->data > v) return root->left;
else return root->right;
}
// find leaf with v or nearest to it (prev)
BTree *bt_search(BTree *root, int v, BTree **prev){
if(!root){
if(prev) *prev = NULL;
return NULL;
}
BTree *p;
do{
p = root;
root = bt_get(root, v);
}while(root && root->data != v);
if(prev) *prev = p;
return root;
}
BTree *bt_create_leaf(int v){
BTree *node = calloc(1, sizeof(BTree));
if(!node) return NULL;
node->data = v;
return node;
}
// insert new leaf (or leave it as is, if exists)
// return root node
BTree *bt_insert(BTree *root, int v){
BTree *closest, *node;
node = bt_search(root, v, &closest);
if(node) return node; // value exists
if(!closest) // there's no values at all!
return bt_create_leaf(v); // create root node
// we have a closest node to our data, so create node and insert it:
node = bt_create_leaf(v);
if(!node) return NULL;
int D = closest->data;
if(D > v) // there's no left leaf of closest node, add our node there
closest->left = node;
else
closest->right = node;
return root;
}
int main(void){
BTree *tp, *root_p = NULL;
int i, ins[] = {4,2,6,1,3,4,7,14,0,12};
for(i = 0; i < 10; i++)
if(!(root_p = bt_insert(root_p, ins[i]))) err(1, "Malloc error"); // can't insert
for(i = 0; i < 15; i++){
tp = bt_search(root_p, i, NULL);
printf("%d ", i);
if(!tp) printf("not ");
printf("found\n");
}
return 0;
}
//我在上一个回答中错了,正确
答案 4 :(得分:0)
这里没有双指针就可以了。我没有看到任何区别。
int t_insert(struct tree_node *root, int v) {
while (root) {
if (root->data == v)
return 1;
if (root->data > v)
root = &(root->left_p);
else
root = &(root->right_p);
}
if ((root = (struct tree_node *) malloc(sizeof(struct tree_node))) == 0)
return 2;
root->data = v;
root->left_p = 0;
root->right_p = 0;
return 0;
}