基于归并排序的算法题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
就是在 合并的时候,当左边数组的数小于右边数组的数的时候,就开始统计当前左边数组的小和。是当前右边数组个数*当前左边数组的值。
注意:当左右数组值相等的时候。必须是合并右树组的值。
package basicsort import "fmt" func Small_sum_ms(arr []int) int { return process_ms(arr) } func process_ms(arr []int) int { if len(arr) <= 1 { return 0 //递归的终止条件。只有1个数,就是0 } mid := len(arr) / 2 // 开辟俩个数组 l_arr := make([]int, len(arr[:mid])) r_arr := make([]int, len(arr[mid:])) copy(l_arr, arr[:mid]) copy(r_arr, arr[mid:]) // 递归调用俩个数组 l_res := process_ms(l_arr) r_res := process_ms(r_arr) fmt.Println(l_res, r_res) l_index := 0 r_index := 0 c_index := 0 res := 0 // 小数累加和 for l_index < len(l_arr) && r_index < len(r_arr) { if l_arr[l_index] < r_arr[r_index] { //左数组的数 a 小于 右数组的数, a就是当前右树组所有数的小数, // res += l_arr[l_index] * (len(r_arr) - r_index + 1) //开始计算错误的地方 res += l_arr[l_index] * (len(r_arr) - r_index) arr[c_index] = l_arr[l_index] l_index++ } else { //右数组的数小或等于,直接合并。不需要计算 arr[c_index] = r_arr[r_index] r_index++ } // 数组越界。应该是if else。 不能if if 。l_index已经增1了。淦。。 // if l_arr[l_index] >= r_arr[r_index] { // //右数组的数小或等于,直接合并。不需要计算 // arr[c_index] = r_arr[r_index] // r_index++ // } c_index++ } // 当只有单个数组还有数据,不需要累加 for l_index < len(l_arr) { arr[c_index] = l_arr[l_index] c_index++ l_index++ } for r_index < len(r_arr) { arr[c_index] = r_arr[r_index] c_index++ r_index++ } //返回当前递归计算的结果,左数组的结果 + 右数组的结果 + 当前合并的结果 return res + l_res + r_res }
逆序对问题
剑指 Offer 51. 数组中的逆序对
在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有逆序 对。
也就是在合并的时候,开始计算
func reversePairs(nums []int) int { res := process(nums) return res } func process(arr []int) int { if len(arr) <= 1 { return 0 } mid := len(arr) / 2 l_arr := make([]int, len(arr[:mid])) r_arr := make([]int, len(arr[mid:])) copy(l_arr, arr[:mid]) copy(r_arr, arr[mid:]) l_res := process(l_arr) r_res := process(r_arr) l_i, r_i, c_i := 0, 0, 0 res := 0 for l_i < len(l_arr) && r_i < len(r_arr) { if l_arr[l_i] <= r_arr[r_i] { arr[c_i] = l_arr[l_i] l_i ++ } else { res += len(l_arr) - l_i arr[c_i] = r_arr[r_i] r_i ++ } c_i ++ } for l_i < len(l_arr) { arr[c_i] = l_arr[l_i] l_i++ c_i++ } for r_i < len(r_arr) { arr[c_i] = r_arr[r_i] r_i++ c_i++ } return l_res + r_res + res }
快速排序
根据一个数,将一个数组分成左边全部小于等于num,右边全部大于num
less: 小于等于的区域。初始值 -1
i:当前要看的数的索引
1)当[i] <= num, [i] 和 [less + 1] 交换, i++, less ++ ,此处less + 1可能是大于num的数,也可能是自己和自己交换。
2) 当[i] > num, 只有i++, 再碰到 <= num的数,就和[less + 1] 交换了。
结束条件就是数组 循环看完一遍了。
func QucikSort01(arr []int, num int) { less := -1 // 小于等于 num区域 for i := 0; i < len(arr); i++ { // 如果当前数小于 等于 num if arr[i] <= num { swap(arr, less+1, i) //小于等于 区域右扩 less++ } } }
进阶,要分为三个区域,左边是小于 num, 中间是等于 num,右边是大于num
less: 小于num的区域,初始值-1
more: 大于num的区域,初始值len(arr)
i:当前遍历的索引
终止条件是i == more ,就是等于区域和大于区域碰着了
- 分三种情况
1)[i] < num, [less + 1] 和 [i] 交换,less ++, i++
2) [i] == num, i++
3) [i] > num, [more - 1] 和 [i] 交换,more --, i不动,因为 more-1还未看。
func QuickSort02(arr []int, num int) { less := -1 more := len(arr) i := 0 for i < more { if arr[i] < num { swap(arr, less+1, i) less++ i++ } else if arr[i] == num { i++ } else { swap(arr, more-1, i) more-- } } }
快排
- 快排1.0 2.0 3.0 的区别
快排1.0
func QuickSort_10(arr []int) { partation_01(arr, 0, len(arr)-1) } func partation_01(arr []int, l int, r int) { if l >= r { return //递归结束条件 } pv := arr[r] less := l - 1 i := l for ; i < r; i++ { // 如果当前数小于 等于 num if arr[i] <= pv { swap(arr, less+1, i) //小于等于 区域右扩 less++ } } swap(arr, less+1, r) // 为啥这里需要替换呢。因为i < r ,最小区域less固定了,less +1 就是 pv的位置。 // 递归调用左右数组 partation_01(arr, l, less) partation_01(arr, less+2, r) }
快排2.0
func partation_02(arr []int, l int, r int) { if l >= r { return //递归结束条件 } pv := arr[r] less := l - 1 more := r + 1 i := l for i < more { // 如果当前数小于 等于 num if arr[i] < pv { swap(arr, less+1, i) //小于等于 区域右扩 less++ i++ } else if arr[i] == pv { i++ } else { swap(arr, i, more-1) more-- } } if more <= r { //这里需要判断 more是否没扩。不然越界 swap(arr, more, r) //将 pv 也就是[r] 和more区域里的第一个值替换。 } // swap(arr, more, r) //将 pv 也就是[r] 和more区域里的第一个值替换。 // 递归调用左右数组 partation_02(arr, l, less) partation_02(arr, more, r) }
快排3.0
func partation_03(arr []int, l int, r int) { if l >= r { return //递归结束条件 } random_index := rand.Intn(r - l + 1) //随机选取一个索引 pv := arr[l+random_index] // 将随机索引的数和最后一个数替换,下面的代码就和以前的一致了 swap(arr, l+random_index, r) less := l - 1 more := r + 1 i := l for i < more { // 如果当前数小于 等于 num if arr[i] < pv { swap(arr, less+1, i) //小于等于 区域右扩 less++ i++ } else if arr[i] == pv { i++ } else { swap(arr, i, more-1) more-- } } if more <= r { swap(arr, more, r) //将 pv 也就是[r] 和more区域里的第一个值替换。 } // 递归调用左右数组 partation_03(arr, l, less) partation_03(arr, more, r) }
好了ok.完事。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统