Skip to content

Commit d789c8b

Browse files
author
Isaac Ramirez
committed
- implement Selection Sort algorithm
1 parent fd452c5 commit d789c8b

File tree

6 files changed

+288
-2
lines changed

6 files changed

+288
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ _For a list similar to the book site see this [algorithms list](/docs/algorithms
6161

6262
**Algorithms:**
6363

64-
> TODO
64+
* [Selection Sort - p.249](/src/algorithms/selection-sort/selection-sort.js)
6565

6666
**ADTs:**
6767

docs/algorithms-list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Every algorithm or ADT has a link pointing to the original Java implementation a
5050
* Insertion | js | java |
5151
* InsertionX | js | java |
5252
* BinaryInsertion | js | java |
53-
* Selection | js | java |
53+
* Selection | [js](/src/algorithms/selection-sort/selection-sort.js) | [java](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/Selection.java.html) |
5454
* Shell | js | java |
5555
* Merge | js | java |
5656
* MergeBU | js | java |

src/algorithms/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module.exports = {
33
DijkstraEvaluate: require('./dijkstra-two-stack/dijkstra-two-stack'),
44
Euclidean: require('./euclidean/euclidean'),
55
LinearSearch: require('./linear-search/linear-search'),
6+
SelectionSort: require('./selection-sort/selection-sort'),
67
ThreeSum: require('./three-sum/three-sum'),
78
ThreeSumFast: require('./three-sum-fast/three-sum-fast'),
89
TwoSumFast: require('./two-sum-fast/two-sum-fast'),
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const { StdIn } = require('../../libs')
2+
const Selection = require('./selection-sort')
3+
4+
/**
5+
* SelectionSortClient
6+
* @classdesc Selection Sort Test Client.
7+
* @see p. 245
8+
*/
9+
class SelectionSortClient {
10+
/**
11+
* Runs the Selection Sort Algorithm
12+
* with the input provided.
13+
* @example <caption>Tiny Example</caption>
14+
* ```sh
15+
* $ node selection-sort.client.js < ~/algs4-data/tiny.txt
16+
* A E E L M O P R S T X
17+
* ```
18+
* @example <caption>Words3 Example</caption>
19+
* ```sh
20+
* $ node selection-sort.client.js < ~/algs4-data/words3.txt
21+
* all bad bed bug dad ... yes yet zoo
22+
* ```
23+
*/
24+
static main () {
25+
let array = []
26+
27+
// read all strings from standard input.
28+
StdIn.read()
29+
.on('line', line => {
30+
array = array.concat(line.split(' '))
31+
})
32+
.on('close', () => {
33+
Selection.sort(array)
34+
35+
if (!Selection.isSorted(array)) {
36+
throw new Error('array is NOT sorted')
37+
}
38+
39+
Selection.show(array)
40+
})
41+
}
42+
}
43+
44+
// Execution
45+
// ==============================
46+
SelectionSortClient.main()
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
const { StdOut } = require('../../libs')
2+
3+
/**
4+
* A default comparing function that
5+
* returns -1 when `a` is less than `b`,
6+
* returns 1 when `a` is greater than `b` or
7+
* returns 0 when `a` is equal to `b`.
8+
* @param {*} a Comparable object `a`
9+
* @param {*} b Comparable object `b`
10+
*/
11+
const defaultComparator = (a, b) => {
12+
if (a === b) return 0
13+
14+
return a < b ? -1 : 1
15+
}
16+
17+
/**
18+
* Selection
19+
* @classdesc Selection Sort Algorithm.
20+
* @see p. 245, 249
21+
* @see {@link https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/Selection.java.html}
22+
*/
23+
class Selection {
24+
/**
25+
* Returns whether `a` is less than `b`
26+
* based on a comparing function.
27+
* @param {*} a Comparable object `a`
28+
* @param {*} b Comparable object `b`
29+
* @param {function} comparator A comparing function that
30+
* returns -1 when `a` is less than `b`,
31+
* returns 1 when `a` is greater than `b` or
32+
* returns 0 when `a` is equal to `b`.
33+
*/
34+
static less (a, b, comparator = defaultComparator) {
35+
return comparator(a, b) < 0
36+
}
37+
38+
/**
39+
* Interchanges the values located in indexes `i` and `j`
40+
* for the given `array`.
41+
* @param {[*]} array Array of values.
42+
* @param {number} i Index 1
43+
* @param {number} j Index 2
44+
*/
45+
static exchange (array, i, j) {
46+
const temp = array[i]
47+
48+
array[i] = array[j]
49+
array[j] = temp
50+
}
51+
52+
/**
53+
* Returns `true` if the provided array is sorted,
54+
* `false` otherwise.
55+
* @param {[*]} array Array of values
56+
* @param {function} comparator A comparing function that
57+
* returns -1 when `a` is less than `b`,
58+
* returns 1 when `a` is greater than `b` or
59+
* returns 0 when `a` is equal to `b`.
60+
*/
61+
static isSorted (array, comparator = defaultComparator) {
62+
for (let i = 1; i < array.length; i++) {
63+
if (Selection.less(array[i], array[i - 1], comparator)) {
64+
return false
65+
}
66+
}
67+
68+
return true
69+
}
70+
71+
/**
72+
* A function that prints to the StdOut
73+
* the contents of the `array` provided.
74+
* @param {[*]} array Array of values.
75+
*/
76+
static show (array) {
77+
StdOut.println(array.join(' '))
78+
}
79+
80+
/**
81+
* Sorts the `array` using the Selection Sort algorithm.
82+
* @param {[*]} array Array of values.
83+
* @param {*} comparator A comparing function that
84+
* returns -1 when `a` is less than `b`,
85+
* returns 1 when `a` is greater than `b` or
86+
* returns 0 when `a` is equal to `b`.
87+
*/
88+
static sort (array, comparator = defaultComparator) {
89+
const n = array.length
90+
91+
for (let i = 0; i < n; i++) {
92+
let min = i
93+
94+
for (let j = i + 1; j < n; j++) {
95+
if (Selection.less(array[j], array[min], comparator)) {
96+
min = j
97+
}
98+
}
99+
100+
Selection.exchange(array, i, min)
101+
}
102+
}
103+
}
104+
105+
module.exports = Selection
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
const Selection = require('./selection-sort')
2+
const { newArrayOf } = require('../../utils')
3+
const { StdRandom } = require('../../libs')
4+
5+
const comparator = (a, b) => {
6+
if (a < b) return -1
7+
else if (a > b) return 1
8+
return 0
9+
}
10+
11+
describe('Unit Tests: Selection Sort Algorithm', () => {
12+
beforeEach(() => {
13+
this.orderedArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
14+
this.reversedArray = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
15+
this.unorderedArray = [0, 9, 5, 2, 1, 8, 7, 6, 4, 3]
16+
})
17+
18+
describe('static less method', () => {
19+
it('should return true if a is less than b', () => {
20+
const a = 1
21+
const b = 2
22+
23+
expect(Selection.less(a, b, comparator)).toBe(true)
24+
})
25+
26+
it('should return false if a is greater than b', () => {
27+
const a = 2
28+
const b = 1
29+
30+
expect(Selection.less(a, b, comparator)).toBe(false)
31+
})
32+
33+
it('should return false if a is equal to b', () => {
34+
const a = 1
35+
const b = 1
36+
37+
expect(Selection.less(a, b, comparator)).toBe(false)
38+
})
39+
40+
it('should have a defaultComparator function', () => {
41+
expect(Selection.less(1, 10)).toBe(true)
42+
expect(Selection.less(1, 1)).toBe(false)
43+
expect(Selection.less(10, 1)).toBe(false)
44+
})
45+
})
46+
47+
describe('static exchange method', () => {
48+
it('should interchange the values of the two given indexes in the array', () => {
49+
const array = [5, 6, 7]
50+
const expectedArray = [7, 6, 5]
51+
52+
Selection.exchange(array, 0, 2)
53+
54+
expect(array).toEqual(expectedArray)
55+
})
56+
})
57+
58+
describe('static isSorted method', () => {
59+
it('should return true if an array is sorted', () => {
60+
expect(Selection.isSorted(this.orderedArray)).toBe(true)
61+
})
62+
63+
it('should return false if the array is not sorted', () => {
64+
expect(Selection.isSorted(this.unorderedArray)).toBe(false)
65+
expect(Selection.isSorted(this.reversedArray)).toBe(false)
66+
})
67+
68+
it('should accept a comparator function', () => {
69+
const orderedArray = ['A', 'B', 'C']
70+
const unorderedArray = ['Z', 'X', 'Y']
71+
72+
expect(Selection.isSorted(orderedArray, comparator)).toBe(true)
73+
expect(Selection.isSorted(unorderedArray, comparator)).toBe(false)
74+
})
75+
76+
it('should return true for a sorted big array', () => {
77+
const n = 1000000 // a million!
78+
const array = newArrayOf(n, i => i) // from 0 to 999999
79+
80+
expect(Selection.isSorted(array)).toBeTrue()
81+
})
82+
83+
it('should return false for a random big array', () => {
84+
const n = 1000000 // a million!
85+
const array = newArrayOf(n, i => i) // from 0 to 999999
86+
array[array.length - 1] = 0 // change last number to be 0
87+
88+
expect(Selection.isSorted(array)).toBeFalse()
89+
})
90+
})
91+
92+
describe('static sort method', () => {
93+
it('should be a function', () => {
94+
// I could test the call to StdOut, but I don't want to.
95+
expect(Selection.show).toBeInstanceOf(Function)
96+
})
97+
})
98+
99+
describe('static sort method', () => {
100+
it('should sort a unordered array', () => {
101+
const expectedArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
102+
103+
Selection.sort(this.unorderedArray)
104+
105+
expect(this.unorderedArray).toEqual(expectedArray)
106+
})
107+
108+
it('should sort an ordered array', () => {
109+
const expectedArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
110+
111+
Selection.sort(this.orderedArray)
112+
113+
expect(this.orderedArray).toEqual(expectedArray)
114+
})
115+
116+
it('should sort a reversed array', () => {
117+
const expectedArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
118+
119+
Selection.sort(this.reversedArray)
120+
121+
expect(this.reversedArray).toEqual(expectedArray)
122+
})
123+
124+
it('should sort a small random array', () => {
125+
// considering that Selection Sort is slow...
126+
const n = 1000 // a thousand
127+
const array = newArrayOf(n, () => StdRandom.uniform(n))
128+
129+
Selection.sort(array)
130+
131+
expect(Selection.isSorted(array)).toBeTrue()
132+
})
133+
})
134+
})

0 commit comments

Comments
 (0)