なぜ無限ループ?
無限ループは、関数を呼び出した後のリスト内の自己ループが原因です。swap()次swap()のコードでは、ステートメントにバグがあります。
(*b)->next = (temp1)->next;
なんで?swap(): functionの nextの代入文の後にnodetemp1を指し始めるためです。bAndnode[b]の次のポイントはループ内にあります。そして、自己ループは無限ループの理由であり、リンクリストをトラバースするコードのどこかにあります。
swap()以下に、段階的にどのように機能するかを示すために描きました。エラーを理解するのにこれが役立つかもしれません:
aあなたは言及しませんでしたが、私はリンクされたリストが と の間に次の関係を持っていると仮定していますb:(赤いコメントを読んでください)
(ステップ1):
+----+----+----+ +---+----+----+
| one |----->| two |
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| *a | *b
| |
temp1 temp2, temp3 "after assignment to temp variables"
(step-2): ^
|
*a = *b | *a "<--- next step"
(step-3): バグのあるステートメント
(*b)->next = (temp1)->next; "Change link: (temp1)->next; is `two` node"
" *b is `two`, So Self loop"
+----+----+----+ +---+----+----+ <---|
| one | | two |-----|
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp1 temp2, temp3 " after assignment to temp"
(temp1)->next;実際に参照してください。実行しbて割り当て(*b)->next = (*b)ている(*b)->next = (temp1)->next;ため、自己ループを追加しています。
(ステップ 4):図を使用すると、コード
の最後の 2 行が何を行っているかを簡単に理解できると思います。swap()
temp2 = temp1;
(temp2)->next = temp3->next;
以下は、この 2 行の図です。
temp2 = temp1;
+----+----+----+ +---+----+----+ <---|
| one | | two |-----| "<--- Self loop"
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp2 = temp1; temp3
(step-5): 関数の最後の行でさえswap() 、以下のようにループを残しました:
(temp2)->next = temp3->next; " last line of your code"
+----+----+----+ +---+----+----+ <---|
| one |----->| two |-----| "<-- Self loop"
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp2 = temp1; temp3
したがって、ループはまだtwoノードにあるため、無限ループです。
単一のリンクされたリストで 2 つのノードを交換するには?
1つの方法は、ノードの位置をリンクリスト内でスワップするのではなく、ノードのデータをスワップすることです(質問にコメントしたように)。しかし、リスト内のノードの位置を入れ替えたいとします。
まあこれでいい!ノードのデータ サイズが大きい場合は、ノードのデータをスワップするよりもノードの位置をスワップする方が適切です (データのスワップは悪い選択です) 。
単一のリンクされたリストがあるため、リスト内の任意の 2 つのノードを交換するには、以前のノード アドレスも必要です。(これは、スワッピングロジックで考慮しないポイントです)
なぜ以前のポインターが必要なのですか? :
いくつかの挿入 (プッシュ) 操作が成功した後、リストが次のようになったとします。
0 <--------TOP - "head"
9 <--p
2
6 <--q
5
水平ダイアグラム - たとえば、 2 つのノード とを交換するとします。(q)(p)
+---+ +---+ +---+ +---+ +---+
| 0 |--->| 9 |--->| 2 |--->| 6 |--->| 5 |---
+---+ +---+ +---+ +---+ +---+ |
^ ^ ^ null
| | |
| (q) (p)
(head)
先ほど言ったように、スワップするには以前のポインターが必要です。次のことを考える必要があります(
理論的には、説明を簡単にするために特定のノードについて書いています。しかし、私の実装は非常に一般的です(p)(q))。
リストの前のポインター:
node[0] points to node[9] that is (q), and
node[2] points to node[6] that is (p)
と
node[9] points to node[2]
node[6] points to node[5]
注意: 2 つのノードを交換したい場合は、これら 2 つのノードの前のノードのポインターを使用する必要がありますnode[ 9 ] 。
例: 2 つの swapとの場合、上の図の next pointer ofと next pointer of も変更する必要があります。node[ 6 ]
node[ 9 ][ 6 ]node[ 0 ]node[ 2 ]
この 2 つのノードを交換した後のリストはどうなりますか?
+---+ +---+ +---+ +---+ +---+
| 0 |--->| 6 |--->| 2 |--->| 9 |--->| 5 |---
+---+ +---+ +---+ +---+ +---+ |
^ ^ ^ null
| | |
| (p) (q)
(head)
以前のノード[o]とには何があり[2]ますか?
スワップ後、以前のポインタのリストに
node[0] points to node[6] that is (q), and
node[2] points to node[9] that is (p)
と
node[9] points to node[5]
node[6] points to node[2]
したがって、2 つのノードを交換する場合は、次のようになります。直前のノードも影響し、リストは単一のリンクリストであるため、以前のポインターも必要です。
前のノード ポインタを見つける方法は?
任意の 2 つのノードを交換し、前のノードを見つけるために 使用できるnode[p]とします。node[q]head pointer
したがって、スワップ関数の構文(私の実装では)は次のようになります。
void swap(struct stack **head, // head node
struct stack **a, // first candidate node to swap
struct stack **b); // first candidate node to swap
そして、次のような関数を呼び出します:
swap(&head, &p, &q);
定義: (コードを理解するには、ほぼ各行に追加したコメントをお読みください)
void swap(struct stack **head,
struct stack **a,
struct stack **b){
// first check if a agrgument is null
if( (*head) == NULL || // Empty list
(*a) == NULL || (*b) == NULL){ // one node is null
// Nothing to swap, just return
printf("\n Nothing to swap, just return \n");
return;
}
// find previos nodes
struct stack* pre_a = get_prevnd(*head, *a);
struct stack* pre_b = get_prevnd(*head, *b);
//Now swap previous node's next
if(pre_a) pre_a->next = (*b); // a's previous become b's previous, and
if(pre_b) pre_b->next = (*a); // b's previous become a's previous
//Now swap next fiels of candidate nodes
struct stack* temp = NULL;
temp = (*a)->next;
(*a)->next = (*b)->next;
(*b)->next = temp;
//change head: if any node was a head
if((*head)==(*a))
*head = *b;
else
if((*head)==(*b))
*head = *a;
}
functionではswap()、ヘルパー関数を呼び出していることがわかりますget_prevnd(, );。この関数は、リスト内の前のノードのアドレスを返します。functionget_prevnd(, );では、最初の引数はリスト ヘッドで、2 番目の引数は探しているノードです。
// find previous node function()
struct stack* get_prevnd(
struct stack* head,
struct stack* a
){
if(head == a){
// node[a] is first node
return NULL;
}
struct stack* temp = head; // temp is current node
struct stack* pre_a = NULL;
while(temp && temp!=a){ //search while not reach to end or the node
pre_a = temp; // find previous node
temp = temp->next;
}
if(temp!=a){// node[a] not present in list
fprintf(stderr, "\n error: node not found!\n");
exit(EXIT_FAILURE); // bad technique to exit()
}
return pre_a;
}
幸いなことに、コードは機能しています:)。以下は、このコードのオンライン テストへのリンクです。さまざまな種類の入力をテストしました。
CodePad:単一のリンクされたリストでノードを交換します。出力を確認してください。
そして下手な英語でごめんなさい