ポインタを非常に簡単に変更して、ノードを実際に複製することなく2つの独立したリストを作成できます。これは、新しいリストの作成を避けたいと言うときの意味です。元のノードのポインタのみが変更されます。
まず、リストの構造を作成しましょう。
#include <stdio.h>
#include <stdlib.h>
// Structure for singly linked list.
typedef struct sNode {
char ch;
struct sNode *next;
} tNode;
次に、2つのユーティリティ関数を提供します。最初の関数はリストに文字を追加します。
// Append to list, not very efficient but debug code anyway.
static tNode *append (tNode *head, char ch) {
// Allocate new node and populate it.
tNode *next = malloc (sizeof (tNode));
if (next == NULL) {
puts ("Out of memory");
exit (1);
}
next->ch = ch;
next->next = NULL;
// First in list, just return it.
if (head == NULL)
return next;
// Else get last, adjust pointer and return head.
tNode *this = head;
while (this->next != NULL)
this = this->next;
this->next = next;
return head;
}
そして、デバッグ目的でリストをダンプする2番目:
// Debug code to dump a list.
static void dump (tNode *this) {
if (this == NULL)
return;
printf ("(%08x)%c", this, this->ch);
while ((this = this->next) != NULL)
printf (" -> (%08x)%c", this, this->ch);
putchar ('\n');
}
それを超えて、ノードが母音であるかどうかを判断する簡単な方法が必要です。ここでは、大文字のみを使用します。
// Check for vowel (uppercase only here).
static int isVowel (tNode *this) {
char ch = this->ch;
return (ch == 'A') || (ch == 'E') || (ch == 'I')
|| (ch == 'O') || (ch == 'U');
}
これが重要なビットであり、単一のリストを2つの別個のリスト(1つは母音、もう1つは子音)に変換するビットです。どのリストがどのタイプであるかは、リストの最初のエントリが何であるかによって異なります。
基本的には、リストの先頭にあるすべての共通ノード(この場合は「ST」)からサブリストを作成し、次の一致しないタイプ(「A」)の別のサブリストを作成し、次に、「C」から始めて、残りのノードを1つずつ処理し始めます。
後続の各ノードが調べられると、ポインターが調整されて、最初のリストまたは2番目のリストに追加されます(ここでも、実際に新しいノードを作成することはありません)。リストの最後でNULLに達したら、2番目のリストを最初のリストに追加するか、またはその逆を行うかを決定します(母音が最初に来る必要があります)。
このすべてのポインター操作のコードを以下に示します。
// Meat of the solution, reorganise the list.
static tNode *regroup (tNode *this) {
// No reorg on empty list.
if (this == NULL)
return this;
// Find first/last of type 1 (matches head), first of type 2.
tNode *firstTyp1 = this, *firstTyp2 = this, *lastTyp1 = this, *lastTyp2;
while ((firstTyp2 != NULL) && (isVowel (firstTyp1) == isVowel (firstTyp2 ))) {
lastTyp1 = firstTyp2;
firstTyp2 = firstTyp2->next;
}
// No type 2 means only one type, return list as is.
if (firstTyp2 == NULL)
return firstTyp1;
// Type 2 list has one entry, next node after that is for checking.
lastTyp2 = firstTyp2;
this = firstTyp2->next;
//dump (firstTyp1);
//dump (firstTyp2);
//putchar ('\n');
// Process nodes until list is exhausted.
while (this != NULL) {
// Adjust pointers to add to correct list.
if (isVowel (this) == isVowel (lastTyp1)) {
lastTyp2->next = this->next;
lastTyp1->next = this;
lastTyp1 = this;
} else {
lastTyp1->next = this->next;
lastTyp2->next = this;
lastTyp2 = this;
}
// Advance to next node.
this = this->next;
//dump (firstTyp1);
//dump (firstTyp2);
//putchar ('\n');
}
// Attach last of one list to first of the other,
// depending on which is the vowel list.
if (isVowel (firstTyp1)) {
lastTyp1->next = firstTyp2;
return firstTyp1;
}
lastTyp2->next = firstTyp1;
return firstTyp2;
}
そして最後に、何らかの説明のテストハーネスがなければ、複雑なプログラムは完成しません。したがって、ここでは、リストを初期形式で作成してダンプし、再編成して結果をダンプします。
int main (void) {
char *str = "STACKOVERFLOW";
tNode *list = NULL;
while (*str != '\0')
list = append (list, *(str++));
dump (list);
puts("");
list = regroup (list);
dump (list);
return 0;
}
すべてのコードを入力、コンパイル、および実行すると、期待どおりの結果が得られます。
(09c03008)S -> (09c03018)T -> (09c03028)A -> (09c03038)C ->
(09c03048)K -> (09c03058)O -> (09c03068)V -> (09c03078)E ->
(09c03088)R -> (09c03098)F -> (09c030a8)L -> (09c030b8)O ->
(09c030c8)W
(09c03028)A -> (09c03058)O -> (09c03078)E -> (09c030b8)O ->
(09c03008)S -> (09c03018)T -> (09c03038)C -> (09c03048)K ->
(09c03068)V -> (09c03088)R -> (09c03098)F -> (09c030a8)L ->
(09c030c8)W
読みにくい場合は、ポインタを削除して、文字を順番にリストします。
S -> T -> A -> C -> K -> O -> V -> E -> R -> F -> L -> O -> W
A -> O -> E -> O -> S -> T -> C -> K -> V -> R -> F -> L -> W