Skip to content

sorting questions #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions book/D-interview-questions-solutions.asc
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,101 @@ This algorithm only works with DFS.

//

:leveloffset: +1

=== Solutions for Sorting Questions
(((Interview Questions Solutions, sorting)))

:leveloffset: -1


[#sorting-q-merge-intervals]
include::content/part04/sorting-algorithms.asc[tag=sorting-q-merge-intervals]

The first thing we need to understand is all the different possibilities for overlaps:

// image::merge-intervals-cases.png[merge intervals cases] // blurry
// image::intervals-overlap-cases.jpg[merge intervals cases] // too big
// image::intervals-overlap-cases.svg[merge intervals cases] // errors

// my own image
image::intervals-overlap-cases-owned.png[merge intervals cases]

One way to solve this problem, is sorting by start time. That will eliminate half of the cases!

Since A will always start before B, only 3 cases apply:
- No overlap: `[[1, 3], [4, 6]]`.
- Overlap at the end: `[[1, 3], [2, 4]]`.
- Eclipse: `[[1, 9], [3, 7]]`.

*Algorithm*:

* Sort intervals by start time
* If the `curr`ent interval's start time is _equal_ or less than the `last` interval's end time, then we have an overlap.
** Overlaps has two cases: 1) `curr`'s end is larger 2) `last`'s end is larger. For both cases `Math.max` works.
* If there's no overlap, we add the interval to the solution.

*Implementation*:

[source, javascript]
----
include::interview-questions/merge-intervals.js[tags=description;solution]
----

For the first interval, it will be added straight to the solution array. For all others, we will do the comparison.

*Complexity Analysis*:

- Time: `O(n log n)`. Standard libraries has a sorting time of `O(n log n)`, then we visit each interval in `O(n)`.
- Space: `O(n)`. In the worst-case is when there's no overlapping intervals. The size of the solution array would be `n`.







//

[#sorting-q-sort-colors]
include::content/part04/sorting-algorithms.asc[tag=sorting-q-sort-colors]

We are asked to sort an array with 3 possible values. If we use the standard sorting method `Array.sort`, that will be `O(n log n)`. However, we are asked to solve in linear time and constant space complexity.

The concept on quicksort can help here. We can choose 1 as a pivot and move everything less than 1 to the left and everything bigger than 1 to the right.

*Algorithm*:

* Initialize 3 pointers: `left = 0`, `right = len - 1` and `current = 0`.
* While the `current` pointer is less than `right`
** If `current` element is less than pivot 1, swap it to the left and increase the `left` and `current` pointer.
*** We can safely increase the current pointer
** If `current` element is bigger than pivot 1, swap it to the right and decrease `right` pointer.
*** Here, we don't increase the `current` pointer because the number that we swapped with could be another 2 and we might need to keep swapping while decreasing `right`.

*Implementation*:

[source, javascript]
----
include::interview-questions/sort-colors.js[tags=description;solution]
----

We are using the destructive assigment to swap the elements. Here's another version a little bit more compact.

[source, javascript]
----
include::interview-questions/sort-colors.js[tags=compact]
----

*Complexity Analysis*:

- Time: `O(n)`. We only visit each number once.
- Space: `O(1)`. Operations are in-place. Only O(1) space variables were used.







//
2 changes: 1 addition & 1 deletion book/config
75 changes: 75 additions & 0 deletions book/content/part04/sorting-algorithms.asc
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,78 @@ We explored many algorithms some of them simple and other more performant. Also,
// | Tim sort | O(n log n) | O(log n) | Yes | No | No | Yes
|===
// end::table[]









==== Practice Questions
(((Interview Questions, sorting)))





// tag::sorting-q-merge-intervals[]
===== Merge Intervals

*so-1*) _Given an array of intervals `[start, end]`, merge all overlapping intervals._

// end::sorting-q-merge-intervals[]

// _Seen in interviews at: X._

*Starter code*:

[source, javascript]
----
include::../../interview-questions/merge-intervals.js[tags=description;placeholder]
----

*Examples*:

[source, javascript]
----
merge([[0, 2], [2, 4]]); // [[0, 4]] (0-2 overlaps with 2-4)
merge([[2, 2], [3, 4]]); // [[2, 2], [3, 4]] (no overlap)
merge([[1, 10], [3, 4]]); // [[1, 10]] (1-10 covers the other)
----


_Solution: <<sorting-q-merge-intervals>>_






// tag::sorting-q-sort-colors[]
===== Sort Colors (The Dutch flag problem)

*so-2*) _Given an array with 3 possible values (0, 1, 2), sort them in linear time and in-place. Hint: similar to quicksort, where the pivot is 1._

// end::sorting-q-sort-colors[]

// _Seen in interviews at: Amazon, Microsoft, Facebook._

*Starter code*:

[source, javascript]
----
include::../../interview-questions/sort-colors.js[tags=description;placeholder]
----

*Examples*:

[source, javascript]
----
sortColors([0, 2, 1]); // [0, 1, 2]
sortColors([2, 0, 2, 1, 0, 1, 0]); // [0, 0, 0, 1, 1, 2, 2]
sortColors([1, 1, 1]); // [1, 1, 1]
----

_Solution: <<sorting-q-sort-colors>>_
Binary file modified book/images/Find-the-largest-sum.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/Recursive-Fibonacci-call-tree-with-dp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/Words-Permutations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/course-schedule-examples.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/critical-connections-sol-examples.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/critical-path-examples.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added book/images/intervals-overlap-cases-owned.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added book/images/intervals-overlap-cases.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions book/images/intervals-overlap-cases.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added book/images/merge-intervals-cases.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified book/images/recursive-fibonacci-call-tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions book/interview-questions/merge-intervals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// const { } = require('../../src/index');

// tag::description[]
/**
* Merge overlapping intervals.
* @param {[numer, number][]} intervals - Array with pairs [start, end]
* @returns {[numer, number][]} - Array of merged pairs [start, end]
*/
function merge(intervals) {
// end::description[]
// tag::placeholder[]
// write your code here...
// end::placeholder[]
// tag::solution[]
const ans = [];

intervals.sort((a, b) => a[0] - b[0]); // sort by start time

for (let i = 0; i < intervals.length; i++) {
const last = ans[ans.length - 1];
const curr = intervals[i];
if (last && last[1] >= curr[0]) { // check for overlaps
last[1] = Math.max(last[1], curr[1]);
} else ans.push(curr);
}
return ans;
// end::solution[]
// tag::description[]
}
// end::description[]

module.exports = { merge };
30 changes: 30 additions & 0 deletions book/interview-questions/merge-intervals.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { merge } = require('./merge-intervals');
// const { } = require('../../src/index');

[merge].forEach((fn) => {
describe(`TOPIC: ${fn.name}`, () => {
it('should work with null/empty', () => {
const actual = fn([]);
const expected = [];
expect(actual).toEqual(expected);
});

it('should work with small case', () => {
const actual = fn([[1, 3]]);
const expected = [[1, 3]];
expect(actual).toEqual(expected);
});

it('should work with other case', () => {
const actual = fn([[0, 1], [1, 3], [3, 5], [6, 6]]);
const expected = [[0, 5], [6, 6]];
expect(actual).toEqual(expected);
});

it('should work with other case', () => {
const actual = fn([[10, 99], [20, 50], [9, 11], [98, 100]]);
const expected = [[9, 100]];
expect(actual).toEqual(expected);
});
});
});
49 changes: 49 additions & 0 deletions book/interview-questions/sort-colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// const { } = require('../../src/index');

// tag::description[]
/**
* Sort array of 0's, 1's and 2's in linear time and in-place.
* @param {numbers[]} nums - Array of number (0, 1, or 2).
* @returns {void} Don't return anything, modify the input array.
*/
function sortColors(nums) {
// end::description[]
// tag::placeholder[]
// write your code here...
// end::placeholder[]
// tag::solution[]
let left = 0;
let right = nums.length - 1;
let curr = 0;

while (curr <= right) {
if (nums[curr] < 1) {
[nums[curr], nums[left]] = [nums[left], nums[curr]]; // swap
left++;
curr++;
} else if (nums[curr] > 1) {
[nums[curr], nums[right]] = [nums[right], nums[curr]]; // swap
right--;
} else {
curr++;
}
}
// end::solution[]
// tag::description[]
}
// end::description[]

// tag::compact[]
function sortColorsCompact(nums) {
let i = 0, lo = 0, hi = nums.length - 1;
const swap = (k, j) => [nums[k], nums[j]] = [nums[j], nums[k]];

while (i <= hi) {
if (nums[i] < 1) swap(i++, lo++);
else if (nums[i] > 1) swap(i, hi--);
else i++;
}
}
// end::compact[]

module.exports = { sortColors, sortColorsCompact };
55 changes: 55 additions & 0 deletions book/interview-questions/sort-colors.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { sortColors, sortColorsCompact } = require('./sort-colors');
// const { } = require('../../src/index');

[sortColors, sortColorsCompact].forEach((fn) => {
describe(`Sorting: ${fn.name}`, () => {
it('should work with null/empty', () => {
const actual = [];
fn(actual);
const expected = [];
expect(actual).toEqual(expected);
});

it('should work with small case', () => {
const actual = [0, 2, 1];
fn(actual);
const expected = [0, 1, 2];
expect(actual).toEqual(expected);
});

it('should work with small case', () => {
const actual = [2, 1, 2];
fn(actual);
const expected = [1, 2, 2];
expect(actual).toEqual(expected);
});

it('should work with small case', () => {
const actual = [1, 0, 2];
fn(actual);
const expected = [0, 1, 2];
expect(actual).toEqual(expected);
});

it('should work with small case', () => {
const actual = [2, 0, 1];
fn(actual);
const expected = [0, 1, 2];
expect(actual).toEqual(expected);
});

it('all numbers the same', () => {
const actual = Array(3).fill(1);
fn(actual);
const expected = [1, 1, 1];
expect(actual).toEqual(expected);
});

it('larger cases', () => {
const actual = [2, 0, 2, 1, 0, 1, 0];
fn(actual);
const expected = [0, 0, 0, 1, 1, 2, 2];
expect(actual).toEqual(expected);
});
});
});