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