190 likes | 433 Views
Chapter 4: Linked Lists. Arrays. Pointers. List Operations. List Variations. Skip Lists. CS 240. 1. a. a. b. b. c. c. a. b. c. d. e. d. d. e. e. Linked lists enable us to dynamically adjust the size of a list, eliminating the need to preallocate a specific amount of memory.
E N D
Chapter 4: Linked Lists Arrays Pointers List Operations List Variations Skip Lists CS 240 1
a a b b c c a b c d e d d e e Linked lists enable us to dynamically adjust the size of a list, eliminating the need to preallocate a specific amount of memory. Linked List Static Array • Preallocation Required! • Wasteful if you allocate too much! • Potentially fatal if you allocate too little! • Completely dynamic! • No space is wasted due to excess allocation! • Additional space is allocated as needed! Dynamic Array • Determine the size of the list before allocating memory! • Not dynamic enough – you can’t change the size once it’s been decided! CS 240 2
Let’s review linked lists with a little example that also refreshes our memory (so to speak) about recursion. The task is to read text from an input file, keeping track of how many times each word is used in the text. Each node in the linked list will require a string field (to hold the word), an integer field (to hold the count), and a pointer field (to point to the next node). string word ptr next int count CS 240 3
“wassup” “yadda” “wassup” “groovy” “groovy” “yadda” “yadda” “oops” “groovy” “wassup” 1 4 3 2 4 4 3 3 3 3 When a word is inserted in the list, the list is traversed to see if the word’s already there... “groovy” If the word is found, its node is removed, its count is incremented, and the node is reinserted to preserve the ordering (primary: high-count, secondary: alphabetical)... “oops” If the word is not found, a new node is created with a count of one, and inserted appropriately... CS 240 4
Desired linked list functionality: Default Constructor Copy Constructor Destructor Make an empty list Make a deep copy list Delete all list nodes INSERT member function RETRIEVE member function GETNODE member function Find a word’s count Create word/ count node Insert a new(?) word Input (>>) friend operator Output (<<) friend operator Unpunctuate & upper-case function Read a text file Write word/ count list Edit a word Recursive Find function Recursive Insert function Recursive Output function Find a word Insert a word Write a word/count CS 240 5
///////////////////////////////////////// // Class definition file: LinkedList.h // // // // Each node in a LinkedList will have // // two components: an elementType (a // // string representing a word and an // // integer counting that word's number // // of occurrences) and a pointer to // // the next node in the LinkedList. // ///////////////////////////////////////// #ifndefLINKED_LIST_H #include <string> using namespace std; structelementType { string word; intcount; }; structnode; typedefnode *nodePtr; structnode { elementTypeitem; nodePtrnext; }; classLinkedList { public: // Constructors and destructor LinkedList(); LinkedList(constLinkedList &list); ~LinkedList(); // Member functions boolinsert(string str); intretrieve(string str); friendistream& operator>> (istream &sourceFile, LinkedList &list); friendostream& operator << (ostream &destFile, constLinkedList &list); private: // Data member nodePtrhead; // Member function nodePtrgetNode(string str, intct); }; voidrecursiveOutput(ostream &outputFile, nodePtrptr); nodePtrrecursiveFind(string str, nodePtrprevPtr, nodePtr&currPtr); voidrecursiveInsert(nodePtrnewPtr, nodePtrprevPtr, nodePtr&currPtr); voidunpunctuate(string &str); #define LINKED_LIST_H #endif CS 240 6
/////////////////////////////////////////////// // Class implementation file: LinkedList.cpp // // // // In addition to standard constructor and // // destructor members, the LinkedList class // // has members for inserting a word into the // // list and determining a particular word's // // count. Friend input and output operators // // are also included. Non-member functions // // for enacting recursive output, insertion, // // and search have been implemented, as well // // as a function for removing punctuation // // from the beginning and ending of a word, // // and converting what's left to upper case. // /////////////////////////////////////////////// #include "LinkedList.h" #include <iomanip> #include <assert.h> // Default Constructor: Sets // // up empty LinkedList. // LinkedList::LinkedList() { head = NULL; } // Copy Constructor: Makes deep copy // // of the parameterized LinkedList. // LinkedList::LinkedList(constLinkedList &list) { nodePtrcurrPtr, thisCurrPtr, thisPrevPtr; if (list.head == NULL) head = NULL; else { head = getNode(list.head->item.word, list.head->item.count); thisPrevPtr = head; currPtr = list.head->next; while (currPtr != NULL) { thisCurrPtr = getNode(currPtr->item.word, currPtr->item.count); thisPrevPtr->next = thisCurrPtr; thisPrevPtr = thisCurrPtr; currPtr = currPtr->next; } } } // Destructor: Deletes every // // node in LinkedList. // LinkedList::~LinkedList() { nodePtrcurrPtr; while (head != NULL) { currPtr = head; head = head->next; currPtr->next = NULL; delete currPtr; } } CS 240 7
// Member Function: insert // // // // This function searches the // // LinkedList for the parameterized // // word, removing the corresponding // // node and incrementing its count // // if it's found. Otherwise, a new // // node is created containing the // // word with a count of one. In // // any case, the function inserts // // the node into the list so that // // the words are sorted primarily // // by their count, and then in // // alphabetical order. A boolean // // is returned, based on the // // insertion's success. // boolLinkedList::insert(string str) { nodePtrinsertPtr = recursiveFind(str, NULL, head); if (insertPtr == NULL) return false; recursiveInsert(insertPtr, NULL, head); return true; } // Member Function: retrieve // // // // This function locates the // // parameterized string in the // // LinkedList, returning the // // corresponding count for that // // word. Zero is returned if the // // string isn't found in the list. // intLinkedList::retrieve(string str) { nodePtrcurrPtr = head; while (currPtr != NULL) { if (currPtr->item.word == str) return currPtr->item.count; else currPtr = currPtr->next; } return0; } CS 240 8
// Friend Input Operator: >> // // // // The input operator reads strings // // from the parameterized input stream, // // strips off all unnecessary punctua- // // tion, and inserts them into the // // parameterized LinkedList, until the // // input stream has been depleted. // istream& operator >> (istream &sourceFile, LinkedList&list) { string nextWord; sourceFile >> nextWord; while (!sourceFile.eof()) { unpunctuate(nextWord); list.insert(nextWord); sourceFile >> nextWord; } return sourceFile; } // Friend Output Operator: << // // // // The output operator outputs the // // values values in the LinkedList, // // each on a separate output line in // // the parameterized output stream, // // starting with the head element. // ostream& operator << (ostream &destFile, constLinkedList &list) { recursiveOutput(destFile, list.head); return destFile; } // Member Function: getNode // // // // This function creates and returns // // a new nodePtr, pointing to a node // // with the parameterized string as // // its item's word, the parameterized // // integer as its item’s count, and // // NULL as its next pointer. // nodePtrLinkedList::getNode(string str, intct) { nodePtr temp = new node; if (temp != NULL) { temp->item.word = str; temp->item.count = ct; temp->next = NULL; } return temp; } CS 240 9
// Non-Member Function: recursiveOutput // // // // This function recursively outputs the word // // counts to the parameterized output file, // // beginning with the node pointed to by the // // parameterized pointer, and traversing to // // the tail of the (implicit) linked list. // void recursiveOutput(ostream &outputFile, nodePtrptr) { if (ptr != NULL) { outputFile << setw(20) << ptr->item.word << " (" << ptr->item.count << ")" << endl; recursiveOutput(outputFile, ptr->next); } return; } // Non-Member Function: recursiveFind // // // // This function recursively locates the para- // // meterized word in the (implicit) linked list // // starting at the node to which the currPtr // // pointer is pointing. The prevPtr pointer is // // assumed to point to currPtr's predecessor in // // the list. If the desired string is located, // // its node is delinked from the list and its // // count is incremented by one. If the entire // // list is searched without success, then a new // // node containing the word (and a count of one) // // is created. In either case, a pointer to the // // node containing the desired word is returned. // nodePtrrecursiveFind(string str, nodePtrprevPtr, nodePtr &currPtr) { nodePtr temp; if (currPtr == NULL) { temp = new node; if (temp != NULL) { temp->item.word = str; temp->item.count = 1; temp->next = NULL; } return temp; } else if (currPtr->item.word == str) { temp = currPtr; if (prevPtr == NULL) currPtr = currPtr->next; else prevPtr->next = currPtr->next; temp->item.count++; temp->next = NULL; return temp; } else return recursiveFind(str, currPtr, currPtr->next); } CS 240 10
// Non-Member Function: recursiveInsert // // // // This function recursively traverses the list // // until the currPtr parameter points to the // // node whose contents should occur right after // // the contents of the node to which newPtr is // // pointing. Once this occurs, the newPtr node // // is inserted between currPtr's node and its // // predecessor (to which prevPtr is pointing). // void recursiveInsert(nodePtrnewPtr, nodePtrprevPtr, nodePtr &currPtr) { if ((currPtr != NULL) && ((currPtr->item.count > newPtr->item.count) || ((currPtr->item.count == newPtr->item.count) && (currPtr->item.word < newPtr->item.word)))) recursiveInsert(newPtr,currPtr,currPtr->next); else if (prevPtr == NULL) { newPtr->next = currPtr; currPtr = newPtr; } else if (currPtr == NULL) prevPtr->next = newPtr; else { newPtr->next = currPtr; prevPtr->next = newPtr; } } // Non-Member Function: unpunctuate // // // // This function removes all non-alpha- // // numeric characters from the beginning // // and ending of the parameterized // // string, and then converts what's left // // left of the string into upper-case. // void unpunctuate(string &str) { conststring ALPHANUMERIC = (string)"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + (string)"abcdefghijklmnopqrstuvwxyz" + (string)"0123456789"; while (str.find_first_not_of(ALPHANUMERIC) == 0) str = str.substr(1,str.size()-1); while ((str.size() > 0) && (str.find_last_of(ALPHANUMERIC) < str.size()-1)) str = str.substr(0,str.size()-1); for (int i = 0; i < str.size(); i++) str[i] = toupper(str[i]); } CS 240 11
////////////////////////////////////////////////// // WordCountDriver.cpp // // Driver program to test the LinkedList class. // ////////////////////////////////////////////////// #include <iostream> #include <fstream> #include <string> #include “LinkedList.h" using namespace std; ////////////////////////////////////////////////////////////////////////////////// // The main function uses text from a hard-coded input file to create a linked // // list of the distinct words in that file and how many times each word occurs. // // The function then outputs that list to a hard-coded output file. // ////////////////////////////////////////////////////////////////////////////////// void main() { LinkedListwordCountList; ifstreamstringFile; ofstreamresultFile; stringFile.open("essay.txt"); resultFile.open("count.txt"); stringFile >> wordCountList; resultFile << wordCountList << endl; stringFile.close(); resultFile.close(); return; } CS 240 12
Sample essay.txt file: IBM announced today that researchers are testing the Linux operating system on a prototype wristwatch device in an attempt to prove that Linux can be used as the basic software on even the smallest devices. “Designed to communicate wirelessly with PCs, cell phones and other wireless-enabled devices, the ‘smart watch’ will have the ability to view condensed e-mail messages and directly receive pager-like messages,” IBM said in a statement. However, IBM does not have plans to commercialize the Linux watch itself, a spokeswoman said. “This is just a research prototype,” said Takako Yamakura. “Some say Linux cannot be scaled down. This is just to show Linux is capable of doing this.” The Linux operating system is seen as an alternative to Microsoft Corp.’s Windows operating system, and is popular with programmers for its open source code, which allows programmers to develop and tinker with programs. “Several benefits accrue from the use of Linux in small pervasive devices,” IBM said in the statement. “The availability of source code and a well-understood application programming environment makes it easy for students, researchers, and software companies to add new features and develop applications.” Linux, which was developed by Finnish programmer Linus Torvalds, is used for many basic functions of Web sites, but is not yet considered mature enough for heavier business tasks. IBM has been working to develop the system for everything from the wrist watch to supercomputers. “With Linux rapidly becoming an industry standard, it’s important that developers be able to create new applications across all platforms, including pervasive devices, and the intent of IBM’s research is to further that work,” IBM said. CS 240 13
Output file count.txt: THE (13) TO (12) LINUX (9) AND (8) IS (8) IBM (6) A (5) FOR (5) OF (5) DEVICES (4) IN (4) SAID (4) SYSTEM (4) THAT (4) WITH (4) AN (3) BE (3) DEVELOP (3) OPERATING (3) THIS (3) WATCH (3) APPLICATIONS (2) AS (2) BASIC (2) CODE (2) FROM (2) HAVE (2) JUST (2) MESSAGES (2) NEW (2) NOT (2) ON (2) PERVASIVE (2) PROGRAMMERS (2) PROTOTYPE (2) RESEARCH (2) RESEARCHERS (2) SOFTWARE (2) SOURCE (2) STATEMENT (2) USED (2) WHICH (2) ABILITY (1) ABLE (1) ACCRUE (1) ACROSS (1) ADD (1) ALL (1) ALLOWS (1) ALTERNATIVE (1) ANNOUNCED (1) APPLICATION (1) ARE (1) ATTEMPT (1) AVAILABILITY (1) BECOMING (1) BEEN (1) BENEFITS (1) BUSINESS (1) BUT (1) BY (1) CAN (1) CANNOT (1) CAPABLE (1) CELL (1) COMMERCIALIZE (1) COMMUNICATE (1) COMPANIES (1) CONDENSED (1) CONSIDERED (1) CORP.’S (1) CREATE (1) DESIGNED (1) DEVELOPED (1) DEVELOPERS (1) DEVICE (1) DIRECTLY (1) DOES (1) DOING (1) DOWN (1) E-MAIL (1) EASY (1) ENOUGH (1) ENVIRONMENT (1) EVEN (1) EVERYTHING (1) FEATURES (1) FINNISH (1) FUNCTIONS (1) FURTHER (1) HAS (1) HEAVIER (1) HOWEVER (1) IBM’S (1) IMPORTANT (1) INCLUDING (1) INDUSTRY (1) INTENT (1) IT (1) ITS (1) ITSELF (1) IT’S (1) LINUS (1) MAKES (1) MANY (1) MATURE (1) MICROSOFT (1) OPEN (1) OTHER (1) PAGER-LIKE (1) PCS (1) PHONES (1) PLANS (1) PLATFORMS (1) POPULAR (1) PROGRAMMER (1) PROGRAMMING (1) PROGRAMS (1) PROVE (1) RAPIDLY (1) RECEIVE (1) SAY (1) SCALED (1) SEEN (1) SEVERAL (1) SHOW (1) SITES (1) SMALL (1) SMALLEST (1) SMART (1) SOME (1) SPOKESWOMAN (1) STANDARD (1) STUDENTS (1) SUPERCOMPUTERS (1) TAKAKO (1) TASKS (1) TESTING (1) TINKER (1) TODAY (1) TORVALDS (1) USE (1) VIEW (1) WAS (1) WEB (1) WELL-UNDERSTOOD (1) WILL (1) WINDOWS (1) WIRELESS-ENABLED (1) WIRELESSLY (1) WORK (1) WORKING (1) WRIST (1) WRISTWATCH (1) YAMAKURA (1) YET (1) CS 240 14
Standard Linked Lists • List has definite beginning and end. • NULL pointer used to indicate empty list. • Head and tail must be treated as special cases. • Complicated “previous” pointer needed for insertion & removal. Circular Linked Lists • Nice for applications requiring cycling through list. • Pointer used to keep track of current position in list. • No actual “head” or “tail” in list, so watch out for infinite loops! • “Previous” pointer still needed for insertion and removal. CS 240 15
Dummy Head Node Linked Lists • Dummy head node always exists, no special “head” case. • Head node’s “next” pointer is NULL when list is empty. • Wasteful use of node; its “data” field is usually vacuous. • “Previous” pointer still needed for insertion and removal. Doubly Linked Lists • Eliminates need for tracking previous node during traversal. • Improves efficiency if used with circularity & dummy head node. • Complex maintenance of predecessor & successor pointers. • Like all of the linked list variations, linear search is inefficient! CS 240 16
Skip Lists One possibility for improving the efficiency of list traversals is the idea of a “skip list”. Assume that the “values” of the data held in the list are evenly distributed between some value and some value , and assume that there will be approximately N values in the list. Yes, it’s pretty complicated, but it essentially enables you to search a linked list in logarithmic time (rather than that awful linear time)! Each node has a “data” field and an array of at most n “next” pointers, where n is log2 N. When a new value is inserted, randomly assign it k “next” pointers in such a way that half of the time, the node gets 1 “next” pointer, a quarter of the time it gets 2 “next” pointers, and so on up to (1/2n-1)th of the time it gets either n-1 or n pointers. CS 240 17
Implementing A Skip List structnode; typedefnode *nodePtr; typedefnodePtr* nextArray; structnode { elementType item; // Node’s data intnbrOfNexts; // Number of next pointers nextArray next; // Dynamic array of next pointers }; The skip list nodes have “data” and “next” components, just like regular linked list nodes, but skip list nodes have a dynamic array of next pointers, rather than just a single next pointer. // Member Function: insert // // This function creates a new node containing the parameterized value as its // // item. It uses the randomized algorithm to decide how many next pointers the // // node will have and then links the new node into its proper place within the // // list. A boolean is returned based on whether the node creation was successful. // boolSkipList::insert(elementType value) { nodePtrcurrPtr; nodePtrinsertPtr = getNode(value); if (insertPtr == NULL) return false; // How many next pointers? Between 1 and MAX_NEXTS, but logarithmically weighted // so 1 occurs half the time, 2 occurs a quarter of the time, and so on. insertPtr->nbrOfNexts = MAX_NEXTS + 1 - (int)ceil(log10(generateRandomNumber(2, (int)pow(2,MAX_NEXTS)))/log10(2)); insertPtr->next = new nodePtr[insertPtr->nbrOfNexts]; currPtr = head; for (int i = insertPtr->nbrOfNexts - 1; i >= 0; i--) { while ((currPtr->next[i] != NULL) && (currPtr->next[i]->item <= value)) currPtr = currPtr->next[i]; insertPtr->next[i] = currPtr->next[i]; currPtr->next[i] = insertPtr; } return true; } CS 240 18