Набор символов в формате списка разбить в список чисел и список слов - C (СИ)
Формулировка задачи:
Всем добрый день!
Мне дали задачу написать программу, в которую вводится набор символов в формате списка. после обработки этого списка должно получиться 2 списка: список чисел и список слов (напр. вводится: qwert123 43yt, на выходе должны получиться: список чисел 123 43, список слов: qwert yt). Я смог реализовать эту программу, но для решения этой задачи я использовал выделение mallocом памяти под новые элементы списка и просто копировал туда элементы. Препод такое решение забраковал. Сказал, что необходимо модифицировать исходный список, т.е. берется элемент из списка и переносится в другой список и никаких дополнительных выделений памяти...
Подскажите, как сделать правильно работающую программу?
Прикладываю свой вариант решения:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <malloc.h> #include <string.h> #include <stdbool.h> typedef struct Item { char c; struct Item *next; } Item; /* Ввод строки - списка */ int getList(Item **); /* Вывод строки - списка */ void putList(const char *, Item *); /* Освобождение памяти */ Item *deleteList(Item *); /* Формирование новой строки - списка */ Item *newStringWord(Item *); Item *newStringNum(Item *); /* удаление пробелов из строки - списка */ Item *delSpace(Item *); /* пропуск слова в строке - списке */ Item *skipWord(Item *); /* удаление слова из строки - списка */ Item *delWord(Item *); bool isalpha(char); bool isdigit(char); /* Функция для ввода строки - списка. ** Функция возвращает указатель на список, в котором размещена введенная строка. ** По условиям задачи, может быть введена пустая строка, тогда ** функция возвращает значение NULL - пустой список. ** Функция должна отслеживать состояние "Конец файла", и это состояние нельзя ** связать со значением указателя NULL - пустая строка. ** Поэтому предлагаемая функция с прототипом int getList(Item **) возвращает ** результат 1, если строка введена, или 0, если обнаружен конец файла, ** а саму строку возвращает через параметр функции. */ int getList(Item **pptr) { char buf[21], *str; Item head = { '*', NULL }; Item *last = &head; int n, rc = 1; do { n = scanf("%20[^\n]", buf); if (n < 0) { deleteList(head.next); head.next = NULL; rc = 0; continue; } if (n > 0) { for (str = buf; *str != '\0'; ++str) { last->next = (Item *)malloc(sizeof(Item)); last = last->next; last->c = *str; } last->next = NULL; } else { scanf("%*c"); } } while (n > 0); *pptr = head.next; return rc; } /* Вывод строки - списка */ void putList(const char *msg, Item *ptr) { printf("%s: "", msg); for (; ptr != NULL; ptr = ptr->next) { printf("%c", ptr->c); } printf(""\n"); } /* Освобождение памяти */ Item *deleteList(Item *ptr) { Item *tmp = NULL; while (ptr != NULL) { tmp = ptr; ptr = ptr->next; free(tmp); } return ptr; } bool isalpha(char c) { if ((c >= 'a') && (c <= 'z') || (c >= 'A') && (c <= 'Z')) return true; return false; } bool isdigit(char c) { if ((c >= '0') && (c <= '9')) return true; return false; } int main() { Item *ptr = NULL; Item *pword = NULL; Item *pnum = NULL; while (puts("enter string"), getList(&ptr)) { putList("Entered string", ptr); pword = newStringWord(ptr); putList("Result string 1", pword); pnum = newStringNum(ptr); putList("Result string 2", pnum); ptr = deleteList(ptr); pword = deleteList(pword); pnum = deleteList(pnum); } return 0; } /* Формирование новой строки - списка */ Item *newStringWord(Item *p) { char bChar; char eChar; Item head = { '*', NULL }; Item head2 = { '*', p }; Item *cur = &head2; Item *last = &head, *prev = &head; /* prev - для корректной обработки конца списка */ int fl = 0; /* Используется список с головным элементом; поэтому cur указывает ** на элемент списка, предшествующий тому, который будет анализироваться */ while (cur->next) { /* здесь надо анализировать и cur, так как в результате пропуска ** слова cur указывает на элемент списка, следующий за последним символом ** слова, а его может и не быть - последний символ слова есть последний ** символ списка */ cur = cur->next; if (isalpha(cur->c)) { last->next = (Item *)malloc(sizeof(Item)); last = last->next; last->c = cur->c; last->next = NULL; prev = last; fl = 1; } else if (fl) { last->next = (Item *)malloc(sizeof(Item)); last = last->next; last->c = ' '; last->next = NULL; fl = 0; } } /* возможно, в конце строки - списка остался лишний пробел */ if (prev->next) { free(prev->next); prev->next = NULL; } return head.next; } Item *newStringNum(Item *p) { char bChar; char eChar; Item head = { '*', NULL }; Item head2 = { '*', p }; Item *cur = &head2; Item *last = &head, *prev = &head; /* prev - для корректной обработки конца списка */ int fl = 0; /* Используется список с головным элементом; поэтому cur указывает ** на элемент списка, предшествующий тому, который будет анализироваться */ while (cur->next) { /* здесь надо анализировать и cur, так как в результате пропуска ** слова cur указывает на элемент списка, следующий за последним символом ** слова, а его может и не быть - последний символ слова есть последний ** символ списка */ cur = cur->next; if (isdigit(cur->c)) { last->next = (Item *)malloc(sizeof(Item)); last = last->next; last->c = cur->c; last->next = NULL; prev = last; fl = 1; } else if (fl) { last->next = (Item *)malloc(sizeof(Item)); last = last->next; last->c = ' '; last->next = NULL; fl = 0; } } /* возможно, в конце строки - списка остался лишний пробел */ if (prev->next) { free(prev->next); prev->next = NULL; } return head.next; } /* удаление пробелов из строки - списка ** результат - указатель на элемент списка, содержащего ** непробельный символ, или NULL */ Item *delSpace(Item *p) { Item *tmp; while (p && (p->c == ' ' || p->c == '\t')) { tmp = p; p = p->next; free(tmp); } return p; } /* пропуск слова в строке - списке ** результат - указатель в исходном списке на ** послений символ слова */ Item *skipWord(Item *p) { while (p->next && p->next->c != ' ' && p->next->c != '\t') { p = p->next; } return p; } /* удаление слова из строки - списка ** результат - указатель на элемент списка, содержащего ** пробельный символ, или NULL */ Item *delWord(Item *p) { Item *tmp; while (p && p->c != ' ' && p->c != '\t') { tmp = p; p = p->next; free(tmp); } return p; }
Решение задачи: «Набор символов в формате списка разбить в список чисел и список слов»
textual
Листинг программы
Item *item = list; // исходный список Item *words=NULL; // начало списка, содержащего буквы Item *wordsEnd=NULL; // конец списка, содержащего буквы Item *numbers=NULL, *numbersEnd=NULL; while (item) { Item *next = item->next; if (isdigit(item->c)) { // нужно ещё отдельно учесть случай wordsEnd == NULL wordsEnd->next = item; wordsEnd = item; } else if (isalpha(item->c)) { numbersEnd->next = item; numbersEnt = item; } item = next; } list = NULL;
Объяснение кода листинга программы
Предполагая, что входной список представлен в виде последовательности чисел и слов, код выполняет следующие действия:
- Создает три указателя:
item
указывает на текущий элемент входного списка,words
указывает на начало списка слов (букв),wordsEnd
указывает на конец списка слов. Указательnumbers
указывает на начало списка чисел, а указательnumbersEnd
указывает на конец списка чисел. - В цикле
while
перебирает элементы входного списка. Для каждого элемента выполняется следующее:- Создается новый указатель
next
, который указывает на следующий элемент в списке. - Если текущий элемент является числом (проверка
isdigit(item->c)
), он добавляется в список слов, начиная сwordsEnd
. В этом случаеwordsEnd
обновляется, чтобы указывать на последний добавленный элемент. - Если текущий элемент является буквой (проверка
isalpha(item->c)
), он добавляется в список чисел, начиная сnumbersEnd
. В этом случаеnumbersEnd
обновляется, чтобы указывать на последний добавленный элемент.
- Создается новый указатель
- После завершения цикла, все указатели обнуляются, что означает, что список слов и список чисел теперь являются независимыми списками. Вот список действий в виде нумерованного списка с названиями и значениями переменных:
Item *item = list;
- Инициализирует указательitem
для первого элемента входного списка.Item *words=NULL;
- Инициализирует указательwords
для начала списка слов (букв) с значениемNULL
.Item *wordsEnd=NULL;
- Инициализирует указательwordsEnd
для конца списка слов (букв) с значениемNULL
.Item *numbers=NULL, *numbersEnd=NULL;
- Инициализирует указательnumbers
для начала списка чисел с значениемNULL
, и обновляетnumbersEnd
для указания на конец списка чисел.while (item)
- Начинает цикл, который перебирает элементы входного списка.Item *next = item->next;
- Создает новый указательnext
для следующего элемента в списке.if (isdigit(item->c))
- Если текущий элемент является числом, выполняется следующая операция.wordsEnd->next = item;
- Добавляет текущий элемент в список слов (букв), начиная сwordsEnd
.wordsEnd = item;
- ОбновляетwordsEnd
, чтобы указывать на последний добавленный элемент в списке слов (букв).else if (isalpha(item->c))
- Если текущий элемент является буквой, выполняется следующая операция.numbersEnd->next = item;
- Добавляет текущий элемент в список чисел, начиная сnumbersEnd
.numbersEnt = item;
- ОбновляетnumbersEnt
, чтобы указывать на последний добавленный элемент в списке чисел.item = next;
- Переходит к следующему элементу входного списка.list = NULL;
- После завершения цикла, обнуляет указательlist
, что означает, что список слов и список чисел теперь являются независимыми списками. Это интерпретация кода на основе предположений о постановке задачи и синтаксисе языка программирования.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д