13

私はアルゴリズムとデータ構造を学んでおり、トレーニングのために、objective-c を使用してバイナリ ツリーを設計および実装しようとしています。

これまでのところ、次のクラスがあります。

  • main- テスト用
  • Node- ツリーのノード
  • BinaryTree- ツリーに関連するすべてのメソッド

BinaryTree私が実装したクラスの最初のメソッドの 1 つはinsertNode:forRoot:.

- (void)insertNodeByRef:(Node **)node forRoot:(Node **)root{

    if (head == NULL) {
        head = *node;
    }
    // Case 2 root is null so can assign the value of the node to it
    if (root == NULL) {
        root = node;
    } else {
        if (node.data > root.data) { // to the right
            [self insertNode:node forRoot:root.right];
        } else if (node.data < root.data) { //or to the left
            [self insertNode:node forRoot:root.left];
        }
    }
}

Nodeクラスのインターフェースは次のようになります。

@interface Node : NSObject

@property(nonatomic, assign) int data;
@property(nonatomic, strong) Node * right;
@property(nonatomic, strong) Node * left;

@end

私の問題は、Node を参照として渡す場合、Node クラスのメンバー変数にアクセスする方法がわからないことです。ノード プロパティ (左または右のデータなど) にアクセスしようとすると、次のエラー メッセージが表示されます。

Member reference base type 'Node *__autoreleasing *' is not a structure or union

私の質問は次のとおりです。これらのプロパティ (データ、左または右) にアクセスし、それらを使用して int データまたは別のノードへの参照を保存するにはどうすればよいですか?

それが理にかなっていることを願っています。ありがとう!

4

4 に答える 4

28

あなたのコードは、タスクに対する 2 つの一般的なアプローチを混在させているため、問題が発生しています。オブジェクト指向ではなく、抽象データ型(ADT) 型のアプローチも使用しているため、考慮すべき 3 つのアプローチがあります。

どちらの ADT アプローチでも、ツリーはそのルートへの参照によって表されます。Objective-C では、これはおそらくインスタンス変数に格納されます。

Node *TreeRoot;

また、これらのアルゴリズムはa->bどちらも、プロパティ参照ではなくフィールド参照を使用することに注意してください。a.bこれは、前者が変数を参照し、2 番目のアルゴリズムが変数への参照を渡す必要があるためです。

Functional ADT: 値渡しと代入結果

このアプローチでは、ノードがツリーに挿入され、変更されたツリーが返されて割り当てられます。たとえば、挿入するトップレベルの呼び出しは次のNode nodeToInsertようになります。

TreeRoot = insertNode(nodeToInsert, TreeRoot);

insertNode関数は次のようになります。

Node *insertNode(Node *node, Node *root)
{
   if(root == nil)
   {  // empty tree - return the insert node
      return node;
   }
   else
   {  // non-empty tree, insert into left or right subtree
      if(node->data > root->data) // to the right
      {
        root->right = insertNode(node, root->right);
      }
      else if(node->data < root->data)//or to the left
      {
        root->left = insertNode(node, root->left);
      }
      // tree modified if needed, return the root
      return root;
   }
}

このアプローチでは、空でない (サブ) ツリーの場合、アルゴリズムは変数への冗長な割り当てを実行することに注意してください。割り当てられた値は、変数に既に含まれているものです...このため、一部の人々は好みます:

手続き型 ADT: 参照渡し

このアプローチでは、(サブ) ツリーのルートを保持する変数は、その値が渡されるのではなく、参照によって渡され、必要に応じて呼び出されたプロシージャによって変更されます。たとえば、最上位の呼び出しは次のようになります。

insertNode(nodeToInsert, &TreeRoot); // & -> pass the variable, not its value

手順は次のようになりinsertNodeます。

void insertNode(Node *node, Node **root)
{
   if(*root == nil)
   {  // empty tree - insert node
      *root = node;
   }
   else
   {  // non-empty tree, insert into left or right subtree
      Node *rootNode = *root;
      if(node->data > rootNode->data) // to the right
      {
        insertNode(node, &rootNode->right);
      }
      else if(node->data < rootNode->data)//or to the left
      {
        insertNode(node, &root->left);
      }
   }
}

あなたの方法が上記の 2 つのアプローチを組み合わせたものであることがわかります。どちらも有効ですが、Objective-C を使用しているため、3 番目のアプローチを採用することをお勧めします。

オブジェクト指向ADT

これは、手続き型 ADT のバリエーションです。変数を手続きに渡すのではなく、現在objectと呼ばれている変数が、それ自体を更新するメソッドを所有しています。このようにすることは、ノードを挿入する呼び出しを行う前に空の (サブ) ツリーをテストする必要があることを意味しますが、前の 2 つのアプローチは呼び出しでテストします。これで、メソッドがNode次のようになりました。

- (void) insert:(Node *)node
{
   if(node.data > self.data) // using properties, could also use fields ->
   {
      if(self.right != nil)
         [self.right insert:node];
      else
         self.right = node;
   }
   else if(node.data < rootNode.data)
   {
      if(self.left != nil)
         [self.left insert:node];
      else
         self.left = node;
   }
}

空のツリーに対して同じテストを行うには、最上位の呼び出しも変更する必要があります。

if(TreeRoot != nil)
   [TreeRoot insert:nodeToInsert];
else
   TreeRoot = nodeToInsert;

最後に、メモリ管理に ARC や GC ではなく MRC を使用している場合は、適切な保持/解放呼び出しを挿入する必要があります。

物事を整理するのに役立つことを願っています。

于 2012-08-22T05:19:34.937 に答える
27

まず第一に、メソッドを take に書かないでくださいNode **。混乱するだけです。

第二に、それがどのように機能するかを考えてください。かなり抽象的なレベルで、それがどのように機能するかを自分自身に説明してください。その説明をコードに直接変換し、必要に応じて新しい (まだ作成されていない!) メッセージを作成します。まだ方法がわからない手順がある場合は、後で書き込む新しいメッセージにそれらをパントします。順を追って説明します。

のパブリック API にBinaryTree次のメッセージを含めたいとします。

@interface BinaryTree

- (void)insertValue:(int)value;

では、どのように実装しinsertValue:ますか? あなたがBinaryTreeオブジェクトであるふりをします。値を挿入するために必要なことの概要を教えてください。新しい を作成しますNode。次に、その新しいものNodeを自分自身に挿入します。その説明をコードに直接変換します。

@implementation BinaryTree {
    Node *root_; // root node, or nil for an empty tree
}

- (void)insertValue:(int)value {
    Node *node = [[Node alloc] initWithData:value];
    [self insertNode:node];
}

次に、挿入をどのように行うかを考えてみましょう。あなたが空のツリーであれば、あなたroot_は nil であり、それを新しいノードに設定することができます。それ以外の場合は、ルート ノードに新しいノードを自分の下に挿入するように依頼できます。その説明をコードに直接変換します。

- (void)insertNode:(Node *)node {
    if (root_ == nil) {
        root_ = node;
    } else {
        [root_ insertNode:node];
    }
}

今、あなたがのふりをしますNodeNode自分の下に新しい を挿入するように求められました。どのようにしますか?新しいノードの値を自分の値と比較する必要があります。新しいノードの値が自分の値よりも小さい場合は、新しいノードを左側に挿入します。それ以外の場合は、右側に挿入します。その説明をコードに直接変換します。

@implementation Node

- (void)insertNode:(Node *)node {
    if (node.data < self.data) {
        [self insertNodeOnLeftSide:node];
    } else {
        [self insertNodeOnRightSide:node];
    }
}

今でもあなたはNodeで、左側に新しいノードを挿入するように求められています。どのようにしますか?左側にまだ子がない場合は、新しいノードを左側の子として使用してください。それ以外の場合は、左の子供に新しいノードを自分の下に挿入するように依頼します。その説明をコードに直接変換します。

- (void)insertNodeOnLeftSide:(Node *)node {
    if (self.left == nil) {
        self.left = node;
    } else {
        [self.left insertNode:node];
    }
}

の実装はinsertNodeOnRightSide:、読者の演習として残しておきます。;^)

于 2012-08-22T04:55:39.163 に答える
0

(Node **)nodeはオブジェクトポインタへのポインタであるため、オブジェクトnode.somethingから遠く離れた場所への参照であるため無効です。

しかし、(*node).something動作します。


コメントの追加:
最初にこのメソッドを呼び出したとき:-(void)insertNodeByRef:(Node **)node forRoot:(Node **)root どのように呼び出しますか?
あなたがあなたのコメントに投稿したエラーから、あなたがしているように私には見えます:
Node *n = [[Node alloc] init];
[aNode insertNodeByRef:n forRoot:aRoot];

メソッドのシグネチャに、次のように呼び出す必要があると記載されている場合:
[aNode insertNodeByRef:&n forRoot:&aRoot];
ポインタのアドレスをオブジェクトに渡します。

これは、2つの異なるものではNode *なく、送信しているというエラーが表示されているためです。Node **((Incompatible pointer types sending 'Node *' to parameter of type 'Node **')2 *の間の__autoreleasingを削除しました。これにより、エラーメッセージが不明瞭になりました。)つまり、メソッドが。を要求しているときに、
を渡しています。 pointer to an objectpointer TO A pointer to an object

于 2012-08-22T03:53:47.980 に答える
0

私の意見では、あなたのコードには多くの論理エラーがあります。目的の効果を設計していることを確認するために、ポインター ツー ポインターとは何かを確認することを検討してください。同様に、通常の状態でアクセスするには、ノード/ルートを逆参照する必要があります。それ以外の場合、エラーは有効です。Node** は構造体または共用体の型ではありません。

于 2012-08-22T04:01:35.943 に答える