├── go ├── .gitignore ├── src │ └── algocasts │ │ ├── util.go │ │ ├── BubbleSort.go │ │ ├── insertsort.go │ │ ├── shellsort.go │ │ ├── CocktailSort.go │ │ ├── shellsort_test.go │ │ ├── BubbleSort_test.go │ │ ├── insertsort_test.go │ │ ├── CocktailSort_test.go │ │ ├── selectsort.go │ │ ├── BucketSort_test.go │ │ ├── BucketSort.go │ │ ├── selectsort_test.go │ │ ├── quicksort_test.go │ │ ├── CountSort_test.go │ │ ├── mergesort_test.go │ │ ├── HeapSort_test.go │ │ ├── RadixSort_test.go │ │ ├── CountSort.go │ │ ├── quicksort.go │ │ ├── RadixSort.go │ │ ├── mergesort.go │ │ └── HeapSort.go ├── go.mod ├── Readme.md └── go.sum ├── README.md ├── .gitignore └── java ├── src ├── main │ └── java │ │ └── io │ │ └── algocasts │ │ ├── ListNode.java │ │ ├── TreeNode.java │ │ ├── InsertionSort.java │ │ ├── ShellSort.java │ │ ├── SelectionSort.java │ │ ├── HeapSort.java │ │ ├── CountingSortFixedK.java │ │ ├── MergeSort.java │ │ ├── BucketSortDouble.java │ │ ├── CountingSort.java │ │ ├── BubbleSort.java │ │ ├── BucketSort.java │ │ ├── CocktailSort.java │ │ ├── RadixSort.java │ │ └── QuickSort.java └── test │ └── java │ └── io │ └── algocasts │ ├── HeapSortTest.java │ ├── ShellSortTest.java │ ├── InsertionSortTest.java │ ├── QuickSortTest.java │ ├── MergeSortTest.java │ ├── SelectionSortTest.java │ ├── CocktailSortTest.java │ ├── BubbleSortTest.java │ ├── BucketSortDoubleTest.java │ ├── BucketSortTest.java │ ├── CountingSortFixedKTest.java │ ├── CountingSortTest.java │ ├── SortTest.java │ ├── RadixSortTest.java │ └── Helper.java └── pom.xml /go/.gitignore: -------------------------------------------------------------------------------- 1 | src/algocasts/.idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlgoCasts 排序算法专题 2 | 3 | 这是 AlgoCasts 排序算法专题的代码仓库。 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java 2 | .idea 3 | *.iml 4 | target 5 | 6 | # go 7 | go/.idea 8 | 9 | -------------------------------------------------------------------------------- /go/src/algocasts/util.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func swap(array []int, a int, b int) { 4 | tmp := array[b] 5 | array[b] = array[a] 6 | array[a] = tmp 7 | } 8 | -------------------------------------------------------------------------------- /go/go.mod: -------------------------------------------------------------------------------- 1 | module sorting-algorithems 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/stretchr/testify v1.3.0 7 | golang.org/x/tools v0.0.0-20190911230505-6bfd74cf029c // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/ListNode.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class ListNode { 4 | public int val; 5 | public ListNode next; 6 | 7 | public ListNode(int x) { 8 | val = x; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/TreeNode.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class TreeNode { 4 | public int val; 5 | public TreeNode left; 6 | public TreeNode right; 7 | 8 | public TreeNode(int x) { 9 | val = x; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/HeapSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class HeapSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | HeapSort h = new HeapSort(); 10 | test(h::sort); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/ShellSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class ShellSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | ShellSort s = new ShellSort(); 10 | test(s::sort); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/InsertionSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class InsertionSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | InsertionSort i = new InsertionSort(); 10 | test(i::sort); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/QuickSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class QuickSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | QuickSort q = new QuickSort(); 10 | test(q::lomutoSort); 11 | test(q::hoareSort); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /go/src/algocasts/BubbleSort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func BubbleSort(input []int) { 4 | length := len(input) 5 | for i := length - 1; i > 0; i-- { 6 | for j := 0; j < i; j++ { 7 | if input[j] > input[i] { 8 | tmp := input[j] 9 | input[j] = input[i] 10 | input[i] = tmp 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/MergeSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class MergeSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | MergeSort m = new MergeSort(); 10 | test(m::sortRecursive); 11 | test(m::sortIterative); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /go/src/algocasts/insertsort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func insertsort(input []int) { 4 | if input == nil || len(input) == 0 { 5 | return 6 | } 7 | for i := 1; i < len(input); i++ { 8 | cur := input[i] 9 | j := i - 1 10 | for j >= 0 && input[j] > cur { 11 | input[j+1] = input[j] 12 | j -= 1 13 | } 14 | input[j+1] = cur 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/SelectionSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class SelectionSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | SelectionSort s = new SelectionSort(); 10 | test(s::sort); 11 | } 12 | 13 | @Test 14 | public void testFromBackVersion() { 15 | SelectionSort s = new SelectionSort(); 16 | test(s::sortFromEnd); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /go/Readme.md: -------------------------------------------------------------------------------- 1 | # Algocasts sorting-algorithem(Go version) 2 | 3 | 4 | ## usage 5 | 6 | ### precondition 7 | 1. go version >= 1.11 8 | 2. go mod is enabled (if not enable, google how to enable ) 9 | 10 | ### usage 11 | 1. go mod download 12 | 2. use cmd like this to run test 13 | 14 | (using go mod somehow can't use goland test button to run test , this problem will be solve later) 15 | ``` 16 | go test mergesort_test.go mergesort.go 17 | ``` 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /go/src/algocasts/shellsort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func shellsort(input []int) { 4 | if input == nil || len(input) == 0 { 5 | return 6 | } 7 | // >>= means bit counting left move 8 | for gap := len(input) >> 1; gap > 0; gap >>= 1 { 9 | for i := gap; i < len(input); i++ { 10 | cur := input[i] 11 | j := i - gap 12 | for j >= 0 && input[j] > cur { 13 | input[j+gap] = input[j] 14 | j -= gap 15 | } 16 | input[j+gap] = cur 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go/src/algocasts/CocktailSort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func CocktailSort(input []int) { 4 | if len(input) == 0 { 5 | return 6 | } 7 | left := 0 8 | right := len(input) - 1 9 | for left < right { 10 | for i := left; i < right; i++ { 11 | if input[i] > input[i+1] { 12 | swap(input, i, i+1) 13 | } 14 | } 15 | right -= 1 16 | 17 | for i := right; i > left; i-- { 18 | if input[i] < input[i-1] { 19 | swap(input, i-1, i) 20 | } 21 | } 22 | left += 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/InsertionSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class InsertionSort { 4 | 5 | // Time: O(n^2), Space: O(1) 6 | public void sort(int[] arr) { 7 | if (arr == null || arr.length == 0) return; 8 | for (int i = 1; i < arr.length; ++i) { 9 | int cur = arr[i]; 10 | int j = i - 1; 11 | while (j >= 0 && arr[j] > cur) { 12 | arr[j+1] = arr[j]; 13 | --j; 14 | } 15 | arr[j+1] = cur; 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /go/src/algocasts/shellsort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestShellSort(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 15 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 16 | } 17 | for _, tt := range tests { 18 | shellsort(tt.input) 19 | assert.Equal(t, tt.input, tt.want) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/src/algocasts/BubbleSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestBubbleSort(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | BubbleSort(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go/src/algocasts/insertsort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestInsertSort(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | insertsort(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go/src/algocasts/CocktailSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestCocktailSort(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | CocktailSort(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/CocktailSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class CocktailSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | CocktailSort c = new CocktailSort(); 10 | test(c::sort); 11 | } 12 | 13 | @Test 14 | public void testEarlyReturnVersion() { 15 | CocktailSort c = new CocktailSort(); 16 | test(c::sortEarlyReturn); 17 | } 18 | 19 | @Test 20 | public void testSkipVersion() { 21 | CocktailSort c = new CocktailSort(); 22 | test(c::sortSkip); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /go/src/algocasts/selectsort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func selectsortMin(input []int) { 4 | if input == nil || len(input) == 0 { 5 | return 6 | } 7 | for i := 0; i < len(input); i++ { 8 | minId := i 9 | for j := i + 1; j < len(input); j++ { 10 | if input[j] < input[minId] { 11 | minId = j 12 | } 13 | } 14 | swap(input, i, minId) 15 | } 16 | } 17 | 18 | func selectsortMax(input []int) { 19 | if input == nil || len(input) == 0 { 20 | return 21 | } 22 | for i := len(input) - 1; i >= 0; i-- { 23 | maxId := i 24 | for j := 0; j < i; j++ { 25 | if input[j] > input[maxId] { 26 | maxId = j 27 | } 28 | } 29 | swap(input, i, maxId) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/BubbleSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | public class BubbleSortTest extends SortTest { 6 | 7 | @Test 8 | public void test() { 9 | BubbleSort b = new BubbleSort(); 10 | test(b::sort); 11 | } 12 | 13 | @Test 14 | public void testShortVersion() { 15 | BubbleSort b = new BubbleSort(); 16 | test(b::sortShort); 17 | } 18 | 19 | @Test 20 | public void testEarlyReturnVersion() { 21 | BubbleSort b = new BubbleSort(); 22 | test(b::sortEarlyReturn); 23 | } 24 | 25 | @Test 26 | public void testSkipVersion() { 27 | BubbleSort b = new BubbleSort(); 28 | test(b::sortSkip); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /go/src/algocasts/BucketSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func gen100() []int { 9 | result := make([]int, 0, 100) 10 | for i := 1; i <= 100; i++ { 11 | result = append(result, i*100) 12 | } 13 | return result 14 | } 15 | 16 | func genR100() []int { 17 | result := make([]int, 0, 100) 18 | for i := 100; i > 0; i-- { 19 | result = append(result, i*100) 20 | } 21 | return result 22 | } 23 | 24 | func TestBucketSort(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | input []int 28 | want []int 29 | }{ 30 | {"sort100", genR100(), gen100()}, 31 | } 32 | for _, tt := range tests { 33 | BucketSort(tt.input) 34 | assert.Equal(t, tt.input, tt.want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/BucketSortDoubleTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class BucketSortDoubleTest { 7 | 8 | private final int CNT = Helper.getRandomInt(10, 100); 9 | private final int LENGTH = Helper.getRandomInt(1000, 2000); 10 | private final double DELTA = 0.000001; 11 | 12 | @Test 13 | public void test() { 14 | // Possible bucket size: 5~15 15 | int bucketSize = Helper.getRandomInt(5, 16); 16 | BucketSortDouble b = new BucketSortDouble(bucketSize); 17 | for (int i = 0; i < CNT; ++i) { 18 | double[] arr = Helper.getRandomDoubleArray(LENGTH); 19 | double[] sorted = Helper.sortByBuiltinMethod(arr); 20 | b.sort(arr); 21 | Assert.assertArrayEquals(sorted, arr, DELTA); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/BucketSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class BucketSortTest { 7 | 8 | private final int CNT = Helper.getRandomInt(10, 100); 9 | private final int LENGTH = Helper.getRandomInt(1000, 2000); 10 | 11 | @Test 12 | public void test() { 13 | // Possible bucket size: 5~15 14 | int bucketSize = Helper.getRandomInt(5, 16); 15 | BucketSort b = new BucketSort(bucketSize); 16 | for (int i = 0; i < CNT; ++i) { 17 | int lowerBound = Helper.getRandomInt(-100, 100); 18 | int[] arr = Helper.getRandomIntArray(LENGTH, lowerBound, lowerBound+LENGTH); 19 | int[] sorted = Helper.sortByBuiltinMethod(arr); 20 | b.sort(arr); 21 | Assert.assertArrayEquals(sorted, arr); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /go/src/algocasts/BucketSort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func BucketSort(input []int) { 4 | if len(input) == 0 { 5 | return 6 | } 7 | 8 | BucketSize := 10 9 | min := input[0] 10 | max := input[0] 11 | for _, val := range input { 12 | if val > max { 13 | max = val 14 | } 15 | if val < min { 16 | min = val 17 | } 18 | } 19 | 20 | bucketCount := len(input) / BucketSize 21 | 22 | buckets := make([][]int, bucketCount) 23 | for i := 0; i < bucketCount; i++ { 24 | buckets[i] = make([]int, 0, BucketSize) 25 | } 26 | 27 | for _, num := range input { 28 | idx := int(((num - min) / (max - min + 1.0) * bucketCount)) 29 | buckets[idx] = append(buckets[idx], num) 30 | } 31 | 32 | idx := 0 33 | for _, bucket := range buckets { 34 | insertsort(bucket) 35 | for _, num := range bucket { 36 | input[idx] = num 37 | idx += 1 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /go/src/algocasts/selectsort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestSelectSortMin(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 15 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 16 | } 17 | for _, tt := range tests { 18 | selectsortMin(tt.input) 19 | assert.Equal(t, tt.input, tt.want) 20 | } 21 | } 22 | 23 | func TestSelectSortMax(t *testing.T) { 24 | tests := []struct { 25 | name string 26 | input []int 27 | want []int 28 | }{ 29 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 30 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 31 | } 32 | for _, tt := range tests { 33 | selectsortMax(tt.input) 34 | assert.Equal(t, tt.input, tt.want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /go/src/algocasts/quicksort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestQuickSort(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | Quicksort(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | 24 | func TestLomutoSort(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | input []int 28 | want []int 29 | }{ 30 | {"double", []int{2, 1}, []int{1, 2}}, 31 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 32 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 33 | } 34 | for _, tt := range tests { 35 | Lomutosort(tt.input) 36 | assert.Equal(t, tt.input, tt.want) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/ShellSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class ShellSort { 4 | 5 | // Time: O(n^2), Space: O(1) 6 | public void sort(int[] arr) { 7 | if (arr == null || arr.length == 0) return; 8 | for (int gap = arr.length>>1; gap > 0; gap >>= 1) { 9 | for (int i = gap; i < arr.length; ++i) { 10 | int cur = arr[i]; 11 | int j = i - gap; 12 | while (j >= 0 && arr[j] > cur) { 13 | arr[j+gap] = arr[j]; 14 | j -= gap; 15 | } 16 | arr[j+gap] = cur; 17 | } 18 | } 19 | } 20 | 21 | // Time: O(n^2), Space: O(1) 22 | public void insertionSort(int[] arr) { 23 | if (arr == null || arr.length == 0) return; 24 | for (int i = 1; i < arr.length; ++i) { 25 | int cur = arr[i]; 26 | int j = i - 1; 27 | while (j >= 0 && arr[j] > cur) { 28 | arr[j + 1] = arr[j]; 29 | j -= 1; 30 | } 31 | arr[j + 1] = cur; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /go/src/algocasts/CountSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestCountSortRight(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | CountSortRight(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | 24 | func TestCountSortLeft(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | input []int 28 | want []int 29 | }{ 30 | {"double", []int{2, 1}, []int{1, 2}}, 31 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 32 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 33 | } 34 | for _, tt := range tests { 35 | CountSortLeft(tt.input) 36 | assert.Equal(t, tt.input, tt.want) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /go/src/algocasts/mergesort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestMergeSortRecursive(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | input []int 12 | want []int 13 | }{ 14 | {"double", []int{2, 1}, []int{1, 2}}, 15 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 16 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 17 | } 18 | for _, tt := range tests { 19 | MergeSortRecursive(tt.input) 20 | assert.Equal(t, tt.input, tt.want) 21 | } 22 | } 23 | 24 | func TestMergeSortIterative(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | input []int 28 | want []int 29 | }{ 30 | {"double", []int{2, 1}, []int{1, 2}}, 31 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 32 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 33 | } 34 | for _, tt := range tests { 35 | mergerSortIterative(tt.input) 36 | assert.Equal(t, tt.input, tt.want) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /go/src/algocasts/HeapSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | 6 | "testing" 7 | ) 8 | 9 | func TestHeapSort(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | input []int 13 | want []int 14 | }{ 15 | {"double", []int{2, 1}, []int{1, 2}}, 16 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 17 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 18 | } 19 | for _, tt := range tests { 20 | HeapSort(tt.input) 21 | assert.Equal(t, tt.input, tt.want) 22 | } 23 | } 24 | 25 | func TestHeapSortGolang(t *testing.T) { 26 | tests := []struct { 27 | name string 28 | input []int 29 | want []int 30 | }{ 31 | {"double", []int{2, 1}, []int{1, 2}}, 32 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 33 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 34 | } 35 | for _, tt := range tests { 36 | actual := HeapSortGolangImp(tt.input) 37 | //fmt.Printf("actual is %v", actual) 38 | assert.Equal(t, actual, tt.want) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/SelectionSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class SelectionSort { 4 | 5 | private void swap(int[] arr, int i, int j) { 6 | if (i == j) return; 7 | int tmp = arr[i]; 8 | arr[i] = arr[j]; 9 | arr[j] = tmp; 10 | } 11 | 12 | // Time: O(n^2), Space: O(1) 13 | public void sort(int[] arr) { 14 | if (arr == null || arr.length == 0) return; 15 | int n = arr.length; 16 | for (int i = 0; i < n; ++i) { 17 | int minIdx = i; 18 | for (int j = i+1; j < n; ++j) 19 | if (arr[j] < arr[minIdx]) 20 | minIdx = j; 21 | swap(arr, i, minIdx); 22 | } 23 | } 24 | 25 | // Time: O(n^2), Space: O(1) 26 | public void sortFromEnd(int[] arr) { 27 | if (arr == null || arr.length == 0) return; 28 | int n = arr.length; 29 | for (int i = n-1; i > 0; --i) { 30 | int maxIdx = i; 31 | for (int j = 0; j < i; ++j) 32 | if (arr[j] > arr[maxIdx]) 33 | maxIdx = j; 34 | swap(arr, i, maxIdx); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /go/src/algocasts/RadixSort_test.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRadixSort8Bit(t *testing.T) { 8 | tests := []struct { 9 | name string 10 | input []int 11 | want []int 12 | }{ 13 | {"double", []int{2, 1}, []int{1, 2}}, 14 | {"single", []int{1, 5, 4}, []int{1, 4, 5}}, 15 | {"normal", []int{1, 9, 5, 3, 2, 6}, []int{1, 2, 3, 5, 6, 9}}, 16 | } 17 | for _, tt := range tests { 18 | RadixSort8Bit(tt.input) 19 | for i:=0;i= 0; i-- { 51 | idx := counter[input[i]] 52 | tmp_res[idx] = input[i] 53 | counter[input[i]] -= 1 54 | } 55 | copy(input, tmp_res) 56 | 57 | } 58 | -------------------------------------------------------------------------------- /java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.algocasts 8 | sorting-algorithms 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 3.3 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.12 29 | test 30 | 31 | 32 | 33 | 34 | UTF-8 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/HeapSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class HeapSort { 4 | 5 | private void swap(int[] arr, int i, int j) { 6 | int tmp = arr[i]; 7 | arr[i] = arr[j]; 8 | arr[j] = tmp; 9 | } 10 | 11 | // Time: O(log(n)) 12 | private void siftDown(int[] arr, int i, int end) { 13 | int parent = i, child = 2 * parent + 1; 14 | while (child <= end) { 15 | if (child+1 <= end && arr[child+1] > arr[child]) ++child; 16 | if (arr[parent] >= arr[child]) break; 17 | swap(arr, parent, child); 18 | parent = child; 19 | child = 2 * parent + 1; 20 | } 21 | } 22 | 23 | // i 从 end/2 开始即可,因为在二叉堆中,更大的 i 是没有子节点的,没必要做 siftDown 24 | // Time: O(n) 25 | // Reference: 26 | // * https://www.geeksforgeeks.org/time-complexity-of-building-a-heap/ 27 | // * https://www2.cs.sfu.ca/CourseCentral/307/petra/2009/SLN_2.pdf 28 | private void buildMaxHeap(int[] arr, int end) { 29 | for (int i = end/2; i >= 0; --i) { 30 | siftDown(arr, i, end); 31 | } 32 | } 33 | 34 | // Time: O(n*log(n)), Space: O(1) 35 | public void sort(int[] arr) { 36 | if (arr == null || arr.length == 0) return; 37 | buildMaxHeap(arr, arr.length - 1); 38 | for (int end = arr.length - 1; end > 0; --end) { 39 | swap(arr, 0, end); 40 | siftDown(arr, 0, end - 1); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /go/src/algocasts/quicksort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func lomutopartition(input []int, low, high int) int { 4 | pivot := input[high] 5 | i := low 6 | for j := low; j <= high; j++ { 7 | if input[j] < pivot { 8 | swap(input, i, j) 9 | i += 1 10 | } 11 | } 12 | swap(input, i, high) 13 | return i 14 | } 15 | 16 | func lomutosort(input []int, low, high int) { 17 | if low < high { 18 | k := lomutopartition(input, low, high) 19 | lomutosort(input, low, k-1) 20 | lomutosort(input, k+1, high) 21 | } 22 | } 23 | 24 | func Lomutosort(input []int) { 25 | if input == nil || len(input) == 0 { 26 | return 27 | } 28 | lomutosort(input, 0, len(input)-1) 29 | } 30 | 31 | func partition(input []int, low, high int) int { 32 | mid := low + (high-low)/2 33 | pivot := input[mid] 34 | var i, j = low, high 35 | for true { 36 | for input[i] < pivot { 37 | i += 1 38 | } 39 | for input[j] > pivot { 40 | j -= 1 41 | } 42 | if i >= j { 43 | return j 44 | } 45 | swap(input, i, j) 46 | } 47 | return j 48 | } 49 | 50 | func quicksort(input []int, low, high int) { 51 | if low >= high { 52 | return 53 | } 54 | k := partition(input, low, high) 55 | quicksort(input, low, k) 56 | quicksort(input, k+1, high) 57 | } 58 | 59 | func Quicksort(input []int) { 60 | if input == nil || len(input) == 0 { 61 | return 62 | } 63 | quicksort(input, 0, len(input)-1) 64 | } 65 | -------------------------------------------------------------------------------- /go/src/algocasts/RadixSort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | func sort(input []int, bits int, mask int) { 4 | if len(input) <= 0 { 5 | return 6 | } 7 | n := len(input) 8 | count := 32 / bits 9 | // saving the middle result, clean every time 10 | tmp := make([]int, n) 11 | // save bit mask saving part 12 | indexes := make([]int, 1<> uint(bits*d)) & mask 16 | indexes[idx] += 1 17 | } 18 | // Why here needed to mines one 19 | indexes[0] -= 1 20 | 21 | for i:=1;i=0;i--{ 26 | idx := (input[i] >> uint(bits * d)) 27 | tmp[indexes[idx]] = input[i] 28 | indexes[idx] -= 1 29 | } 30 | 31 | for i:=0;i array[j] { 7 | tmp[k] = array[j] 8 | k += 1 9 | j += 1 10 | } else { 11 | tmp[k] = array[i] 12 | k += 1 13 | i += 1 14 | } 15 | } 16 | for i <= mid { 17 | tmp[k] = array[i] 18 | k += 1 19 | i += 1 20 | } 21 | for j <= high { 22 | tmp[k] = array[j] 23 | k += 1 24 | j += 1 25 | } 26 | copy(array[low:low+k], tmp[0:k]) 27 | } 28 | 29 | func mergeSort(array []int, low int, high int, tmp []int) { 30 | if low < high { 31 | mid := low + (high-low)/2 32 | mergeSort(array, low, mid, tmp) 33 | mergeSort(array, mid+1, high, tmp) 34 | merge(array, low, mid, high, tmp) 35 | } 36 | } 37 | 38 | func MergeSortRecursive(input []int) { 39 | if input == nil || len(input) == 0 { 40 | return 41 | } 42 | tmp := make([]int, len(input)) 43 | mergeSort(input, 0, len(input)-1, tmp) 44 | } 45 | 46 | func min(a, b int) int { 47 | if a > b { 48 | return b 49 | } else { 50 | return a 51 | } 52 | } 53 | 54 | func mergerSortIterative(input []int) { 55 | if input == nil || len(input) == 0 { 56 | return 57 | } 58 | var length int 59 | tmp := make([]int, len(input)) 60 | for length = 1; length < len(input); length *= 2 { 61 | for low := 0; low < len(input); low += 2 * length { 62 | mid := min(low+length-1, len(input)-1) 63 | high := min(low+2*length-1, len(input)-1) 64 | merge(input, low, mid, high, tmp) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/CountingSortFixedK.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class CountingSortFixedK { 4 | 5 | private int k; 6 | 7 | // 对于数组中元素 x,有 0 <= x <= k, k 是一个较小的数字。 8 | public CountingSortFixedK(int k) { 9 | this.k = k; 10 | } 11 | 12 | // indexes 最后存储的是排序后,相同数字在结果数组的开始位置,相同数字会依次向后(右)填充。 13 | // Time: O(n+k), Space: O(n+k) 14 | public void sortLeft2Right(int[] arr) { 15 | if (arr == null || arr.length == 0) return; 16 | int[] indexes = new int[k+1]; 17 | for (int num: arr) ++indexes[num]; 18 | 19 | int start = 0; 20 | for (int i = 0; i <= k; ++i) { 21 | int count = indexes[i]; 22 | indexes[i] = start; 23 | start += count; 24 | } 25 | 26 | int[] tmp = new int[arr.length]; 27 | for (int num: arr) { 28 | int idx = indexes[num]; 29 | tmp[idx] = num; 30 | ++indexes[num]; 31 | } 32 | System.arraycopy(tmp, 0, arr, 0, arr.length); 33 | } 34 | 35 | // indexes 最后存储的是排序后,相同数字在结果数组的结束位置,相同数字会依次向前(左)填充。 36 | // Time: O(n+k), Space: O(n+k) 37 | public void sortRight2Left(int[] arr) { 38 | if (arr == null || arr.length == 0) return; 39 | int[] indexes = new int[k+1]; 40 | for (int num: arr) ++indexes[num]; 41 | 42 | --indexes[0]; 43 | for (int i = 1; i <= k; ++i) 44 | indexes[i] = indexes[i] + indexes[i-1]; 45 | 46 | int[] tmp = new int[arr.length]; 47 | for (int i = arr.length-1; i >= 0; --i) { 48 | int idx = indexes[arr[i]]; 49 | tmp[idx] = arr[i]; 50 | --indexes[arr[i]]; 51 | } 52 | System.arraycopy(tmp, 0, arr, 0, arr.length); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/MergeSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class MergeSort { 4 | 5 | // sorted sub-array 1: arr[low ... mid] 6 | // sorted sub-array 2: arr[mid+1 ... high] 7 | private void merge(int[] arr, int low, int mid, int high, int[] tmp) { 8 | int i = low, j = mid + 1, k = 0; 9 | while (i <= mid && j <= high) { 10 | if (arr[i] <= arr[j]) tmp[k++] = arr[i++]; 11 | else tmp[k++] = arr[j++]; 12 | } 13 | while (i <= mid) tmp[k++] = arr[i++]; 14 | while (j <= high) tmp[k++] = arr[j++]; 15 | System.arraycopy(tmp, 0, arr, low, k); 16 | } 17 | 18 | private void mergeSort(int[] arr, int low, int high, int[] tmp) { 19 | if (low < high) { 20 | int mid = low + (high - low) / 2; 21 | mergeSort(arr, low, mid, tmp); 22 | mergeSort(arr, mid + 1, high, tmp); 23 | merge(arr, low, mid, high, tmp); 24 | } 25 | } 26 | 27 | // Time: O(n*log(n)), Space: O(n) 28 | public void sortRecursive(int[] arr) { 29 | if (arr == null || arr.length == 0) return; 30 | int[] tmp = new int[arr.length]; 31 | mergeSort(arr, 0, arr.length - 1, tmp); 32 | } 33 | 34 | // Time: O(n*log(n)), Space: O(n) 35 | public void sortIterative(int[] arr) { 36 | if (arr == null || arr.length == 0) return; 37 | int n = arr.length; 38 | int[] tmp = new int[n]; 39 | for (int len = 1; len < n; len = 2*len) { 40 | for (int low = 0; low < n; low += 2*len) { 41 | int mid = Math.min(low+len-1, n-1); 42 | int high = Math.min(low+2*len-1, n-1); 43 | merge(arr, low, mid, high, tmp); 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/BucketSortDouble.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class BucketSortDouble { 7 | 8 | private void insertionSort(List arr) { 9 | if (arr == null || arr.size() == 0) return; 10 | for (int i = 1; i < arr.size(); ++i) { 11 | double cur = arr.get(i); 12 | int j = i - 1; 13 | while (j >= 0 && arr.get(j) > cur) { 14 | arr.set(j+1, arr.get(j)); 15 | --j; 16 | } 17 | arr.set(j+1, cur); 18 | } 19 | } 20 | 21 | // 每个桶的大小,由于桶内使用插入排序,因此桶的大小使用一个较小值会比较高效。 22 | // 23 | // 一般来说,当处理的数组大小在 5-15 时,使用插入排序往往会比快排或归并更高效。 24 | // 因此在桶排序中,我们尽量让单个桶内的元素个数是在 5-15 个之间,这样可以用插入排序高效地完成桶内排序。 25 | // 参考链接:https://algs4.cs.princeton.edu/23quicksort/ 26 | // 参考段落: 27 | // Cutoff to insertion sort. As with mergesort, 28 | // it pays to switch to insertion sort for tiny arrays. 29 | // The optimum value of the cutoff is system-dependent, 30 | // but any value between 5 and 15 is likely to work well in most situations. 31 | private int bucketSize; 32 | 33 | public BucketSortDouble(int bucketSize) { 34 | this.bucketSize = bucketSize; 35 | } 36 | 37 | // for all x in arr, we have 0 <= x < 1 38 | public void sort(double[] arr) { 39 | if (arr == null || arr.length == 0) return; 40 | 41 | int bucketCount = arr.length / bucketSize; 42 | List> buckets = new ArrayList<>(bucketCount); 43 | for (int i = 0; i < bucketCount; ++i) 44 | buckets.add(new ArrayList<>()); 45 | 46 | for (double num: arr) 47 | buckets.get((int)(num * bucketCount)).add(num); 48 | 49 | int idx = 0; 50 | for (List bucket: buckets) { 51 | insertionSort(bucket); 52 | for (double num: bucket) 53 | arr[idx++] = num; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/CountingSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class CountingSort { 4 | 5 | // indexes 最后存储的是排序后,相同数字在结果数组的开始位置,相同数字会依次向后(右)填充。 6 | // Time: O(n+k), Space: O(n+k) 7 | public void sortLeft2Right(int[] arr) { 8 | if (arr == null || arr.length == 0) return; 9 | int max = arr[0], min = arr[0]; 10 | for (int num: arr) { 11 | if (num > max) max = num; 12 | if (num < min) min = num; 13 | } 14 | 15 | int k = max - min; 16 | int[] indexes = new int[k+1]; 17 | for (int num: arr) ++indexes[num-min]; 18 | 19 | int start = 0; 20 | for (int i = 0; i <= k; ++i) { 21 | int count = indexes[i]; 22 | indexes[i] = start; 23 | start += count; 24 | } 25 | 26 | int[] tmp = new int[arr.length]; 27 | for (int num: arr) { 28 | int idx = indexes[num-min]; 29 | tmp[idx] = num; 30 | ++indexes[num-min]; 31 | } 32 | System.arraycopy(tmp, 0, arr, 0, arr.length); 33 | } 34 | 35 | // indexes 最后存储的是排序后,相同数字在结果数组的结束位置,相同数字会依次向前(左)填充。 36 | // Time: O(n+k), Space: O(n+k) 37 | public void sortRight2Left(int[] arr) { 38 | if (arr == null || arr.length == 0) return; 39 | int max = arr[0], min = arr[0]; 40 | for (int num: arr) { 41 | if (num > max) max = num; 42 | if (num < min) min = num; 43 | } 44 | 45 | int k = max - min; 46 | int[] indexes = new int[k+1]; 47 | for (int num: arr) ++indexes[num-min]; 48 | 49 | --indexes[0]; 50 | for (int i = 1; i <= k; ++i) 51 | indexes[i] = indexes[i] + indexes[i-1]; 52 | 53 | int[] tmp = new int[arr.length]; 54 | for (int i = arr.length-1; i >= 0; --i) { 55 | int idx = indexes[arr[i]-min]; 56 | tmp[idx] = arr[i]; 57 | --indexes[arr[i]-min]; 58 | } 59 | System.arraycopy(tmp, 0, arr, 0, arr.length); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/BubbleSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class BubbleSort { 4 | 5 | private void swap(int[] arr, int i, int j) { 6 | int tmp = arr[i]; 7 | arr[i] = arr[j]; 8 | arr[j] = tmp; 9 | } 10 | 11 | // Time: O(n^2), Space: O(1) 12 | public void sort(int[] arr) { 13 | if (arr == null || arr.length == 0) return; 14 | int n = arr.length; 15 | for (int end = n-1; end > 0; --end) { 16 | for (int i = 0; i < end; ++i) { 17 | if (arr[i] > arr[i+1]) { 18 | int tmp = arr[i]; 19 | arr[i] = arr[i+1]; 20 | arr[i+1] = tmp; 21 | } 22 | } 23 | } 24 | } 25 | 26 | // Time: O(n^2), Space: O(1) 27 | public void sortShort(int[] arr) { 28 | if (arr == null || arr.length == 0) return; 29 | int n = arr.length; 30 | for (int end = n-1; end > 0; --end) 31 | for (int i = 0; i < end; ++i) 32 | if (arr[i] > arr[i+1]) 33 | swap(arr, i, i+1); 34 | } 35 | 36 | // Time: O(n^2), Space: O(1) 37 | public void sortEarlyReturn(int[] arr) { 38 | if (arr == null || arr.length == 0) return; 39 | int n = arr.length; 40 | boolean swapped; 41 | for (int end = n-1; end > 0; --end) { 42 | swapped = false; 43 | for (int i = 0; i < end; ++i) { 44 | if (arr[i] > arr[i+1]) { 45 | swap(arr, i, i+1); 46 | swapped = true; 47 | } 48 | } 49 | if (!swapped) return; 50 | } 51 | } 52 | 53 | // Time: O(n^2), Space: O(1) 54 | public void sortSkip(int[] arr) { 55 | if (arr == null || arr.length == 0) return; 56 | int n = arr.length; 57 | int newEnd; 58 | for (int end = n-1; end > 0;) { 59 | newEnd = 0; 60 | for (int i = 0; i < end; ++i) { 61 | if (arr[i] > arr[i+1]) { 62 | swap(arr, i, i+1); 63 | newEnd = i; 64 | } 65 | } 66 | end = newEnd; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /go/src/algocasts/HeapSort.go: -------------------------------------------------------------------------------- 1 | package algocasts 2 | 3 | import ( 4 | "container/heap" 5 | ) 6 | 7 | // An IntHeap is a min-heap of ints. 8 | type IntHeap []int 9 | 10 | func (h IntHeap) Len() int { return len(h) } 11 | func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } 12 | func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 13 | 14 | func (h *IntHeap) Push(x interface{}) { 15 | // Push and Pop use pointer receivers because they modify the slice's length, 16 | // not just its contents. 17 | *h = append(*h, x.(int)) 18 | } 19 | 20 | func (h *IntHeap) Pop() interface{} { 21 | old := *h 22 | n := len(old) 23 | x := old[n-1] 24 | *h = old[0 : n-1] 25 | return x 26 | } 27 | 28 | func HeapSortGolangImp(input []int) []int { 29 | if len(input) == 0 { 30 | return nil 31 | } 32 | minHeap := make(IntHeap, 0, len(input)) 33 | minHeapP := &minHeap 34 | heap.Init(minHeapP) 35 | for i := 0; i < len(input); i++ { 36 | heap.Push(minHeapP, input[i]) 37 | //fmt.Printf("minHeapP is %v", minHeapP) 38 | } 39 | result := make([]int, len(input)) 40 | for i := 0; i < len(input); i++ { 41 | minVal := heap.Pop(minHeapP) 42 | result[i] = minVal.(int) 43 | } 44 | return result 45 | } 46 | 47 | // not use container/heap implement 48 | 49 | func shifDown(arr []int, i, end int) { 50 | parent := i 51 | child := 2*parent + 1 52 | for child <= end { 53 | // get the bigger child ( 54 | if child+1 <= end && arr[child] < arr[child+1] { 55 | child += 1 56 | } 57 | if arr[parent] >= arr[child] { 58 | break 59 | } 60 | swap(arr, parent, child) 61 | parent = child 62 | child = 2*parent + 1 63 | } 64 | 65 | } 66 | 67 | func buildMapHeap(arr []int, end int) { 68 | for i := end / 2; i >= 0; i-- { 69 | shifDown(arr, i, end) 70 | } 71 | } 72 | 73 | func HeapSort(input []int) { 74 | if len(input) == 0 { 75 | return 76 | } 77 | buildMapHeap(input, len(input)-1) 78 | for i := len(input) - 1; i > 0; i-- { 79 | swap(input, 0, i) 80 | shifDown(input, 0, i-1) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/BucketSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class BucketSort { 7 | 8 | private void insertionSort(List arr) { 9 | if (arr == null || arr.size() == 0) return; 10 | for (int i = 1; i < arr.size(); ++i) { 11 | int cur = arr.get(i); 12 | int j = i - 1; 13 | while (j >= 0 && arr.get(j) > cur) { 14 | arr.set(j+1, arr.get(j)); 15 | --j; 16 | } 17 | arr.set(j+1, cur); 18 | } 19 | } 20 | 21 | // 每个桶的大小,由于桶内使用插入排序,因此桶的大小使用一个较小值会比较高效。 22 | // 23 | // 一般来说,当处理的数组大小在 5-15 时,使用插入排序往往会比快排或归并更高效。 24 | // 因此在桶排序中,我们尽量让单个桶内的元素个数是在 5-15 个之间,这样可以用插入排序高效地完成桶内排序。 25 | // 参考链接:https://algs4.cs.princeton.edu/23quicksort/ 26 | // 参考段落: 27 | // Cutoff to insertion sort. As with mergesort, 28 | // it pays to switch to insertion sort for tiny arrays. 29 | // The optimum value of the cutoff is system-dependent, 30 | // but any value between 5 and 15 is likely to work well in most situations. 31 | private int bucketSize; 32 | 33 | public BucketSort(int bucketSize) { 34 | this.bucketSize = bucketSize; 35 | } 36 | 37 | // Time(avg): O(n+k), Time(worst): O(n^2), Space: O(n) 38 | public void sort(int[] arr) { 39 | if (arr == null || arr.length == 0) return; 40 | int max = arr[0], min = arr[0]; 41 | for (int num: arr) { 42 | if (num > max) max = num; 43 | if (num < min) min = num; 44 | } 45 | 46 | int bucketCount = arr.length / bucketSize; 47 | List> buckets = new ArrayList<>(bucketCount); 48 | for (int i = 0; i < bucketCount; ++i) 49 | buckets.add(new ArrayList<>()); 50 | 51 | for (int num: arr) { 52 | int idx = (int)((num - min) / (max - min + 1.0) * bucketCount); 53 | buckets.get(idx).add(num); 54 | } 55 | 56 | int idx = 0; 57 | for (List bucket: buckets) { 58 | insertionSort(bucket); 59 | for (int num: bucket) 60 | arr[idx++] = num; 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/CocktailSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class CocktailSort { 4 | 5 | private void swap(int[] arr, int i, int j) { 6 | int tmp = arr[i]; 7 | arr[i] = arr[j]; 8 | arr[j] = tmp; 9 | } 10 | 11 | // Time: O(n^2), Space: O(1) 12 | public void sort(int[] arr) { 13 | if (arr == null || arr.length == 0) return; 14 | int left = 0, right = arr.length - 1; 15 | 16 | while (left < right) { 17 | for (int i = left; i < right; ++i) 18 | if (arr[i] > arr[i+1]) 19 | swap(arr, i, i+1); 20 | --right; 21 | 22 | for (int i = right; i > left; --i) 23 | if (arr[i-1] > arr[i]) 24 | swap(arr, i-1, i); 25 | ++left; 26 | } 27 | } 28 | 29 | // Time: O(n^2), Space: O(1) 30 | public void sortEarlyReturn(int[] arr) { 31 | if (arr == null || arr.length == 0) return; 32 | int left = 0, right = arr.length - 1; 33 | boolean swapped; 34 | 35 | while (left < right) { 36 | swapped = false; 37 | 38 | for (int i = left; i < right; ++i) { 39 | if (arr[i] > arr[i+1]) { 40 | swap(arr, i, i+1); 41 | swapped = true; 42 | } 43 | } 44 | --right; 45 | 46 | for (int i = right; i > left; --i) { 47 | if (arr[i-1] > arr[i]) { 48 | swap(arr, i-1, i); 49 | swapped = true; 50 | } 51 | } 52 | ++left; 53 | 54 | if (!swapped) return; 55 | } 56 | } 57 | 58 | // Time: O(n^2), Space: O(1) 59 | public void sortSkip(int[] arr) { 60 | if (arr == null || arr.length == 0) return; 61 | int left = 0, right = arr.length - 1; 62 | int newLeft, newRight; 63 | 64 | while (left < right) { 65 | newRight = left; 66 | for (int i = left; i < right; ++i) { 67 | if (arr[i] > arr[i+1]) { 68 | swap(arr, i, i+1); 69 | newRight = i; 70 | } 71 | } 72 | right = newRight; 73 | 74 | newLeft = right; 75 | for (int i = right; i > left; --i) { 76 | if (arr[i-1] > arr[i]) { 77 | swap(arr, i-1, i); 78 | newLeft = i; 79 | } 80 | } 81 | left = newLeft; 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/SortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Assert; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.function.Consumer; 8 | 9 | class SortTest { 10 | private final int CNT = Helper.getRandomInt(50, 100); 11 | private final int LENGTH = Helper.getRandomInt(1, 200); 12 | 13 | // some special cases 14 | private final List specialArrays = Arrays.asList( 15 | null, // 空 16 | new int[]{0}, // 一个元素 17 | new int[]{0,1}, // 两个元素,递增 18 | new int[]{1,0}, // 两个元素,递减 19 | new int[]{1,1}, // 两个元素,相同 20 | new int[]{0,1,2}, // 三个元素,递增 21 | new int[]{2,1,0}, // 三个元素,递减 22 | new int[]{1,1,1}, // 三个元素,相同 23 | new int[]{-1}, // [负数] 一个元素 24 | new int[]{-2,-1}, // [负数] 两个元素,递增 25 | new int[]{-1,-2}, // [负数] 两个元素,递减 26 | new int[]{-1,-1}, // [负数] 两个元素,相同 27 | new int[]{-3,-2,-1}, // [负数] 三个元素,递增 28 | new int[]{-1,-2,-3}, // [负数] 三个元素,递减 29 | new int[]{-1,-1,-1}, // [负数] 三个元素,相同 30 | Helper.getSameElementArray(LENGTH, 42), // LENGTH 个元素,相同 31 | Helper.getSortedArray(LENGTH), // LENGTH 个元素,递增 32 | Helper.getReverseSortedArray(LENGTH), // LENGTH 个元素,递减 33 | Helper.getSameElementArray(2*LENGTH, 42), // 2*LENGTH 个元素(偶数个元素),相同 34 | Helper.getSortedArray(2*LENGTH), // 2*LENGTH 个元素(偶数个元素),递增 35 | Helper.getReverseSortedArray(2*LENGTH), // 2*LENGTH 个元素(偶数个元素),递减 36 | Helper.getSameElementArray(2*LENGTH+1, 42), // 2*LENGTH+1 个元素(奇数个元素),相同 37 | Helper.getSortedArray(2*LENGTH+1), // 2*LENGTH+1 个元素(奇数个元素),递增 38 | Helper.getReverseSortedArray(2*LENGTH+1) // 2*LENGTH+1 个元素(奇数个元素),递减 39 | ); 40 | 41 | void test(Consumer sort) { 42 | // test special cases 43 | testSpecialCases(sort); 44 | // test random cases 45 | for (int i = 0; i < CNT; ++i) { 46 | int[] arr = Helper.getRandomIntArray(LENGTH); 47 | int[] sorted = Helper.sortByBuiltinMethod(arr); 48 | sort.accept(arr); 49 | Assert.assertArrayEquals(sorted, arr); 50 | } 51 | } 52 | 53 | private void testSpecialCases(Consumer sort) { 54 | for (int[] array: specialArrays) { 55 | int[] arr = array == null ? null : array.clone(); 56 | int[] sorted = Helper.sortByBuiltinMethod(arr); 57 | sort.accept(arr); 58 | Assert.assertArrayEquals(sorted, arr); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/RadixSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import java.util.Arrays; 4 | 5 | public class RadixSort { 6 | 7 | /** 8 | * @param arr 待排数组 9 | * @param bits 每次处理的二进制位数(可选值:1, 2, 4, 8, 16) 10 | * @param mask 每次移动 bits 个二进制位后,使用 mask 取出最低的 bits 位。 11 | */ 12 | // b 表示每次处理的二进制位数 13 | // Time: O(32/b * n), Space: O(n + 2^b) 14 | private void sort(int[] arr, int bits, int mask) { 15 | if (arr == null || arr.length == 0) return; 16 | int n = arr.length, cnt = 32/bits; 17 | int[] tmp = new int[n]; 18 | int[] indexes = new int[1<> (bits*d)) & mask; 22 | ++indexes[idx]; 23 | } 24 | 25 | --indexes[0]; 26 | for (int i = 1; i < indexes.length; ++i) 27 | indexes[i] = indexes[i] + indexes[i-1]; 28 | 29 | for (int i = n-1; i >= 0; --i) { 30 | int idx = (arr[i] >> (bits*d)) & mask; 31 | tmp[indexes[idx]] = arr[i]; 32 | --indexes[idx]; 33 | } 34 | 35 | Arrays.fill(indexes, 0); 36 | int[] t = arr; 37 | arr = tmp; 38 | tmp = t; 39 | } 40 | // handle the negative number 41 | // get the length of positive part 42 | int len = 0; 43 | for (; len < n; ++len) 44 | if (arr[len] < 0) break; 45 | 46 | System.arraycopy(arr, len, tmp, 0, n-len); // copy negative part to tmp 47 | System.arraycopy(arr, 0, tmp, n-len, len); // copy positive part to tmp 48 | System.arraycopy(tmp, 0, arr, 0, n); // copy back to arr 49 | } 50 | 51 | // 基数为 256,每次取 8 个二进制位作为一个部分进行处理,32 位整数需要处理 4 次。 52 | // 每次取出的 8 个二进制位会作为计数排序的键值,去排序原始数据。 53 | // 每次处理 8 个二进制位,是时间/空间上比较折衷的方法。 54 | // 如果一次处理 16 个二进制位,速度会稍微快一些。但需要额外的空间是 2^16 = 65536,远大于每次处理 8 个二进制位所需空间。 55 | // 如果一次只处理 4 个二进制位,速度则会慢很多。 56 | public void sort4pass(int[] arr) { 57 | sort(arr, 8, 0xff); 58 | } 59 | 60 | // 基数为 16,每次取 4 个二进制位作为一个部分进行处理。32 位整数需要处理 8 次。 61 | // 时间上比起 sort4pass 要差很多。 62 | public void sort8pass(int[] arr) { 63 | sort(arr, 4, 0x0f); 64 | } 65 | 66 | // 基数为 65536,每次取 16 个二进制位作为一个部分进行处理。32 位整数需要处理 2 次。 67 | // 时间上比 sort4pass 要稍微好一些,但额外要使用多得多的空间。 68 | public void sort2pass(int[] arr) { 69 | sort(arr, 16, 0xffff); 70 | } 71 | 72 | // 基数为 2,每次取 1 个二进制位作为一个部分进行处理。32 位整数需要处理 32 次。 73 | // 时间上比快排要差很多。 74 | public void sort32pass(int[] arr) { 75 | sort(arr, 1, 1); 76 | } 77 | 78 | // 基数为 4,每次取 2 个二进制位作为一个部分进行处理。32 位整数需要处理 16 次。 79 | // 我是打酱油的。 80 | public void sort16pass(int[] arr) { 81 | sort(arr, 2, 3); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/RadixSortTest.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Arrays; 6 | 7 | public class RadixSortTest extends SortTest { 8 | 9 | @Test 10 | public void test() { 11 | RadixSort r = new RadixSort(); 12 | test(r::sort32pass); 13 | test(r::sort16pass); 14 | test(r::sort8pass); 15 | test(r::sort4pass); 16 | test(r::sort2pass); 17 | } 18 | 19 | @Test 20 | public void benchmark() { 21 | final int CNT = Helper.getRandomInt(100, 200); 22 | final int LENGTH = Helper.getRandomInt(100000, 200000); 23 | final double ONE_BILLION = 1000000000.0; 24 | 25 | RadixSort r = new RadixSort(); 26 | // warm up 27 | for (int i = 0; i < 10; ++i) { 28 | int[] arr = Helper.getRandomIntArray(LENGTH); 29 | int[] arr2 = arr.clone(); 30 | int[] arr4 = arr.clone(); 31 | int[] arr8 = arr.clone(); 32 | Arrays.sort(arr); 33 | r.sort2pass(arr2); 34 | r.sort4pass(arr4); 35 | r.sort8pass(arr8); 36 | } 37 | 38 | // bench mark 39 | long timeQuickSort = 0L; 40 | long timeRadixSort2Pass = 0L; 41 | long timeRadixSort4Pass = 0L; 42 | long timeRadixSort8Pass = 0L; 43 | long start; 44 | for (int i = 0; i < CNT; ++i) { 45 | int[] arr = Helper.getRandomIntArray(LENGTH); 46 | int[] arrFor2pass = arr.clone(); 47 | int[] arrFor4pass = arr.clone(); 48 | int[] arrFor8pass = arr.clone(); 49 | // measure builtin quick sort 50 | start = System.nanoTime(); 51 | Arrays.sort(arr); 52 | timeQuickSort += (System.nanoTime() - start); 53 | 54 | // measure radix sort (2 pass) 55 | start = System.nanoTime(); 56 | r.sort2pass(arrFor2pass); 57 | timeRadixSort2Pass += (System.nanoTime() - start); 58 | 59 | // measure radix sort (4 pass) 60 | start = System.nanoTime(); 61 | r.sort4pass(arrFor4pass); 62 | timeRadixSort4Pass += (System.nanoTime() - start); 63 | 64 | // measure radix sort (8 pass) 65 | start = System.nanoTime(); 66 | r.sort8pass(arrFor8pass); 67 | timeRadixSort8Pass += (System.nanoTime() - start); 68 | } 69 | 70 | System.out.println("Loop count: " + CNT + ", array length: " + LENGTH); 71 | System.out.println("Builtin Quick Sort Time(s): " + timeQuickSort/ONE_BILLION); 72 | System.out.println("Radix Sort(2 pass) Time(s): " + timeRadixSort2Pass/ONE_BILLION); 73 | System.out.println("Radix Sort(4 pass) Time(s): " + timeRadixSort4Pass/ONE_BILLION); 74 | System.out.println("Radix Sort(8 pass) Time(s): " + timeRadixSort8Pass/ONE_BILLION); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /java/src/main/java/io/algocasts/QuickSort.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | public class QuickSort { 4 | 5 | private void swap(int[] arr, int i, int j) { 6 | int tmp = arr[i]; 7 | arr[i] = arr[j]; 8 | arr[j] = tmp; 9 | } 10 | 11 | // [ ... elem < pivot ... | ... elem >= pivot ... | unprocessed elements ] 12 | // i j 13 | private int lomutoPartition(int[] arr, int low, int high) { 14 | int pivot = arr[high]; 15 | int i = low; 16 | for (int j = low; j < high; ++j) { 17 | if (arr[j] < pivot) { 18 | swap(arr, i, j); 19 | ++i; 20 | } 21 | } 22 | swap(arr, i, high); 23 | return i; 24 | } 25 | 26 | // lomuto partition 的另一种实现,可以把最后的 swap 合并到循环中。 27 | private int lomutoPartition2(int[] arr, int low, int high) { 28 | int pivot = arr[high]; 29 | int i = low; 30 | for (int j = low; j <= high; ++j) { 31 | if (arr[j] <= pivot) { 32 | swap(arr, i, j); 33 | ++i; 34 | } 35 | } 36 | return i-1; 37 | } 38 | 39 | private void lomutoSort(int[] arr, int low, int high) { 40 | if (low < high) { 41 | int k = lomutoPartition(arr, low, high); 42 | lomutoSort(arr, low, k-1); 43 | lomutoSort(arr, k+1, high); 44 | } 45 | } 46 | 47 | private int hoarePartitionDoWhile(int[] arr, int low, int high) { 48 | int pivot = arr[low + (high-low)/2]; 49 | int i = low-1, j = high+1; 50 | while (true) { 51 | do { 52 | ++i; 53 | } while (arr[i] < pivot); 54 | do { 55 | --j; 56 | } while (arr[j] > pivot); 57 | if (i >= j) return j; 58 | swap(arr, i, j); 59 | } 60 | } 61 | 62 | // [ ... elem <= pivot ... | unprocessed elements | ... elem >= pivot ... ] 63 | // i j 64 | private int hoarePartition(int[] arr, int low, int high) { 65 | int pivot = arr[low + (high-low)/2]; 66 | int i = low, j = high; 67 | while (true) { 68 | while (arr[i] < pivot) ++i; 69 | while (arr[j] > pivot) --j; 70 | if (i >= j) return j; 71 | swap(arr, i++, j--); 72 | } 73 | } 74 | 75 | private void hoareSort(int[] arr, int low, int high) { 76 | if (low < high) { 77 | int k = hoarePartition(arr, low, high); 78 | hoareSort(arr, low, k); 79 | hoareSort(arr, k+1, high); 80 | } 81 | } 82 | 83 | // Time: O(n*log(n)), Space: O(n) 84 | public void lomutoSort(int[] arr) { 85 | if (arr == null || arr.length == 0) return; 86 | lomutoSort(arr, 0, arr.length-1); 87 | } 88 | 89 | // Time: O(n*log(n)), Space: O(n) 90 | public void hoareSort(int[] arr) { 91 | if (arr == null || arr.length == 0) return; 92 | hoareSort(arr, 0, arr.length-1); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /java/src/test/java/io/algocasts/Helper.java: -------------------------------------------------------------------------------- 1 | package io.algocasts; 2 | 3 | import org.junit.Assert; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Queue; 12 | import java.util.Random; 13 | 14 | public class Helper { 15 | 16 | /////////////////// List //////////////////////// 17 | public static String list2String(List list) { 18 | if (list == null) return "null"; 19 | StringBuilder sb = new StringBuilder("["); 20 | for (T e: list) { 21 | if (e == null) sb.append("null, "); 22 | else sb.append(e).append(", "); 23 | } 24 | if (sb.length() >= 2) sb.delete(sb.length()-2, sb.length()); 25 | sb.append("]"); 26 | return sb.toString(); 27 | } 28 | 29 | @SafeVarargs 30 | @SuppressWarnings("varargs") 31 | public static List buildList(T... a) { 32 | return Arrays.asList(a); 33 | } 34 | 35 | public static List> buildListList(T[][] a) { 36 | List> result = new ArrayList<>(); 37 | for (T[] e: a) { 38 | result.add(Arrays.asList(e)); 39 | } 40 | return result; 41 | } 42 | 43 | /////////////////// Linked List //////////////////////// 44 | public static String linkedList2String(ListNode list) { 45 | if (list == null) return "null"; 46 | StringBuilder sb = new StringBuilder(String.valueOf(list.val)); 47 | for (list = list.next; list != null; list = list.next) { 48 | sb.append(" -> ").append(list.val); 49 | } 50 | return sb.toString(); 51 | } 52 | 53 | public static void printListNode(ListNode list) { 54 | System.out.println(linkedList2String(list)); 55 | } 56 | 57 | public static ListNode buildLinkedList(int... array) { 58 | if (array == null || array.length == 0) return null; 59 | ListNode dummy = new ListNode(0), p = dummy; 60 | for (int i = 0; i < array.length; ++i) { 61 | p.next = new ListNode(array[i]); 62 | p = p.next; 63 | } 64 | return dummy.next; 65 | } 66 | 67 | /* 68 | 使用两个数组构建两条链表,并允许链表相交。 69 | 数组尾部相同的元素会被构建成两链表相交后的部分。 70 | 71 | 例如,输入的两个数组为: 72 | [1, 2, 6, 7] 73 | [3, 4, 5, 6, 7] 74 | 75 | 构建后的两条链表为: 76 | 77 | A: 1 -> 2 78 | \ 79 | 6 -> 7 -> null 80 | / 81 | B: 3 -> 4 -> 5 82 | 83 | */ 84 | public static List buildLinkedList(int[] arr1, int[] arr2) { 85 | if (arr1 == null || arr1.length == 0) { 86 | return buildList(null, buildLinkedList(arr2)); 87 | } 88 | if (arr2 == null || arr2.length == 0) { 89 | return buildList(buildLinkedList(arr1), null); 90 | } 91 | int n1 = arr1.length, n2 = arr2.length; 92 | int i = n1-1, j = n2-1; 93 | for (; i >= 0 && j >= 0 && arr1[i] == arr2[j]; --i, --j); 94 | // split to 3 arrays 95 | int[] a = new int[i+1], b = new int[j+1], c = new int[n1-i-1]; 96 | System.arraycopy(arr1, 0, a, 0, i+1); 97 | System.arraycopy(arr1, i+1, c, 0, n1-i-1); 98 | System.arraycopy(arr2, 0, b, 0, j+1); 99 | // build 3 linked lists 100 | ListNode dummyA = new ListNode(0), dummyB = new ListNode(0); 101 | dummyA.next = buildLinkedList(a); 102 | dummyB.next = buildLinkedList(b); 103 | ListNode tail = buildLinkedList(c); 104 | // find the end 105 | ListNode endA = dummyA, endB = dummyB; 106 | for (; endA.next != null; endA = endA.next); 107 | for (; endB.next != null; endB = endB.next); 108 | // link to the tail 109 | endA.next = tail; 110 | endB.next = tail; 111 | return buildList(dummyA.next, dummyB.next); 112 | } 113 | 114 | /** 115 | * 根据数组和环的开始节点下标,构建一个带环链表。 116 | * 117 | * @param array 整数数组 118 | * @param startNodeIndex 0-based. 该下标指向的数字作为链表中环的开始节点 119 | * @return 120 | */ 121 | public static ListNode buildLinkedListWithCycle(int[] array, int startNodeIndex) { 122 | if (array == null || array.length == 0) return null; 123 | ListNode dummy = new ListNode(0), p = dummy, start = null; 124 | for (int i = 0; i < array.length; ++i, --startNodeIndex) { 125 | p.next = new ListNode(array[i]); 126 | p = p.next; 127 | if (startNodeIndex == 0) start = p; 128 | } 129 | p.next = start; 130 | return dummy.next; 131 | 132 | } 133 | 134 | /////////////////// Tree //////////////////////// 135 | public static String tree2String(TreeNode root) { 136 | if (root == null) return "null"; 137 | StringBuilder sb = new StringBuilder(); 138 | Queue q = new LinkedList<>(); 139 | q.add(root); 140 | while (!q.isEmpty()) { 141 | TreeNode node = q.poll(); 142 | String str = node == null ? "null" : String.valueOf(node.val); 143 | sb.append(str).append(", "); 144 | if (node != null && (node.left != null || node.right != null)) { 145 | q.add(node.left); q.add(node.right); 146 | } 147 | } 148 | return sb.toString(); 149 | } 150 | 151 | public static TreeNode buildTree(int[] preorder, int[] inorder) { 152 | Map inPos = new HashMap<>(); 153 | for (int i = 0; i < inorder.length; ++i) 154 | inPos.put(inorder[i], i); 155 | return buildTree(preorder, 0, preorder.length-1, 0, inPos); 156 | } 157 | 158 | private static class Item { 159 | TreeNode parent; 160 | Integer childVal; 161 | boolean isLeft; 162 | Item(TreeNode parent, Integer childVal, boolean isLeft) { 163 | this.parent = parent; 164 | this.childVal = childVal; 165 | this.isLeft = isLeft; 166 | } 167 | } 168 | 169 | /* 170 | * 根据树的层序遍历数组,构建一棵二叉树。例如: 171 | * 输入:[3,2,3,null,3,null,1] 172 | * 173 | * 构建的二叉树是: 174 | * 3 175 | * / \ 176 | * 2 3 177 | * \ \ 178 | * 3 1 179 | * 180 | * 注意:如果某个位置上的节点为 null,那么它左右子树的 null 不需要写出来。 181 | * 比如对于下面这棵树: 182 | * 1 183 | * \ 184 | * 2 185 | * \ 186 | * 4 187 | * 应该表示为:[1,null,2,null,4] 188 | * 而不是:[1,null,2,null,null,null,4] 189 | * 190 | */ 191 | public static TreeNode buildTree(Integer... nums) { 192 | if (nums == null || nums.length == 0) return null; 193 | TreeNode dummy = new TreeNode(0); 194 | Queue q = new LinkedList<>(); 195 | q.add(new Item(dummy, nums[0], true)); 196 | int p = 1; 197 | 198 | while (!q.isEmpty()) { 199 | Item item = q.poll(); 200 | TreeNode child = item.childVal == null ? null : new TreeNode(item.childVal); 201 | if (item.isLeft) item.parent.left = child; 202 | else item.parent.right = child; 203 | if (child != null && p < nums.length) { 204 | q.add(new Item(child, nums[p++], true)); 205 | q.add(new Item(child, nums[p++], false)); 206 | } 207 | } 208 | return dummy.left; 209 | } 210 | 211 | public static List> levelOrderTraversal(TreeNode root) { 212 | if (root == null) return new ArrayList<>(); 213 | List> result = new ArrayList<>(); 214 | Queue q = new LinkedList<>(); 215 | q.add(root); 216 | 217 | while (!q.isEmpty()) { 218 | List elem = new ArrayList<>(); 219 | int size = q.size(); 220 | for (int i = 0; i < size; ++i) { 221 | TreeNode s = q.poll(); 222 | elem.add(s.val); 223 | if (s.left != null) q.add(s.left); 224 | if (s.right != null) q.add(s.right); 225 | } 226 | result.add(elem); 227 | } 228 | 229 | return result; 230 | } 231 | 232 | public static void printTreeByLevel(TreeNode root){ 233 | List> result = levelOrderTraversal(root); 234 | for (List level: result) { 235 | for (Integer num: level) { 236 | System.out.print(num + " "); 237 | } 238 | System.out.println(); 239 | } 240 | System.out.println(); 241 | } 242 | 243 | // [3,2,3,null,3,null,1] 244 | // 3 245 | // / \ 246 | // 2 3 247 | // \ \ 248 | // 3 1 249 | public static void printTreeByLevelWithNull(TreeNode root) { 250 | if (root == null) { 251 | System.out.println("null"); 252 | return; 253 | } 254 | Queue q = new LinkedList<>(); 255 | q.add(root); 256 | while (!q.isEmpty()) { 257 | TreeNode node = q.poll(); 258 | String str = node == null ? "null" : String.valueOf(node.val); 259 | System.out.print(str + ", "); 260 | if (node != null && (node.left != null || node.right != null)) { 261 | q.add(node.left); q.add(node.right); 262 | } 263 | } 264 | System.out.println(); 265 | } 266 | 267 | public static boolean isValidBST(TreeNode root) { 268 | return isValidBSTBound(root, null, null); 269 | } 270 | 271 | public static boolean isBalancedTree(TreeNode root) { 272 | return getHeightAndCheck(root) != -1; 273 | } 274 | 275 | ///////////////////////// Assert ////////////////////////////// 276 | public static void assertEquals(ListNode expected, ListNode actual) { 277 | ListNode e = expected, a = actual; 278 | while (e != null && a != null) { 279 | if (e.val != a.val) 280 | throw new AssertionError("expected: " + linkedList2String(expected) + ", actual: " + linkedList2String(actual)); 281 | e = e.next; 282 | a = a.next; 283 | } 284 | if (e != null || a != null) 285 | throw new AssertionError("expected: " + linkedList2String(expected) + ", actual: " + linkedList2String(actual)); 286 | } 287 | 288 | public static void assertEquals(TreeNode expected, TreeNode actual) { 289 | if (!isSameTree(expected, actual)) { 290 | throw new AssertionError("expected: " + tree2String(expected) + ", actual: " + tree2String(actual)); 291 | } 292 | } 293 | 294 | public static void assertEquals(List expecteds, List actuals) { 295 | if (expecteds != null && actuals != null) { 296 | if (expecteds.size() != actuals.size()) 297 | throw new AssertionError("expected: " + list2String(expecteds) + ", actual: " + list2String(actuals)); 298 | for (int i = 0; i < expecteds.size(); ++i) { 299 | if (!expecteds.get(i).equals(actuals.get(i))) 300 | throw new AssertionError("expected: " + list2String(expecteds) + ", actual: " + list2String(actuals)); 301 | } 302 | } else if (expecteds != null || actuals != null) 303 | throw new AssertionError("expected: " + list2String(expecteds) + ", actual: " + list2String(actuals)); 304 | } 305 | 306 | public static void assertListListEquals(List> expecteds, List> actuals) { 307 | if (expecteds != null && actuals != null) { 308 | Assert.assertEquals(expecteds.size(), actuals.size()); 309 | for (int i = 0; i < expecteds.size(); ++i) 310 | assertEquals(expecteds.get(i), actuals.get(i)); 311 | } else if (expecteds != null || actuals != null) 312 | throw new AssertionError("In expecteds and actuals, one if null but another is not null."); 313 | } 314 | 315 | private static final Random RANDOM = new Random(); 316 | private static final int FACTOR = 10; 317 | private static final String ALPHANUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 318 | 319 | public static void println(String str) { 320 | System.out.println(str); 321 | } 322 | 323 | public static void println() { 324 | System.out.println(); 325 | } 326 | 327 | public static void printArray(final int[] arr) { 328 | if (arr == null || arr.length == 0) return; 329 | for (int i = 0; i < arr.length; ++i) 330 | System.out.print(arr[i] + " "); 331 | System.out.println(); 332 | } 333 | 334 | public static void printArray(String message, final int[] arr) { 335 | System.out.println(message + ": "); 336 | printArray(arr); 337 | } 338 | 339 | public static void printMatrix(final int[][] matrix) { 340 | for (int i = 0; i < matrix.length; ++i) { 341 | for (int j = 0; j < matrix[0].length; ++j) { 342 | System.out.print(matrix[i][j] + " "); 343 | } 344 | System.out.println(); 345 | } 346 | System.out.println(); 347 | } 348 | 349 | public static void printList(final List list) { 350 | for (int e: list) { 351 | System.out.print(e + " "); 352 | } 353 | System.out.println(); 354 | } 355 | public static void printListList(final List> lists) { 356 | for (List list: lists) { 357 | for (T e: list) { 358 | System.out.print(e + " "); 359 | } 360 | System.out.println(); 361 | } 362 | System.out.println(); 363 | } 364 | 365 | public static int getRandomInt(int upperBound) { 366 | return RANDOM.nextInt(upperBound); 367 | } 368 | 369 | // return a random int value between lowerBound(included) and upperBound(excluded) 370 | public static int getRandomInt(int lowerBound, int upperBound) { 371 | return RANDOM.nextInt(upperBound - lowerBound) + lowerBound; 372 | } 373 | 374 | public static String getRandomString(int length) { 375 | if (length <= 0) return ""; 376 | StringBuilder sb = new StringBuilder(length); 377 | for (int i = 0; i < length; ++i) { 378 | int idx = RANDOM.nextInt(ALPHANUMERIC.length()); 379 | sb.append(ALPHANUMERIC.charAt(idx)); 380 | } 381 | return sb.toString(); 382 | } 383 | 384 | public static int[] getRandomIntArray(int arrayLength) { 385 | int minValue = -arrayLength * FACTOR; 386 | int maxValue = arrayLength * FACTOR; 387 | int flag = RANDOM.nextInt(3); 388 | if (flag == 0) { 389 | // all positive (include 0) 390 | return getRandomIntArray(arrayLength, 0, maxValue); 391 | } else if (flag == 1) { 392 | // all negative 393 | return getRandomIntArray(arrayLength, minValue, 0); 394 | } else { 395 | // mixed positive and negative 396 | return getRandomIntArray(arrayLength, minValue, maxValue); 397 | } 398 | } 399 | 400 | // All elements are in [minValue, maxValue) 401 | public static int[] getRandomIntArray(int arrayLength, int minValue, int maxValue) { 402 | int[] arr = new int[arrayLength]; 403 | for (int i = 0; i < arrayLength; ++i) { 404 | arr[i] = RANDOM.nextInt(maxValue - minValue) + minValue; 405 | } 406 | return arr; 407 | } 408 | 409 | // All elements are in [0, 1) 410 | public static double[] getRandomDoubleArray(int arrayLength) { 411 | double[] arr = new double[arrayLength]; 412 | for (int i = 0; i < arrayLength; ++i) { 413 | arr[i] = RANDOM.nextDouble(); 414 | } 415 | return arr; 416 | } 417 | 418 | public static int[][] getRandomMatrix(int row, int col, int minValue, int maxValue) { 419 | int[][] matrix = new int[row][col]; 420 | for (int i = 0; i < row; ++i) 421 | for (int j = 0; j < col; ++j) 422 | matrix[i][j] = RANDOM.nextInt(maxValue - minValue) + minValue; 423 | 424 | return matrix; 425 | } 426 | 427 | public static int[] reverseArray(int[] arr) { 428 | if (arr == null || arr.length == 0) return arr; 429 | for (int i=0, j=arr.length-1; i < j; ++i, --j) 430 | swap(arr, i, j); 431 | return arr; 432 | } 433 | 434 | public static int[] getSortedArray(int arrayLength) { 435 | int[] arr = getRandomIntArray(arrayLength); 436 | Arrays.sort(arr); 437 | return arr; 438 | } 439 | 440 | public static int[] getReverseSortedArray(int arrayLength) { 441 | return reverseArray(getSortedArray(arrayLength)); 442 | } 443 | 444 | public static int[] getSameElementArray(int arrayLength, int value) { 445 | int[] arr = getRandomIntArray(arrayLength); 446 | Arrays.fill(arr, value); 447 | return arr; 448 | } 449 | 450 | public static int[] sortByBuiltinMethod(final int[] arr) { 451 | if (arr == null) return null; 452 | int[] sorted = new int[arr.length]; 453 | System.arraycopy(arr, 0, sorted, 0, arr.length); 454 | Arrays.sort(sorted); 455 | return sorted; 456 | } 457 | 458 | public static double[] sortByBuiltinMethod(final double[] arr) { 459 | if (arr == null) return null; 460 | double[] sorted = new double[arr.length]; 461 | System.arraycopy(arr, 0, sorted, 0, arr.length); 462 | Arrays.sort(sorted); 463 | return sorted; 464 | } 465 | 466 | 467 | //////////////////// private method /////////////////////// 468 | private static void swap(int[] arr, int i, int j) { 469 | int tmp = arr[i]; 470 | arr[i] = arr[j]; 471 | arr[j] = tmp; 472 | } 473 | 474 | private static TreeNode buildTree( 475 | int[] preorder, int preStart, int preEnd, 476 | int inStart, Map inPos) { 477 | 478 | if (preStart > preEnd) return null; 479 | TreeNode root = new TreeNode(preorder[preStart]); 480 | int rootIdxInorder = inPos.get(preorder[preStart]); 481 | int leftLen = rootIdxInorder - inStart; 482 | root.left = buildTree(preorder, preStart+1, preStart+leftLen, inStart, inPos); 483 | root.right = buildTree(preorder, preStart+leftLen+1, preEnd, rootIdxInorder+1, inPos); 484 | return root; 485 | } 486 | 487 | private static boolean isSameTree(TreeNode p, TreeNode q) { 488 | if (p == null && q == null) return true; 489 | if (p == null || q == null) return false; 490 | return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right); 491 | } 492 | 493 | private static boolean isValidBSTBound(TreeNode root, TreeNode lower, TreeNode upper) { 494 | if (root == null) return true; 495 | if (lower != null && lower.val >= root.val) return false; 496 | if (upper != null && upper.val <= root.val) return false; 497 | return isValidBSTBound(root.left, lower, root) && isValidBSTBound(root.right, root, upper); 498 | } 499 | 500 | private static int getHeightAndCheck(TreeNode root) { 501 | if (root == null) return 0; 502 | 503 | int left = getHeightAndCheck(root.left); 504 | if (left == -1) return -1; 505 | 506 | int right = getHeightAndCheck(root.right); 507 | if (right == -1) return -1; 508 | 509 | if (Math.abs(left - right) > 1) return -1; 510 | return Math.max(left, right) + 1; 511 | } 512 | 513 | } 514 | --------------------------------------------------------------------------------