- 排序算法的稳定性
顺便将排序算法的时间复杂度也进行分析
- 稳定性的概念:在待排序的记录序列中,存在多个具有相同的关键字记录以后,即类似ri = rj这种相邻的不变,在排序以后的相对次序也不发生改变,则称这种算法是稳定的
不稳定稳定的排序算法:堆排序、快速排序、希尔排序、直接选择排序、桶排序是不稳定的
稳定的排序算法:基数排序、冒泡排序、插入排序、折半插入排序、归并排序
冒泡排序,对比相邻元素之间的大小,O(n2)
稳定,因为在变换的时候不会改变相邻之间的相对次序
func BubbleSort(arr []int) []int {
nLen := len(arr)
for i := 0; i < nLen; i++ {
for j := i + 1; j < nLen; j++ {
if arr[j+1] < arr[j] {
arr[j+1], arr[j] = arr[j], arr[j+1]
}
}
}
return arr
}
选择排序,每次选出最小的值,如果值小于最小数,则将其放在最前面 O(n2)
不稳定,因为每次取出来一位以后就会改变相对位置
func SelectSort(arr []int) []int {
nLen := len(arr)
for i := 0; i < nLen; i++ {
min := i // 记录每次的最小值
for j := i + 1; j < nLen; j++ {
if arr[j] < arr[min] {
min = j
}
}
arr[i], arr[min] = arr[min], arr[i]
}
return arr
}
// 插入排序,将没有排序好的值插到前面以后排好顺序的值里头
// 插入排序由于是从后往前插入的,所以不会改变相对位置 所以是稳定的
// 二分插入法,基于之前都是排序好的list,使用二分法插入
func InsertSort(arr []int) []int {
nLen := len(arr)
for i := 1; i < nLen; i++ {
for j := i; j > 0; j-- {
// 如果小于旁边的值,则交换相对位置
if arr[j-1] > arr[j] {
arr[j], arr[j-1] = arr[j-1], arr[j]
} else {
break
}
}
}
return arr
}
// 归并排序,归并排序的时间复杂度O(nlogn) 空间复杂度是O(n)
// 稳定
func Merge(arr []int) []int {
nLen := len(arr)
if nLen < 2 {
return arr
}
mid := nLen / 2
leftArr := Merge(arr[:mid])
rightArr := Merge(arr[mid:])
res := MergeSort(leftArr, rightArr)
return res
}
func MergeSort(leftArr []int, rightArr []int) []int {
arrNew := []int{}
var i, j int
for i < len(leftArr) && j < len(rightArr) {
if leftArr[i] < rightArr[j] {
arrNew = append(arrNew, leftArr[i])
i += 1
} else {
arrNew = append(arrNew, rightArr[j])
j += 1
}
}
arrNew = append(arrNew, leftArr[i:]...)
arrNew = append(arrNew, rightArr[j:]...)
return arrNew
}
// 单路快排,时间复杂度O(nlogn), 最差时间复杂度是O(n2)
// 快排是不稳定的,受到原来数组的顺序影响,若原来的数组就是就有序,则会将数据全分到一遍,最后导致变成冒泡排序
// 单路快排没有考虑数据与基准值相等的情况,容易一边大一边小
// 双路快排对于相等的值就是不交换,多路快排则针对相等的值单独提取
func QSortRecursion(arr []int){
if len(arr) <2{
return
}
head, tail := 0, len(arr) -1
randomNum := getRandNum(len(arr)-1)
arr[0], arr[randomNum] = arr[randomNum], arr[0]
poivt := arr[0]
for i := 1; i<=tail; {
if arr[i] <= poivt {
arr[i], arr[head] = arr[head], arr[i]
head ++
i ++
} else {
arr[i], arr[tail] = arr[tail], arr[i]
tail --
}
}
QSortRecursion(arr[:head])
QSortRecursion(arr[head+1:])
}
func getRandNum(num int) int{
rand.Seed(time.Now().UnixNano())
return rand.Intn(num)
}
- redis是单线程还是多线程的,怎么证明
- 搜索算法有哪些
顺序查找法:即顺序遍历,时间复杂度是O(n)
二分查找法:折半查找,每次mid:=1/2进行比较,时间复杂度是O(logn)
插值查找法:针对数字的特殊性进行查找,比如在0~10000中查找5,在二分法的基础上进行查找,将公式改进为mid=low+(key-a[low])/(a[high]-a[low])*(high-low),时间复杂度是O(logn),数据均匀比较好查找
斐波那契查找法:它也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率,时间复杂度是O(logn)
- Top k算法,大堆顶和小堆顶
从亿万数据从取出前100的数字,维护一个堆栈实现
大顶堆:每个结点的值都大于或等于其左右孩子结点的值
小顶堆:每个结点的值都小于或等于其左右孩子结点的值
- 如果发现SQL很慢怎么查询索引有没有覆盖
在这里插入代码片
- 加密算法