Вывести гистограммы длин слов во входном потоке - C (СИ)
Формулировка задачи:
Доброго времени суток,
Учу си, читаю книгу "Керниган, Ричи. Язык C", попутно выполняю задания, стараюсь делать это с максимальной эффективностью.
Упражнение 1.13. Напишите программу для вывода гистограммы длин слов во входном потоке. Построить гистограмму с горизонтальными рядами довольно легко, а вот с вертикальными столбцами труднее.
сначала надо заполнить массив значениями длин слов встречающихся в потоке ввода:
Простая горизонтальная гистограмма выводится легко, один цикл перебирает массив (перебирает длины), а вложенный цикл выводит символ гистограммы на вывод столько раз, сколько эта длина встретилась в потоке ввода (значение массива для заданной длины). Здесь хорошо то, что не будут выведены лишние символы пробелов(или других разделителей), это очевидно.
Вот мой вариант для вывода вертикальной гистограммы. (вторая часть программы, последняя } - закрывает main)
Здесь получается поле max_i*max_j часть которого заполнена точками а часть #-и. Мне интересно существует ли другой алгоритм, который позволил бы вывести вертикальную гистограмму, не тратя процессорное время на обход тех точек, в которых и после которых уже не будет символа #? Я додумался только до введения еще одного массива ,так сказать, максимумов, в котором будет указано, что, например, для количества совпадений 17 максимальная длина 2, тогда вместо "for(j=0;j<max_j;++j){" будет что то вроде "for(j=0;j<max_arr[i];++j){", то есть если в 17 строке (для 17-ти совпадений) максимальная длина 2, то нет нужды рисовать остальные точки, но с другой стороны, это еще один массив который надо заполнить.
В общем, хотелось бы выслушать замечания по программе, которая уже написана, и насчет её оптимизации. Так же может кто то предложит решение которое позволит вывести гистограмму прямо во время чтения потока...
в плане вычислительной сложности, так сказать?
#include <stdio.h> main(){ int c,i,j,max_i=0,max_j=0; /*max_i=0,max_j=0, максимальные значения длины и количества, нужны для рисования гистограммы */ int word_length_array[100];/*100 чтобы точно все влезло, дин. выделение памяти в предыдущих главах не рассматривалось, буду считать, что не знаю.*/ for(i=0;i<100;++i){ word_length_array[i]=0;} /*обнуляю значения массива, потому что в языке си программа просто возьмет кусок памяти под массив из 100 значений, но ничего обнулять не будет (так ведь? или от компилятора либо его флагов зависит?)*/ i=0; /*в дальнейшем использую i для подсчета длин слов поэтому в 0*/ while((c=getchar())!=EOF){ if((c==' '||c=='\n'||c=='\t')&&(i!=0)){ /*если на вводе уже не слово и длина не равна 0 (на случай если ввод начался с новой строки или табуляции и чтобы в будущем не считать несколько пробелов подряд за слово нулевой длины (а здесь вопрос?))*/ ++word_length_array[i]; /*все ясно*/ if(i>max_j){max_j=i;} /*для гистограммы, чтобы потом не перебирать массив в поисках макимума*/ if(word_length_array[i]>max_i){max_i=word_length_array[i];}/*для гистограммы, чтобы потом не перебирать массив в поисках макимума*/ i=0; /*то же ясно*/ }else{ ++i;}} /*собственно счетчик длины*/
for(i=max_i;i>-1;--i){ for(j=0;j<max_j;++j){ if(word_length_array[j]!=0){ if(i>0){ if(word_length_array[j]>=i){ printf("%3c",'#'); }else{ printf("%3c",'.');} }else{ printf("%3d",j);} } } printf("\n"); } }
программе, которая уже написана, и насчет её оптимизации.
Решение задачи: «Вывести гистограммы длин слов во входном потоке»
textual
Листинг программы
#include <stdio.h> #define IN 1 /* внутри слова */ #define OUT 0 /* снаружи слова */ #define MAXWLEN 100 /* максимальная длина слова */ /* выводит гистограмму длин слов во входном потоке; версия с вертикальными столбцами */ main() { int wlen, wlengths[MAXWLEN]; int maxlen, wlstatus[MAXWLEN]; int c, state, i; for (i = 0; i < MAXWLEN; ++i) wlengths[i] = wlstatus[i] = 0; state = OUT; while ((c = getchar()) != EOF) if (c == ' ' || c == '\n' || c == '\t') { if (state == IN) { state = OUT; ++wlengths[wlen - 1]; } } else if (state == OUT) { state = IN; wlen = 1; } else ++wlen; maxlen = wlengths[0]; for (i = 1; i < MAXWLEN; ++i) if (maxlen < wlengths[i]) maxlen = wlengths[i]; for (i = 0; i < MAXWLEN; ++i) if (wlengths[i] > 0) printf("|%3d", i + 1); putchar('|'); putchar('\n'); while (maxlen > 0) { for (i = 0; i < MAXWLEN; ++i) if (wlstatus[i] < wlengths[i]) { printf("%4c", '.'); ++wlstatus[i]; } else if (wlengths[i] > 0) printf("%4c", ' '); --maxlen; putchar('\n'); } putchar('\n'); for (i = 0; i < MAXWLEN; ++i) if ((wlen = wlengths[i]) > 0) printf(" %d,%d", i + 1, wlen); putchar('\n'); }
Объяснение кода листинга программы
- Включаем необходимые заголовочные файлы
- Определяем значения для IN и OUT
- Задаем максимальную длину слова (100)
- Создаем переменные для хранения длин слов и их статуса (0 - не встречено, 1 - встречено)
- Инициализируем массивы длин слов и их статуса нулями
- Устанавливаем начальное состояние (вне слова)
- В цикле считываем символы из стандартного ввода (до конца файла)
- Если встречен пробел, табуляция или перевод строки, меняем состояние на
внутри слова
и увеличиваем соответствующий счетчик длин слов - Если состояние
вне слова
, то встречен символ, который не является пробелом, табуляцией или переводом строки, значит, это начало нового слова, сохраняем его длину в массиве длин слов и увеличиваем соответствующий счетчик длин слов - Находим максимальную длину слова (считаем, что это длина первого слова)
- В цикле, пока максимальная длина слова больше нуля, выводим вертикальные столбцы для каждого значения длины слова (используя точку для обозначения
-
и пробел для обозначения - Выводим значения длин слов в строке (используя пробел для разделения значений)
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д