Christopher James Hayward
4 years ago
2 changed files with 0 additions and 287 deletions
@ -1,218 +0,0 @@ |
|||||
#+TITLE: Data Structures |
|
||||
#+AUTHOR: Christopher James Hayward |
|
||||
|
|
||||
#+ROAM_KEY: https://chrishayward.xyz/notes/data-structures/ |
|
||||
|
|
||||
#+HUGO_BASE_DIR: ~/.local/source/website |
|
||||
#+HUGO_AUTO_SET_LASTMOD: t |
|
||||
#+HUGO_SECTION: notes |
|
||||
|
|
||||
+ Built on primitive data types |
|
||||
+ Organization and management format |
|
||||
+ Forms the basis of abstract data types |
|
||||
|
|
||||
* Linked List |
|
||||
|
|
||||
#+begin_export |
|
||||
(0 ->) (1 ->) (2 ->) (3 ->) (4 ->) |
|
||||
#+end_export |
|
||||
|
|
||||
+ Linear data structure |
|
||||
+ Elements linked using pointers |
|
||||
+ Not stored in a contiguous location |
|
||||
|
|
||||
Benefits to this approach are: |
|
||||
|
|
||||
+ Dynamic size |
|
||||
+ Ease of manipulation |
|
||||
|
|
||||
Drawbacks of this approach: |
|
||||
|
|
||||
+ Random access not allowed |
|
||||
+ Additional memory overhead |
|
||||
+ Not friendly to caching |
|
||||
|
|
||||
** Example |
|
||||
|
|
||||
Begin with the ~stc++~ header and the ~std~ namespace. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
#include <bits/stc++.h> |
|
||||
using namespace std; |
|
||||
#+end_src |
|
||||
|
|
||||
Define the data structure that will represent a single element (node) in the linked list. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
struct Node { |
|
||||
int data; |
|
||||
struct Node* next; |
|
||||
|
|
||||
Node(int d) { |
|
||||
this->data = d; |
|
||||
this->next = NULL; |
|
||||
} |
|
||||
}; |
|
||||
#+end_src |
|
||||
|
|
||||
Initialize a new linked list, with an initial value of =0=. |
|
||||
|
|
||||
#+begin_example |
|
||||
(0 ->) |
|
||||
#+end_example |
|
||||
|
|
||||
To illustrate our example we will create a simple linked list with three distinct nodes. The first step is creating our root node. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
Node* head = new Node(0); |
|
||||
#+end_src |
|
||||
|
|
||||
We can then append new items through the ~next~ field, which is a pointer to the next item in the list. When this value is ~NULL~ we have reached the end of the list. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
head->next = new Node(1); |
|
||||
head->next->next = new Node(2); |
|
||||
#+end_src |
|
||||
|
|
||||
This gives us the expected result of: |
|
||||
|
|
||||
#+begin_example |
|
||||
(0 ->) (1 ->) (2 ->) |
|
||||
#+end_example |
|
||||
|
|
||||
* Binary Tree |
|
||||
|
|
||||
#+begin_example |
|
||||
a |
|
||||
/ \ |
|
||||
b c |
|
||||
/ \ / \ |
|
||||
d e f |
|
||||
#+end_example |
|
||||
|
|
||||
+ Hierarchical data structure |
|
||||
+ Different from Arrays, Lists, Stacks & Queues |
|
||||
|
|
||||
| Node | Type | Description | |
|
||||
|---------+------+-----------------------------------| |
|
||||
| a | Root | Topmost node of the tree | |
|
||||
| b, c | Node | Children of a, parents of d, e, f | |
|
||||
| d, e, f | Node | Leaves (nothing below) | |
|
||||
|
|
||||
** Example |
|
||||
|
|
||||
Using =C++=, we begin by including the ~stdc++~ header and the ~std~ namespace. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
#include <bits/stc++.h> |
|
||||
using namespace std; |
|
||||
#+end_src |
|
||||
|
|
||||
We then define the data structure to represent a =Node= within the tree, including a constructor method for creating new instances easily. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
struct Node { |
|
||||
int data; |
|
||||
struct Node* left; |
|
||||
struct Node* right; |
|
||||
|
|
||||
Node(int d) { |
|
||||
this->data = d; |
|
||||
this->left = NULL; |
|
||||
this->right = NULL; |
|
||||
} |
|
||||
}; |
|
||||
#+end_src |
|
||||
|
|
||||
We begin by initializing the root of the tree, leaving us with something like this: |
|
||||
|
|
||||
#+begin_example |
|
||||
1 |
|
||||
/ \ |
|
||||
? ? |
|
||||
#+end_example |
|
||||
|
|
||||
Initialize it with the value of ~1~. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
struct Node* node = new Node(1); |
|
||||
#+end_src |
|
||||
|
|
||||
Creating children for nodes ~2~ and ~3~ will expand the tree outwards in both directions, leaving us with something like this: |
|
||||
|
|
||||
#+begin_example |
|
||||
1 |
|
||||
/ \ |
|
||||
2 3 |
|
||||
/ \ / \ |
|
||||
? ? ? |
|
||||
#+end_example |
|
||||
|
|
||||
This is done by adding the children to the root. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
root->left = new Node(2); |
|
||||
root->right = new Node(3); |
|
||||
#+end_src |
|
||||
|
|
||||
Expanding down the left side only. |
|
||||
|
|
||||
#+begin_example |
|
||||
1 |
|
||||
/ \ |
|
||||
2 3 |
|
||||
/ \ / \ |
|
||||
4 ? ? |
|
||||
/ \ |
|
||||
? ? |
|
||||
#+end_example |
|
||||
|
|
||||
Adding a branch of ~4~ to ~2~ gives us the final result. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
root->left->left = new Node(4); |
|
||||
#+end_src |
|
||||
|
|
||||
** Inverting |
|
||||
|
|
||||
#+begin_quote |
|
||||
Google: 90% of our engineers use the software you wrote (homebrew), but you can't invert a binary tree on a whiteboard? F*** off! |
|
||||
#+end_quote |
|
||||
|
|
||||
It's actually not as daunting a task as you may think, given the hype around past statements like that. But what is the process of inverting a binary tree? Starting with the given input: |
|
||||
|
|
||||
#+begin_example |
|
||||
1 |
|
||||
/ \ |
|
||||
2 5 |
|
||||
/ \ |
|
||||
3 4 |
|
||||
#+end_example |
|
||||
|
|
||||
We expect to produce an output matching: |
|
||||
|
|
||||
#+begin_example |
|
||||
1 |
|
||||
/ \ |
|
||||
5 2 |
|
||||
/ \ |
|
||||
4 3 |
|
||||
#+end_example |
|
||||
|
|
||||
Using a recursive approach, we can operate on the tree as to produce the expected result. |
|
||||
|
|
||||
#+begin_src cpp |
|
||||
Node* invert(Node* root) { |
|
||||
if (root == NULL) { |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Node* left = invert(root->left); |
|
||||
Node* right = invert(root->right); |
|
||||
|
|
||||
root->left = right; |
|
||||
root->right = left; |
|
||||
|
|
||||
return root; |
|
||||
} |
|
||||
#+end_src |
|
@ -1,69 +0,0 @@ |
|||||
#+TITLE: Efficiency of Algorithms |
|
||||
#+AUTHOR: Christopher James Hayward |
|
||||
|
|
||||
#+ROAM_KEY: https://chrishayward.xyz/notes/efficiency-of-algorithms/ |
|
||||
|
|
||||
#+HUGO_BASE_DIR: ~/.local/source/website |
|
||||
#+HUGO_AUTO_SET_LASTMOD: t |
|
||||
#+HUGO_SECTION: notes |
|
||||
|
|
||||
+ Algorithms must be analyzed to determine resource usage |
|
||||
+ Efficiency is analogous to engineering productivity for a repeating process |
|
||||
+ Measured in complexity of time and space (resources) |
|
||||
|
|
||||
* Background |
|
||||
|
|
||||
Importance of efficiency with respect to time was first emphasized by *Ada Lovelace* in 1843 regarding the babbage machine: |
|
||||
|
|
||||
#+begin_quote |
|
||||
In almost every computation a great variety of arrangements for the succession of the process is possible, and variouis considerations must influence the selections amongst them for the purpose of a calculating engine. One essential object is to chose that arrangement which shall tend to reduce to a minimum the time necessary for completing the calculation. |
|
||||
#+end_quote |
|
||||
|
|
||||
* Analysis |
|
||||
|
|
||||
The most commonly used notation to describe resource consumption (complexity) is using *Donald Knuth's* =Big O= notation. |
|
||||
|
|
||||
| Notation | Name | Example | |
|
||||
|--------------+--------------+----------------------------------------------------------------| |
|
||||
| O(1) | Constant | Finding the median in a sorted list of measurements | |
|
||||
| O(log n) | Logarithmic | Finding an item in an sorted array with a binary search | |
|
||||
| O(n) | Linear | Finding an item in an unsorted array | |
|
||||
| O(n log n) | Linearithmic | Performing a fast sorting algorithm | |
|
||||
| O(n²) | Quadratic | Multiplying two n digit numbers by a simple algorithm | |
|
||||
| O(n²), c > 1 | Exponential | Finding the optimal solution to the traveling salesman problem | |
|
||||
|
|
||||
* Measurement |
|
||||
|
|
||||
Expression as a function of the size of the input ~n~, with the main measurements being: |
|
||||
|
|
||||
+ Time :: How long does the algorithm take to complete? |
|
||||
+ Space :: How much working memory is required? |
|
||||
|
|
||||
Other measurements for devices whose power needs to be factored in, such as for battery operated devices or super computers: |
|
||||
|
|
||||
+ Direct power :: How much power is needed to compute the algorithm? |
|
||||
+ Indirect power :: How much power is needed for cooling, lighting, etc? |
|
||||
|
|
||||
Less commonly used: |
|
||||
|
|
||||
+ Transmission size :: How much data must be transferred for operation? |
|
||||
+ External space :: Space needed physically (such as on disk) |
|
||||
+ Response time :: Relevant in real time applications |
|
||||
+ Total cost :: Cost of hardware dedication |
|
||||
|
|
||||
** Time |
|
||||
|
|
||||
+ Uses time complexity analysis |
|
||||
+ Measured in CPU time usage |
|
||||
+ Detailed estimates needed to compare performance |
|
||||
+ Difficult to measure in parallel processing |
|
||||
|
|
||||
** Space |
|
||||
|
|
||||
Concerned with the usage of memory resources, typically the memory or storage needed to hold: |
|
||||
|
|
||||
+ Code for the algorithm |
|
||||
+ Working space |
|
||||
+ Input data |
|
||||
+ Output data |
|
||||
+ Processing data |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue