diff --git a/readme.md b/readme.md index 61d5511..0fca93a 100644 --- a/readme.md +++ b/readme.md @@ -40,9 +40,11 @@ 类似上面的方案,在游戏中可以在活动创建的时候,初始化所有的方案放到redis,然后每一次抽取都筛选下标来确认方案.这样就可以保证在一定的范围或者数量内确认对应的方案。 -2020/8/6: 添加求背包问题最优解三种方式(KnapsackOptimizationUtil.go)及其单测类,添加求背包问题最优解过程路径(KnapsackSearchAnswerUtil.go)及其单测类.添加背包问题的readme相关说明 +2020/8/6: 添加求背包问题最优解三种方式(KnapsackOptimizationUtil.go)及其单测类,添加求背包问题最优解过程路径(KnapsackSearchAnswerUtil.go)及其单测类.添加背包问题的readme相关说明 -2020/8/18:修改单测位置,添加lomuto查找第k小元素算法代码及其单测(分治法) +2020/8/18: 修改单测位置,添加lomuto查找第k小元素算法代码及其单测(分治法) + +2020/8/20:新增二分法查找算法及其单测、插入查询及其单测、lomuto划分查询第k小元素及其单测、归并排序及其单测、快速排序及其单测. # mod vendor模式加载包 diff --git a/util/search/BinarySearch.go b/util/search/BinarySearch.go new file mode 100644 index 0000000..1cd1d49 --- /dev/null +++ b/util/search/BinarySearch.go @@ -0,0 +1,28 @@ +/* + * @Author : huangzj + * @Time : 2020/8/18 17:45 + * @Description:二分查找法,对于有序数组进行查找 + */ + +package search + +func BinarySearch(list []int, value int) int { + low := 0 + high := len(list) - 1 + var mid int + + for low <= high { + mid = (low + high) >> 1 //相当于(low + high) / 2 + if value == list[mid] { + return mid + } + + if value < list[mid] { + high = mid - 1 + } else { + low = mid + 1 + } + } + + return -1 +} diff --git a/util/search/BinarySearch_test.go b/util/search/BinarySearch_test.go new file mode 100644 index 0000000..f4debed --- /dev/null +++ b/util/search/BinarySearch_test.go @@ -0,0 +1,42 @@ +/* + * @Author : huangzj + * @Time : 2020/8/18 17:59 + * @Description: + */ + +package search + +import ( + "fmt" + "testing" +) + +func TestBinarySearch(t *testing.T) { + + evenNumList := []int{1, 2, 3, 5, 6, 7, 9, 10, 22, 111, 333, 431} + + for _, r := range evenNumList { + position := BinarySearch(evenNumList, r) + fmt.Println(position) + } + + position := BinarySearch(evenNumList, 999) + fmt.Println(position) + + position = BinarySearch(evenNumList, -10) + fmt.Println(position) + + fmt.Println() + + oddNumList := []int{1, 2, 3, 5, 6, 7, 9, 10, 22, 111, 333, 431, 3211} + for _, r := range oddNumList { + position := BinarySearch(oddNumList, r) + fmt.Println(position) + } + + position = BinarySearch(oddNumList, 999) + fmt.Println(position) + + position = BinarySearch(oddNumList, -10) + fmt.Println(position) +} diff --git a/util/search/InsertionSearch.go b/util/search/InsertionSearch.go new file mode 100644 index 0000000..9bb4a04 --- /dev/null +++ b/util/search/InsertionSearch.go @@ -0,0 +1,34 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 10:24 + * @Description: + * + */ + +package search + +func InsertionSearch(list []int, value int) int { + low := 0 + high := len(list) - 1 + var mid int + + //插值查找这边如果数字和起始数字差别大的话,计算出来的下标会越界挺多的 + if value > list[high-1] || value < list[0] { + return -1 + } + + for low < high { + mid = low + (high-low)*(value-list[low])/(list[high]-list[low]) + if value == list[mid] { + return mid + } + + if value < list[mid] { + high = mid - 1 + } else { + low = mid + 1 + } + } + + return -1 +} diff --git a/util/search/InsertionSearch.md b/util/search/InsertionSearch.md new file mode 100644 index 0000000..bc7ebb9 --- /dev/null +++ b/util/search/InsertionSearch.md @@ -0,0 +1,7 @@ +从折半查找中可以看出,折半查找的查找效率还是不错的。可是为什么要折半呢?为什么不是四分之一、八分之一呢?打个比方,在牛津词典里要查找“apple”这个单词,会首先翻开字典的中间部分,然后继续折半吗? +肯定不会,对于查找单词“apple”,我们肯定是下意识的往字典的最前部分翻去,而查找单词“zero”则相反,我们会下意识的往字典的最后部分翻去。 +所以在折半查找法的基础上进行改造就出现了插值查找法,也叫做按比例查找。所以插值查找与折半查找唯一不同的是在于mid的计算方式上,它的计算方式为: + +mid = low + (high - low) * (searchValue - data[low]) / (data[high] - data[low]) + +插值查找的时间复杂度也是O(log2n),但是对于数据集合较长,且关键字分布比较均匀的数据集合来说,插值查找的算法性能比折半查找要好,其它的则不适用。 \ No newline at end of file diff --git a/util/search/InsertionSearch_test.go b/util/search/InsertionSearch_test.go new file mode 100644 index 0000000..a7ece0c --- /dev/null +++ b/util/search/InsertionSearch_test.go @@ -0,0 +1,42 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 10:38 + * @Description: + */ + +package search + +import ( + "fmt" + "testing" +) + +func TestInsertionSearch(t *testing.T) { + + evenNumList := []int{1, 2, 3, 5, 6, 7, 9, 10, 22, 111, 333, 431} + + for _, r := range evenNumList { + position := InsertionSearch(evenNumList, r) + fmt.Println(position) + } + + position := InsertionSearch(evenNumList, 999) + fmt.Println(position) + + position = InsertionSearch(evenNumList, -10) + fmt.Println(position) + + fmt.Println() + + oddNumList := []int{1, 2, 3, 5, 6, 7, 9, 10, 22, 111, 333, 431, 3211} + for _, r := range oddNumList { + position := InsertionSearch(oddNumList, r) + fmt.Println(position) + } + + position = InsertionSearch(oddNumList, 999) + fmt.Println(position) + + position = InsertionSearch(oddNumList, -10) + fmt.Println(position) +} diff --git a/util/search/LomutoPartition.go b/util/search/LomutoPartition.go index ff6067e..596c10c 100644 --- a/util/search/LomutoPartition.go +++ b/util/search/LomutoPartition.go @@ -11,7 +11,7 @@ package search //start : 开始比较的数字下标 //end :结束比较的数字下标 //作用说明: -func partition(list []int, start int, end int) int { +func LomutoPartition(list []int, start int, end int) int { moveSubscript := start compareValue := list[start] for i := start + 1; i <= end; i++ { @@ -27,13 +27,13 @@ func partition(list []int, start int, end int) int { func LomutoQuiteSelect(list []int, pointPosition int) int { positionCheck(pointPosition, len(list)) //下标校验 - nowPosition := partition(list, 0, len(list)-1) + nowPosition := LomutoPartition(list, 0, len(list)-1) //循环进行lomuto划分处理,知道找到我们需要的那个位置的元素 for nowPosition != pointPosition-1 { if nowPosition > pointPosition-1 { - nowPosition = partition(list, 0, nowPosition) + nowPosition = LomutoPartition(list, 0, nowPosition) } else { - nowPosition = partition(list, nowPosition+1, len(list)-1) + nowPosition = LomutoPartition(list, nowPosition+1, len(list)-1) } } diff --git a/util/search/LomutoPartition_test.go b/util/search/LomutoPartition_test.go index c18c717..0891730 100644 --- a/util/search/LomutoPartition_test.go +++ b/util/search/LomutoPartition_test.go @@ -13,13 +13,13 @@ import ( func TestPartition(t *testing.T) { list := []int{99, 1, 2, 3, 4, 100, 200, 90, 5} - s := partition(list, 0, 9-1) + s := LomutoPartition(list, 0, 9-1) fmt.Println(fmt.Sprintf("当前第一个元素应该存在位置为:%d", s)) for _, r := range list { fmt.Print(r, " ") } - s = partition(list, 0, 9-1) + s = LomutoPartition(list, 0, 9-1) fmt.Println(fmt.Sprintf("当前第一个元素应该存在位置为:%d", s)) for _, r := range list { fmt.Print(r, " ") diff --git a/util/sort/MergeSort.go b/util/sort/MergeSort.go new file mode 100644 index 0000000..5ee525c --- /dev/null +++ b/util/sort/MergeSort.go @@ -0,0 +1,54 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 11:44 + * @Description:这边提供一个递归的归并排序算法,非递归的理解成本比较高,并且还有多路归并排序,需要理解对应概念 + * 最坏时间复杂度O(nlogn) 最好时间复杂度O(logn) 空间复杂度O(n) + */ + +package sort + +func MergeSort(list []int) { + middle := len(list) / 2 + leftList := copyList(list, 0, middle) + rightList := copyList(list, middle, len(list)) + if len(list) > 1 { + MergeSort(leftList) //先对左边进行归并排序 + MergeSort(rightList) //再对右边进行归并排序 + mergeAll(list, leftList, rightList) //对左右排序的结果进行组合 + } +} + +func copyList(list []int, start int, end int) []int { + result := make([]int, 0) + for i := start; i < end; i++ { + result = append(result, list[i]) + } + return result +} + +func mergeAll(array []int, left []int, right []int) { + l, r, i := 0, 0, 0 //左数组的下标、右数组的下标、组合数组的下标 + for l < len(left) && r < len(right) { + if left[l] > right[r] { + array[i] = right[r] + i++ + r++ + } else { + array[i] = left[l] + i++ + l++ + } + } + + for r < len(right) { + array[i] = right[r] + r++ + i++ + } + + for l < len(left) { + array[i] = left[l] + l++ + i++ + } +} diff --git a/util/sort/MergeSort_test.go b/util/sort/MergeSort_test.go new file mode 100644 index 0000000..0259b78 --- /dev/null +++ b/util/sort/MergeSort_test.go @@ -0,0 +1,42 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 11:45 + * @Description: + */ + +package sort + +import ( + "fmt" + "testing" +) + +func TestMergeSort(t *testing.T) { + list1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + MergeSort(list1) + for _, r := range list1 { + fmt.Print(r, " ") + } + fmt.Println() + + list2 := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + MergeSort(list2) + for _, r := range list2 { + fmt.Print(r, " ") + } + fmt.Println() + + list3 := []int{100, 5, 222, 312, 552, 354, 8, 21, 90, 2, 0, 12, 345, 67} + MergeSort(list3) + for _, r := range list3 { + fmt.Print(r, " ") + } + fmt.Println() + + list4 := []int{1, 19, 42, 7, 3131, 44, 56, 123, 589, 21, 975, 123, 5467, 143, 7, 321, 77, 3} + MergeSort(list4) + for _, r := range list4 { + fmt.Print(r, " ") + } + fmt.Println() +} diff --git a/util/sort/QuickSort.go b/util/sort/QuickSort.go new file mode 100644 index 0000000..cf97fdb --- /dev/null +++ b/util/sort/QuickSort.go @@ -0,0 +1,46 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 17:06 + * @Description: + */ + +package sort + +import "Go-Tool/util/search" + +//通过hoare划分来实现快速排序 +func QuickSort(list []int, start, end int) { + if start < end { + position := hoarePartition(list, start, end) + QuickSort(list, start, position) //注意这边结束的下表是position,而不是position + 1 + QuickSort(list, position+1, end) + } +} + +//通过lomuto划分来实现快速排序 +func QuickSort1(list []int, start, end int) { + if start < end { + position := search.LomutoPartition(list, start, end) + QuickSort(list, start, position) + QuickSort(list, position+1, end) + } +} + +func hoarePartition(list []int, start int, end int) int { + value := list[start] //比较的元素 + s := start + e := end + for s < e { + //找到数组终止前,比目标数(value)大的数字 + for ; s < end && list[s] < value; s++ { + } + //找到数组结束前,比目标数(value)小的数字 + for ; e > start && list[e] >= value; e-- { + } + if s < e { + list[s], list[e] = list[e], list[s] + } + } + list[start], list[e] = list[e], list[start] + return e +} diff --git a/util/sort/QuickSort_test.go b/util/sort/QuickSort_test.go new file mode 100644 index 0000000..0711c7a --- /dev/null +++ b/util/sort/QuickSort_test.go @@ -0,0 +1,80 @@ +/* + * @Author : huangzj + * @Time : 2020/8/19 17:57 + * @Description: + */ + +package sort + +import ( + "fmt" + "testing" +) + +func TestQuickSort(t *testing.T) { + list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + QuickSort(list, 0, len(list)-1) + + for _, r := range list { + fmt.Print(r, " ") + } + fmt.Println() + + list1 := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + QuickSort(list1, 0, len(list1)-1) + + for _, r := range list1 { + fmt.Print(r, " ") + } + fmt.Println() + + list2 := []int{432, 412, 213, 456, 7, 213, 4, 78, 13, 87, 312321, 456, 7, 231, 234, 3} + QuickSort(list2, 0, len(list2)-1) + + for _, r := range list2 { + fmt.Print(r, " ") + } + fmt.Println() + + list3 := []int{1, 312, 356, 76, 2, 54, 78, 321, 3455, 65732, 123534, 54351, 4213, 123, 56, 6, 732, 123, 44, 51, 5, 61, 543} + QuickSort(list3, 0, len(list3)-1) + + for _, r := range list3 { + fmt.Print(r, " ") + } + fmt.Println() +} + +func TestQuickSort1(t *testing.T) { + list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + QuickSort1(list, 0, len(list)-1) + + for _, r := range list { + fmt.Print(r, " ") + } + fmt.Println() + + list1 := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + QuickSort1(list1, 0, len(list1)-1) + + for _, r := range list1 { + fmt.Print(r, " ") + } + fmt.Println() + + list2 := []int{432, 412, 213, 456, 7, 213, 4, 78, 13, 87, 312321, 456, 7, 231, 234, 3} + QuickSort1(list2, 0, len(list2)-1) + + for _, r := range list2 { + fmt.Print(r, " ") + } + fmt.Println() + + list3 := []int{1, 312, 356, 76, 2, 54, 78, 321, 3455, 65732, 123534, 54351, 4213, 123, 56, 6, 732, 123, 44, 51, 5, 61, 543} + QuickSort1(list3, 0, len(list3)-1) + + for _, r := range list3 { + fmt.Print(r, " ") + } + fmt.Println() +}