[英]Binary Search Tree node insertion not working properly
我有一個作業分配來實現一個二進制搜索樹(創建,刪除,搜索)。 我使用了老師提供的示例,但無法使其正常工作。
到目前為止,這是我的代碼:
void insert_node(int k){
struct node *nodnou,*flow,*parent;
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
if (root==NULL)
{
root = (struct node*)malloc(sizeof(node));
root = nodnou;
}
else
{
flow = (struct node*)malloc(sizeof(node));
parent = (struct node*)malloc(sizeof(node));
flow = root;
parent = root;
while (((flow->st!=NULL) || (flow->dr!=NULL)) && flow!=NULL)
{
if (k<flow->nr)
{
parent = flow;
flow = flow->st;
}
else
{
parent = flow;
flow = flow->dr;
}
}
if (k<flow->nr)
{
parent->st = nodnou;
}
else
{
parent->dr = nodnou;
}
}
}
思維方式:此函數獲取我們要插入的節點的值作為k參數。 該函數只會插入樹的根(根是全局變量)。
我認為我最大的問題是while
循環遍歷樹以查找新節點的位置。
如果我使用while (flow!=NULL)
,它將不起作用,因為流指針獲得了對不存在對象的賦值。 請幫助我了解我錯了(作業)。
您的代碼有幾個重要的缺陷,其中最重要的一個就是對動態內存分配在C中的工作方式的誤解。 永遠不要遵循這樣的模式:
Type *pointer = malloc(sizeof(Type));
pointer = <<something else>>
從字面上看,它會泄漏內存,並且在短短兩行之內就沒有任何幫助 。 這不是像Java或C#這樣的基於對象引用的語言。 指針是保存內存地址的變量。 就像int
可以容納整數一樣,指針也可以容納address 。 就像下面的例子一樣:
int n = 6;
n = 5; //Hmm. Where did the 6 go? Oh yeah, We overwrote it with 5.
您將失去使用指針執行相同操作的分配鏈接:
struct node *root = malloc(sizeof(*root));
root = nodnou; // memory allocated above is gone. forever leaked.
指針是變量 。 就像任何其他變量一樣,它們保存值。 但是,在使用指針的情況下,其值為address 。 您可以在C語言中擁有幾乎所有內容的指針,包括指向指針的指針。 保存指針變量地址的變量。 我之所以提出這些建議,是因為它們為您的插入要求提供了特別優雅的解決方案。
以下是二進制樹插入的一般實現,該插入不支持樹中的重復項(如果允許重復項,則代碼會更短)。 此外,它使用提供的函數參數之外的零個局部變量來執行此操作,因此我挑戰您剖析並確定其工作方式。 它甚至可以在最初為NULL的樹根指針上工作,從而無需使用特殊的大小寫if (root) {} else {}
邏輯:
void insert_node(struct node **pp, int k)
{
while (*pp)
{
if (k < (*pp)->nr) // move down left side?
pp = &(*pp)->st;
else if ((*pp)->nr < k) // move down right side?
pp = &(*pp)->dr;
else return; // found the key, no dupes. leave
}
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
如果您的樹應支持重復項,則需要對其插入的哪一側保持一致,但會大大縮短上述算法:
void insert_node(struct node **pp, int k)
{
while (*pp)
pp = (k < (*pp)->nr) ? &(*pp)->st : &(*pp)->dr;
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
無論哪種情況,都可以在調用方調用,如下所示:
struct node *root = NULL;
insert(&root, 5);
insert(&root, 10);
insert(&root, 7);
...etc...
我認為您應該使用while(flow!= NULL)並在此之后將元素作為流插入。 現在的方式是在不應該停止的情況下停止運行,並在停止時進行奇怪的操作。 嘗試使用筆和紙研究一些示例。
差不多了 趕上!
首先,您需要了解更好的內存分配。 實際上,您只需要在函數中進行第一個malloc()
調用。 那就是您在每次insert_node()
調用期間為要附加到樹上的節點分配的內存。 您正在執行的所有剩余malloc
都是不必要的。 似乎您從直覺上感覺需要為正在使用的其他指針分配內存,但是它們全部都是臨時的,不需要任何分配,只需在嘗試取消引用它們之前就分配給有效節點。 實際上,這些不必要的分配會在如下代碼中創建所謂的內存泄漏(您請求的內存而無法釋放):
root = (struct node*)malloc(sizeof(node));
root = nodnou;
第二個Assignmet( root = nodnou
)覆蓋前一個malloc()
調用的結果,由於您沒有將覆蓋的指針值保存在其他任何地方,因此您將無法再釋放該內存,它將被標記為用於您的應用程序的生命周期!
接下來,您可以簡化遍歷樹尋找插入點的代碼。 您似乎擔心流變為NULL,但這無關緊要。 重要節點是父節點。 while循環結束后,它將指向需要鏈接插入的節點的實際節點。 這是您代碼的修改版本。
void insert_node(int k) {
struct node *nodnou, *flow, *parent;
// this is the only memory allocation that should be done
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
parent = NULL;
if( root == NULL ) {
root = nodnou;
} else {
flow = root;
// We will walk the tree in order until we reach the bottom of the
// tree (flow becomes null). What we are trying to do is to find out
// the node that will become the parent of the new node we are inserting
// It doesn't matter if flow becomes NULL, the important value after the
// while loop ends is parent
while( flow != NULL ) {
// update the current potential parent node
parent = flow;
if( k < flow->nr ) {
// the number we are inserting is lower than the flow node, walk to the left
flow = flow->st;
} else {
// the number we are inserting is greater or equal than the flow node,
// walk to the right
flow = flow->dr;
}
}
// We have reached the bottom, now compare number again with the node that
// will become parent, to find out if we need to link this node to the left
// or to the right of the parent node
if( k < parent->nr ) {
parent->st = nodnou;
} else {
parent->dr = nodnou;
}
}
}
而已。 嘗試對其余的樹操作進行編碼,不要猶豫,請問是否感到困惑。 =)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.