Presentation is loading. Please wait.

Presentation is loading. Please wait.

Abstract Data Type (ADT) &List (Part II) Lecture Objectives Implement linked list using pointers Concept of iterators STL's list class Other variations.

Similar presentations


Presentation on theme: "Abstract Data Type (ADT) &List (Part II) Lecture Objectives Implement linked list using pointers Concept of iterators STL's list class Other variations."— Presentation transcript:

1

2 Abstract Data Type (ADT) &List (Part II)

3 Lecture Objectives Implement linked list using pointers Concept of iterators STL's list class Other variations of linked list

4 Review of Pointers #include using namespace std; struct Position { int x; int y; }; int main() { Position pos; Position* p; p = &pos; p->x = 2; p->y = 4; cout << pos.x << " " << pos.y << endl; cout << (*p).x << " " << (*p).y << endl; cout x y << endl; pos.x = 5; pos.y = 7; cout << pos.x << " " << pos.y << endl; cout << (*p).x << " " << (*p).y << endl; cout x y << endl; } 2 4 5 7 output:

5 #include using namespace std; struct Position { int x; int y; }; int main() { Position* p; p = new Position; p->x = 2; p->y = 4; cout << (*p).x << " " << (*p).y << endl; cout x y << endl; delete p; } 2 4 output:

6 Review of Array-based Linked List The most basic structure of an array-based implementation of linked list is a LIstNode as represented in the diagram above and implemented as the code shown. Here the field item is the data to be stored in the list and the field next is basically acting as a 'pointer' to the next ListNode. item next ListNode struct ListNode { char item; int next; }; T 4 X M 5 R 7 A 0 I 1 data[0] data[1] data[2] data[3] data[4] data[5] data[6] data[7] 3 head 1 tail

7 In the array-based implementation of linked list, the 'pointer' such as head, tail, cur, prev etc. are not really C++ pointers, they are actually an integer value indicating the index to the array used to store all the nodes. In the example above, cur has a value of 0, that means this 'pointer' is now pointing at data[0] which contain a LIstNode. The item stored there is data[cur].item which is 'A'. data[cur].next would give us the 'pointer' to the next node. A pointer that is not pointing to anything is represented as having value -1. B 0 data[1] A 3 data[0] R 5 data[4] T 4 data[3] X data[5] M 1 data[2] 2 head 0 cur 3 data[cur].next 5 tail

8 Therefore, if I want to move the cur 'pointer' to 'point' to the next node, all I need to do is the statement below : cur = data[cur].next; B 0 data[1] A 3 data[0] R 5 data[4] T 4 data[3] X data[5] M 1 data[2] 2 head 0 cur 3 data[cur].next 5 tail

9 Likewise, in some function such as insert() and remove(), we will normally make the 'pointer' prev to 'point' to the node before the node at which we wanted to erase or insert. Therefore, given prev 'pointer', we would be able to find the cur 'pointer' with the statement below : cur = data[prev].next; B 0 data[1] A 3 data[0] R 5 data[4] T 4 data[3] X data[5] M 1 data[2] 2 head 0 prev 3 cur 5 tail

10 To erase the node pointed by cur, we will do the following : 1. Mark the array location as free slot by : data[cur].item = '\0'; 2. Connect the node pointed by prev to the node after the cur node by : data[prev].next = data[cur].next; B 0 data[1] A 4 data[0] R 5 data[4] \0 4 data[3] X data[5] M 1 data[2] 2 head 0 prev 3 cur 5 tail

11 To insert a node at the node position pointed by cur, we will do the following : 1. Find a free slot, and call the 'pointer' to the free slot as slot : int slot = getFirstEmptySlot(); 2. Assign the item to this slot by : data[slot].item = ch; 3. Connect the node pointed by prev to the new node by : data[prev].next = slot; 4. Connect the new node to the node pointed by cur : data[slot].next = cur; B 0 data[1] A 6 data[0] R 5 data[4] \0 4 data[3] X data[5] M 1 data[2] 2 head 0 prev 3 cur 5 tail Z 3 data[6] 6 slot

12 For all the cases of inserting a node or erasing a node, we must always make sure we handle 2 special cases : 1. When the node to be erased or inserted is at the head of the linked list. 1. When the node to be erased or inserted is at the tail of the linked list. For these cases, we need to ensure : 1. Nodes are connected properly 2. the 'pointer' head and tail are updated correctly.

13 Linked List : Pointer-based Implementation In the pointer-based implementation of linked list, there are THREE major differences as compared with array-based implementation : 1. The pointer such as head, tail, cur, prev etc. are REAL C++ pointers, they are actually pointer to the structure LIstNode, i.e. type is LIstNode* as declared above. 2. There are no internal array within the list class to store the LIstNodes, instead all nodes are stored in the heap memory using the statement : ListNode* slot = new ListNode; Note : slot now is also a REAL C++ pointer. 3. The LIstNode itself is different, the field next is also a REAL C++ pointer (see next slide) ListNode *head, *tail, *prev, *cur;

14 The most basic structure of an array-based implementation of linked list is a LIstNode as represented in the diagram above and implemented as the code shown. Here the field next is basically a pointer to another ListNode. item next ListNode struct ListNode { char item; ListNode* next; };

15 In the example above, cur is a pointer to a ListNode containing the item 'A'. The item stored there is cur->item which is 'A'. cur->next would give us the pointer to the next node. A pointer that is not pointing to anything is represented as having value NULL as shown in the last node. B 4650 4580 A 2104 4650 R 8692 1374 T 2104 X null 8692 M 4580 3122 head 4650 cur 2104 cur->next 8692 tail Whenever in the array-based linked list you have data[ptr].field, you should have ptr->field in the pointer-based linked list

16 Therefore, if I want to move the cur pointer to point to the next node, all I need to do is the statement below : cur = cur->next; B 4650 4580 A 2104 4650 R 8692 1374 T 2104 X null 8692 M 4580 3122 head 4650 cur 2104 cur->next 8692 tail

17 B 4650 4580 A 2104 4650 R 8692 1374 T 2104 X null 8692 M 4580 3122 head 4650 prev 2104 cur 8692 tail Likewise, in some function such as insert() and remove(), we will normally make the pointer prev to point to the node before the node at which we wanted to erase or insert. Therefore, given prev pointer, we would be able to find the cur pointer with the statement below : cur = prev->next;

18 B 4650 4580 A 1374 4650 R 8692 1374 2104 X null 8692 M 4580 3122 head 4650 prev 2104 cur 8692 tail To erase the node pointed by cur, we will do the following : 1. Connect the node pointed by prev to the node after the cur node by : prev->next = cur->next; 2. Free the memory pointed by cur : delete cur; Make sure the sequence is correct, make sure cur->next is assigned to prev->next before you free memory pointed by cur.

19 B 4650 4580 A 5114 4650 R 8692 1374 T 2104 X null 8692 M 4580 3122 head 4650 prev 2104 cur 8692 tail To insert a node at the node position pointed by cur, we will do the following : 1. Find a free slot, and call the pointer to the free slot as slot : ListNode* slot = new ListNode; 2. Assign the item to this slot by : slot->item = ch; 3. Connect the node pointed by prev to the new node by : prev->next = slot; 4. Connect the new node to the node pointed by cur : slot->next = cur; Z 2104 5114 slot

20 Just as in array-based implementation, for all the cases of inserting a node or erasing a node, we must always make sure we handle 2 special cases : 1. When the node to be erased or inserted is at the head of the linked list. 1. When the node to be erased or inserted is at the tail of the linked list. For these cases, we need to ensure : 1. Nodes are connected properly 2. the 'pointer' head and tail are updated correctly.

21 class MyCharListPtr { public: MyCharListPtr(); ~MyCharListPtr(); MyCharListPtr(const MyCharListPtr& list); MyCharListPtr& operator= (const MyCharListPtr& list); void push_front(char ch); void pop_front(); void push_back(char ch); void pop_back(); char& at(int index); char& operator[](int index); char& front(); char& back(); int size() const; bool empty() const; void clear(); void display(); void erase(int index); void insert(int index, char ch); private:.... }; Class Declaration : If you notice, this class interface is EXACTLY the SAME with the earlier array-based linked list class. This is a GOOD example of data abstraction, the interface of a ADT is independent of its implementation !!

22 class MyCharListPtr { public:... private: //OLD// int getFirstEmptySlot(); //OLD// void increaseCapacity(); //OLD// ListNode* data; //OLD// int Capacity; struct ListNode { char item; //OLD// int next; /*NEW*/ ListNode* next; }; //OLD// int getDataIndexOfNode(int nodeNo); /*NEW*/ ListNode* getPointerToNode(int nodeNo); int Size; //OLD// int head, tail, cur, prev; /*NEW*/ ListNode *head, *tail, *cur, *prev; }; Private data and member functions : Lines begin with //OLD// are lines from earlier array-based implementation, they are now NOT used and commented. These lines are shown here for comparison purposes. Lines begin with /*NEW*/ are new lines as compared with earlier array-based implementation More explanation next slide... These lines are same with array-based implementation.

23 class MyCharListPtr { public:... private: //OLD// int getFirstEmptySlot(); //OLD// void increaseCapacity(); //OLD// ListNode* data; //OLD// int Capacity; struct ListNode { char item; //OLD// int next; /*NEW*/ ListNode* next; }; //OLD// int getDataIndexOfNode(int nodeNo); /*NEW*/ ListNode* getPointerToNode(int nodeNo); int Size; //OLD// int head, tail, cur, prev; /*NEW*/ ListNode *head, *tail, *cur, *prev; }; Implementation is now very different from earlier array-based implementation. Do not need this anymore as memory allocation is now done with new operator and stored at the heap memory. these pointers are now REAL C++ pointer of type LIstNode*.

24 MyCharListPtr::MyCharListPtr() //OLD// : Capacity(8), Size(0) /*NEW*/ : Size(0) { //OLD// head = tail = cur = prev = -1; /*NEW*/ head = tail = cur = prev = NULL; //OLD// data = new ListNode[Capacity]; //OLD// for (int i=0; i<Capacity; i++) //OLD// { //OLD// data[i].item = '\0'; //OLD// data[i].next = -1; //OLD// } } MyCharListPtr::~MyCharListPtr() { //OLD// delete[] data; /*NEW*/ while ( !empty() ) /*NEW*/ pop_front(); } Default Constructor / Destructor : NULL means the pointers are not pointing to anything yet. remove (pop out) all the nodes.

25 MyCharListPtr& MyCharListPtr::operator= (const MyCharListPtr& list) { if (this==&list) return *this; Size = list.Size; //OLD// Capacity = list.Capacity; //OLD// head = list.head; tail = list.tail; //OLD// cur = list.cur; prev = list.prev; //OLD// delete[] data; //OLD// data = new ListNode[Capacity]; //OLD// for (int i=0; i<Capacity; i++) //OLD// { //OLD// data[i].item = list.data[i].item; //OLD// data[i].next = list.data[i].next; //OLD// } /*NEW*/ clear(); /*NEW*/ head = tail = NULL; /*NEW*/ cur = list.head; /*NEW*/ while (cur != NULL) { /*NEW*/ push_back( cur->item ); /*NEW*/ cur = cur->next; /*NEW*/ } return *this; } Assignment operator : Start with a empty list push all the items from the source list to the current list.

26 MyCharListPtr::MyCharListPtr(const MyCharListPtr& list) //OLD// : Capacity(list.Capacity), Size(list.Size) { //OLD// data = new ListNode[Capacity]; //OLD// for (int i=0; i<Capacity; i++) //OLD// { //OLD// data[i].item = list.data[i].item; //OLD// data[i].next = list.data[i].next; //OLD// } /*NEW*/ *this = list; } Copy Constructor : Making use of the assignment operator

27 capacity / size functions : remove (pop out) items until the list is empty int MyCharListPtr::size() const { return Size; } bool MyCharListPtr::empty() const { //OLD// if (head == -1) return true; /*NEW*/ if (head == NULL) return true; } void MyCharListPtr::clear() { //OLD// Size = 0; //OLD// head = tail = cur = prev = -1; //OLD// for (int i=0; i<Capacity; i++) //OLD// data[i].item = '\0'; /*NEW*/ while ( !empty() ) /*NEW*/ pop_front(); } NOTE : there are no functions regarding capacity since this issues does not arise.

28 slot points to newly allocated LIstNode in the heap memory void MyCharListPtr::push_front(char ch) { if ( empty() ) { //OLD// data[0].item = ch; //OLD// data[0].next = -1; //OLD// head = tail = 0; /*NEW*/ head = new ListNode; /*NEW*/ head->item = ch; /*NEW*/ head->next = NULL; /*NEW*/ tail = head; } else { //OLD// int slot = getFirstEmptySlot(); //OLD// data[slot].item = ch; //OLD// data[slot].next = head; /*NEW*/ ListNode* slot = new ListNode; /*NEW*/ slot->item = ch; /*NEW*/ slot->next = head; head = slot; } Size++; } Push data to front of the list :

29 void MyCharListPtr::push_back(char ch) { if ( empty() ) { //OLD// data[0].item = ch; //OLD// data[0].next = -1; //OLD// head = tail = 0; /*NEW*/ head = new ListNode; /*NEW*/ head->item = ch; /*NEW*/ head->next = NULL; /*NEW*/ tail = head; } else { //OLD// int slot = getFirstEmptySlot(); //OLD// data[slot].item = ch; //OLD// data[slot].next = -1; /*NEW*/ ListNode* slot = new ListNode; /*NEW*/ slot->item = ch; /*NEW*/ slot->next = NULL; //OLD// data[tail].next = slot; /*NEW*/ tail->next = slot; tail = slot; } Size++; } Push data to back of the list :

30 void MyCharListPtr::display() { cout << "["; cur = head; //OLD// while (cur != -1) /*NEW*/ while (cur != NULL) { //OLD// cout << data[cur].item; //OLD// cur = data[cur].next; /*NEW*/ cout item; /*NEW*/ cur = cur->next; } cout << "]" << endl; } Displaying the content of the list :

31 //OLD// int MyCharList::getDataIndexOfNode(int nodeNo) { /*NEW*/ ListNode* MyCharListPtr::getPointerToNode(int nodeNo) { if ( empty() ) { cout << "Error : empty list" << endl; exit(1); } if (nodeNo >= Size) { cout << "Error : past the end" << endl; exit(1); } cur = head; int i = 0; while (i < nodeNo) { //OLD// cur = data[cur].next; /*NEW*/ cur = cur->next; i++; } return cur; } char& MyCharListPtr::at(int nodeNo) { //OLD// int i = getDataIndexOfNode(nodeNo); //OLD// return data[i].item; /*NEW*/ ListNode* p = getPointerToNode(nodeNo); /*NEW*/ return p->item; } char& MyCharListPtr::operator[](int nodeNo) { //OLD// int i = getDataIndexOfNode(nodeNo); //OLD// return data[i].item; /*NEW*/ ListNode* p = getPointerToNode(nodeNo); /*NEW*/ return p->item; } Index operation :

32 void MyCharListPtr::erase(int nodeNo) { if ( empty() ) { cout << "Error : empty list" << endl; exit(1); } if (nodeNo==0) { //OLD// data[head].item = '\0'; //OLD// head = data[head].next; /*NEW*/ ListNode* oldhead = head; /*NEW*/ head = head->next; /*NEW*/ delete oldhead; } else { //OLD// prev = getDataIndexOfNode(nodeNo-1); //OLD// cur = data[prev].next; //OLD// data[prev].next = data[cur].next; //OLD// data[cur].item = '\0'; /*NEW*/ prev = getPointerToNode(nodeNo-1); /*NEW*/ cur = prev->next; /*NEW*/ prev->next = cur->next; /*NEW*/ delete cur; } Size--; } Erasing a node :

33 void MyCharListPtr::insert(int nodeNo, char ch) { //OLD// int slot = getFirstEmptySlot(); //OLD// data[slot].item = ch; /*NEW*/ ListNode* slot = new ListNode; /*NEW*/ slot->item = ch; if (nodeNo==0) { //OLD// data[slot].next = head; /*NEW*/ slot->next = head; head = slot; } else { //OLD// prev = getDataIndexOfNode(nodeNo-1); //OLD// cur = data[prev].next; //-1 if at end of list //OLD// data[prev].next = slot; //OLD// data[slot].next = cur; //-1 if at end of list //OLD// if (cur == -1) /*NEW*/ prev = getPointerToNode(nodeNo-1); /*NEW*/ cur = prev->next; /*NEW*/ prev->next = slot; /*NEW*/ slot->next = cur; /*NEW*/ if (cur == NULL) tail = slot; } Size++; } inserting a node :

34 void MyCharListPtr::pop_front() { if ( empty() ) { cout << "Error : pop empty list" << endl; exit(1); } //OLD// data[head].item = '\0'; //OLD// head = data[head].next; /*NEW*/ ListNode* oldhead = head; /*NEW*/ head = head->next; /*NEW*/ delete oldhead; Size--; } void MyCharListPtr::pop_back() { if ( empty() ) { cout << "Error : pop empty list" << endl; exit(1); } if ( Size == 1 ) pop_front(); //OLD// prev = getDataIndexOfNode(Size-2); //OLD// cur = data[prev].next; //OLD// data[cur].item = '\0'; //OLD// data[prev].next = -1; /*NEW*/ prev = getPointerToNode(Size-2); /*NEW*/ cur = prev->next; /*NEW*/ delete cur; /*NEW*/ prev->next = NULL; tail = prev; Size--; } Other functions :

35 char& MyCharListPtr::front() { if ( empty() ) { cout << "Error : empty list in front()" << endl; exit(1); } //OLD// return data[head].item; /*NEW*/ return head->item; } char& MyCharListPtr::back() { if ( empty() ) { cout << "Error : empty list in back()" << endl; exit(1); } //OLD// return data[tail].item; /*NEW*/ return tail->item; }

36 Linked List : Concept of Iterator MyCharListPtr list;... for (int i=0; i<list.size(); i++) cout << list[i] << endl; Given our pointer-based linked list, what is the weakness of the following code?

37 MyCharListPtr list;... for (int i=0; i<list.size(); i++) cout << list[i] << endl; The main weakness of this code is that each time the operator[] is called, internally, the linked list will search for the node i starting from the head node (node 0) It will be good if we can have some kind of mechanism that allows us to iterate through the list one node after another.

38 char data[] = { 'H', 'E', 'L', 'L', 'O', '\0' }; char* begin = &data[0]; char* end = &data[5]; char* itr; for (itr=begin; itr!=end; ++itr) cout << *itr << endl; Before we continue, let's consider the code below : Here the pointer itr is acting as an 'locator' of the elements stored in data. It starts at address as given by the value begin which happened to be the first element of the array. This pointer itr will iterate through each element of the array, the way it iterates through is by moving one element forward as given by the ++itr. In each iteration, we can access the items stored by using the dereferencing operator *itr. This process will continue until it reaches the address as given by the value end which is the address passed the last element. HELLOHELLO output:

39 MyCharListPtr list;... MyIterator itr; for (itr=list.begin(); itr!=list.end(); ++itr) cout << *itr << endl; It will be nice if we could iterate through our linked list in similar way as shown in the program below : From the code above, we can see that we will need to define some kind of class called MyIterator which contain the following operation : 1. operator *()2. operator ++ 3. operator ==4. operator != As for the MyCharListPtr class, it will need to implement the begin() and end() function which returns object of type MyIterator. begin() return an iterator at the head position of the list and end() return a iterator indicating position passed the end of the list. some data type, yet to be defined Of course, we will need to call some kind of function of list to know where the first item is and where the last item is.

40 class MyCharListPtr { public: struct iterator { char& operator *() { return ptr->item; } void operator ++() //prefix { ptr = ptr->next; } void operator ++(int) //postfix { ptr = ptr->next; } bool operator == (const iterator& itr) { return (ptr == itr.ptr); } bool operator != (const iterator& itr) { return (ptr != itr.ptr); } ListNode* ptr; }; iterator begin() { iterator i; i.ptr = head; return i; } iterator end() { iterator i; i.ptr = NULL; return i; }... }; we define a class called iterator within our linked list itself. This iterator class is basically very simple, it just keep track of a pointer which point to one node of the linked list. The operator ++ simple advance the pointer to the next listnode. This simply set the iterator to point to head node In our linked list, a pointer passed the end of list is assigned a value of NULL.

41 int main() { MyCharListPtr list; list.push_back('L'); list.push_back('I'); list.push_back('N'); list.push_back('K'); list.push_back('E'); list.push_back('D'); MyCharListPtr::iterator itr; for (itr= list.begin(); itr!=list.end(); ++itr) cout << *itr << endl; } LINKEDLINKED output: The iterator class is defined within the MyCharListPtr class.

42 Linked List : STL's list class The STL's list class is very much similar to the MyCharListPtr class that we have implemented, the major differences are : 1. list class is based on template, which means it can be used as container to store any kind of data. 2. list class does not have the operator [], meaning it is not a random access container but rather sequential container 3. Since list class does not have the operator [], it does not have the notion of list index, that means for erasing and inserting data, we can not specify the index to insert or erase. to insert or erase data, we must have an iterator to iterate to the point of deletion or insertion. (examples later) 4. list is a doubly linked list (more on this later). In our MyCharListPtr class, it is efficient to iterate through the nodes from head to tail (using the iterator) but not the other way round. However, for the list class, we can iterate through the list using a forward iterator or reverse iterator. 5. list class does not have the display() function. 6. Of course, list class has many more other functions.

43 STL's list : Sample Runs #include using namespace std; int main() { list lst; list ::iterator itr; lst.push_back('s'); lst.push_back('t'); lst.push_back('r'); lst.push_back('u'); lst.push_front('a'); lst.push_front('t'); lst.push_front('a'); lst.push_front('d'); lst.push_back('a'); lst.push_back('l'); lst.push_back('g'); lst.push_back('o'); for (itr=lst.begin(); itr!=lst.end(); ++itr) cout << *itr << endl; } datastrualgodatastrualgo output: must have this iterator iterate through the list

44 #include using namespace std; int main() { list lst; list ::iterator itr; lst.push_back('s'); lst.push_back('t'); lst.push_back('r'); lst.push_back('u'); cout << lst.front() << endl; cout << lst.back() << endl; lst.pop_front(); lst.pop_back(); for (itr=lst.begin(); itr!=lst.end(); ++itr) cout << *itr << endl; } sutrsutr output:

45 #include using namespace std; int main() { list planetlist; list ::iterator itr; planetlist.push_back("Venus"); planetlist.push_back("Earth"); planetlist.push_back("Sun"); planetlist.push_back("Mars"); itr = planetlist.begin(); itr++; planetlist.erase(itr); for (itr=planetlist.begin(); itr!=planetlist.end(); ++itr) cout << *itr << endl; } Venus Earth Mars output: erase function accept NOT index value, but iterator pointing at point of deletion.

46 #include using namespace std; int main() { list planetlist; list ::iterator itr; planetlist.push_back("Venus"); planetlist.push_back("Earth"); planetlist.push_back("Sun"); planetlist.push_back("Mars"); itr = find (planetlist.begin(), planetlist.end(), "Sun"); planetlist.erase(itr); for (itr=planetlist.begin(); itr!=planetlist.end(); ++itr) cout << *itr << endl; } Venus Earth Mars output: the STL's find function is an example of the algorithm component of STL.

47 #include using namespace std; int main() { list planetlist; list ::iterator itr; planetlist.push_back("Venus"); planetlist.push_back("Earth"); planetlist.push_back("Mars"); planetlist.push_back("Uranus"); itr = planetlist.begin(); planetlist.insert(itr,"Mercury"); itr = find (planetlist.begin(), planetlist.end(), "Uranus"); itr = planetlist.insert(itr,"Saturn"); itr = planetlist.insert(itr,"Jupiter"); for (itr=planetlist.begin(); itr!=planetlist.end(); ++itr) cout << *itr << endl; } Mercury Venus Earth Mars Jupiter Saturn Uranus output: insert function accept iterator pointing at point of insertion.

48 #include using namespace std; int main() { list planetlist; list ::iterator itr; planetlist.push_back("Venus"); planetlist.push_back("Earth"); planetlist.push_back("Mars"); planetlist.push_back("Jupiter"); itr = planetlist.begin(); cout << *itr << endl; itr++; cout << *itr << endl; itr++; cout << *itr << endl; itr--; cout << *itr << endl; } Venus Earth Mars Earth output: This iterator is bi-directional, meaning it can iterate backward

49 #include using namespace std; int main() { list planetlist; list ::reverse_iterator rItr; planetlist.push_back("Venus"); planetlist.push_back("Earth"); planetlist.push_back("Mars"); planetlist.push_back("Jupiter"); rItr = planetlist.rbegin(); cout << *rItr << endl; rItr++; cout << *rItr << endl; rItr++; cout << *rItr << endl; rItr--; cout << *rItr << endl; cout << "-----" << endl; for (rItr=planetlist.rbegin(); rItr!=planetlist.rend(); ++rItr) cout << *rItr << endl; } Jupiter Mars Earth Mars ----- Jupiter Mars Earth Venus output: this is a reverse iterator, its beginning is the tail of the list, forward means going from tail to head.

50 The STL's list class vs. STL's vector class, which one to use? 1. vector class is an array-based list while list class is based on linked list. 2. vector class suitable for random access while list class is more for sequential access. Therefore, if you have large set of data and the size of your data may shrink or grow from time to time and you constantly need to add or remove data from the list, then list class will be a good choice for you. Bear in mind that for the same amount of data, the list class will use more memory as compare with vector class. If your data set is small, vector class may be a better choice since whatever resizing may not be a big problem as compare with the ease of use (the random access nature).

51 Linked List Variation : Doubly LInked List If we compare the STL's list class with our MyCharListPtr class, one major different is that the iterator of the STL's list class can go forward or backward (bi-directional) while the iterator of our MyCharListPtr class can only go forward. For our MyCharListPtr class, if the iterator is currently pointing to a node and we want to retrieve the node before it, we will have to go all the way to the beginning of the list and iterate through the list again. This is very inefficient. The list class does not suffer this limitation, this is because it is implemented as a doubly linked list.

52 Doubly linked list : The most basic structure of a doubly linked list is represented in the diagram above and implemented as the code shown. Here there is an extra field called previous which will be used to connect to the node before the current node. item next ListNode struct ListNode { char item; ListNode* previous; ListNode* next; }; previous

53 S headcur cur->next tail null T R U As before, if we want to get the pointer of the next node, all I need to do is the statement below : nextnode = cur->next; On top of that, we are also able to get the pointer to the previous node, then we can use the following statement below : prevnode = cur->previous; cur->previous

54 S headcur nextnode tail null R U To erase the node pointed by cur, we will do the following : 1. Identify the previous node and next node : prevnode = cur->previous; nextnode = cur->next; 1. Connect the node pointed by prevnode to node pointed by nextnode : prevnode->next = nextnode; nextnode->previous = prevnode; 2. Free the memory pointed by cur : delete cur; prevnode

55 S headcur nextnode tail null T R U prevnode To insert a node at node position pointed by cur, we will do the following : 1. Identify the previous node : prevnode = cur->previous; 2. Find a free slot, and call the pointer to the free slot as slot : ListNode* slot = new ListNode; 3. Assign the item to this slot by : slot->item = ch; 4. Connect the node pointed by prevnode to the new node by : prevnode->next = slot; slot->previous = prevnode; 5. Connect new node to node pointed by cur : slot->next = cur;cur->previous = slot; Z slot

56 As usual, for all the cases of inserting a node or erasing a node, we must always make sure we handle 2 special cases : 1. When the node to be erased or inserted is at the head of the linked list. 1. When the node to be erased or inserted is at the tail of the linked list. For these cases, we need to ensure : 1. Nodes are connected properly 2. the pointer head and tail are updated correctly.

57 Advantage of Doubly Linked List : 1. You may have noticed that for node deletion or insertion, the previous node can be directly obtained based on the current node, without having to search through the list starting from the head node. 2. The iterator became bi-directional as it can now iterate forward as well as backward to the previous node.

58 Linked List Variation : Circular LInked List B 4650 4580 A 2104 4650 R 8692 1374 T 2104 X 3122 8692 M 4580 3122 ptr S T R U A circular list is basically a linked list of which the the next pointer of the last node points back to the first node (node 0). For doubly linked list, the previous pointer of first node points to the last node. There is no head or tail pointer, but we still need a pointer to one of the node in order to access the list. Application : Implementing a queue

59 Note about STL There are still a lot of things you can use in STL. In real life application, it is unlikely you would ever need to develop your own vector classes and linked list classes, you are strongly recommended to use STL's vector and list class instead. Even if you would program in Java, there are Java Collection Classes which contains classes for list and linked list as well. Then why do we ever need to learn to do our own list and linked list?

60 There are still a lot of things you can use in STL. In real life application, it is unlikely you would ever need to develop your own vector classes and linked list classes, you are strongly recommended to use STL's vector and list class instead. Even if you would program in Java, there are Java Collection Classes which contains classes for list and linked list as well. Then why do we ever need to learn to do our own list and linked list? ANSWER : 1. In the future, you may need to program in a different programming language that may not have build-in list and linked list classes 2. Understand how list and linked list are constructed will enable you to make wiser decision on what to use based on your need. 3. Understand how to constructed these classes will enable you to construct other type of user-defined ADT.


Download ppt "Abstract Data Type (ADT) &List (Part II) Lecture Objectives Implement linked list using pointers Concept of iterators STL's list class Other variations."

Similar presentations


Ads by Google