参考:https://juejin.im/post/59fbe7766fb9a0451c39bf21
排序可视化:https://visualgo.net/bn/sorting
一. 几种常见的算法
1. 冒泡排序(体育委员两两摸头法)
1.1 介绍:
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名“冒泡排序”。
- 2原理:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
1.3伪代码和流程图
伪代码:
a <- {
'0':6,
'1':3,
'2':7,
'3':11,
'4':4,
'5':9,
'length':6
}
轮数n <- 1
while (n < a['length'])
选中的数字的下标i <- 0
while (i < a['length'] - n)
if (a[i] < a[i+1])
i <- i+1
else
t <- a[i]
a[i] <- a[i+1]
a[i+1] <- t
i <- i+1
end
n <- n+1
end
end
流程图:
2.选择排序(体育老师一指禅法)
2.1 介绍:
选择排序是另一个很容易理解和实现的简单排序算法。学习它之前首先要知道它的两个很鲜明的特点。
1.运行时间和输入无关。为了找出最小的元素而扫描一遍数组并不能为下一遍扫描提供任何实质性帮助的信息。因此使用这种排序的我们会惊讶的发现,一个已经有序的数组或者数组内元素全部相等的数组和一个元素随机排列的数组所用的排序时间竟然一样长!而其他算法会更善于利用输入的初始状态,选择排序则不然。
2.数据移动是最少的。选择排序的交换次数和数组大小关系是线性关系。看下面的原理时可以很容易明白这一点。
2.2 原理:
在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
然后在 剩下的数 当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
2.3 伪代码和流程图
伪代码:
a <- {
'0':3,
'1':5,
'2':1,
'3':7,
'4':2,
'5':6,
'length':6
}
轮数n <- 1
while (n < a['length'])
minIndex <- n-1
index <- minIndex+1
while (index < a['length'])
if(a[minIndex] < a[index])
index <- index+1
else
minIndex <- index
index <- index+1
end
t <- a[n-1]
a[n-1] <- a[minIndex]
a[minIndex] <- t
index <- index+1
end
print a
end
流程图:
3.计数排序
伪代码:
a <- {
'0':0,
'1':5,
'2':1,
'3':7,
'4':2,
'5':5,
'length':6
}
hash <- {
}
//入桶
index <- 0
while (index < a['length'])
number <- a['index']
if hash[number] == undefined
hash[number] <- 1
else
hash[number] <- hash[number] + 1
end
index <- index + 1
end
//出桶
index2 <- 0
//确定hash长度(最大的数+1)
max <-findMax(a)
/*
findMax(a):
index3 <- 0
max <- a['index3']
while(index3 < a['length']-1)
if(a['index3'] < a['index3'+1])
max <- a['index3'+1]
else
max <- a['index3']
index3 <- index3+1
end
*/
while(index2 < max+1)
count <- hash[index2]
if count != undefined
countIndex <- 0
while(countIndex < count)
newArr.push(index2)
countIndex <- countIndex+1
end
end
index2 <- index2+1
print newArr
end
流程图:
4.桶排序
伪代码:
a <- {
'0':0,
'1':5,
'2':1,
'3':7,
'4':2,
'5':5,
'length':6
}
hash <- {
}
//入桶
capacity <- 10 //每个桶存放的数字范围
findMaxMin(a): //找出数组a中最大值和最小值
index3 <- 0
max <- a['index3']
min <- a['index3']
while(index3 < a['length']-1)
if(a['index3'] > max)
max <- a['index3']
if(a['index3'] < min)
min <- a[index3]
index3 <- index3+1
end
bucketCount <- 取整[(max-min)/capacity] //确定桶的数量
index <- 0
while(index < a['length'])
bucketIndex <- 0
while(bucketIndex < bucketCount)
if a[index] <= (min+capacity*(bucketIndex+1)) && a[index] > (min+capacity*bucketIndex)
if(bucket[bucketIndex] == undefined)
bucket[bucketIndex] <- []
else
bucket[bucketIndex].push(a[index])
end
end
bucketIndex <- bucketIndex+1
end
index <- index+1
end
//用冒泡排序将桶内数据排序
轮数n <- 1
while (n < bucketIndex)
minIndex <- n-1
index <- minIndex+1
while (index < bucketIndex)
if(bucket[bucketIndex][minIndex] < bucket[bucketIndex][index])
index <- index+1
else
minIndex <- index
index <- index+1
end
t <- bucket[bucketIndex][n-1]
bucket[bucketIndex][n-1] <- bucket[bucketIndex][minIndex]
bucket[bucketIndex][minIndex] <- t
index <- index+1
end
end
//出桶
outBucket <- 0
while(outBucket < bucketCount)
if bucket[outBucket] != undefined
outBucketIndex <- 0
while(outBucketIndex < bucket[outBucket].length)
newArr.push(bucket[outBucket][outBucketIndex])
end
outBucket <- outBucket+1
end
print newArr
end
流程图:
二.一些注意点
基于比较的排序算法的极限(最快),时间复杂度是O(nlgn),像冒泡排序,选择排序,插入排序等
而计数排序不是基于比较的,而是空间换时间,使得时间复杂度能够达到线性O(n+k)
计数排序的缺点:
1.必须要借助于哈希作为计数工具(哈希就是键值对这样的数据结构)
2.无法对小数和负数进行排序(小数一定要告诉精度,因为两个整数间的小数有无数个; 哈希是从0开始计数的,所以负数不行)
计数排序, 桶排序和基数排序的区别:
计数排序:一个桶只放一个数字,浪费空间
桶排序:一个桶放一个区间的数字,所以要进行二次排序,浪费时间 ,空间和时间只能二选一
基数排序:桶的数目是固定的,十进制的数字就是十个桶(先根据个位数大小进行排序,在十位数开始排,先进先出)