排序
冒泡排序
func BubbleSort(arr []int,arrLength int){
for i:=0; i<arrLength; i++{
flag := false
//每一轮操作完,有i个元素已经是有序的,同时最后一个元素没有比较对象,不用比
for j:=0;j<arrLength-i-1;j++{
if arr[j] > arr[j+1]{
arr[j],arr[j+1] = arr[j+1],arr[j]
flag = true
}
}
if !flag{
break
}
}
}
插入排序
把数字插入到合适的位置
package algo
import "testing"
/**
第一个数字是有序的,从后面的数字取一个出来,放在有序范围内
*/
func insertionSort(arr []int,arrLength int){
if arrLength <= 1 {
return
}
for i:=1; i<arrLength; i++ {
//从没有排序的区域取第一个
value := arr[i]
//从后往前,跟有序区域每一个数字比较
j := i-1 //i-1是有序区域最后一个数字
for ;j>=0;j--{
//如果值比某个数字小,则该数字需要往后挪动一个位置
if(arr[j] > value){
arr[j+1] = arr[j]
}else{
//有序区域已经没有比该数字更大,退出
break
}
}
//把数字放在挪动出来的空位,j+1是为了补偿j--
arr[j+1] = value
}
}
选择排序
每次选择后面最小的数字放到有序区
//在无序区选择一个最小的数字放在有序区后面
func selectSort(arr []int,arrLength int){
for i:=0; i<arrLength; i++{
minIndex := i //先记录当前元素作为最小元素的索引
//在无序区(i+1),从前往后找最小的索引
for j:=i+1;j<arrLength;j++{
if arr[j] < arr[minIndex] {
minIndex = j
}
}
//用最小元素替换有序区末尾元素,此时该区域(0~i)才是真正有序
arr[minIndex],arr[i] = arr[i],arr[minIndex]
}
}
快排
选择一个数字作为基准值,小的放左边,基准值放中间,大的放右边。不断重复这个过程
/**
选择一个数字作为基准值,小的放左边,大的放右边
*/
func QuickSort(arr []int){
QuickSortInner(arr,0,len(arr)-1)
}
func QuickSortInner(arr []int,left int,right int){
//递归结束条件,左右两边的索引重合,表示已经所有条件
if(left >= right){
return
}
q := partition(arr,left,right) //分区,并返回两个区中间数的索引
partition(arr,left,q-1) //对小数区[left,q-1]分区
partition(arr,right,q+1)//对大数区分区
}
func partition(arr []int,left int,right int) int{
//选取最后一个值作为基准值
pivot := arr[right]
//有序区开始
j := left
//从左到右遍历,如果某个数字比基数小,该数字加入(交换) 已小数字区间[left,j-1]尾部,区间扩充
//否则加入大数字区间,区间位置不变
//(小->大)
for i:=left;i<right;i++{
if arr[i] < pivot {
arr[i],arr[j] = arr[j],arr[i]
j++
}
}
//基数需要跟大数字区间第一个数字交换(因为比他大的都放在第一个数字后了)
arr[j],arr[right] = arr[right],arr[j]
return j
}
归并排序
把数组不断拆分成两半,直到成为一个单独的元素,然后传入数组的起点,中间点,终点,以中间点为边界合并两个有序的数组
func mergerSort(arr []int,start int,end int){
//start >= end 表示整个数组已经拆分成一个个元素
if start >= end {
return
}
//把数组分成两半,分别对两半进行拆分
middle := start + (end - start) / 2
mergerSort(arr,start,middle)
mergerSort(arr,middle+1,end)
//拆分完后对两半进行合并操作
merge(arr,start,middle,end)
}
//对两个有序数组进行合并操作,i是起点到中间点,j是中间点+1到终点
func merge(arr []int,start int, middle int, end int){
//分别设置三个变量,两个作为有序数组的偏移量,k作为新数组的偏移量
i := start
j := middle+1
k := 0
//申请一个两个数组合并后大小的空间
tmpArr := make([]int,end-start+1)
//循环结束的条件是 其中一个数组已经遍历完
//遍历的过程中新数组递加
for ;i<=middle && j<=end; k++ {
//把值更大的元素放入新数组,并且用该数组下一个元素对比
if arr[j] > arr[i]{
tmpArr[k] = arr[j]
j++
}else{
tmpArr[k] = arr[i]
i++
}
}
//把i数组未循环完的元素放入临时变量
for ;i<=middle;i++{
tmpArr[k] = arr[i]
k++
}
//把j数组未循环完的元素放入临时变量
for ;j<=end;j++{
tmpArr[k] = arr[j]
k++
}
//把新数组的元素放回原数组,原数组需要从起点start偏移
for i=0; i< len(tmpArr); i++{
arr[start+i] = tmpArr[i]
}
}
二分法
等值查找
场景:有序数据
注意点:
- 循环退出条件是 low <= high
- low和high的更新,low=mid+1,high=mid-1。注意这里的 +1 和 -1,如果直接写成 low=mid 或者 high=mid,就可能会发生死循环。
func bsearch(arr []int,value int) int{
length := len(arr)
high := length - 1
low := 0
for high >= low {
mid := low + (high - low)/2
if value == arr[mid] {
return mid
}else if(value > arr[mid]){
low = mid + 1
}else{
high = mid - 1
}
}
return -1
}
查找第一个大于等于value的元素
- 如果 a[mid]小于要查找的值 value,那要查找的值肯定在[mid+1, high]之间,所以,我们更新 low=mid+1。
- 对于 a[mid]大于等于给定值 value 的情况,我们要先看下这个 a[mid]是不是我们要找的第一个值大于等于给定值的元素。如果 a[mid]前面已经没有元素,或者前面一个元素小于要查找的值 value,那 a[mid]就是我们要找的元素。
- 如果 a[mid-1]也大于等于要查找的值 value,那说明要查找的元素在[low, mid-1]之间,所以,我们将 high 更新为 mid-1。
func bsearch(arr []int,value int) int{
length := len(arr)
high := length - 1
low := 0
for low <= high {
mid := low + (high - low)/2
//找到了一个大于等于value的元素
if arr[mid] >= value {
//看看它前面有没有元素
//看看前一个元素是不是小于value
//都符合表示找到了
if mid == 0 || arr[mid - 1] < value {
return mid
}else{
//否则从mid前面找比value大的元素
high = mid - 1
}
}else{
//从后面找
low = mid + 1
}
}
return -1
}