Дан текстовый файл. Строки этого файла расположить в порядке убывания их длины и удалить пять самых коротких из них.
Для размещения в памяти содержимого файлов использовать односвязные линейные списки.
Предполагаю заносить каждую строку в одно поле списка, но не знаю, как это сделать именно со строкой и сколько выделять памяти.
После прочтения строки из файла, проверяй ее длину (strlen) и выделяй под информационное поле в списке на 1 байт больше (для хранения завершающего '\0')
хочу сделать функцию для расположения строк в порядке убывания их длины.
пусть до этого у меня есть ф-я, которая формирует список (ф-я возвращает указатель на голову списка). тогда в ф-ю упорядочения передаём указатель на голову списка. а дальше я не разберусь , как с помощью списка собственно упорядочить строки....
помогите пожалуйста!
Ты какой путь выбираешь, сразу заносить очередной элемент на соответствующее место, или сначала занести ВСЕ элементы, а потом - сортировать?
Скорее всего, второй? Тогда я уже выкладывал где-то переведенную на Паскаль рекурсивную функцию сортировки списка вставками (из книги Sedgewick-а "Fundamental C++"), если надо - выложу оригинал на С ...
Ну, тогда тебе и карты в руки... Опять изобретение велосипеда? Что же за преподаватели-то такие? Они же просто угробят все, что только можно!!! С одной стороны они ограничили тебя односвязным списком, то есть, у тебя только последовательный проход по списку (про сотню проходов по списку из 100 элементов я не говорю - это вообще полный бред), а с другой стороны - те методы, которые эффективны при последовательном доступе (в частности - mergesort) - они рекурсивные... А рекурсия запрещена. Замкнутый круг...
попыталась написать ф-ю, которая формирует список (ф-я возвращает указатель на голову списка).
но насчёт занесения строки в информационное поле элемента списка - ???
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <alloc.h>
typedef struct list {
char number[50];
struct list *next;
} LIST;
LIST *read_list(LIST *lst)
{
LIST *p;
FILE *f;
f=fopen("rg.txt","r");
if (!feof(f))
{
lst=(LIST *)malloc(sizeof(LIST));
p=lst;
while (1)
{
fscanf(f,"%s",p->number[50]);
if (!feof(f))
{
p->next=(LIST *)malloc(sizeof(LIST));
p=p->next;
} else break;
}
p->next=NULL;
}
else printf("Файл пустой\n");
fclose(f);
return lst;
}
#include <stdio.h>Так работает?
#include <conio.h>
#include <string.h>
#include <alloc.h>
typedef struct list {
char number[50];
struct list *next;
} LIST;
LIST *read_list() {
char s[255];
LIST *p, *lst, *final;
FILE *f;
f=fopen("rg.txt","r");
lst = NULL;
while(!feof(f)) {
fscanf(f, "%s", s);
p = (LIST*)malloc(sizeof(list));
strcpy(p -> number, s);
if(!lst) lst = p;
else final -> next = p;
final = p;
}
fclose(f);
if(!lst) {
printf("empty file\n");
return NULL;
}
final -> next = NULL;
return lst;
}
void sort(LIST **first) {
LIST *L, *p;
char s[255];
for(L = *first; L -> next; L = L -> next)
for(p = L -> next; p; p = p -> next) {
if(strcmp(L -> number, p -> number) > 0) {
strcpy(s, L -> number);
strcpy(L -> number, p -> number);
strcpy(p -> number, s);
}
}
}
int main() {
struct list *p;
p = read_list();
sort(&p);
for(; p; p = p -> next)
printf("%s\n", p -> number);
return 0;
}
strcpy(p -> number, s);
if(!lst) lst = p;
else final -> next = p;
А как будет выглядеть удаление 5 нужных элементов? ( тогда это ещё одна ф-я, куда, наверно, нужно передать указатель на голову списка...а что-то кроме этого нужно передавать?)
А когда всё необходимое выполнено, нам ведь нужно освободить занятые участки памяти и указателям, ссылающимся на них, присвоить NULL? (т.е. я так понимаю - отсортировали, удалили лишнее, выводим на экран содержимое оставшихся информационных полей и попутно освобождаем занятые участки.... а как это выполняется?)
Будет гораздо проще сделать наоборот: отсортировать слова по возрастанию длины, а потом удалить 5 первых, поскольку у тебя односвязный список, и работать от "головы" к "хвосту" легче, чем от "хвоста" к "голове"... Смотри:
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <alloc.h>
typedef struct list {
char number[50];
struct list *next;
} LIST;
LIST *read_list() {
char s[255];
LIST *p, *lst, *final;
FILE *f;
f=fopen("rg.txt","r");
lst = NULL;
while(!feof(f)) {
fscanf(f, "%s", s);
p = (LIST*)malloc(sizeof(list));
strcpy(p -> number, s);
if(!lst) lst = p;
else final -> next = p;
final = p;
}
fclose(f);
if(!lst) {
printf("empty file\n");
return NULL;
}
final -> next = NULL;
return lst;
}
void sort(LIST **first) {
LIST *L, *p;
char s[255];
for(L = *first; L -> next; L = L -> next)
for(p = L -> next; p; p = p -> next) {
if(strlen(L -> number) > strlen(p -> number)) { /* сортируем по длине */
strcpy(s, L -> number);
strcpy(L -> number, p -> number);
strcpy(p -> number, s);
}
}
}
int main() {
struct list *root, *p, *T;
int n;
/* читаем список из файла */
root = read_list();
/* сортируем его по возрастанию длин строк */
sort(&root);
/* и печатаем ... */
for(p = root; p; p = p -> next)
printf("%s\n", p -> number);
/* теперь удаляем первые 5 элементов */
n = 0;
for(p = root; p && n < 5; n++) {
p = (T = p) -> next; /* запоминаем текущее P в переменной T, для того чтобы потом удалить */
free(T);
}
/* и устанавливаем новое начало списка */
root = p;
/* еще раз проходим, и печатаем весь список, сразу удаляя все узлы, которые уже распечатаны */
for(p = root; p; ) {
printf("%s\n", p -> number);
p = (T = p) -> next;
free(T);
}
root = NULL;
return 0;
}
вот такие тестовые данные ввожу в исходный файл:
qwerty
asdf
qwertyu
qwertyqwerty
qwertyuioop
zxcvbnmjhy
qw
q
zxcvfgh
результат в прикреплённом файле.
не пойму, почему строчка zxcvfgh выводится 2 раза?
Прикрепленные файлы
Новый_рисунок__2_.bmp ( 75.76 килобайт )
Кол-во скачиваний: 580
Потому что из списка удаляется всего 5 элементов, а не все элементы, с 5-ю наименьшими длинами...
Добавлено через 3 мин.
А, так он у тебя именно дублируется? Интересно... У меня - нет...
Эскизы прикрепленных изображений
Так... Кажется мне удалось повторить проблему, которая есть у тебя...
Убедись, что после последней строки файла данных НЕТ перевода строки, т.е., последняя строка - НЕ пустая... Если она будет пустой - то получишь то, что ты сказала...
всё-таки хочу разобраться и таким вариантом : как, после сотировки строк по убыванию их длины, удалить самые короткие, идя к ним от головы списка? (т.е. считаем, что самые короткие в конце списка). Как предусмотреть, если самых коротких меньше 5? (например вывести сообщение о том, что нужного кол-ва строк нет)
С односвязным списком - только через извращение, которое убивает все преимущества использования списков: первым проходом пробегать по всему списку и считать, сколько элементов в нем есть, и вторым проходом - пропускать первые N - 5 элементов, а остальные удалять...
Но это делай сама, я ЭТО делать не буду.
Я хочу добиться того, чтобы после удаления 5 элементов (остальные элементы расположены в порядке возрастания их длины), оставшиеся элементы были расположены в обратном порядке (т.е. в порядке убывания длины).
Как это будет выглядеть?
И куда в этом случае привязать проверку на наличие пяти самых коротких строк?
И ещё такой вопрос:
void sort(LIST **first) - почему мы в эту ф-ю передаём указатель на указатель на голову списка?
объясните пожалуйста некоторые моменты:
1) о сортировке
for(L = *first; L -> next; L = L -> next)
for(p = L -> next; p; p = p -> next)
sort(&root);
for(p = root; p; p = p -> next)
printf("%s\n", p -> number);
for(p = root; p && n < 5; n++)
for(p = root; p; )
Давай по порядку:
1) организацию циклов в С знаешь? Я имею в виду, синтаксис оператора цикла?
for(начальное_присвоение;условие_окончания;и
зменение_на_каждой_итерации) {
собственно_операторы_тела_цикла;
}
где
начальное_присвоение - делается один раз перед началом цикла;
условие_окончания - проверяется после завершения каждой итерации
изменение_на_каждой_итерации - делается после завершения итерации ПЕРЕД проверкой условия_окончания
причем любой (!!!) из этих параметров может отсутствовать (моогут, кстати, отсутствовать и 2 и все 3 - тогда цикл будет "вечным", и выходе из него возможен будет только через break)...
А теперь с учетом этого - смотри, что происходит:
/*
ВНЕШНИЙ цикл
Поскольку first передается как указатель на указатель
(причину я объяснял выше), то L присваиваем значению
разыменованного first (т.е. изначально L = указателю на
голову списка)
Условие окончания внешнего цикла - (L->next) подразумевает НЕравенство
этого значения нулю... То есть, как только значение L->next станет NULL, цикл завершится...
После обработки одной итерации продвигаемся по списку, путем L = L -> next
*/
for(L = *first; L -> next; L = L -> next)
/*
ВНУТРЕННИЙ цикл
изначально p = L -> next
условие окончания: закончится цикл - когда p будет нулевым
продвижение - понятно как осуществляется...
*/
for(p = L -> next; p; p = p -> next)
/*
изначально p = root
окончание - когда p = NULL
продвижение - p = p -> next
*/
for(p = root; p; p = p -> next)
printf("%s\n", p -> number);
for(p = root; p; ) {
printf("%s\n", p -> number);
p = (T = p) -> next; /* <--- вот эту строку ты разобрала, как она работает? */
free(T);
}
volvo, большое спасибо! теперь всё поняла!
Возник ещё вопрос:
for(L = *first; L -> next; L = L -> next)
for(p = L -> next; p; p = p -> next) {
if(strlen(L -> number) > strlen(p -> number)) { /* сортируем по длине */
strcpy(s, L -> number);
strcpy(L -> number, p -> number);
strcpy(p -> number, s);
}
}
Можно, только код будет больше по размеру... Не забывай, у тебя односвязные списки, то есть, для того, чтобы пройти к предыдущему (менять-то указатель с предыдущего на новое значение надо?) тебе придется пробегать еще раз по списку с самого начала...
Ты сама просила
void sort(LIST **first) {
LIST *L, *p, *pp, *T, *prv_L, *prv_p;
char s[255];
start_over:
;
for(L = *first; L -> next; L = L -> next)
for(p = L -> next; p; p = p -> next) {
if(strlen(L -> number) > strlen(p -> number)) {
// Вот тут раньше было 3 строки... Смотри теперь.
if(L == *first) {
for(pp = *first; pp -> next != p; pp = pp -> next);
pp -> next = *first;
T = (*first) -> next;
*first = p;
L -> next = p -> next;
p -> next = T;
}
else {
LIST *prv_L, *prv_p;
for(prv_L = *first; prv_L -> next != L; prv_L = prv_L -> next);
for(prv_p = *first; prv_p -> next != p; prv_p = prv_p -> next);
prv_L -> next = p;
prv_p -> next = L;
T = p -> next;
p -> next = L -> next;
L -> next = T;
}
goto start_over;
}
}
}
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <alloc.h>
typedef struct list {
char number[255];
struct list *next;
} LIST;
LIST *read_list() {
char s[255];
LIST *p, *lst, *final;
FILE *f;
f=fopen("rgz.txt","r");
lst = NULL;
while(!feof(f)) {
fgets(s,255, f);
p = (LIST*)malloc(sizeof(LIST));
strcpy(p -> number, s);
if(!lst) lst = p;
else final -> next = p;
final = p;
}
fclose(f);
if(!lst) {
printf("empty file\n");
return NULL;
}
final -> next = NULL;
return lst;
}
void *sort(LIST *first)
{
LIST *p1=first, *p2=p1->next, *prev,*temp1, *temp2;
int flag=1;
while (flag&&p2)
{
flag=0;
prev=first;
while (p2)
{
if (strlen(p1->number)>strlen(p2->number))
{
flag=1;
temp1=p1;
temp2=p2->next;
p1=p2;
p2=temp1;
p1->next=p2;
p2->next=temp2;
if (temp1==first) first=p1; else prev->next=p1;
}
prev=p1;
p1=p1->next;
p2=p2->next;
}
p1=first; p2=p1->next;
}
}
void *sort1(LIST *first)
{
LIST *p1=first, *p2=p1->next, *prev,*temp1, *temp2;
int flag=1;
while (flag&&p2)
{
flag=0;
prev=first;
while (p2)
{
if (strlen(p1->number) < strlen(p2->number))
{
flag=1;
temp1=p1;
temp2=p2->next;
p1=p2;
p2=temp1;
p1->next=p2;
p2->next=temp2;
if (temp1==first) first=p1; else prev->next=p1;
}
prev=p1;
p1=p1->next;
p2=p2->next;
}
p1=first; p2=p1->next;
}
}
void main() {
struct list *root, *p, *T;
int n;
FILE *f;
f=fopen("RGZ2.txt","w");
root = read_list();
sort1(root);
for(p = root; p; p = p -> next)
fprintf(f,"%s\n", p -> number);
fprintf(f,"\n---\n");
sort(root);
n = 0;
for(p = root; p && n < 5; n++) {
p = (T = p) -> next;
free(T);
}
root = p;
sort1(root);
for(p = root; p; ) {
fprintf(f,"%s\n", p -> number);
p = (T = p) -> next;
free(T);
}
root = NULL; fprintf(f,"\n--------\n");
}
Сорри, я больше в эту тему НЕ отвечаю! Я тебе привел работающую функцию сортировки, на отладку которой ушло больше 2-х часов, ты ее полностью перековеркала, и теперь тебя не устраивает результат?
Извини, но функции твои, вот и разбирайся, как они работают сама (а они НЕ работают, чтобы в этом убедиться достаточно вывести на экран содержимое списка ПЕРЕД первой сортировкой, и ПОСЛЕ нее)...
LIST *prv_L, *prv_p;
for(prv_L = *first; prv_L -> next != L; prv_L = prv_L -> next);
for(prv_p = *first; prv_p -> next != p; prv_p = prv_p -> next);