Chapter 4: Linked Lists

18
Chapter 4: Linked Lists ARRAYS POINTERS LIST OPERATIONS LIST VARIATIONS SKIP LISTS CS 240 1

description

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. - PowerPoint PPT Presentation

Transcript of Chapter 4: Linked Lists

Page 1: Chapter 4: Linked Lists

Chapter 4: Linked Lists

ARRAYS

POINTERS

LIST OPERATIONS

LIST VARIATIONS

SKIP LISTS

CS 240 1

Page 2: Chapter 4: Linked Lists

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

Page 3: Chapter 4: Linked Lists

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

Page 4: Chapter 4: Linked Lists

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...

Page 5: Chapter 4: Linked Lists

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

Page 6: Chapter 4: Linked Lists

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

Page 7: Chapter 4: Linked Lists

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; }}

Page 8: Chapter 4: Linked Lists

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;}

Page 9: Chapter 4: Linked Lists

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;}

Page 10: Chapter 4: Linked Lists

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);}

Page 11: Chapter 4: Linked Lists

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]);}

Page 12: Chapter 4: Linked Lists

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;}

Page 13: Chapter 4: Linked Lists

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:

Page 14: Chapter 4: Linked Lists

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)

Page 15: Chapter 4: Linked Lists

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.

Page 16: Chapter 4: Linked Lists

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!

Page 17: Chapter 4: Linked Lists

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)!

Page 18: Chapter 4: Linked Lists

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;}