以下程序显示了如何在C程序中构建二叉树。它使用动态内存分配,指针和递归。二叉树是一种非常有用的数据结构,因为它允许在排序列表中进行有效的插入,搜索和删除。因为这样一个树本质上是一个递归定义的结构,递归编程是处理它的自然而有效的方法。
tree
empty
node left-branch right-branch
left-branch
tree
right-branch
tree
以下是代码:
#include <stdlib.h>
#include <stdio.h>
struct tree_el {
int val;
struct tree_el * right, * left;
};
typedef struct tree_el node;
void insert(node ** tree, node * item) {
if(!(*tree)) {
*tree = item;
return;
}
if(item->val<(*tree)->val)
insert(&(*tree)->left, item);
else if(item->val>(*tree)->val)
insert(&(*tree)->right, item);
}
void printout(node * tree) {
if(tree->left) printout(tree->left);
printf("%d\n",tree->val);
if(tree->right) printout(tree->right);
}
void main() {
node * curr, * root;
int i;
root = NULL;
for(i=1;i<=10;i++) {
curr = (node *)malloc(sizeof(node));
curr->left = curr->right = NULL;
curr->val = rand();
insert(&root, curr);
}
printout(root);
}
为什么使用指针指针?
答案 0 :(得分:6)
因为insert方法需要修改树的根。
答案 1 :(得分:2)
白板吧。或者修改示例代码来执行此操作:
for(i=1;i<=10;i++) {
curr = (node *)malloc(sizeof(node));
curr->left = curr->right = NULL;
curr->val = rand();
printf("before: %d\n", root);
insert(&root, curr);
printf("after: %d\n", root);
}
// printout(root);
打印结果如下所示:
之前:0
后:3871888
之前:3871888
后:3871888
之前:3871888
之后:3871888
为什么会这样?
因为它改变了传递给它的指针。
插入是如何工作的:插入遍历树。首先,它访问当前节点。然后左边的节点(通过递归)。然后右边的节点(通过递归)。如果它是一个空的节点(NULL),它将用该项替换该节点。
为此,它取消引用外部指针,并将指针“item”的值赋给该内部指针。
在一般情况下,指针就像int或char。您按值传递指针。如果你取消引用它,你可以修改它指向的任何内容,但不能修改指针本身。如果传递指向指针的指针,则可以更改内部指针,但外部指针不能。
这里有一些可以咀嚼的指针示例代码:
#include <cstdio>
#include <cstdlib>
void some_func(int* value)
{
*value = 12;
}
int main(int argc, char* argv[])
{
int a = 5;
printf("%d\n", a); // prints 5
some_func(&a);
printf("%d\n", a); // prints 12
int b = 7;
int* c = &b;
printf("%d\n", b); // prints 7
printf("%d\n", c); // prints 2029440 (some random memory address)
some_func(c);
printf("%d\n", b); // prints 12
printf("%d\n", c); // prints 2029440 (the same value as before)
}
和
#include <cstdio>
#include <cstdlib>
void some_other_func(int** other_value)
{
*other_value = NULL;
}
int main(int argc, char* argv[])
{
int b = 7;
int* c = &b;
printf("%d\n", c); // prints 4718288 (some random memory address)
some_other_func(&c);
printf("%d\n", c); // prints 0
}
最后但并非最不重要:
#include <cstdio>
#include <cstdlib>
void some_third_func(int* third_value)
{
*third_value = 12;
int** lets_modify_third_value = &third_value;
*lets_modify_third_value = NULL;
}
int main(int argc, char* argv[])
{
int b = 7;
int* c = &b;
printf("%d\n", b); // prints 7
printf("%d\n", c); // prints 1637380 (some random memory address)
some_third_func(c);
printf("%d\n", b); // prints 12
printf("%d\n", c); // prints 1637380 (same as before. Note that the pointer was passed by value, so "third_value" was just a COPY of the address)
}
答案 2 :(得分:0)
更准确的答案是: 因为函数需要替换给定字段(指针)的内容
答案 3 :(得分:0)
“输入”参数的替代 - 返回值,需要在每个访问级别中额外分配:
typedef node* node_ref;
node_ref insert ( node_ref tree, node_ref item) {
if ( !tree )
return item;
if ( item -> val < tree -> val )
tree -> left = insert ( tree -> left, item );
else if ( item -> val > tree -> val )
tree -> right = insert ( tree -> right, item );
return tree;
}
...
// in the loop
root = insert ( root, curr );
该方法(或原始代码)的另一个缺点是无法判断节点是否已插入;如果您添加一个返回值来表示它,如果没有插入,则不必泄漏curr
:
typedef node* node_ref;
bool insert ( node_ref *tree, node_ref item) {
if ( !*tree ) {
*tree = item;
return true;
}
if ( item -> val < ( *tree ) -> val )
return insert ( & ( *tree ) -> left, item );
if ( item -> val > ( *tree ) -> val )
return insert ( & ( *tree ) -> right, item);
return false;
}
...
// in the loop
for ( int i = 1; i <= 10; ++i ) {
node_ref curr = malloc ( sizeof ( node ) );
curr -> left = curr -> right = NULL;
curr -> val = rand();
if ( ! insert ( &root, curr ) )
free ( curr );
}