Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS204 – Advanced Programming Pointers and Linked Lists Part 2: Advanced Issues.

Similar presentations


Presentation on theme: "CS204 – Advanced Programming Pointers and Linked Lists Part 2: Advanced Issues."— Presentation transcript:

1 CS204 – Advanced Programming Pointers and Linked Lists Part 2: Advanced Issues

2 Linked Lists: deleting... //iterative void DeleteList ( node *head) { node *temp; while (head != NULL) { temp = head->next; delete head; head = temp; } } //recursive void DeleteList ( node *head) { if (head != NULL) { DeleteList(head->next); delete head; } } head 2

3 Linked Lists: deleting... head //recursive void DeleteList ( node *head) { if (head != NULL) { DeleteList(head->next); delete head; } } Unrolling the execution steps would look like this with the above list (after the 1st call): DeleteList(ptr to node2) DeleteList(ptr to node 3) DeleteList(null) //returns immediately Delete ptr-to-node3 //"delete head" line Delete ptr-to-node2 Delete ptr-to-node1 Notice that statements which are aligned occur in the same call to and execution order is top to bottom

4 4 Adding a node to a sorted list You are given a linked list in which the elements are ordered. You want to add a node with a new element, but you want to keep the list still ordered. To do so, you have to spot the place to insert the new node by traversing the list. Here, there are some cases to consider: –What if the list is empty? –What if I need to add a node before the beginning of the list? –What if I need to add a node somewhere inside the list? Caution you cannot go back! –What if I need to add a node at the end of the list? Now we are going to see some methods for this task. See ptrfunc.cpp for the implementation and a small demo

5 Adding a node to a sorted list node * AddInOrder(node * head, int newkey) // pre: list is sorted // post: add newkey to list, keep list sorted, return head of new list { node * ptr = head; // loop variable // if new node should be first, handle this case and return // in this case, we return address of new node since it is new head if (head == NULL || newkey info) { node * temp = new node; //node to be inserted temp->info = newkey; temp->next = head; //connect the rest return temp; } // check node one ahead so we don't pass! while (ptr->next != NULL && ptr->next->info < newkey) { ptr = ptr->next; } // postcondition: new node to be inserted just after the node ptr points //now insert new node with newkey after where ptr points to node * temp = new node; //node to be inserted temp->info = newkey; temp->next = ptr->next; //connect the rest ptr->next = temp; return head; } 5

6 Alternative using the constructor: Adding a node to a sorted list node * AddInOrder(node * head, int newkey) // pre: list is sorted // post: add newkey to list, keep list sorted, return head of new list { node * ptr = head; // loop variable // if new node should be first, handle this case and return // in this case, we return address of new node since it is new head if (head == NULL || newkey info) { node * temp = new node; //node to be inserted temp->info = newkey; temp->next = head; //connect the rest return temp; } // check node one ahead so we don't pass! while (ptr->next != NULL && ptr->next->info < newkey) { ptr = ptr->next; } // postcondition: new node to be inserted just after the node ptr points //now insert new node with newkey after where ptr points to node * temp = new node; //node to be inserted temp->info = newkey; temp->next = ptr->next; //connect the rest ptr->next = temp; return head; } return new node(newkey, head); ptr->next = new node(newkey,ptr->next); 6

7 Adding a node to a sorted list – Another alternative method node * AddInOrder(node * head, int newkey) // pre: list is sorted // post: add newkey to list, keep list sorted, return head of new list { node * ptr = head; // loop variable // if new node should be first, handle this case and return // in this case, we return address of new node since it is new head if (head == NULL || newkey info) { return new node(newkey, head); } node * prev; //to point to the previous node while (ptr != NULL && ptr->info < newkey) { prev = ptr; //hold onto previous node so we do not pass too far ptr = ptr->next; } // postcondition: new node to be inserted between prev and ptr //now insert node with newkey prev->next = new node(newkey,ptr); return head; } 7

8 Recursive version (no longer discussed in class, so you are not responsible) node * AddInOrderRecursive(node * head, int newkey) // pre: list is sorted // post: add newkey to list, keep list sorted, return head of new list // with newkey in it { //base condition of the recursion if (head == NULL || newkey info) return new node(newkey, head); head->next = AddInOrderRecursive (head->next, newkey); //recursive call return head; } In the main function it is called as e.g. AddInOrderRecursive (head, 6) in order to insert a node with 6 as the info field. Neat code but maybe hard to understand. Let's trace a case on the board. newkey head 8

9 Deleting a single node Several algorithms exist We will cover one –toBeDeleted points to the node to be deleted –Need to keep another pointer for the previous node –Special case if the first node to be deleted No previous node Head must be updated See the next slide for the code –Also in ptrfunc.cpp 9 head toBeDeleted

10 Deleting a single node void DeleteOneNode (node * toBeDeleted, node * & head) /* pre: toBeDeleted points to the node to be deleted from the list post: deletes the node pointed by toBeDeleted, updates head if changes */ { node * ptr; if (toBeDeleted == head) //if the node to be deleted is the first node { head = head->next; delete toBeDeleted; } else //if the node to be deleted is in the middle or at the end { ptr = head; while (ptr->next != toBeDeleted) ptr = ptr->next; //after while, ptr points to the node just before toBeDeleted //connect the previous node to the next node and delete ptr->next = toBeDeleted->next; delete toBeDeleted; } 10 toBeDeleted head

11 Circular lists Last node does not point to NULL, but points to the first node. No change in the node struct definition, but processing the list is different now Actually there is no head or tail –All nodes are semantically same –Keeping a pointer for any node would work –However, for the sake of visualization and ease of coding this pointer can be for the pseudo-last node of the list. First node of the list is next of last! For traversal the entire list, no sentinel check such as NULL –instead stop where you started –Of course, if the list is not empty; if empty, last is NULL See next slide for an example (also see ptrfunc.cpp) 11 last

12 Circular lists Count number of elements in a circularly linked list. 12 last int countCircular (node * last) //pre: list is a circularly linked one, last points to the last node //post: returns number of nodes in the list { if (last == NULL) return 0; //list is empty int count = 0; node * ptr = last; do { count++; ptr = ptr->next; //advance to the next line } while (ptr != last); //loop until you reach where started return count; }

13 Doubly linked lists Each node does not only keep a pointer for the next node, but also for the previous node. Implications –In this way you can traverse the list in both directions –Need to keep not only head, but also tail –Insert, delete operations are a bit more complex Convention –Next of tail is NULL (as in regular linked list) –Prev of head is also NULL 13 head tail struct node { int info; node *next; node *prev; }; Think of routines for building, insertion, deletion, etc.

14 Other type of linked lists Circular and doubly linked lists Two dimensional linked lists Multidimensional linked lists Hybrid lists –e.g. Head is a struct of a different type and includes a pointer for our list –Two dimensional version of the above Circular and/or doubly linked versions of the above As you see, sky is the limit :-) 14

15 15 Linklist class with pointers An example class Class definition (in the header file – say linkedlist.h) Definition of struct node can be either in the same header file or in another included header file class linkedlist { private: node * head; public: linkedlist (); void printList(); void addToBeginning(int n); };

16 16 Linklist class with pointers Class implementation (in a cpp file - say linkedlist.cpp): linkedlist::linkedlist () { head = NULL; } void linkedlist::addToBeginning (int n) { node *ptr = new node(n,head); head = ptr; } void linkedlist::printList () { node * ptr = head; while (ptr != NULL) { cout info << endl; ptr = ptr->next; } cout << endl; } Notice here that head is updated, but we did not need addToBeginning to return a parameter or use reference variables because member functions already access the fields of the current object. in main cpp file (linkedlistdemo.cpp): linkedlist mylist, list2; mylist.AddToBeginning(5); list2.AddToBeginning(1);

17 Using Pointers: Be Careful Remember to guard every pointer dereference so as not to access the contents of a NULL pointer. e.g.: while (p != NULL && p->info < key)... Thanks to short-circuit evaluation rule of C++; when p is null, p != NULL is evaluated to false and the rest of the boolean expression is not evaluated. On the other hand, the following would crash your program when p is null pointer. while (p->info < key)...

18 18 Using Pointers: good practices C++ allows programmers to define new types (actually an alias for a type) using the typedef statement: typedef int* IntPtr; IntPtr p1, p2; //same as int * p1, p2; Avoids mistake of forgetting the * character Simpler and more intuitive to use pointer reference parameter: void get_space(IntPtr & ptr); same as void get_space(int* & ptr);

19 Using pointers for pass-by-reference You can use a pointer variable for the same job of a reference parameter –As the argument, you pass the address of a regular variable –So that when you use dereferencing within the function, you reach and change the argument's value. –This is more of C style, but know it! void swap (int & x, int & y) { int temp = x; x = y; y = temp; } void swap (int * x, int * y) { int temp = *x; *x = *y; *y = temp; } This is called as: int num1=13, num2=4;... swap (&num1, &num2); //num1 becomes 4, num2 becomes Using reference parameters Using pointers

20 C style dynamic memory management malloc ( size ) –A function that dynamically allocates size bytes –Returns void pointer (to be cast to other types of pointers) calloc ( num, length ) –A function that dynamically allocates num*length bytes In order to allocate an array of num elements, length is calculated using sizeof function. sizeof (type_name) returns the number of bytes used by type_name. –Returns void pointer (to be cast to other types of pointers) free( ptr ) –Deallocates (or frees) a memory block pointed by ptr. The memory block to be freed should have been previously allocated with malloc or calloc. You may need to include and The usage these memory blocks is mostly the same as the ones allocated with new. Let's see an example use at malloc_calloc.cpp 20

21 Pointers and Dynamic Arrays

22 1D arrays with the new operator To dynamically allocate an array, specify the array size in square brackets [] after the type: int *ptr; ptr = new int [20]; // dynamically allocate enough // memory for an array of 20 ints. // ptr now points to the // 1st element of the array. Instead of 20 (size of the allocated array), you can also use a user-defined variable: cin >> size; ptr = new int [size]; Here be careful that memory allocation is dynamic, but the size of the array cannot be enlarged or reduced once the memory allocation is done To free the memory allocated to a dynamically allocated array, you must include a pair of empty square brackets in the delete statement, just after the delete command: delete [] ptr; ptr 22

23 Memory allocation with the new operator Points to be careful What happens if you don't have enough memory? NULL pointer is returned You need to check for that possibility int * ptr; ptr = new int [100000]; if ( ptr != NULL)... 23

24 24 Pointers and 2D Dynamic Arrays

25 25 When we need 2-dimensional data for matrices, images etc, we have two option: 1) Create a 1-dimensional data, by using: data = new int [rows * columns]; Now data represents the 2D structure implicitly. For instance the element M[x,y] is found in data[x*columns+y]. [0…columns-1] indices belong to first row, [columns …2*columns-1] indices belong to 2nd row, etc. 2) Create a truly 2-dimensional data. In this case array indexing is easier. Will see both cases

26 26 Case 1) 1d data w/ class (See matrixclass.h, matrixclass.cpp, matrixclassdemo.cpp) class Matrix1D { private: int rows, cols; int * data; public: Matrix1D(int r, int c); int GetIndex(int i, int j); void SetIndex(int i, int j, int val); }; Matrix1D::Matrix1D(int r, int c) { rows = r; cols = c; int size = rows * cols; data = new int [size]; //this is a one long array of ints – 1D } int Matrix1D::GetIndex(int i, int j) { return data[i*cols+j]; } void Matrix1D::SetIndex(int i, int j, int value) { data[i*cols+j] = value; } Usage in main: Matrix1D m(5,10); //a 5x10 matrix m.SetIndex(0,5,33); cout << m.GetIndex(0,5) << endl;

27 Pointer to pointer Syntax Type ** variable ; Example int ** p; p = new int *; *p = new int; **p = 12; cout << **p; //Displays 12 We can make use of pointer to pointer for the implementation of 2D arrays (see next) 27 p?? p ? p ? p 12

28 28 Case 2) 2d data allocation – w/o classes int ** M; int rows, columns, i,j; cout > rows; cout > columns; // M is an array of int pointers M = new int* [rows]; //each M[i] is a pointer (array of int) for (i = 0; i> M[i][j]; cout<< endl; } print_table(M, rows, columns); for (i = 0; i< rows; i++) // Returning memory to free heap for reuse delete [] M[i]; delete [] M; }

29 29 Case 2) 2d data allocation – w/o classes void print_table(int** values, int num_rows, int num_cols) { int i, j; for (i = 0; i < num_rows; i++) { for (j= 0 ; j< num_cols ; j++) cout << values[i][j] << " "; cout << endl; } Question: What happens if we change the values of the matrix elements in the above function? –e.g. values[2][1] = 100; The above code (this and prev. slide) is shown in matrixnoclass.cpp

30 30 Case 2) 2d data allocation w/ class (See matrixclass.h, matrixclass.cpp, matrixclassdemo.cpp) class Matrix2D { private: int rows, cols; int ** data; public: Matrix2D(int r, int c); int GetIndex(int i, int j); void SetIndex(int i, int j, int val); }; Matrix2D::Matrix2D(int r, int c) { rows = r; cols = c; data = new int* [r]; for (int i = 0; i

31 Pointers and Static Arrays Efficiency Issues

32 Pointers and Arrays int myarray[10]; //static (a.k.a. built-in) array definition int *ptr; ptr = myarray; //address stored in pointer is the address of the // first element in array OR ptr = &myarray[0]; //equivalent and more clear After that, you can refer to myarray[i] also as ptr[i] See ptr_staticarrays.cpp for an example 32

33 Pointers and Arrays: Increment int students[10]; int *ptr; ptr = students; //ptr points to students [0] ptr = &students[0]; //same as above ptr++;//ptr points to students[1] ptr increments by the size of the type pointed by the pointer –in this case the pointer points to 4 bytes ahead (not 1 byte) to the next integer 33

34 Pointer Increment/Decrement Depends on Pointed Type char c = 'z'; char *p; //p points nowhere p = &c; // p now points to c. p--; // p now points to the address of the BYTE in the runtime stack before c p++; // p points to c again. If the pointer pointed to integers: int myintarr [20]; int * p = myintarr; p++; // p now points to 4 BYTES ahead (when integers take 4 bytes) This is the reason why we dont just say: pointer p ; We want the compiler to move the pointer by the size of the data type it points to. Actually, not only increment/decrement, any integer addition/subtraction works in the same manner 34

35 Pointers and 2D static arrays int my2d [3][5]; //2D static array definition //to have pointer for this array, we need an array of ptrs for each row //and assign each row of 2D array to each element of ptr array int * ptr2[3]; ptr2[0] = my2d[0]; ptr2[1] = my2d[1]; ptr2[2] = my2d[2]; int **ptr3 = ptr2; //ptr3 points to the pointer array After that, you can refer to my2d[i,j] also as ptr3[i,j] See ptr_staticarrays.cpp for an example 35

36 Pointer use for efficiency - 1 const int NUM_STUDENTS 10; int students[NUM_STUDENTS]; int *p; for (i=0; i < NUM_STUDENTS; i++) cout << students[i]; or p = &students[0]; for (i=0; i < NUM_STUDENTS; i++) cout << *p++; 2nd one is direct access to memory. 1st one requires addition to calculate the address of array element each time. Thus 2nd one is more efficient, use it!! 36

37 37 Ptr use for efficiency - 2 Assume we have a static 1D array which actually holds 2D data: char img [width*height]; //grayscale image with pixel values size = width*height; for (i=0; i < size; i++) { if (img[i] > 200) img[i] = 255;//assign a new gray value } char * ptr = &img[0]; //How much do you save?? for (i=0; i < size; i++)//Lets say size is 10,000. {//2*10,000 additions for array indexing if (*ptr > 200)//are saved replaced with 10,000 increments *ptr=255; ptr++; }

38 Ptr use for efficiency - 3 Typical array/string copying routine Note that memory for destination has already been allocated in the calling program void my_strcpy (char * destination, char * source) { char *p = destination; while (*source != '\0') { *p++ = *source++; //I had told you not to use cryptic code //but this piece of code is very common that everyone can understand it easily } *p = '\0'; //why do we need this? } 38


Download ppt "CS204 – Advanced Programming Pointers and Linked Lists Part 2: Advanced Issues."

Similar presentations


Ads by Google