+ All Categories
Home > Documents > Introductory CS Cheatsheet

Introductory CS Cheatsheet

Date post: 16-Nov-2015
Category:
Upload: victor-kwan
View: 23 times
Download: 0 times
Share this document with a friend
Description:
Helpful cheatsheet for introductory CS classes. C++.
Popular Tags:
21
STACKS A stack is an ADT that holds a collection of items where elements are always added to one end. It is a LIFO data structure: the last item is pushed onto the stack is also that first item popped off the stack. The stack can be implemented using either an array with a counter variable or a linked list with a counter variable. The counter variable keeps track of the top of the stack. 1 class Stack { public: Stack():m_top(0) {} void push(int i) { if(m_top >= SIZE) return; m_stack[m_top] = val; m_top++; // We post-increment our m_top variable. } int pop() { if(m_top == 0) return; m_top--; // We pre-decrement our m_top variable. return m_stack[m_top]; } bool is_empty(); int peek_top(); private: int m_stack[100]; int m_top; // m_top represents the next open slot. };
Transcript
  • STACKSA stack is an ADT that holds a collection of items where elements are always added to one end. It

    is a LIFO data structure: the last item is pushed onto the stack is also that first item popped off

    the stack. The stack can be implemented using either an array with a counter variable or a linked

    list with a counter variable. The counter variable keeps track of the top of the stack.

    1

    class Stack { public: Stack():m_top(0) {} void push(int i) { if(m_top >= SIZE) return; m_stack[m_top] = val; m_top++; // We post-increment our m_top variable. } int pop() { if(m_top == 0) return; m_top--; // We pre-decrement our m_top variable. return m_stack[m_top]; } bool is_empty(); int peek_top(); private: int m_stack[100]; int m_top; // m_top represents the next open slot. };

  • We can use the STL stack by including to the file. Common uses of stacks include

    storing undo items for your word processor, evaluating mathematical expressions, converting

    from infix expressions to postfix expressions, and solving mazes.

    When solving a maze, the stack solution visits the cells in a depth-first order: it continues along a

    path until it hits a dead end, then backtracks to the most recently visited intersection that has

    unexplored branches. Because we're using a stack, the next cell to be visited will be a neighbor

    of the most recently visited cell with unexplored neighbors.

    2

    #include #include using namespace std;

    int main() { stack intStack; intStack.push(10); cout

  • QUEUEA queue is another ADT that functions in a FIFO manner: first in first out. You enqueue items at

    the rear and dequeue people from the front. Queues are implemented using linked lists or arrays.

    When implementing a queue using a linked list, every time you enqueue an item, add a new node

    to the end of the linked list. Every time you dequeue an item, youre essentially removing the

    head node. Alternatively, a queue can be implemented as a circular queue by using an array.

    There is no need to shift items with the circular queue.

    3

  • 4

    class Queue {

    public: Queue():m_head(0),m_tail(0),m_count(0) {} void enqueue (int val) {

    m_integers[m_tail] = val; // Place item in tail position. m_count++; // Increment the tail value. m_tail++; // Increment the count value. if(m_tail > 5) m_tail = 0; // Set to 0 if it passes the end.

    } int dequeue() {

    int temp_head = m_integers[m_head]; m_count--; // Decrement the count value. m_head++; // Increment the head value. if(m_head > 5) m_head = 0; // Set to 0 if it passes the end. return temp_head;

    } private:

    int m_integers[6]; int m_head; int m_tail; int m_count;

    };

  • Like the stack, the queue also has its own STL class implementation.

    When solving a maze, the queue solution visits the cells in a breadth-first order: it visits all the

    cells at distance 1 from the start cell, then all those at distance 2, then all those at distance 3, etc.

    Because we're using a queue, the next cell to be visited will be a neighbor of the least recently

    visited cell with unexplored neighbors.

    5

    #include #include using namespace std;

    int main() { queue intQueue; intQueue.push(10); cout

  • INHERITANCEWhenever we have a relationship A is a type of B, we can apply inheritance to the situation. Terminology includes the superclass (base class) from which subclasses (derived classes) are

    derived. Subclasses can also function as superclasses for subclasses.

    INHERITANCE RULES:1. If you define a function in the base class and the derived class, then the derived version of

    the function will hide the base version of the function.

    2. Any public functions defined in the base class, but not redefined in a derived class can be called normally by all parts of your program.

    3. If your code wants to call a function in the base class thats been redefined in the derived

    class, it must use the class:: prefix. For example, a Student with variable name Yoshi

    can call the original function through Yoshi.Person::doSomething().

    4. Classes are constructed from superclass to subclass.5. Classes are destructed from subclass to superclass.6. A derived class may not access private members of the base class.

    6

    class Person { public:

    Person(string nm, int age) :m_name(nm),m_age(age){} virtual void doSomething() {

    cout

  • INHERITANCE AND ASSIGNMENT OPERATORSWhen assigning from one derived class to the other, C++ first copies the base data and then copies the derived data using the copy constructor and assignment operator if present. However, if the base and derived classes have dynamically allocated member variables, then you must define assignment operators and copy constructors for the base class and also special versions

    of these functions for the derived class.

    DISTINGUISHING INHERITANCE AND POLYMORPHISMInheritance is when we derive one or more classes from a common base class. All of the derived

    classes, by definition, inherit a common set of functions from our base class. Each derived class

    may redefine any function originally defined in the base class; the derived class will then have its

    own specialized version of those functions.

    The idea of polymorphism is invoked when C++ allows us to use a base pointer or a base

    reference to access a derived object. Dynamic binding enables the appropriate version of

    functions to be called during runtime when the keyword virtual is used before both base class

    and derived class function declarations.

    POLYMORPHISM

    Use the virtual keyword in both base and derived classes when redefining a function or

    writing the destructor. This, however, is unnecessary for constructors.

    7

    void PrintPrice(Shape& x) {

    cout

  • POLYMORPHISM RULES:1. You may never point a derived class pointer or reference to a base class variable.2. C++ always calls the most-derived version of a function associated with a variable as long

    as it is marked with the keyword virtual.

    3. You cant access private members of the base class from the derived class.

    4. You should always make sure that you use the virtual keyword in front of destructors.

    5. Make a base class function pure virtual if you realize that the base class version of your function doesnt or cant do anything useful.

    Writing pure virtual functions and abstract base classes force users to prevent common mistakes,

    such as forgetting to define relevant functions in derived classes.

    8

    class Person { public:

    Person(string nm, int age) :m_name(nm),m_age(age){} virtual doWork = 0;

    private: string m_name; int m_age;

    };

    /* If you define at least one pure virtual function in a base class, then the class is called an abstract base class. */

    class Student: public Person { public:

    Student(string nm, int age) :Person(nm, age) {} virtual void doWork() {

    cout

  • POLYMORPHISM AND FUNCTIONS

    9

    struct SomeBaseClass {

    virtual void VirtualFunction(){} // #1 void notVirtualFunction() {} // #2 void tricky() // #3 {

    VirtualFunction(); notVirtualFunction();

    } };

    struct SomeDerivedClass: public SomeBaseClass {

    virtual void VirtualFunction() {} // #4 void notVirtualFunction() {} // #5

    };

    int main() {

    SomeDerivedClass d; SomeBaseClass *b = &d;

    b->VirtualFunction(); // Calls #4 b->notVirtualFunction(); // Calls #2 b->tricky(); // Calls #3 which calls #4 then #2

    }

    /* When you use a base pointer to access a derived object, all function calls to virtual functions will be directed to the objects version, even if the function calling the virtual function is not virtual itself. */

  • RECURSIVE STEPS TO SUCCESS1. Figure out what argument(s) your function will take and what it needs to return.2. Show how you would call your new function with inputs of size n and n-1.3. Determine your base case(s) and write the code to handle them without recursion.4. Split your input into two parts of size 1 and n-1. Have your recursive function call itself to solve

    the larger part. This is known as our simplifying code.

    5. Write the code that gets the result from your recursive call, processes it, and returns the appropriate result, if any.

    6. Validate the function.

    DIVIDING ARRAYS FOR RECURSION

    DIVIDING LINKED LISTS FOR RECURSION

    10

    int arrayFunction(int arr[], int n) {...}

    int main() {

    ... // Create some array arr[SIZE]. int s1 = arrayFunction(arr, n); // Input of size n int s2 = arrayFunction(arr+1, n-1); // Last n-1 items int s3 = arrayFunction(arr, n-1); // First n-1 items

    }

    int linkedListFunction(Node* h) {...}

    int main() {

    ... // Create some linked list and cur, a pointer to m_head. int s1 = linkedListFunction(cur); // Input of size n int s2 = linkedListFunction(cur->m_next); // Last n-1 items

    }

  • RECURSION INVOLVING EQUALLY SIZED SUB-PROBLEMS

    RECURSION INVOLVING SUB-PROBLEMS OF SIZE 1 AND N-1

    11

    ProcessSomeData(SomeDataType x[], int N) { If x is small enough to be trivially processed Process it directly and return the result Otherwise Break x into two smaller problems of equal size Call our function to solve each sub-problem: result1 = ProcessSomeData(x[0 to N/2],N/2) result2 = ProcessSomeData(x[N/2 to N],N/2) Combine result1 and result2 into a solution Return the result }

    ProcessSomeData(SomeDataType x[], int N) { If x is small enough to be trivially processed Process it directly and return the result Otherwise Break x into two chunks: one of size 1, and one of size N-1 Call our function to solve the larger sub-problem: result1 = Process x[0] without using recursion result2 = ProcessSomeData(x[1 to N], N-1) Combine result1 and result2 into a solution Return the result }

  • STACK IMPLEMENTED USING LINKED LIST

    12

    class LLStack { public:

    LLStack():m_count(0),m_head(NULL){} void push(int val) {

    Node* node = new Node; node->m_value = val; if (m_head == NULL)

    m_head = node; else {

    node->m_next = m_head; m_head = node;

    } m_count++;

    } int pop() {

    if (m_count == 0) return 0; int val = m_head->m_value; Node* hnode = m_head; m_head = hnode->m_next; delete hnode; m_count--; return val;

    } int top() {

    if (m_count == 0) return 0; return m_head->m_value;

    } private:

    int m_count; Node* m_head;

    };

  • PSEUDOCODE FOR ASSIGNMENT OPERATOR AND COPY CONSTRUCTOR

    POINTER TIDBITS

    13

    class SomeClass { public:

    SomeClass (const SomeClass& src) {

    // 1. Allocate memory for the new variable // 2. Copy data from old variable into the new one

    } void swap(Squares& other) {

    // exchange m_n and other.m_n ints // exchange m_sq and other.m_sq pointers

    } SomeClass& operator= (const SomeClass& src) {

    if (this != &src) {

    Squares temp(src); swap(temp);

    } return *this;

    } };

    int main() {

    /* Behavior of an array of pointers to integers */ int* intPtrArray[10]; // Array of pointers to integers. intPtrArray[0] = new int; // Use new to store an integer // in dynamically allocated memory. (**intPtrArray) = 3; // Access location pointer to by pointer.

    int k = 3; (*(integerArray+1)) = &k; // Determine address of k.

    /* Pointer arithmetic */ int intArray[10]; // intArray is a pointer to the first position in the array. // &intArray[3] is equivalent to (intArray+3). int* p = intArray + 2; // p[0] now refers to the value at intArray[2].

    }

  • LINKED LIST INSERTION MECHANISM

    14

    bool SortedLinkedList::insert(const ItemType &value) {

    Node *p = m_head; Node *q = NULL;

    while (p != NULL) {

    if (value == p->m_value) return false; if (value < p->m_value) break; q = p; p = p->m_next;

    } Node *newNode = new Node; newNode->m_value = value; newNode->m_next = p; newNode->m_prev = q;

    if (p != NULL) p->m_prev = newNode;

    else m_tail = newNode;

    if (q != NULL) q->m_next = newNode;

    else m_head = newNode;

    m_size++; }

  • CLASS TEMPLATES

    15

    General 1. Put this in front of the class declaration: B: class foo {...}; A: template class foo {...};

    2. Update appropriate types in the class to the new ItemType

    Updating internally-defined methods 1. For normal methods, just update all types to ItemType: B: int bar(int a) {...} A: ItemType bar(ItemType a) {...}

    2. Assignment operator: B: foo &operator=(const foo &other) A: foo& operator=(const foo& other)

    3. Copy constructor: B: foo(const foo &other) A: foo(const foo& other)

    Updating externally-defined methods 1. For non-inline methods: B: int foo::bar(int a) A: template ItemType foo::bar(ItemType a)

    2. For inline methods: B: inline int foo::bar(int a) A: template inline ItemType foo::bar(ItemType a)

    3. Copy constructor: B: foo &foo::operator=(const foo &other) A: foo& foo::operator=(const foo& rhs)

    4. Assignment operator: B: foo::foo(const foo& other) A: foo::foo(const foo& other)

  • NON-CLASS FUNCTION TEMPLATES

    TEMPLATE CLASSES WITH INTERNALLY DEFINED STRUCT

    16

    1. Update the function header: B: int bar(int a) A: template ItemType bar(ItemType a);

    2. Replace appropriate types: B: {int a; float b;} A: {ItemType a; float b;}

    For some internally defined struct blah in a class: class foo {... struct blah {int val;}; ...};

    1. Replace appropriate internal variables in your struct (e.g., int val;) with your ItemType (e.g., ItemType val;).

    2. If an internal method in a class is trying to return an internal struct (or a pointer to an internal struct), you dont need to change the functions declaration at all inside the class declaration; just update relevant variables to your ItemType.

    3. If an externally-defined method in a class is trying to return an internal struct (or a pointer to an internal struct): a. Assuming your internal structure is called blah, update your

    external function bar definitions as follows:B: blah foo::bar(...) {...}A: template typename foo::blah foo::bar(...) {...}B: blah* foo::bar(...) {...}A: template typename foo::blah* foo::bar(...) {...}

    4. Try to pass template items by const reference if you can:Bad: template void foo(ItemType x)Good: template void foo(const ItemType &x)

  • THE STANDARD TEMPLATE LIBRARYOnly the vector class has the capability of being accessed using square brackets; other classes rely on iterators. The iterator has the following properties:

    1. It is much like a pointer variable, but is only used with STL containers.

    2. The iterator, however, is not a pointer. It is an object that knows what element it points to, how to find the previous element in the container, and how to find the next element in the container.

    3. They are usually used by first pointing them to the first item in the container.

    4. Just like a pointer, you can increment and decrement and iterator to move through each item.

    5. You can also read or write each value an iterator points to.

    17

    class Nerd {

    public: void beNerdy() const; ...

    };

    int main() {

    vector v; ... // Adding some integers into the vector. vector::iterator it = v.begin(); // Sets to first item in v. cout

  • ITERATOR QUIRKS

    The STL also provides useful some useful functions under the library.

    18

    int main() {

    vector v; v.push_back(3); vector::iterator it = v.begin(); v.erase(it); // Erasing from a vector invalidates other iterators // The same behavior applies when you add a new item. // This doesnt affect sets, lists, or maps. // As with pointers, you should not access iterators // that dont point to items. v.push_back(4); v.push_back(5); it = v.begin(); it = v.erase(it); // Returns the iterator to the next element. v.erase(v.begin(),v.end()); // Erases all elements between the // first iterator and the position // before the second iterator.

    }

    bool is_even(int n) {...}

    int main() {

    vector v; v.push_back(3); vector::iterator it = find(v.begin(),v.end(),3); /* As is the case with the erase function, the first argument is the first element to search, and the second argument is the element after the last element to be searched. */

    int array[5] = {0, 1, 2, 3, 4}; int* ptr = find(&array[0], &array[5], 5); if(ptr == &array[5]) // Returns the second argument if not found.

    cout

  • STL CONTAINERSSequence containers maintain the original ordering of inserted elements. This allows you to

    specify where to insert the element in the container.

    1. The list containers allows for fast insertions and deletions anywhere in the container, but you cannot randomly access an element in the container.

    2. The vector behaves like an array, but will automatically grow as required.

    The defining characteristics of associative containers is that elements are inserted in a pre-

    defined order, such as sorted ascending. The associative containers can be grouped into two

    subsets: maps and sets. A map consists of a key/value pair. The key is used to order the sequence, and the value is somehow associated with that key. A set is simply an ascending container of unique elements.

    Both map and set only allow one instance of a key or element to be inserted into the container.

    19

    #include

    int main() {

    vector v; v.push_back(Yoshi); v.push_back(Steve); string i = v.front(); string j = v.back(); unsigned long k = v.size(); bool v = s.empty(); v[0] = Wallace; v.pop_back(); v.clear();

    }

    #include

    int main() {

    list l; l.push_front(Yoshi); l.push_back(Steve); string a = l.front(); string b = l.back(); unsigned long c = l.size(); bool d = l.empty(); l.pop_front(); l.pop_back(); l.clear();

    }

    /* In lists, you cant access elements using square brackets. You want to use vectors for fast random access and lists for fast insertion or deletion at the head and tail of the container. */

  • PROPERTIES OF A MAP AND SET1. Both of these order their elements in alphabetical order.

    2. For either of them to work, you must define an operator< method for the relevant classes.

    3. Only unique keys and items are added to maps and sets respectively.

    UNORDERED MAP (HASH MAP)

    20

    #include

    int main() {

    map n_p; n_p[Arnold] = 9293819; n_p[Steve] = 9238104; // [ key ] value

    map::iterator it = n_p.find(Steve); cout first; cout second; n_p.clear();

    }

    /* This works because maps work much like alphabetically ordered structs with first and second as member variables. */

    #include

    int main() {

    set s; s.insert(2); s.insert(3); s.insert(4); s.insert(2); // Ignored. set::iterator = it; it = s.find(3); cout

  • CHOOSING A SORTThe bubble sort, selection sort, insertion sort, and shell sort.

    1. The first three of these sorts have a Big-O of O(n2). The shell sort has a Big-O that is at best

    O(n(log2n)2) but is typically fond to be O(n3/2).

    2. All of these sorts operate on constant memory.

    3. Unlike the selection sort, the bubble and insertion sort is efficient on pre-sorted containers.

    The merge sort, heap sort, and quick sort.

    1. Quick sort is known to be significantly faster in practice than other O(nlog2n) algorithms.

    However, using quick sort always warrants the possibility of stumbling upon a set of data that

    produces a Big-O of O(n2) in a case where the container is pre-sorted or fully sorted in normal

    or reverse order. Space used, however, is constant. Quick sort is performed on arrays.

    2. Merge sort has a guaranteed Big-O of O(nlog2n). However, it is not space constant. It can be

    space constant, however, at the expense of a higher constant of proportionality. It is typically

    used when bad worst-case performance cannot be tolerated. It works for linked lists.

    3. Like quick sort, the heap sort is performed on arrays. Although it is slower than quick sort, it is

    assured a Big-O of O(nlog2n). It is performed in position. Heap sort is chosen if the container

    being sorted is an array and the worst case scenario of quick sort cannot be tolerated.

    CHOOSING A STL CONTAINER1. Lists for fast insertion or deletion from the head and tail of the container.

    2. Vectors for fast random access.

    3. Maps when you need to associate a value to another; sorted in alphabetical order.

    4. Unordered maps when you want speed but not sorting.

    5. Sets when you have unique elements sorted in alphabetical order.

    6. Unordered sets when you want speed but not sorting.

    MISCELLANIES1. We can preprocess arrays into hash tables for faster future searching.

    2. We can merge items using an unordered set or a regular set.

    3. If we want to provide a method to search, we should use a hash table.

    4. If we were to choose between a sorted vector or linked list to search for an item, we should opt

    for the vector where binary search can be performed.

    21


Recommended