/************* * * sortWords.c * * September 2002 assignment in Algorithms class * * -- original assignment -- * * Expand the linked list example (see ../listwords/) to include sorting * the list. Again, find the time it takes to run as a function of * nWords. What is its behavior on this data? * * Modify the program again so that it stores the data in an array * rather than a list. Does this make it faster or slower? * What are the advantages or disadvantages? * * Jim Mahoney (mahoney@marlboro.edu) * * -- version history ------------------------------------------------- * * version 2.1 Oct 2 * working on sortingList - in progress. * * version 2.0, Sep 30 (version 1.x is ../listwords/listWords.c) * added and tested cloneList, cloneElment * ******************************************************/ #include #include #include // ===== defines, typedefs and subroutine declarations ================ // ===== (Might put into a .h file later.) ============================= // maximum number of characters in one word. #define MaxChars 128 // A doubly linked list element to hold a string and its value. typedef struct ListElementStruct_ { struct ListElementStruct_* next; struct ListElementStruct_* prev; char* data; int value; } ListElementStruct, *ListElement; typedef struct ListStruct_ { ListElement first, last; int nElements; } ListStruct, *List; int getStringValue(char* aString); List makeEmptyList(); ListElement makeElement(char* inputData); void printElement(ListElement theList); void printList(List theList); void appendElementToList(List theList, ListElement newElement); void appendStringToList(List theList, char* theString); ListElement nthElement(List theList, int n); List cloneList(List theList); ListElement cloneElement(ListElement theElement); List readWordsIntoList(int howManyWords); void findMaxValue(List theList); ListElement findMaxOfNWords(ListElement element, int nWords); double timeFindMax(int nWords, List theList); void analyzeFindMax(int nPoints); void sortList(List theList); void testList(); void testGetStringValue(); void trialReadWords(); // ==== subroutine implementations ============================== // ----------------------------------------------------------- // Create and return a new empty list. List makeEmptyList() { List newList = (List)malloc(sizeof(ListStruct)); newList->nElements = 0; newList->first = NULL; newList->last = NULL; return(newList); } // ----------------------------------------------------------- // Create and return a new list element given an input string. // Also, calculate and store the value of the string. ListElement makeElement(char* inputData){ ListElement newElement = (ListElement)malloc(sizeof(ListElementStruct)); newElement->next = NULL; newElement->prev = NULL; newElement->data = (char*)malloc(strlen(inputData)); newElement->value = getStringValue(inputData); strcpy(newElement->data, inputData); return(newElement); } // ----------------------------------------------------------- // Create and return a shallow copy of a given list element. // This routine does *not* copy the data, but instead points // the new element at the same data. // The prev and next links are set to NULL. ListElement cloneElement(ListElement theElement){ ListElement thisElement = (ListElement)malloc(sizeof(ListElementStruct)); thisElement->next = NULL; thisElement->prev = NULL; thisElement->data = theElement->data; thisElement->value = theElement->value; return(thisElement); } // ----------------------------------------------------------- // Create and return a shallow copy of a given list. // The listElement structures are duplicated, // but the data that they point to is not. List cloneList(List theList) { List thisList = (List)malloc(sizeof(ListStruct)); ListElement thisElement = NULL; ListElement previousElement = NULL; ListElement originalElement = theList->first; int i; for (i=0;inElements;i++){ thisElement = cloneElement(originalElement); thisElement->prev = previousElement; if (previousElement == NULL){ thisList->first = thisElement; } else { previousElement->next = thisElement; } originalElement = originalElement->next; previousElement = thisElement; } thisList->nElements = theList->nElements; thisList->last = thisElement; return(thisList); } // ----------------------------------------------------------- void printElement(ListElement theList){ printf("self=%p, next=%p, prev=%p. Data is '%s, value=%i\n", theList, theList->next, theList->prev, theList->data, theList->value); } // ----------------------------------------------------------- void printList(List theList){ ListElement el; int i; if (theList->nElements == 0){ printf(" This list is empty.\n"); } else { printf(" This list has %i element(s): \n", theList->nElements); i=0; el = theList->first; while (el != NULL && i<100) { // don't show more than 100 elements printf(" %i : ",i); printElement(el); el = el->next; i++; } } } // ----------------------------------------------------------- void appendElementToList(List theList, ListElement newElement){ theList->nElements++; if (theList->last == NULL){ theList->first = newElement; theList->last = newElement; } else { theList->last->next = newElement; newElement->prev = theList->last; theList->last = newElement; } } // ----------------------------------------------------------- void appendStringToList(List theList, char* theString){ ListElement theElement = makeElement(theString); appendElementToList(theList, theElement); } // ----------------------------------------------------------- void testList(){ List theList = makeEmptyList(); List secondList; ListElement anElement; printf(" ------------------- test list operations ------------ \n"); printf(" adding 'Hello'\n"); appendStringToList(theList, "Hello"); printList(theList); printf(" adding 'Goodbye'\n"); appendStringToList(theList, "Goodbye"); printf(" adding 'Put123Back'\n"); appendStringToList(theList, "Put123Back"); printf(" adding 'Yet another'\n"); appendStringToList(theList, "Yet another"); printList(theList); anElement = nthElement(theList,2); printf(" Element 2 is: \n"); printElement(anElement); secondList = cloneList(theList); printf(" Duplicate copy of that list: \n"); printList(secondList); } // ----------------------------------------------------------- // return the "value" of a string from the assignment definition. int getStringValue(char* theString){ int totalValue = 0; int characterValue=0; int asInt=1; int i=0; while (asInt != 0 && i 48, "9" => 57 , so subtract 48 from these to get 0..9 // "A" => 65, "Z" => 90 , so subtract 55 from these to get 10..35 // "a" => 97, "z" => 122 , so subtract 60 from these to get 37..62 // I'll ignore the other characters. void testGetStringValue(){ char* sampleString = "09.AZ.az."; int value; printf(" ---------- test getStringValue ---------- \n"); printf(" sampleString is '%s'\n", sampleString); printf(" length is %i\n", strlen(sampleString)); value = getStringValue(sampleString); printf(" total value this word is %i\n", value); } // ----------------------------------------------------------- List readWordsIntoList(int howManyWords){ char* fileName = "/usr/share/lib/dict/words"; // 25143 words, one per line. char oneWord[MaxChars]; int n=0; int s; FILE* filePtr = fopen(fileName,"r"); // "r" is "read only" mode List theList = makeEmptyList(); printf("# Reading %i words from '%s'.\n", howManyWords, fileName); if (filePtr==NULL){ fprintf(stderr, "Error opening file '%s'\n"); exit(1); // Non-zero signals an error. } while (nnext; // walk down the list if (element->value > bestSoFar->value){ bestSoFar = element; } } return(bestSoFar); } // ----------------------------------------------------------- void findMaxValue(List theList){ ListElement el = theList->first; ListElement bestSoFar; if (el==NULL) { printf(" Oops: can't do findMaxValue on an empty list.\n"); return; } printf(" Looking for largest value in %i element list...\n", theList->nElements); bestSoFar = el; while (el->next != NULL){ el = el->next; // walk down the list if (el->value > bestSoFar->value){ bestSoFar = el; } } printf(" Done. *** Biggest is %i on word '%s'. ***\n\n", bestSoFar->value, bestSoFar->data); } // ----------------------------------------------------------- // Return the N'th element of the given list, 0<=N<=(size-1) // For N>=size, return NULL. ListElement nthElement(List theList, int n){ ListElement el = theList->first; int i; if (n >= theList->nElements){ return(NULL); } for (i=0;inext; } return(el); } // ----------------------------------------------------------- // Once this piece (and the others) worked, // I cut'n'paste the useful bits into readWordsIntoList. void trialReadWords(){ char* fileName = "/usr/share/lib/dict/words"; // 25143 words, one per line. char oneWord[MaxChars]; int howManyWords = 20; // Just as a warm up. int n=0; int s; FILE* filePtr = fopen(fileName,"r"); // "r" is "read only" mode printf(" ---------- test read words ---------- \n"); if (filePtr==NULL){ fprintf(stderr, "Error opening file '%s'\n"); exit(1); // Non-zero signals an error. } while (nfirst, nWords); } end=gethrtime(); return 1.0e-9*(end-start)/repititions; } // ----------------------------------------------------------- // Find how long it takes for various values n of nWords, // using nPoints values of n. Print out a list of the results. // Put "#" at the start of the non-numeric lines so that gnuplot // will ignore them. (Same in printf() in readWordsIntoList().) void analyzeFindMax(int nPoints){ List theList = readWordsIntoList(50000); // read the whole file. double sec; int dN, n; const int MaxNWords = 25143; // number of words in the file. dN = MaxNWords/nPoints; printf("# Time (in sec) to find max of n words,\n"); printf("# using %i values for n:\n", nPoints); for (n=dN;nnElements < 2) { return; } // Don't sort a 0 or 1 element list. // Pull the first element of the original list out and treat it as the start // of a new, sorted list. (I don't bother with an actual ListStruct, // but just hold the first,last elements of this new sorted list.) // (Note that during the sort I don't try to keep all these pointers // entirely consistent. In particular, unsorted is the first element // of those that are unsorted, so unsorted->prev should really // be NULL - but since all it's pointers will be changed when its moved // into the sorted list anyway, that doesn't matter. sortedFirst = theList->first; // First in the newly sorted list. sortedLast = sortedFirst; // Last in the newly sorted list. unsorted = theList->first->next; // First of the remaining unsorted. sortedFirst->next = NULL; // Outer loop over element number 1 to n-1 (element 0 is already sorted) while ( unsorted != NULL ) { nextUnsorted = unsorted->next; // Remember this before u's links change. sorted = sortedFirst; // Inner loop over (at most) sorted list while (sorted != NULL) { if ( unsorted->value < sorted->value ) { // If u goes before s, sorted->prev = unsorted; // then put it there. unsorted->next = sorted; unsorted->prev = sorted->prev; if (sorted->prev == NULL) { // Is this the start of sorted list? sortedFirst = unsorted; // - Yes, so reset ordered list start. } // else { // - No, so set sorted->prev sorted->prev->next = unsorted; } break; // Done this sorted loop. } sorted = sorted->next; // Continue walk down s's list. } if (sorted == NULL) { // Didn't find a place to put the sortedLast->next = unsorted; // unsorted one, so put it at the end. unsorted->prev = sortedLast; unsorted->next = NULL; sortedLast = unsorted; } unsorted = nextUnsorted; // On to the next unsorted element. } theList->first = sortedFirst; // Done. theList->last = sortedLast; // Reset the start and end of theList. } // ----------------------------------------------------------- void testSortList(){ List theList = makeEmptyList(); ListElement thisElement; thisElement = makeElement("Ten"); thisElement->value = 10; appendElementToList(theList, thisElement); thisElement = makeElement("Eight"); thisElement->value = 8; appendElementToList(theList, thisElement); thisElement = makeElement("Nine"); thisElement->value = 9; appendElementToList(theList, thisElement); thisElement = makeElement("Three"); thisElement->value = 3; appendElementToList(theList, thisElement); thisElement = makeElement("Two"); thisElement->value = 2; appendElementToList(theList, thisElement); thisElement = makeElement("Three again"); thisElement->value = 3; appendElementToList(theList, thisElement); thisElement = makeElement("Seven"); thisElement->value = 7; appendElementToList(theList, thisElement); printf(" Original list order: \n"); printList(theList); sortList(theList); printf(" Sorted list order: \n"); printList(theList); } // ==== Fire it up. ================================================= int main(){ /*** run various test ***/ // testGetStringValue(); // Calculate values from strings // testList(); // Create elements and list, print and clone 'em. // trialReadWords(); // Read in the dictionary. testSortList(); // Insertion sort a short sample list. /** Version 1: find max of entire file **/ //findMaxValue(readWordsIntoList(50000)); /** Version 1.1: timing of various values for nWords. **/ //analyzeFindMax(100); }