770 likes | 895 Vues
Lecture 12. Chapter 6 Lists Plus. What is a Circular Linked List?. A circular linked list is a list in which every node has a successor ; the “ last ” element is succeeded by the “ first ” element. External Pointer to the Last Node. What is a Doubly Linked List?.
E N D
Lecture 12 Chapter 6 Lists Plus
What is a Circular Linked List? • A circular linked list is a list in which every node has a successor; the “last” element is succeeded by the “first” element.
What is a Doubly Linked List? • A doubly linked list is a list in which each node is linked to both its successor and its predecessor.
Deleting from a Doubly Linked List What are the advantages of a circular doubly linked list?
What are Header and Trailer Nodes? • A Header Node is a node at the beginning of a list that contains a key value smaller than any possible key. • A Trailer Node is a node at the end of a list that contains a key larger than any possible key. • Both header and trailer are placeholding nodes used to simplify list processing.
A linked list in static storage ? A Linked List as an Array of Records
What is a Class Template? • A class template allows the compiler to generate multiple versions of a class type by using type parameters. • The formal parameter appears in the class template definition, and • the actual parameter appears in the client code. • Both are enclosed in pointed brackets, < >
//-------------------------------------------------------- // CLASS TEMPLATE DEFINITION //-------------------------------------------------------- #include "ItemType.h" // for MAX_ITEMS and ItemType template<class ItemType> // formal parameter list class StackType { public: StackType( ); boolIsEmpty() const; boolIsFull( ) const; void Push( ItemTypeitem ); void Pop( ItemType item ); ItemTypeTop( ); private: int top; ItemTypeitems[MAX_ITEMS]; };
top 3 [MAX_ITEMS-1] . . . [ 3 ] 789 [ 2 ] -56 [ 1 ] 132 items [ 0 ] 5670 ACTUAL PARAMETER StackType<int> numStack;
top 3 [MAX_ITEMS-1] . . . [ 3 ] 3456.8 [ 2 ] -90.98 [ 1 ] 98.6 items [ 0 ] 167.87 ACTUAL PARAMETER StackType<float> numStack;
top 3 [MAX_ITEMS-1] . . . [ 3 ] Bradley [ 2 ] Asad [ 1 ] Rodrigo items [ 0 ] Max ACTUAL PARAMETER StackType<string> numStack;
//-------------------------------------------------------- // SAMPLE CLASS MEMBER FUNCTIONS //-------------------------------------------------------- template<class ItemType> // formal parameter list StackType<ItemType>::StackType( ) { top = -1; } template<class ItemType> // formal parameter list void StackType<ItemType>::Push ( ItemTypenewItem ) { if (IsFull()) throw FullStack(); top++; items[top] = newItem; // STATIC ARRAY IMPLEMENTATION }
Using class templates The actual parameter to the templateis a data type. Any type can be used, either built-in or user-defined. Templates are instantiated at run time. Can you see how this might cause a problem?
When creating class template • Put .h and .cpp code in the same .h file • or • Have .h include .cpp file at the end
Recall Definition of Stack • Logical (or ADT) level:A stack is an ordered group of homogeneous items (elements), • in which the removal and addition of stack items can take place only at the top of the stack. • A stack is a LIFO“last in, first out” structure.
StackType Top Private data: topPtr IsEmpty 20 30 IsFull Push Pop ~StackType class StackType<int>
What happens . . . • When a function is called that uses pass by value for a class object like our dynamically linked stack?
Pass by value makes a shallow copy StackType<int> myStack; // CLIENT CODE . . . MyFunction( myStack ); // function call myStack momeStack Private data: topPtr7000 Private data: 7000 6000 topPtr7000 20 30 shallow copy
What’s the difference? • A shallow copyshares the pointed to data with the original class object. • A deep copystores its own copy of the pointed to data at different locations than the data in the original class object.
Making a deep copy myStack Private data: 7000 6000 topPtr7000 20 30 someStack Private data: 5000 2000 topPtr5000 20 30 deep copy
Suppose MyFunction Uses Pop // FUNCTION CODE template<class ItemType> void MyFunction( StackType<ItemType> SomeStack ) // Uses pass by value { ItemType item; SomeStack.Pop(item); . . . } What happens in a shallow copy?
MyStack.topPtr is left dangling StackType<int> myStack; // CLIENT CODE . . . MyFunction( myStack ); myStack someStack Private data: topPtr6000 Private data: 7000 6000 topPtr7000 ? 30 shallow copy
MyStack.topPtr is left dangling Notice that the shallow copy and the actual parameter myStack, have changed! myStack someStack Private data: topPtr6000 Private data: 7000 6000 topPtr7000 ? 30 shallow copy
As a result . . . • This default method used for pass by value is not the best way when a data member points to dynamic data. • Instead, you should write what is called acopy constructor, • which makes a deep copy of the dynamic data in a different memory location.
More about copy constructors • When there is a copy constructor provided for a class, the copy constructor is used to make copies for pass by value. • You do not call the copy constructor. • Like other constructors, it has no return type. • Because the copy constructor properly defines pass by value for your class, it must use pass by reference in its definition.
// DYNAMICALLY LINKED IMPLEMENTATION OF STACK template<class ItemType> class StackType { public: StackType( ); // Default constructor. // Post: Stack is created and empty. StackType( constStackType<ItemType>& anotherStack ); // Copy constructor. // Implicitly called for pass by value. . . . ~StackType( ); // Destructor. // Post: Memory for nodes has been deallocated. private: NodeType<ItemType>* topPtr; };
Classes with Data Member Pointers Need CLASS CONSTRUCTOR CLASS COPY CONSTRUCTOR CLASS DESTRUCTOR
template<class ItemType>// COPY CONSTRUCTOR StackType<ItemType>:: StackType( constStackType<ItemType>& anotherStack ) { NodeType<ItemType>* ptr1; NodeType<ItemType>* ptr2; if ( anotherStack.topPtr== NULL ) topPtr = NULL; else // allocate memory for first node { topPtr = new NodeType<ItemType>; topPtr->info = anotherStack.topPtr->info; ptr1 = anotherStack.topPtr->next; ptr2 = topPtr; while ( ptr1 != NULL ) // deep copy other nodes { ptr2->next = new NodeType<ItemType>; ptr2 = ptr2->next; ptr2->info = ptr1->info; ptr1 = ptr1->next; } ptr2->next = NULL; } }
What About the Assignment Operator? • The default method used for assignment of class objects makes a shallow copy. • If your class has a data member pointer to dynamic data, • you should write a member function to overload the assignment operator to make a deep copy of the dynamic data.
// DYNAMICALLY LINKED IMPLEMENTATION OF STACK template<class ItemType> class StackType { public: StackType( ); // Default constructor. StackType(constStackType<ItemType>& anotherStack ); // Copy constructor. void operator= ( StackType<ItemType> ); // Overloads assignment operator. . . . ~StackType( ); // Destructor. private: NodeType<ItemType>* topPtr; };
Using Overloaded Binary operator+ When a Member Function was defined myStack + yourStack myStack.operator+(yourStack) When a Friend Function was defined myStack + yourStack operator+(myStack, yourStack)
C++ Operator Overloading Guides • All operators except these :: . sizeof ?: may be overloaded. • At least one operand must be a class instance. • You cannot change precedence, operator symbols, or number of operands. • Overloading ++ and -- requires prefix form use by default. • An operator can be given multiple meanings if the data types of operands differ.
Polymorphism with Virtual Functions • formalParameter.MemberFunction(...); • then • If MemberFunction is not a virtual function, the type of the formal parameterdetermines which function to call. • Static binding is used. • If MemberFunction is a virtual function, the type of the actual parameterdetermines which function to call. • Dynamic binding is used.
class ItemType { public: virtual RelationTypeComparedTo(ItemType) const; private: char lastName[50] ; } ; class NewItemType : public ItemType { public: RelationTypeComparedTo( ItemType) const; private: char firstName[50] ; } ; void PrintResult( ItemType& first, ItemType& second) { using namespace std; if ( first. ComparedTo( second) ==LESS) cout<< " First comes before second" ; else cout<< " First does not come before second" ; } ItemType item1 , item2; NewItemType item3 , item4: PrintResult( item1 , item2) ; PrintResult( item3 , item4) ; You should pass by reference to access derived data members
Chapter 7 Programming with Recursion
What Is Recursion? • Recursive call A method call in which the method being called is the same as the one making the call • Direct recursionRecursion in which a method directly calls itself • Indirect recursionRecursion in which a chain of two or more method calls returns to the method that originated the chain
Recursion • You must be careful when using recursion. • Recursive solutions are typically less efficient than iterative solutions. • Still, many problems lend themselves to simple, elegant, recursive solutions. • We must avoid making an infinite sequence of function calls • infinite recursion
Some Definitions • Base case The case for which the solution can be stated nonrecursively • General (recursive) case The case for which the solution is expressed in terms of a smaller version of itself • Recursive algorithm A solution that is expressed in terms of • smaller instances of itself and • a base case
Writing a recursive function to find n factorial DISCUSSION The function call Factorial(4) should have value 24, because that is 4 * 3 * 2 * 1 . For a situation in which the answer is known, the value of 0! is 1. So our base case could be along the lines of if ( number == 0 ) return 1;
General format for many recursive functions if (some condition for which answer is known) // base case solution statement else// general case recursive function call • Each successive recursive call should bring you closer to a situation in which the answer is known. • Each recursive algorithm must have at least one base case, as well as the general (recursive) case
Writing a recursive function to find Factorial(n) Now for the general case . . . The value of Factorial(n) can be written as n * the product of the numbers from (n - 1) to 1, that is, n * (n - 1) * . . . * 1 or, n * Factorial(n - 1) And notice that the recursive call Factorial(n - 1) gets us “closer” to the base case of Factorial(0).
Recursive Solution int Factorial ( int number ) // Pre: number is assigned and number >= 0. { if ( number == 0)// base case return 1 ; else // general case return number + Factorial ( number - 1 ) ; }