commit
2f7c42dd5c
@ -0,0 +1,7 @@
|
||||
Bit Manipulation
|
||||
==
|
||||
|
||||
- How do you verify if an interger is a power of 2?
|
||||
- Wirte a program to print the binary representation of an integer.
|
||||
- Write a program to print out the number of 1 bits in a given integer.
|
||||
- Write a program to determine the largest possible integer using the same number of 1 bits in a given number.
|
@ -0,0 +1,20 @@
|
||||
Basics
|
||||
==
|
||||
|
||||
## Disclaimer
|
||||
|
||||
These items will all change based on your specific company and needs but these items area starting point.
|
||||
|
||||
## Items To Consider
|
||||
|
||||
- **Timeliness** - The interviewee should show up on time, but of course things happen and we must all be understanding that things outside of their control may happen. Try to give a few minutes leeway.
|
||||
- **Strengths** - Ask the interviewee what they would consider to be their strengths and maybe rate themselves. This gives you a good idea where to start asking technical questions and sets a baseline for expected knowledge of each subject.
|
||||
- **Keep Things Loose** - This is of course dependent on your industry but try to keep make the interviewee comfortable. Many people get nervous when trying to perform at their best for others and a technical interview is no different. A suggestion is to start with a personal question such as "What are some of your hobbies?" or "What do you like to do for fun?" These types of questions can help relax an interviewee and allows them to perform better.
|
||||
- **Understand The Position** - Understand that a junior level candidate isn't going to have as much knowledge about languages and frameworks as a senior candidate will.
|
||||
- **Save Time For Questions** - The interviewee may have questions for you! Give them the ability to ask. Maybe offer up a few questions if they have none, (ie. "What is the typical day here like for my position?", "What is your favorite part about working at __?")
|
||||
|
||||
## Tech Question Technique
|
||||
|
||||
- **Tools** - Using a text editor such as Sublime or Atom will give the interviewee syntax highlighting but doesn't show compiler errors which can be a help.
|
||||
- **Nitpicking** - Sometimes psuedocode is okay. If testing in C# do you really need the interviewee to write `Console.WriteLine()` or is `Print()` good enough?
|
||||
-**Keep Dialog Open** - Don't leave the interviewee alone or sit quietly by as they attempt to coe. Give some subtle hints like "I see you're doing ____, can you think of any other ways to accomplish this?" It's unlikely that the interviewee will be working in a silo should they get the job, is there any reason they should be during the interview?
|
@ -0,0 +1,103 @@
|
||||
Interview Cheatsheet
|
||||
==
|
||||
|
||||
This is a straight-to-the-point, distilled list of technical interview Do's and Don'ts, mainly for algorithmic interviews. Some of these may apply to only phone screens or whiteboard interviews, but most will apply to both. I revise this list before each of my interviews to remind myself of them and eventually internalized all of them to the point I do not have to rely on it anymore.
|
||||
|
||||
For a detailed walkthrough of interview preparation, refer to the ["Preparing for a Coding Interview"](./) section.
|
||||
|
||||
**Legend:** ✅ = Do, ❌ = Don't, ⚠️ = Situational
|
||||
|
||||
### 1. Before Interview
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Prepare pen, paper and earphones/headphones.|
|
||||
|✅|Find a quiet environment with good Internet connection.|
|
||||
|✅|Ensure webcam and audio are working. There were times I had to restart Chrome to get Hangouts to work again.|
|
||||
|✅|Request for the option to interview over Hangouts/Skype instead of a phone call; it is easier to send links or text across.|
|
||||
|✅|Decide on and be familiar with a programming language.|
|
||||
|✅|Familiarize yourself with the coding environment (CoderPad/CodePen). Set up the coding shortcuts, turn on autocompletion, tab spacing, etc.|
|
||||
|✅|Prepare answers to the [frequently-asked questions](../non-technical/behavioral.md) in an interview.|
|
||||
|✅|Prepare some [questions to ask](../non-technical/questions-to-ask.md) at the end of the interview.|
|
||||
|✅|Dress comfortably. Usually you do not need to wear smart clothes, casual should be fine. T-shirts and jeans are acceptable at most places.|
|
||||
|✅|Stay calm and composed.|
|
||||
|⚠️|Turn off the webcam if possible. Most remote interviews will not require video chat and leaving it on only serves as a distraction.|
|
||||
|
||||
### 2. Introduction
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Introduce yourself in a few sentences under a minute or two.|
|
||||
|✅|Mention interesting points that are relevant to the role you are applying for.|
|
||||
|✅|Sound enthusiastic! Speak with a smile and you will naturally sound more engaging.|
|
||||
|❌|Spend too long introducing yourself. The more time you spend talking the less time you have to code.|
|
||||
|
||||
### 3. Upon Getting the Question
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Repeat the question back at the interviewer.|
|
||||
|✅|Clarify any assumptions you made subconsciously. Many questions are under-specified on purpose. A tree-like diagram could very well be a graph that allows for cycles and a naive recursive solution would not work.|
|
||||
|✅|Clarify input format and range. Ask whether input can be assumed to be well-formed and non-null.|
|
||||
|✅|Work through a small example to ensure you understood the question.|
|
||||
|✅|Explain a high level approach even if it is a brute force one.|
|
||||
|✅|Improve upon the approach and optimize. Reduce duplicated work and cache repeated computations.|
|
||||
|✅|Think carefully, then state and explain the time and space complexity of your approaches.|
|
||||
|✅|If stuck, think about related problems you have seen before and how they were solved. Check out the [tips](../algorithms) in this section.|
|
||||
|❌|Ignore information given to you. Every piece is important.|
|
||||
|❌|Jump into coding straightaway.|
|
||||
|❌|Start coding without interviewer's green light.|
|
||||
|❌|Appear too unsure about your approach or analysis.|
|
||||
|
||||
### 4. During Coding
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Explain what you are coding/typing to the interviewer, what you are trying to achieve.|
|
||||
|✅|Practice good coding style. Clear variable names, consistent operator spacing, proper indentation, etc.|
|
||||
|✅|Type/write at a reasonable speed.|
|
||||
|✅|As much as possible, write actual compilable code, not pseudocode.|
|
||||
|✅|Write in a modular fashion. Extract out chunks of repeated code into functions.|
|
||||
|✅|Ask for permission to use trivial functions without having to implement them; saves you some time.|
|
||||
|✅|Use the hints given by the interviewer.|
|
||||
|✅|Demonstrate mastery of your chosen programming language.|
|
||||
|✅|Demonstrate technical knowledge in data structures and algorithms.|
|
||||
|✅|If you are cutting corners in your code, state that out loud to your interviewer and say what you would do in a non-interview setting (no time constraints). E.g., I would write a regex to parse this string rather than using `split()` which may not cover all cases.|
|
||||
|✅|Practice whiteboard space-management skills.|
|
||||
|⚠️|Reasonable defensive coding. Check for nulls, empty collections, etc. Can omit if input validity has been clarified with the interviewer.|
|
||||
|❌|Remain quiet the whole time.|
|
||||
|❌|Spend too much time writing comments.|
|
||||
|❌|Use extremely verbose variable names.|
|
||||
|❌|Copy and paste code without checking.|
|
||||
|❌|Interrupt your interviewer when they are talking. Usually if they speak, they are trying to give you hints or steer you in the right direction.|
|
||||
|❌|Write too big (takes up too much space) or too small (illegible) if on a whiteboard.|
|
||||
|
||||
### 5. After Coding
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Scan through your code for mistakes as if it was your first time seeing code written by someone else.|
|
||||
|✅|Check for off-by-one errors.|
|
||||
|✅|Come up with more test cases. Try extreme test cases.|
|
||||
|✅|Step through your code with those test cases.|
|
||||
|✅|Look out for places where you can refactor.|
|
||||
|✅|Reiterate the time and space complexity of your code.|
|
||||
|✅|Explain trade-offs and how the code/approach can be improved if given more time.|
|
||||
|❌|Immediately announce that you are done coding. Do the above first!|
|
||||
|❌|Argue with the interviewer. They may be wrong but that is very unlikely given that they are familiar with the question.|
|
||||
|
||||
### 6. Wrap Up
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Ask questions. More importantly, ask good and engaging questions that are tailored to the company! Pick some questions from [this list](../non-technical/questions-to-ask.md).|
|
||||
|✅|Thank the interviewer.|
|
||||
|⚠️|Ask about your interview performance. It can get awkward.|
|
||||
|❌|End the interview without asking any questions.|
|
||||
|
||||
### 7. Post Interview
|
||||
|
||||
|| Things |
|
||||
|-|-|
|
||||
|✅|Record the interview questions and answers down as these can be useful for future reference.|
|
||||
|⚠️|Send a follow up email to your interviewer(s) thanking them for their time and the opportunity to interview with them.|
|
@ -0,0 +1,15 @@
|
||||
// Does not handle negative binary numbers.
|
||||
function binToInt(binary) {
|
||||
let res = 0;
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
res = res * 2 + (+binary[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
console.log(binToInt('0') === parseInt('0', 2) && parseInt('0', 2) === 0);
|
||||
console.log(binToInt('1') === parseInt('1', 2) && parseInt('1', 2) === 1);
|
||||
console.log(binToInt('10') === parseInt('10', 2) && parseInt('10', 2) === 2);
|
||||
console.log(binToInt('11') === parseInt('11', 2) && parseInt('11', 2) === 3);
|
||||
console.log(binToInt('101') === parseInt('101', 2) && parseInt('101', 2) === 5);
|
||||
console.log(binToInt('1100011') === parseInt('1100011', 2) && parseInt('1100011', 2) === 99);
|
@ -0,0 +1,37 @@
|
||||
function deepEqual(val1, val2) {
|
||||
if (typeof val1 !== typeof val2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Array comparison.
|
||||
if (Array.isArray(val1) && Array.isArray(val2)) {
|
||||
if (val1.length !== val2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < val1.length; i++) {
|
||||
if (!deepEqual(val1[i], val2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Object comparison.
|
||||
if (typeof val1 === 'object' && typeof val2 === 'object' && val1 !== null) {
|
||||
const keys1 = Object.keys(val1), keys2 = Object.keys(val2);
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < keys1.length; i++) {
|
||||
if (!deepEqual(val1[keys1[i]], val2[keys2[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Primitive comparison.
|
||||
return val1 === val2;
|
||||
}
|
||||
|
||||
module.exports = deepEqual;
|
@ -0,0 +1,19 @@
|
||||
// Does not handle negative numbers.
|
||||
function intToBin(number) {
|
||||
if (number === 0) {
|
||||
return '0';
|
||||
}
|
||||
let res = '';
|
||||
while (number > 0) {
|
||||
res = String(number % 2) + res;
|
||||
number = parseInt(number / 2, 10);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
console.log(intToBin(0) === 0..toString(2) && 0..toString(2) === '0');
|
||||
console.log(intToBin(1) === 1..toString(2) && 1..toString(2) === '1');
|
||||
console.log(intToBin(2) === 2..toString(2) && 2..toString(2) === '10');
|
||||
console.log(intToBin(3) === 3..toString(2) && 3..toString(2) === '11');
|
||||
console.log(intToBin(5) === 5..toString(2) && 5..toString(2) === '101');
|
||||
console.log(intToBin(99) === 99..toString(2) && 99..toString(2) === '1100011');
|
@ -0,0 +1,17 @@
|
||||
// Interval: [start, end].
|
||||
function intervalsIntersect(a, b) {
|
||||
return a[0] < b[1] && b[0] < a[1];
|
||||
}
|
||||
|
||||
console.log(intervalsIntersect([1, 2], [3, 4]) === false);
|
||||
console.log(intervalsIntersect([1, 2], [2, 4]) === false);
|
||||
console.log(intervalsIntersect([1, 2], [1, 4]) === true);
|
||||
console.log(intervalsIntersect([1, 2], [0, 4]) === true);
|
||||
console.log(intervalsIntersect([1, 2], [0, 2]) === true);
|
||||
console.log(intervalsIntersect([1, 2], [0, 1.5]) === true);
|
||||
console.log(intervalsIntersect([3, 4], [1, 2]) === false);
|
||||
console.log(intervalsIntersect([2, 4], [1, 2]) === false);
|
||||
console.log(intervalsIntersect([1, 4], [1, 2]) === true);
|
||||
console.log(intervalsIntersect([0, 4], [1, 2]) === true);
|
||||
console.log(intervalsIntersect([0, 2], [1, 2]) === true);
|
||||
console.log(intervalsIntersect([0, 1.5], [1, 2]) === true);
|
@ -0,0 +1,16 @@
|
||||
// Interval: [start, end].
|
||||
// Merges two overlapping intervals into one.
|
||||
function intervalsMerge(a, b) {
|
||||
return [Math.min(a[0], b[0]), Math.max(a[1], b[1])];
|
||||
}
|
||||
|
||||
const deepEqual = require('./deepEqual');
|
||||
|
||||
console.log(deepEqual(intervalsMerge([1, 2], [1, 4]), [1, 4]));
|
||||
console.log(deepEqual(intervalsMerge([1, 2], [0, 4]), [0, 4]));
|
||||
console.log(deepEqual(intervalsMerge([1, 2], [0, 2]), [0, 2]));
|
||||
console.log(deepEqual(intervalsMerge([1, 2], [0, 1.5]), [0, 2]));
|
||||
console.log(deepEqual(intervalsMerge([1, 4], [1, 2]), [1, 4]));
|
||||
console.log(deepEqual(intervalsMerge([0, 4], [1, 2]), [0, 4]));
|
||||
console.log(deepEqual(intervalsMerge([0, 2], [1, 2]), [0, 2]));
|
||||
console.log(deepEqual(intervalsMerge([0, 1.5], [1, 2]), [0, 2]));
|
@ -0,0 +1,18 @@
|
||||
function matrixClone(matrix, defaultValue) {
|
||||
return matrix.map(row => {
|
||||
return defaultValue === undefined ? row.slice(0) : Array(row.length).fill(defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
const deepEqual = require('./deepEqual');
|
||||
|
||||
// Test clone.
|
||||
const a = [[1, 2], [1, 4]];
|
||||
console.log(deepEqual(matrixClone(a), [[1, 2], [1, 4]]));
|
||||
a[0][0] = 4;
|
||||
console.log(deepEqual(matrixClone(a), [[1, 2], [1, 4]]) === false);
|
||||
console.log(deepEqual(matrixClone([[1]]), [[1]]));
|
||||
|
||||
// Test clone with default value.
|
||||
console.log(deepEqual(matrixClone([[1, 2], [1, 4]], 1), [[1, 1], [1, 1]]));
|
||||
console.log(deepEqual(matrixClone([[1, 2], [1, 4]], null), [[null, null], [null, null]]));
|
@ -0,0 +1,11 @@
|
||||
function matrixTranspose(matrix) {
|
||||
return matrix[0].map((col, i) => matrix.map(row => row[i]));
|
||||
}
|
||||
|
||||
const deepEqual = require('./deepEqual');
|
||||
|
||||
console.log(deepEqual(matrixTranspose([[1]]), [[1]]));
|
||||
console.log(deepEqual(matrixTranspose([[1, 2]]), [[1], [2]]));
|
||||
console.log(deepEqual(matrixTranspose([[1, 2], [1, 4]]), [[1, 1], [2, 4]]));
|
||||
console.log(deepEqual(matrixTranspose([[1, 2, 3], [4, 5, 6]]), [[1, 4], [2, 5], [3, 6]]));
|
||||
|
@ -0,0 +1,28 @@
|
||||
function traverse(matrix) {
|
||||
const DIRECTIONS = [[0, 1], [0, -1], [1, 0], [-1, 0]];
|
||||
const rows = matrix.length, cols = matrix[0].length;
|
||||
const visited = matrix.map(row => Array(row.length).fill(false));
|
||||
function dfs(i, j) {
|
||||
if (visited[i][j]) {
|
||||
return;
|
||||
}
|
||||
visited[i][j] = true;
|
||||
DIRECTIONS.forEach(dir => {
|
||||
const row = i + dir[0], col = j + dir[1];
|
||||
// Boundary check.
|
||||
if (row < 0 || row >= rows || col < 0 || col >= cols) {
|
||||
return;
|
||||
}
|
||||
// Valid neighbor check.
|
||||
if (matrix[row][col] !== 1) {
|
||||
return;
|
||||
}
|
||||
dfs(row, col);
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < rows; i++) {
|
||||
for (let j = 0; j < cols; j++) {
|
||||
dfs(i, j);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
function mergeSort(arr) {
|
||||
if (arr.length < 2) {
|
||||
// Arrays of length 0 or 1 are sorted by definition.
|
||||
return arr;
|
||||
}
|
||||
|
||||
const left = arr.slice(0, Math.floor(arr.length / 2));
|
||||
const right = arr.slice(Math.floor(arr.length / 2), Math.floor(arr.length));
|
||||
|
||||
return merge(mergeSort(left), mergeSort(right));
|
||||
}
|
||||
|
||||
function merge(arr1, arr2) {
|
||||
const merged = [];
|
||||
let i = 0, j = 0;
|
||||
|
||||
while (i < arr1.length && j < arr2.length) {
|
||||
if (arr1[i] <= arr2[j]) {
|
||||
merged.push(arr1[i]);
|
||||
i++;
|
||||
} else if (arr2[j] < arr1[i]) {
|
||||
merged.push(arr2[j]);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
merged.push(...arr1.slice(i), ...arr2.slice(j));
|
||||
return merged;
|
||||
}
|
||||
|
||||
const deepEqual = require('./deepEqual');
|
||||
|
||||
console.log(deepEqual(
|
||||
mergeSort([]),
|
||||
[],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([1]),
|
||||
[1],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([2, 1]),
|
||||
[1, 2],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([7, 2, 4, 3, 1, 2]),
|
||||
[1, 2, 2, 3, 4, 7],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([1, 2, 3, 4, 5, 0]),
|
||||
[0, 1, 2, 3, 4, 5],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
));
|
||||
console.log(deepEqual(
|
||||
mergeSort([98322, 3242, 876, -234, 34, 12331]),
|
||||
[-234, 34, 876, 3242, 12331, 98322],
|
||||
));
|
@ -1 +0,0 @@
|
||||
# TODO
|
@ -0,0 +1,84 @@
|
||||
# Implements a min-heap. For max-heap, simply reverse all comparison orders.
|
||||
#
|
||||
# Note on alternate subroutine namings (used in some textbooks):
|
||||
# - _bubble_up = siftdown
|
||||
# - _bubble_down = siftup
|
||||
|
||||
def _bubble_up(heap, i):
|
||||
while i > 0:
|
||||
parent_i = (i - 1) // 2
|
||||
if heap[i] < heap[parent_i]:
|
||||
heap[i], heap[parent_i] = heap[parent_i], heap[i]
|
||||
i = parent_i
|
||||
continue
|
||||
break
|
||||
|
||||
def _bubble_down(heap, i):
|
||||
startpos = i
|
||||
newitem = heap[i]
|
||||
left_i = 2 * i + 1
|
||||
while left_i < len(heap):
|
||||
# Pick the smaller of the L and R children
|
||||
right_i = left_i + 1
|
||||
if right_i < len(heap) and not heap[left_i] < heap[right_i]:
|
||||
child_i = right_i
|
||||
else:
|
||||
child_i = left_i
|
||||
|
||||
# Break if heap invariant satisfied
|
||||
if heap[i] < heap[child_i]:
|
||||
break
|
||||
|
||||
# Move the smaller child up.
|
||||
heap[i], heap[child_i] = heap[child_i], heap[i]
|
||||
i = child_i
|
||||
left_i = 2 * i + 1
|
||||
|
||||
def heapify(lst):
|
||||
for i in reversed(range(len(lst) // 2)):
|
||||
_bubble_down(lst, i)
|
||||
|
||||
def heappush(heap, item):
|
||||
heap.append(item)
|
||||
_bubble_up(heap, len(heap) - 1)
|
||||
|
||||
def heappop(heap):
|
||||
if len(heap) == 1:
|
||||
return heap.pop()
|
||||
min_value = heap[0]
|
||||
heap[0] = heap[-1]
|
||||
del heap[-1]
|
||||
_bubble_down(heap, 0)
|
||||
return min_value
|
||||
|
||||
|
||||
|
||||
# Example usage
|
||||
heap = [3, 2, 1, 0]
|
||||
heapify(heap)
|
||||
print('Heap(0, 1, 2, 3):', heap)
|
||||
heappush(heap, 4)
|
||||
heappush(heap, 7)
|
||||
heappush(heap, 6)
|
||||
heappush(heap, 5)
|
||||
print('Heap(0, 1, 2, 3, 4, 5, 6, 7):', heap)
|
||||
|
||||
sorted_list = [heappop(heap) for _ in range(8)]
|
||||
print('Heap-sorted list:', sorted_list)
|
||||
|
||||
# Large test case, for randomized tests
|
||||
import random
|
||||
|
||||
# Heapify 0 ~ 99
|
||||
heap = list(range(100))
|
||||
random.shuffle(heap)
|
||||
heapify(heap)
|
||||
|
||||
# Push 100 ~ 199 in random order
|
||||
new_elems = list(range(100, 200))
|
||||
random.shuffle(new_elems)
|
||||
for elem in new_elems:
|
||||
heappush(heap, elem)
|
||||
|
||||
sorted_list = [heappop(heap) for _ in range(200)]
|
||||
print(sorted_list == sorted(sorted_list))
|
@ -0,0 +1,109 @@
|
||||
# Singly-Linked List
|
||||
#
|
||||
# The linked list is passed around as a variable pointing to the
|
||||
# root node of the linked list, or None if the list is empty.
|
||||
|
||||
class LinkedListNode:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
self.next = None
|
||||
|
||||
def linked_list_append(linked_list, value):
|
||||
'''Appends a value to the end of the linked list'''
|
||||
node = linked_list
|
||||
insert_node = LinkedListNode(value)
|
||||
if not node:
|
||||
return insert_node
|
||||
while node.next:
|
||||
node = node.next
|
||||
node.next = insert_node
|
||||
return linked_list
|
||||
|
||||
def linked_list_insert_index(linked_list, value, index):
|
||||
'''Inserts a value at a particular index'''
|
||||
node = linked_list
|
||||
insert_node = LinkedListNode(value)
|
||||
|
||||
# Check if inserting at head
|
||||
if index == 0:
|
||||
insert_node.next = node
|
||||
return insert_node
|
||||
|
||||
# Skip ahead
|
||||
for _ in range(index - 1):
|
||||
node = node.next
|
||||
if not node:
|
||||
raise ValueError
|
||||
insert_node.next = node.next
|
||||
node.next = insert_node
|
||||
return linked_list
|
||||
|
||||
def linked_list_delete(linked_list, value):
|
||||
'''Deletes the first occurrence of a value in the linked list'''
|
||||
node = linked_list
|
||||
|
||||
# Check if deleting at head
|
||||
if node.value == value:
|
||||
return node.next
|
||||
|
||||
# Skip ahead
|
||||
while node.next:
|
||||
if node.next.value == value:
|
||||
node.next = node.next.next
|
||||
return linked_list
|
||||
node = node.next
|
||||
raise ValueError
|
||||
|
||||
def linked_list_delete_index(linked_list, index):
|
||||
'''Deletes the element at a particular index in the linked list'''
|
||||
node = linked_list
|
||||
|
||||
# Check if deleting at head
|
||||
if index == 0:
|
||||
return node.next
|
||||
|
||||
# Skip ahead
|
||||
for _ in range(index - 1):
|
||||
node = node.next
|
||||
if not node:
|
||||
raise ValueError
|
||||
if not node.next:
|
||||
raise ValueError
|
||||
node.next = node.next.next
|
||||
return linked_list
|
||||
|
||||
def linked_list_iter(linked_list):
|
||||
'''Lazy iterator over each node in the linked list'''
|
||||
node = linked_list
|
||||
while node is not None:
|
||||
yield node
|
||||
node = node.next
|
||||
|
||||
|
||||
# Append to back
|
||||
linked_list = None # Start with an empty linked list
|
||||
linked_list = linked_list_append(linked_list, 1)
|
||||
linked_list = linked_list_append(linked_list, 2)
|
||||
linked_list = linked_list_append(linked_list, 4)
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
|
||||
# Insert by index
|
||||
linked_list = linked_list_insert_index(linked_list, 0, 0) # Front
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
linked_list = linked_list_insert_index(linked_list, 3, 3) # Back
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
|
||||
# Delete "3"
|
||||
linked_list = linked_list_delete(linked_list, 3)
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
|
||||
# Delete by index
|
||||
linked_list = linked_list_delete_index(linked_list, 0)
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
linked_list = linked_list_delete_index(linked_list, 1)
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
||||
|
||||
# Delete until empty
|
||||
linked_list = linked_list_delete_index(linked_list, 0)
|
||||
linked_list = linked_list_delete_index(linked_list, 0)
|
||||
print([node.value for node in linked_list_iter(linked_list)])
|
@ -0,0 +1,60 @@
|
||||
## QuickSelect -- Linear-time k-th order statistic
|
||||
## (i.e. select the k-th smallest element in an unsorted array)
|
||||
## https://en.wikipedia.org/wiki/Quickselect
|
||||
|
||||
def partition(array, start, end, pivot):
|
||||
"""Partitions by a pivot value, which might not necessarily be in the array.
|
||||
This variant is useful when you want to bound your recursion depth by the
|
||||
range of the input values, and not the length of the array."""
|
||||
pivot_index = start
|
||||
for i in range(start, end):
|
||||
if array[i] <= pivot:
|
||||
array[i], array[pivot_index] = array[pivot_index], array[i]
|
||||
pivot_index += 1
|
||||
return pivot_index
|
||||
|
||||
import random
|
||||
def partition_first(array, start, end):
|
||||
"""Selects the first element as pivot. Returns the index where the pivot went to.
|
||||
In this variant, we can guarantee that the pivot will be in its final sorted position.
|
||||
We need this guarantee for QuickSelect."""
|
||||
if start + 1 == end:
|
||||
return start
|
||||
pivot = array[start]
|
||||
pivot_index = start + 1
|
||||
for i in range(start + 1, end):
|
||||
if array[i] <= pivot:
|
||||
array[i], array[pivot_index] = array[pivot_index], array[i]
|
||||
pivot_index += 1
|
||||
# Move pivot to front
|
||||
array[start], array[pivot_index - 1] = array[pivot_index - 1], array[start]
|
||||
return pivot_index - 1
|
||||
|
||||
def quick_select(array, k):
|
||||
"""NOTE: k-th smallest element counts from 0!"""
|
||||
left = 0
|
||||
right = len(array)
|
||||
while True:
|
||||
random_index = random.sample(range(left, right), 1)[0]
|
||||
array[left], array[random_index] = array[random_index], array[left]
|
||||
pivot_index = partition_first(array, left, right)
|
||||
if k == pivot_index:
|
||||
return array[pivot_index]
|
||||
if k < pivot_index:
|
||||
right = pivot_index
|
||||
else:
|
||||
left = pivot_index + 1
|
||||
|
||||
|
||||
|
||||
print(quick_select([0], 0) == 0)
|
||||
print(quick_select([0, 1, 2, 3, 4], 2) == 2)
|
||||
print(quick_select([4, 3, 2, 1, 0], 2) == 2)
|
||||
print(quick_select([1, 3, 4, 2, 0], 2) == 2)
|
||||
|
||||
# Large test case, for randomized tests
|
||||
lst = list(range(1000))
|
||||
for _ in range(10):
|
||||
k = random.randint(0, 999)
|
||||
random.shuffle(lst)
|
||||
print(quick_select(lst, k) == k)
|
@ -0,0 +1,41 @@
|
||||
## Rabin-Karp Rolling Hash
|
||||
## Implementation of: https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm#Hash_function_used
|
||||
##
|
||||
## This rolling hash function is useful when you need to compute the hash of successive substrings
|
||||
## of text. E.g. note that going from 'abcd' to 'bcde', we drop the 'a' from the back and add an 'e'
|
||||
## on the right. The rolling hash function thus allows us to update the hash in-place O(1) instead of
|
||||
## recomputing the full hash of the substring O(m), where m is the length of the substring.
|
||||
##
|
||||
## NOTE: The implementation below takes in a tuple of integers, to be as general as possible. For use
|
||||
## with strings, simply take the ASCII value of each character before passing into the functions.
|
||||
|
||||
BASE = 101 # Arbitrary prime number
|
||||
|
||||
def rk_hash_init(tpl):
|
||||
'''Initializes the hash with a tuple of integers.'''
|
||||
return sum(n * BASE ** i for i, n in enumerate(reversed(tpl)))
|
||||
|
||||
def rk_hash_update(curr_hash, size, add_n, rem_n):
|
||||
'''Updates the hash by removing an integer from the left and appending
|
||||
an integer to the right.
|
||||
|
||||
curr_hash: The previous hash
|
||||
size: The size of the rolling window
|
||||
add_n: The integer appended to the right
|
||||
rem_n: The integer removed from the left'''
|
||||
return (curr_hash - (rem_n * BASE ** (size - 1))) * BASE + add_n
|
||||
|
||||
|
||||
|
||||
abc_hash = rk_hash_init(tuple(map(ord, 'abc'))) # Init the hash with 'abc'
|
||||
print('abc:', abc_hash)
|
||||
bcd_hash_1 = rk_hash_update(abc_hash, 3, ord('d'), ord('a')) # Add a 'd' to the right, remove an 'a' from the left
|
||||
print('bcd 1:', bcd_hash_1)
|
||||
|
||||
zbc_hash = rk_hash_init(tuple(map(ord, 'zbc'))) # Init the hash with 'zbc'
|
||||
print('zbc:', zbc_hash)
|
||||
bcd_hash_2 = rk_hash_update(zbc_hash, 3, ord('d'), ord('z')) # Add a 'd' to the right, remove a 'z' from the left
|
||||
print('bcd 2:', bcd_hash_2)
|
||||
|
||||
# Notice that both hash values are the same despite arriving via different paths
|
||||
print(bcd_hash_1 == bcd_hash_2)
|
@ -1 +0,0 @@
|
||||
# TODO
|
@ -0,0 +1,50 @@
|
||||
## Union-Find data structure
|
||||
## https://en.wikipedia.org/wiki/Disjoint-set_data_structure
|
||||
|
||||
parents = [0, 1, 2, 3, 4, 5, 6] # parent[i] is the parent of i
|
||||
weights = [1, 1, 1, 1, 1, 1, 1]
|
||||
|
||||
def find_root(parents, p):
|
||||
'''Average: O(log n)'''
|
||||
root = p
|
||||
while parents[root] != root:
|
||||
root = parents[root]
|
||||
# Flatten tree
|
||||
while parents[p] != p:
|
||||
parents[p], p = root, parents[p]
|
||||
return root
|
||||
|
||||
def union(parents, p, q):
|
||||
'''Average: O(log n)'''
|
||||
p = find_root(parents, p)
|
||||
q = find_root(parents, q)
|
||||
# Link the smaller node to the larger node
|
||||
if weights[p] > weights[q]:
|
||||
parents[q] = p
|
||||
weights[p] += weights[q]
|
||||
else:
|
||||
parents[p] = q
|
||||
weights[q] += weights[p]
|
||||
|
||||
|
||||
|
||||
# Start with all elements separate
|
||||
# -> [0], [1], [2], [3], [4], [5], [6]
|
||||
print(find_root(parents, 2) == 2)
|
||||
|
||||
# Merge 1, 2, 3 and 4, 5, 6
|
||||
# -> [0], [1, 2, 3], [4, 5, 6]
|
||||
union(parents, 1, 2)
|
||||
union(parents, 2, 3)
|
||||
union(parents, 4, 5)
|
||||
union(parents, 4, 6)
|
||||
|
||||
# Roots of 1, 2, 3 and 4, 5, 6 are the same
|
||||
print(find_root(parents, 0))
|
||||
print(list(find_root(parents, i) for i in (1, 2, 3)))
|
||||
print(list(find_root(parents, i) for i in (4, 5, 6)))
|
||||
|
||||
# Merge 2, 4
|
||||
# -> [0], [1, 2, 3, 4, 5, 6]
|
||||
union(parents, 2, 4)
|
||||
print(list(find_root(parents, i) for i in (1, 2, 3, 4, 5, 6)))
|
Loading…
Reference in new issue