460 likes | 475 Views
Linked Lists. CSCI 3333 Data Structures. by Dr. Bun Yue Professor of Computer Science yue@uhcl.edu http://sce.uhcl.edu/yue/ 2013. Acknowledgement. Mr. Charles Moen Dr. Wei Ding Ms. Krishani Abeysekera Dr. Michael Goodrich. Static Data Structures.
E N D
Linked Lists CSCI 3333 Data Structures byDr. Bun YueProfessor of Computer Scienceyue@uhcl.eduhttp://sce.uhcl.edu/yue/2013
Acknowledgement • Mr. Charles Moen • Dr. Wei Ding • Ms. Krishani Abeysekera • Dr. Michael Goodrich
Static Data Structures • The size of static data structures do not change. • Example: array. During runtime, if there are more elements to be stored in an array: • A new array needs to be created. • The old array may be deleted. • Elements are copied. • Expensive!
Dynamic Data Structures • The size of a dynamic data structure can change during runtime. • Many applications need to use dynamic data structures.
Linked List ADT • A linked list is a sequence of nodes linked in a chain. • Some operations: • Create • Delete • Traverse • Insert a node • Delete a node • Insert a linked list • Delete a linked list • …
Linked Lists vs. Arrays • Advantages: • Dynamic: size changes on demand. • Insertion and deletion of nodes: can be faster in some situations. • Disadvantages: • May not be randomly accessible efficiently. • Options: • Programmers needs to manage memory. • Automatic garbage collection may bring performance issues.
Linked List ADT • There are many kinds of linked lists implemented by different languages. • They are different ADTs. • We can study the ADTs to use them without knowing how they are implemented.
Example: C++ STL list • Some methods: • begin: returns an iterator addressing the first element in a list. • pop_back:deletes the element at the end of a list. • pop_front: deletes the element at the beginning of a list. • push_back: adds an element to the end of a list. • push_front: adds an element to the beginning of a list. • …
Example Program #include <list> #include <iostream> using namespace std ; typedef list<int> INT_LIST; int main() { INT_LIST ilist; ilist.push_back(1); ilist.push_front(2); ilist.push_front(3); ilist.push_front(4); ilist.push_back(5); cout << ilist.front() << endl; cout << ilist.back() << endl;
Example Program (cont) INT_LIST::iterator i; cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; // Note * cout << endl; for (i = ilist.begin(); i != ilist.end(); ++i) (*i)++; cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; // Note * cout << endl; ilist.pop_front(); ilist.pop_back(); cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; // Note * cout << endl; }
Output 1 ilist.push_back(1); ilist.push_front(2); ilist.push_front(3); ilist.push_front(4); ilist.push_back(5); cout << ilist.front() << endl; cout << ilist.back() << endl; Output: 4 5
Output 2 INT_LIST::iterator i; cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; // Note * cout << endl; for (i = ilist.begin(); i != ilist.end(); ++i) (*i)++; cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; // Note * cout << endl; Output: Int list content: 4 3 2 1 5 Int list content: 5 4 3 2 6
Output 3 ilist.pop_front(); ilist.pop_back(); cout << "Int list content:"; for (i = ilist.begin(); i != ilist.end(); ++i) cout << " " << *i; cout << endl; Output: Int list content: 4 3 2
Perl Example • In Perl, an array is also a list. • It is built into the language. (You can see the importance of lists here.) • Array/List variables start with the symbol @, e.g. @a. • List elements are scalar (which starts with the symbol $), e.g. $a[1], $a[12], etc.
Perl’s List Examples @num[1,2] = (3,4); # $num[1] = 3; $num[2] = 4; @num[1,2] = @num[2,1]; # swap $num[1] and $num[2]; @num[1,2] = @num[3,3]; # $num[1] = $num[3]; $num[2] = $num[3]; ($num[1], $num[2]) = ($num[2], $num[1]); # swap $num[1] and $num[2]; @num = (1,2,3,4,5)[3,2,1]; # @num = (4,3,2); ($first, @num) = @num; # remove the first element of @num into $first (@num, $last) = @num; # pop the last element to $last
Java’s List Interface ADT • Java has a List interface for ordered collection (sequence). • Super-interface: • Collection • Iterable • Some known implementation classes: • AbstractList • LinkedList • ArrayList • Vector • … • Practical APIs are much richer than those found in some textbooks!
Interable Interface • Target of a for each statement. • One method only Iterator<T>iterator(): returns an iterator over a set of elements of type T. • Iterator<T> interface’s methods: • hasNext() • next() • remove()
Collection Interface • Group of objects, possibly with duplicates. • Super-interface: iterable. • Some methods: • add(E e) • addAll(Collection<? extends E> c) • contains(Object o) • remove(Object o) • removeAll(Collection<?> c) • size() • toArray() • …
List Interface • New required methods added: • Random access: • E get(int index) • E remove(int index) • Searching: • int indexOf(Object o) • int lastIndexOf(Object o) • List Iterator: • ListIterator<E> listIterator()
Concrete Class LinkedList<E> • Interface implemented: • Serializable • Cloneable • Iterable<E> Collection<E> Queue<E> Deque<E> • Iterable<E> Collection<E> List<E> • <E>: Actual E should be a class (not int, float, etc)
LinkedList Methods • From implementing Dequeue: • void addFirst(E e) • void addLast(E e) • E removeFirst() • E removeLast() • E offerFirst(): retrieve, not remove, exception version for empty Dequeue. • E peekFirst(): retrieve, not remove, return null version for empty Dequeue. • …
LinkedList Methods • From implementing List: • add(E e) • E get(int index) • E remove(int index) • remove(Object o) • int indexOf(Object o)
Finding the Right Structures • APIs are usually feature rich. • Study purposes of classes and interfaces. • Study hierarchical class and interface structures. • Study methods available.
Examples • Good to critically read program code. • http://www.java-samples.com/showtutorial.php?tutorialid=347 • http://www.idevelopment.info/data/Programming/java/collections/LinkedListExample.java
Implementation Issues • You do not need to know how classes are implemented to use them! • However, knowing the implementations: • May deepen understanding of the classes for better usages. • May help selecting the best class for the application. • May transfer this knowledge when you need to implement custom-designed classes yourself.
Example Implementation in C++ • Singly linked list of float • Each node has only one pointer to its successor. • The list stores a head pointer to the head of the list. • If the list is empty, the head pointer is null. • For simplicity, each node stores an float.
Implementation issues • Data members: • Value of float • head pointer • tail pointer: for efficiency • size of the list: for efficiency. • Tips: ensure all data members properly updated in operations.
Node • class FloatList{ private: // Declare a structure for the list. // Simliar to nested classes in Java struct ListNode { float value; struct ListNode *next; }; ListNode *head, *tail; // head and tail pointers int _size;
Method Prototypes public: FloatList() // Constructor { head = tail = NULL; _size = 0; } ~FloatList(); // Destructor • Tips: always include constructors and destructors.
Method Prototypes void appendTailNode(float); // insert node at the tail. bool deleteHeadNode(); // delete node at the head. bool deleteNode(float); // delete the first occurrence of the float // list. void displayList(); int size() { return _size; } bool isEmpty() { return _size == 0; } };
Method Implementations void FloatList::appendTailNode(float num) { ListNode *newNode; // Allocate a new node & store num newNode = new ListNode; newNode->value = num; newNode->next = NULL; if (head==NULL) { // empty list head = tail = newNode; } else { tail->next = newNode; tail = tail-> next; } _size++; }
Discussion • Maintenance of data members. • Trade off of using _size and tail: faster for some operations and slower for others. • Definition of newNode as class instead of struct: use of constructors.
deleteHeadNode bool FloatList::deleteHeadNode() { if (head == NULL) return false; else { ListNode *temp = head; head = head-> next; if (head == NULL) tail = NULL; delete temp; _size--; return true; } }
Discussion • Other possible methods: • deleteTailNode • May change prototype:
deleteNode(float f) bool FloatList::deleteNode(float f) { if (head == NULL) return false; else { ListNode *curr = head; ListNode *prev = NULL; while (curr != NULL && curr->value != f) { prev = curr; curr = curr-> next; } if (curr == NULL) // do not find the number to be deleted. return false; else // to be continued. } }
else part { if (prev == NULL) { // the head node contains the number. head = curr-> next; } if (curr == tail) { // the tail node will be deleted. tail = prev; } //link the prev node to skip the curr node. prev->next = curr->next; delete curr; _size--; return true; }
Discussion • Other possible methods. • deleteLastNode.
displayList void FloatList::displayList() { ListNode *nodePtr; nodePtr = head; while (nodePtr) { cout << nodePtr->value << " "; nodePtr = nodePtr->next; } cout << endl; }
Discussion • Not flexible. • Need an ‘iterator’.
Destructor FloatList::~FloatList(){ ListNode *curr, *next; curr = head; while (curr != NULL) { next = curr->next; delete curr; curr = next; } }
Test Program FloatList list; list.appendTailNode(2.2); list.appendTailNode(3.4); list.appendTailNode(2.2); list.appendTailNode(1.8); list.appendTailNode(2.2); list.displayList(); cout << "size: " << list.size() << endl; • Output: 2.2 3.4 2.2 1.8 2.2 size: 5
Test Program list.deleteHeadNode(); list.displayList(); cout << "size: " << list.size() << endl; list.deleteNode(2.2); list.displayList(); cout << "size: " << list.size() << endl; list.deleteNode(2.2); list.displayList(); cout << "size: " << list.size() << endl; • Outpit: 3.4 2.2 1.8 2.2 size: 4 3.4 1.8 2.2 size: 3 3.4 1.8 size: 2
Test Program list.deleteHeadNode(); list.deleteHeadNode(); list.displayList(); cout << "size: " << list.size() << endl; • Output: size: 0
Discussion • Other possible methods: • getHeadNodeValue() • getTailNodeValue() • deleteTailNode() • …
Discussion • Some operations, deleteTailNode may take longer time, O(N), where N is the number of nodes in the list. • May use a doubly linked list. • Need to have a way to analyze performance. • List only works for float • May use template.