2

これは今より重要なので、以前のCの質問を保留にする必要があります...

二分探索ツリーで挿入関数と削除関数を既にコーディングしましたが、削除関数は不完全です。助けが必要なことがいくつかあります...

1)私の挿入機能は良いですか、それとも何とか改善できますか?

2)私の削除機能には、左右の子を持つノードの削除がありません。過去数時間でたくさん検索しましたが、適切な方法が見つかりませんでした。

2.a) 2 つの子ノードを持つノードを削除するにはどうすればよいですか?

2.b)最初の質問と同様に、削除機能は適切ですか、それとも改善できますか? これらのifで多くのコードを繰り返しているので、これができることはわかっていますが、どうすれば改善できるかわかりません。それについても助けが必要です。

typedef struct sClientProfile *ClientProfile;
typedef struct sClientTree *ClientTree;

typedef struct sClientProfile {
    char *clientName;
    int clientAge;
    int clientNIF;
} nClientProfile;

typedef struct sClientTree {
    ClientProfile clientProfile;
    char *clientName;

    ClientTree leftTree;
    ClientTree rightTree;
} nClientTree;

void addClientToTree(ClientTree *cTree, ClientProfile cProfile) {
    if(!*cTree) {
        ClientTree new = (ClientTree)malloc(sizeof(nClientTree));

        if(!new) {
            perror("malloc");
        }

        new->clientName = strdup(cProfile->clientName);
        new->clientProfile = cProfile;

        new->leftTree = NULL;
        new->rightTree = NULL;

        *cTree = new;
    } else {
        if(strcmp((*cTree)->clientName, cProfile->clientName) > 0) {
            addClientToTree(&(*cTree)->leftTree, cProfile);
        } else {
            addClientToTree(&(*cTree)->rightTree, cProfile);
        }
    }
}

void deleteClientFromTree(ClientTree *cTree, char *cName) {
    if(!cTree) return;

    int nCompare = strcmp((*cTree)->clientName, cName);

    if(nCompare > 0) {
        deleteClientFromTree(&(*cTree)->leftTree, cName);
    } else if(nCompare < 0) {
        deleteClientFromTree(&(*cTree)->rightTree, cName);
    } else {
        if(!(*cTree)->leftTree && !(*cTree)->rightTree) {
            ClientTree cliPtr = *cTree;

            free(cliPtr->clientProfile);
            free(cliPtr);

            cliPtr->clientProfile = NULL;
            cliPtr = NULL;

            *cTree = NULL;
        } else if(!(*cTree)->leftTree) {
            ClientTree cliPtr = *cTree;

            free(cliPtr->clientProfile);
            free(cliPtr);

            cliPtr->clientProfile = NULL;

            *cTree = (*cTree)->rightTree;
        } else if(!(*cTree)->rightTree) {
            ClientTree cliPtr = *cTree;

            free(cliPtr->clientProfile);
            free(cliPtr);

            cliPtr->clientProfile = NULL;

            *cTree = (*cTree)->leftTree;
        } else {

            // MISSING DELETE CASE

        }
    }
}

お気づきかもしれませんが、2点だけ指摘させてください。

  • このツリーは、通常の int 表現の代わりに文字列を使用します。それが私が strcmp() をずっと使っている理由です。私はそれを正しく使っていると思います。
  • 私は再帰を使用していません。むしろ、(この場合は構造体ポインターの) ポインターを渡し、それを操作します。どういうわけかよりきれいに見えます。将来的には、ノードが削除された場合に成功値を返したいと考えています。

以下の更新:
私はすでに削除機能の反復バージョンを実行しましたが、それについていくつか気に入らないことがあります。おそらく改善できる(または改善できない)可能性がありますが、方法がわかりません。また、欠落しているケースをコーディングして、2 つの子を持つノードを削除しようとしましたが、正常に機能していません...

私は、コードを改善できると思う場所と問題の場所をコード全体にコメントしました。また、簡単に参照できるように、これらの問題を A、B (B はもうありません)、C、D と名付けました。

bool deleteClientFromTree(ClientTree *cTree, char *cName) {
    if(!cTree) return FALSE;

    ClientTree currPtr = *cTree;
    ClientTree prevPtr = NULL;
    int nCompare;

    while(currPtr) {
        nCompare = strcmp(currPtr->clientName, cName);

        if(nCompare > 0) {
            prevPtr = currPtr;
            currPtr = currPtr->leftTree;
        } else if(nCompare < 0) {
            prevPtr = currPtr;
            currPtr = currPtr->rightTree;
        } else {
            /*
             * A)
             * 
             * The following cases have 3 lines in common, the free()
             * calls and return statement. Is there anyway to improve
             * this code and make it more compact?
             * 
             * Of course, the printf's are to be removed...
             */
            if(!prevPtr && !currPtr->leftTree && !currPtr->rightTree) {
                printf("CASE #1\n");

                *cTree = NULL;

                free(currPtr->clientProfile);
                free(currPtr);

                return TRUE;
            } else if(!currPtr->leftTree || !currPtr->rightTree) {
                printf("CASE #2\n");

                if(prevPtr->leftTree == currPtr) {
                    prevPtr->leftTree = currPtr->rightTree;
                } else {
                    prevPtr->rightTree = currPtr->leftTree;
                }

                free(currPtr->clientProfile);
                free(currPtr);

                return TRUE;
            } else {
                printf("CASE #3\n");

                ClientTree tempPtr = currPtr->rightTree;

                while(tempPtr->leftTree) {
                    tempPtr = tempPtr->leftTree;
                }

                /*
                 * C)
                 * 
                 * This has a big problem...
                 * 
                 * If you take a look at the ClientProfile structure,
                 * in the first post, you'll see two ints
                 * (clientNIF/clientAge) and one char* (clientName).
                 * 
                 * The problem is that the following code line is only
                 * copying the integer data, not the string. For some
                 * reason, the string remains the old one.
                 * 
                 * I tried to use strdup() directly on clientName like:
                 * currPtr->clientProfile->clientName = strdup(tempPtr->clientProfile->clientName);
                 * but it still doesn't work.
                 * 
                 * Why everything is being copied but the strings?
                 */
                currPtr->clientProfile = tempPtr->clientProfile;

                /*
                 * D)
                 * 
                 * Is there anyway to not call the function itself
                 * and make the while loop once again and delete the
                 * corresponding leaf?
                 */
                return deleteClientFromTree(&currPtr->rightTree, tempPtr->clientProfile->clientName);
            }
        }
    }

    return FALSE;
}
4

5 に答える 5

5

ノードを削除するときは、その子について何かをする必要があります。

子供がいなくても問題ありません。ノードを削除するだけです。

置き去りの子供がいても問題ありません。ノードを削除し、その左の子をその場所に移動します。

右の子も同じです。削除されたノードの場所に子を移動するだけです。

問題は、左右両方の子を持つノードを削除する場合に発生します。左または右の子を削除したノードの場所に移動できますが、その場合、もう一方の子とそのサブツリーはどうすればよいでしょうか?

解決策はこれです; 削除されるノードの論理後続ノードを見つけます。論理的後継者とは、これを意味します。整数で構成されたツリーがあり、値が 35 のノードを削除すると、論理的な後継者は次に大きい数になります。Y A?インオーダー ウォークを行っている場合は、削除する要素の後に到達する要素になります。

さて、論理的な後継者を見つける簡単なルールがあります。1 つ右に移動し (これは 2 人の子供がいる場合であるため、常に右があります)、次にできるだけ左に移動します。

最終的にたどり着く要素は、論理的な後継者です。これは削除された要素よりも大きくなっていますが (最初に行ったことを覚えていますか?)、次に大きい要素の中で最も小さいものです。

さて、その要素には常に子が 1 つしかないか、またはまったくありません。できる限り左に移動したためです。これ以上左に行くことはできません - 左がないため - その要素には子がないか、右の子だけがあり、リンクを解除しやすいカテゴリの 1 つに分類されます (子がないか、子が 1 つだけ)。 . したがって、この要素のリンクを解除するのは簡単です。

クールなビットが来ます-これを考慮してください。次に大きい要素が、削除したい要素とツリー内の同じ場所にある場合でも、ツリーは有効で正しいものになります。各要素の左側はすべて小さく、右側はすべて大きいためです。

あなたがすることはこれです。次に大きいノードのユーザー データを削除対象のノードにコピーし、次に大きいノードを削除します(子がないか、適切な子しかないため、リンクを解除して削除するのは簡単です)。

以上です!

したがって、基本的には、論理的な後継者を見つけて、ツリーからリンクを解除し、実際に最初に削除する要素にそのユーザー データを配置します (もちろん、物理的にツリーの一部であるため、削除しません)。

于 2009-03-24T20:42:52.890 に答える
1

このバイナリ コードは、挿入、削除、検索、終了です。例:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Binary Tree {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        LinkedList ll = new LinkedList();

        ll.add("\n"+"mai    0020");
        ll.add("\n"+"king   0019");
        ll.add("\n"+"maan   0002");
        ll.add("\n"+"dimple 0024");
        ll.add("\n"+"eman   0004");
        ll.add("\n"+"lara   0005");
        ll.add("\n"+"cute   0008");
        ll.add("\n"+"irene  0011");
        ll.add("\n"+"sheena 0030");
        ll.add("\n"+"aisy   0003");

        System.out.println("display: " + ll);
        System.out.println("\n\n");

        for(int c=0; c<=10; c++) {
            System.out.println("select from: 1-insert, 2-delete," + 
                               " 3-display, 4-search, 5-quit");

            String x = br.readLine();
            int y = Integer.parseInt(x);
            switch (y) {
                case 1: //inserting
                    System.out.println("input name");
                    String n= br.readLine();
                    System.out.println("input a list number");
                    String o = br.readLine();
                    int z = Integer.parseInt(o);
                    ll.add("\n"+n+" "+z);
                    break;
                case 2: // delete   
                    ll.removeFirst();
                    break;
                case 3: //Display
                    System.out.println("\n\n"+"List of employee: " + ll);       
                    System.out.println("\n\n");
                    break;
                case 4: //search
                    System.out.println("\n");
                    System.out.println("Search");

                    System.out.println("first element of the Linkedlist is: "
                                       + ll.getFirst());
                    System.out.println("\n\n");
                    System.out.println("last element of linkedlist:"
                                       + ll.getLast());     
                    break;
                case 5: //quit
                    System.out.println("\n\n\n\n\n" 
                                       + " Thank You Very Much!!!!!!!\n\n\n");
                    System.exit(0);
                    break;
            }
        }
    }
}
于 2011-02-11T00:59:30.410 に答える
1

BST でノードを削除するには、次の 3 つのケースが理想的です。

ケース 1:

X has no children: remove X

ケース 2:

X has one children : Splice out X

ケース 3:

X has two children : swap X with its successor and follow case #1 or #2

したがって、欠落している削除の場合:

X (削除するノード) に 2 つの子がある場合、X を X の後継に置き換え、ケース #1 またはケース #2 に従います。前のものと交換することもできます。良い代替手段になるかもしれません。

if ( X->left && X->right) {

NODE *Successor = FindSuccessor(X);
X->data = Successor->data;
free(Successor);

}

于 2009-03-24T08:01:01.317 に答える
0
int delete_value(Tree*&root,Tree*&find,Tree*&ptr,int numb){
    if(find->value==number){
//the number exist in the root,so we should find the highest value inside the right brache and replace it with the root.
    Tree*ptr2=NULL;
    Tree*ptr3=NULL;//pointer pointing to the parent of the last element of ptr2.
    ptr2=root->right;
    while(ptr2!=NULL){
        ptr3=ptr2;
        ptr2=ptr2->left;
    }
    if(ptr2->right!=NULL){
        ptr3->left=ptr2->right;
    }
    swap(ptr2->value,root->value);
    delete ptr2;
    ptr2=ptr3=NULL;
    }

    else{
        while(find->value!=numb){
            if(find->value!=numb){
                ptr=find;
            }
            if(find->value < numb){
                find=find->right;
                return delete_value(root,find,ptr,numb);
            }
            else{
                find=find->left;
                return delete_value(root,find,ptr,numb);
            }
        }//end of while
    }//end of else
//the pointer find is pointing at the element we want to delete.
//the pointer ptr  is pointing at the element before the one's we want to delete.
//case 1:the element to delete don't have any children
    if(find->right==NULL && find->left==NULL){
        if(ptr->left=find){
            ptr->left=NULl;
            delete find;
        }
        else{
            ptr->right=NULL;
            delete find;
        }
    }//end of the first case.
//case 2:the element has one child it could be to the left or to the right.
    //a-child to the right.
    if( find->right!=NULL && find->left==NULL ){
        Tree*ptr2=find->right;
        while(ptr2!=NULL){
            ptr2=ptr2->left;//find the highest value in the right branche and replace it with the delete element
        }
        swap(find->value,ptr2->value);
        delete ptr2;
        ptr2=NULL;
    }
    //b-child to the left.
    if(find->right==NULL && find->left!=NULL){
        Tree*ptr2=find->left;
        //check wether the find element is to the right or to the left of ptr.
        if(ptr->left==find){
            ptr->left=ptr2;
            delete find;
        }
        else{
            ptr->right=ptr2;
            delete find;
        }
    }//end of the second case.

//case3: the element has to children.
    if(find->right!=NULL&&find->left!=NULL){
        Tree*ptr2=find->left;
        while(ptr2->right!=NULL){
            ptr2=ptr2->right;
        }
        swap(ptr2->value,find->value);
        delete ptr2;
        ptr2=NULL;
    }//end of case 3.
}//end of the function.
于 2010-10-17T07:07:57.097 に答える