我正在解决一个问题,其中给定一个链接的字符列表,我们必须将元音移动到开头,以便元音和辅音都按时间顺序排列。那是它们在原始列表中出现的顺序。
Input : S->T->A->C->K->O->V->E->R->F->L->O->W
Output : A->O->E->O->S->T->C->K->V->R->F->L->W
我通过遍历列表一次并创建了两个称为元音和辅音的列表来完成它,然后将它们合并。
可以在不创建额外列表的情况下完成吗?我的意思是就地可能使用指针操作?
我正在解决一个问题,其中给定一个链接的字符列表,我们必须将元音移动到开头,以便元音和辅音都按时间顺序排列。那是它们在原始列表中出现的顺序。
Input : S->T->A->C->K->O->V->E->R->F->L->O->W
Output : A->O->E->O->S->T->C->K->V->R->F->L->W
我通过遍历列表一次并创建了两个称为元音和辅音的列表来完成它,然后将它们合并。
可以在不创建额外列表的情况下完成吗?我的意思是就地可能使用指针操作?
记住列表的开头。当你遇到一个元音时,把它移到列表的开头;元音成为你记忆中的新开始。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
struct list {
struct list *next;
int ch;
};
#define IS_VOWEL(p) strchr("aeiouy", tolower(p->ch))
struct list *shuffle ( struct list *lst )
{
struct list *new=NULL, **dst, **src;
dst = &new;
for (src = &lst; *src; ) {
struct list *this;
this= *src;
if (!IS_VOWEL(this)) { src= &(*src)->next; continue; }
*src = this->next;
this->next = *dst;
*dst = this;
dst = & (*dst)->next;
}
*dst = lst;
return new;
}
int main (void)
{
struct list arr[] = { {arr+1, 'S'} , {arr+2, 'T'} , {arr+3, 'A'}
, {arr+4, 'C'} , {arr+5, 'K'} , {arr+6, 'O'}
, {arr+7, 'V'} , {arr+8, 'E'} , {arr+9, 'R'}
, {arr+10, 'F'} , {arr+11, 'L'} , {arr+12, 'O'} , {NULL, 'W'} };
struct list *result;
result = shuffle (arr);
for ( ; result; result = result->next ) {
printf( "-> %c" , result->ch );
}
printf( "\n" );
return 0;
}
输出:
-> A-> O-> E-> O-> S-> T-> C-> K-> V-> R-> F-> L-> W
1. Traverse the list
2. When you encounter a vowel, check with head if its smaller or greater
3. If smaller, re-place new vowel before head, else move head and check again
4. In the end relocate head to first
temp = head;
while(current.next != null) {
if(current.isVowel()) {
if(head.isVowel()) {
//check the precedence
//Re-place the current with temp
}
else {
//Re-place current in front of head
}
}
current = current.next;
}
这是一种抽象的理解。正确实施。
您可以很容易地修改指针以创建两个独立的列表,而无需实际复制任何节点,这就是我假设您说要避免创建新列表时的意思。仅修改原始节点中的指针。
首先让我们为列表创建结构:
#include <stdio.h>
#include <stdlib.h>
// Structure for singly linked list.
typedef struct sNode {
char ch;
struct sNode *next;
} tNode;
接下来我们提供两个实用函数,第一个将字符附加到列表中:
// 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;
}
第二个是为了调试目的转储列表:
// 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');
}
现在这是重要的一点,它将单个列表变成两个不同的列表(一个元音,一个辅音)。哪个列表是哪个类型取决于列表中的第一个条目是什么。
基本上所做的是从列表开头的所有公共节点(在本例中为“ST”)创建一个子列表,下一个不匹配类型的另一个子列表(“A”),以及然后开始一一处理剩余的节点,从“C”开始。
在检查每个后续节点时,会调整指针以将其添加到第一个或第二个列表中(同样,没有实际创建新节点)。一旦我们到达列表末尾的 NULL,我们就决定是否将第二个列表附加到第一个列表,反之亦然(元音必须先出现)。
所有这些指针操作的代码如下所示:
// 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