Linked Lists: deleting...

Slides:



Advertisements
Similar presentations
Chapter 22 Implementing lists: linked implementations.
Advertisements

Copyright © 2003 Pearson Education, Inc. Slide 1.
Chapter 17 Linked Data Structures. Copyright © 2006 Pearson Addison-Wesley. All rights reserved Learning Objectives Nodes and Linked Lists Creating,
Chapter 3: Linked List Tutor: Angie Hui
Introduction to C Programming
Pointers.
Lecture 15 Linked Lists part 2
Chapter 7: Arrays In this chapter, you will learn about
Chapter 17 Linked Lists.
COMP171 Fall 2005 Lists.
Chapter 4 Linked Lists. © 2005 Pearson Addison-Wesley. All rights reserved4-2 Preliminaries Options for implementing an ADT List –Array has a fixed size.
Stacks, Queues, and Linked Lists
Linked Lists.
CSCE 3110 Data Structures & Algorithm Analysis
Linked Lists Chapter 4.
1/27 COP 3540 Data Structures with OOP Chapter 5 Linked Lists.
1111 Abstract Data Types Cpt S 223. School of EECS, WSU.
Data Structures ADT List
Chapter 24 Lists, Stacks, and Queues
1 Linked Lists III Template Chapter 3. 2 Objectives You will be able to: Write a generic list class as a C++ template. Use the template in a test program.
Data Structures Using C++
Linked Lists CSE 2451 Matt Boggus. Dynamic memory reminder Allocate memory during run-time malloc() and calloc() – return a void pointer to memory or.
PRESENTED BY MATTHEW GRAF AND LEE MIROWITZ Linked Lists.
LIST PROCESSING.
Double-Linked Lists and Circular Lists
CHP-5 LinkedList.
FIFO Queues CSE 2320 – Algorithms and Data Structures Vassilis Athitsos University of Texas at Arlington 1.
Chapter 1 Object Oriented Programming 1. OOP revolves around the concept of an objects. Objects are created using the class definition. Programming techniques.
1 DATA STRUCTURES. 2 LINKED LIST 3 PROS Dynamic in nature, so grow and shrink in size during execution Efficient memory utilization Insertion can be.
CSCI2100B Linked List Jeffrey
Review Pseudo Code Basic elements of Pseudo code
Linked Lists.
Data Structure Lecture-5
Data Structure Lecture-3 Prepared by: Shipra Shukla Assistant Professor Kaziranga University.
Pointers and Arrays Chapter 12
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Chapter 13 Pointers and Linked Lists.
§3 The Stack ADT 1. ADT A stack is a Last-In-First-Out (LIFO) list, that is, an ordered list in which insertions and deletions are.
Introduction to Linked Lists In your previous programming course, you saw how data is organized and processed sequentially using an array. You probably.
David Notkin Autumn 2009 CSE303 Lecture 13 This space for rent.
Chapter 17 Linked List Saurav Karmakar Spring 2007.
Copyright © 2008 Pearson Addison-Wesley. All rights reserved. Chapter 9 Pointers and Dynamic Arrays.
17. ADVANCED USES OF POINTERS. Dynamic Storage Allocation Many programs require dynamic storage allocation: the ability to allocate storage as needed.
CMPSC 16 Problem Solving with Computers I Spring 2014 Instructor: Tevfik Bultan Lecture 12: Pointers continued, C strings.
Dynamic Memory Allocation. Domain A subset of the total domain name space. A domain represents a level of the hierarchy in the Domain Name Space, and.
19&20-2 Know how to declare pointer variables. Understand the & (address) and *(indirection) operators. Dynamic Memory Allocation Related Chapter: ABC.
1 Chapter 16 Linked Structures Dale/Weems. 2 Chapter 16 Topics l Meaning of a Linked List l Meaning of a Dynamic Linked List l Traversal, Insertion and.
Lists Chapter 8. 2 Linked Lists As an ADT, a list is –finite sequence (possibly empty) of elements Operations commonly include: ConstructionAllocate &
Chapter 5 Linked List by Before you learn Linked List 3 rd level of Data Structures Intermediate Level of Understanding for C++ Please.
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Chapter 9 Pointers and Dynamic Arrays.
2005MEE Software Engineering Lecture 7 –Stacks, Queues.
Copyright © 2014 Pearson Addison-Wesley. All rights reserved. Chapter 9 Pointers and Dynamic Arrays.
Sudeshna Sarkar, CSE, IIT Kharagpur1 Structure and list processing Lecture
Data Structures and Algorithm Analysis Dr. Ken Cosh Linked Lists.
LINKED LISTS.
© Oxford University Press All rights reserved. Data Structures Using C, 2e Reema Thareja.
Prof. Amr Goneid, AUC1 CSCE 210 Data Structures and Algorithms Prof. Amr Goneid AUC Part R2. Elementary Data Structures.
DYNAMIC MEMORY ALLOCATION. Disadvantages of ARRAYS MEMORY ALLOCATION OF ARRAY IS STATIC: Less resource utilization. For example: If the maximum elements.
Lecture 6 of Computer Science II
Winter 2009 Tutorial #6 Arrays Part 2, Structures, Debugger
Dynamic Allocation Review Structure and list processing
Linked List :: Basic Concepts
Pointers and Linked Lists
Lectures linked lists Chapter 6 of textbook
Linked lists.
CSCE 210 Data Structures and Algorithms
Stack Lesson xx   This module shows you the basic elements of a type of linked list called a stack.
Prof. Neary Adapted from slides by Dr. Katherine Gibson
Chapter 16 Linked Structures
Linked Lists.
Linked lists.
Presentation transcript:

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

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

Linked Lists: deleting... head 1 2 3 //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.

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

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 < head->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 temp->next = ptr->next; //connect the rest ptr->next = temp; return head; NULL check is the first condition in while loop since otherwise if we need to insert after the last element of the linked list, non-existing element is checked with ptr->next->info < newkey . This causes a possible crash. Now no problem occurs due to "short circuit evaluation" rule of C++.

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 < head->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 temp->next = ptr->next; //connect the rest ptr->next = temp; return head; return new node(newkey, head); ptr->next = new node(newkey,ptr->next);

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

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

Deleting a single node Several algorithms exist We will cover one head toBeDeleted 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

Deleting a single node head toBeDeleted 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;

Circular lists last 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)

Circular lists last Count number of elements in a circularly linked list. 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; }

Think of routines for building, insertion, deletion, etc. Doubly linked lists tail head 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 struct node {    int info;    node *next; node *prev; }; Think of routines for building, insertion, deletion, etc.

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

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

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

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)

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

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 13 Using reference parameters Using pointers

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. 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 <stdlib.h> and <malloc.h> 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

Pointers and Dynamic Arrays

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

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

Pointers and 2D Dynamic Arrays

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

Case 1) 1d data w/ class (See matrixclass. h, matrixclass 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;

Pointer to pointer Syntax Example 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) p ? ? p ? p ? p 12

Case 2) 2d data allocation – w/o classes int ** M; int rows, columns, i,j;   cout<<"Enter the number of rows:"; cin >> rows; cout<<"Enter the number of columns:"; cin >> 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<rows; i++) M[i] = new int [columns];   cout << "Enter the elements" << endl; for (i = 0; i < rows; i++) for (j= 0 ; j< columns ; j++) { cout<< '[' << i << ',' << j << "]: "; cin >> 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;  

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

Case 2) 2d data allocation w/ class (See matrixclass. h, matrixclass 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<rows; i++) data[i] = new int[cols]; } int Matrix2D::GetIndex(int i, int j) { return data[i][j]; } void Matrix2D::SetIndex(int i, int j, int value) data[i][j] = value; Full implementation is in matrixclass.h, matrixclass.cpp. Usage in main: Matrix2D m(5,10); //a 5x10 matrix m.SetIndex(0,5,33); cout << m.GetIndex(0,5) << endl;

Pointers and Static Arrays Efficiency Issues

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

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

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 don’t 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

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

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]; 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!!

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 0-255 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++;

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?