chapter 4: linked lists
Post on 22-Mar-2016
76 Views
Preview:
DESCRIPTION
TRANSCRIPT
Chapter 4: Linked Lists
ARRAYS
POINTERS
LIST OPERATIONS
LIST VARIATIONS
SKIP LISTS
CS 240 1
CS 240 2
Linked lists enable us to dynamically adjust the size of a list, eliminating the need to preallocate a specific amount of memory.
edcba
×Preallocation Required!×Wasteful if you allocate too
much!×Potentially fatal if you allocate
too little!
edcba
×Determine the size of the list before allocating memory!
×Not dynamic enough – you can’t change the size once it’s been decided!
Completely dynamic!
No space is wasted due to excess allocation!
Additional space is allocated as needed!
a b c d e
CS 240 3
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
int countptr
next
CS 240 4
When a word is inserted in the list, the list is traversed to see if the word’s already there...“yadda”
4
“wassup”
3
“groovy”
2
“groovy”
“yadda”
4
“groovy”
3
“wassup”
3
“yadda”
4
“groovy”
3
“wassup”
3
“oops”
1
“oops”
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)...
If the word is not found, a new node is created with a count of one, and inserted appropriately...
CS 240 5
Desired linked list functionality:
DefaultConstructor
CopyConstructor
INSERTmemberfunction
RETRIEVEmemberfunction
Destructor
GETNODEmemberfunction
Input (>>)friend
operator
Output (<<)friend
operator
RecursiveFind
function
RecursiveInsert
function
RecursiveOutputfunction
Unpunctuate& upper-case
function
Make an empty list
Make a deep copy list
Delete all list nodes
Insert a new(?) word
Find a word’s count
Create word/ count
node
Read a text file
Write word/ count list
Find a word Insert a word Write a word/count
Edit a word
CS 240 6
/////////////////////////////////////////// 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. ///////////////////////////////////////////
#ifndef LINKED_LIST_H
#include <string>using namespace std;
struct elementType{ string word; int count;};
struct node;typedef node *nodePtr;struct node{ elementType item; nodePtr next;};
class LinkedList{ public: // Constructors and destructor
LinkedList(); LinkedList(const LinkedList &list); ~LinkedList();
// Member functions bool insert(string str); int retrieve(string str); friend istream& operator >> (istream &sourceFile, LinkedList &list); friend ostream& operator << (ostream &destFile, const LinkedList &list);
private: // Data member nodePtr head;
// Member function nodePtr getNode(string str, int ct);};
void recursiveOutput(ostream &outputFile, nodePtr ptr);nodePtr recursiveFind(string str, nodePtr prevPtr, nodePtr &currPtr);void recursiveInsert(nodePtr newPtr, nodePtr prevPtr, nodePtr &currPtr);void unpunctuate(string &str);
#define LINKED_LIST_H#endif
CS 240 7
///////////////////////////////////////////////// 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(const LinkedList &list){ nodePtr currPtr, 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(){ nodePtr currPtr; while (head != NULL) { currPtr = head; head = head->next; currPtr->next = NULL; delete currPtr; }}
CS 240 8
// 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. //bool LinkedList::insert(string str){ nodePtr insertPtr = 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. //int LinkedList::retrieve(string str){ nodePtr currPtr = head; while (currPtr != NULL) { if (currPtr->item.word == str) return currPtr->item.count; else currPtr = currPtr->next; } return 0;}
CS 240 9
// 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, const LinkedList &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. //nodePtr LinkedList::getNode(string str, int ct){ nodePtr temp = new node; if (temp != NULL) { temp->item.word = str; temp->item.count = ct; temp->next = NULL; } return temp;}
CS 240 10
// 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, nodePtr ptr){ 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. //
nodePtr recursiveFind(string str, nodePtr prevPtr, 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 11
// 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(nodePtr newPtr, nodePtr prevPtr, 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){ const string 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 12
//////////////////////////////////////////////////// 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(){ LinkedList wordCountList; ifstream stringFile; ofstream resultFile;
stringFile.open("essay.txt"); resultFile.open("count.txt");
stringFile >> wordCountList; resultFile << wordCountList << endl;
stringFile.close(); resultFile.close();
return;}
CS 240 13
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.
Sample essay.txt file:
CS 240 14
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 15
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 16
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 17
Skip ListsOne 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.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.
Yes, it’s pretty complicated, but it essentially enables you to search a linked list in logarithmic time (rather than that awful linear time)!
CS 240 18
Implementing A Skip Liststruct node;typedef node *nodePtr;typedef nodePtr* nextArray;struct node{ elementType item; // Node’s data int nbrOfNexts; // 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. //bool SkipList::insert(elementType value){ nodePtr currPtr; nodePtr insertPtr = 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;}
top related