总的来说,数组的排列方式分为循环排列和递归排列,所有的递归排列都能用循环排列替换。下面来一一具体举例数组的排列方法:
1.选择排序
运行下面代码,输入一个sort()值
let sort = (arr) => {
if (arr.length > 2) {
let index = minIndex(arr)
let min = arr[index]
arr.splice(index, 1)
return [min].concat(sort(arr))
} else {
return arr[0] < arr[1] ? arr : arr.reverse()
}
}
let minIndex = (arr) => {
return arr.indexOf(min(arr))
}
let min = (arr) => {
if (arr.length > 2) {
return min(
[arr[0], min(arr.slice(1))]
)
} else {
return Math.min.apply(null, arr)
}
}
代码中的选择排序就是每次将数组中最小值min的角标minIndex找出来,然后splice掉它,再[min].concat(sort(arr)),但这样会导致无限循环下去,所以要加个arr.length>2的条件,递归到直至完成
改成for循环的写法如下:
let sort = arr => {
for (let i = 0; i < arr.length; i++) {
let index = minIndex(arr.slice(i)) + i
if (index !== i) {
swap(arr, i, index)
console.log(`swap:${i},${index}`)
}
console.log(`${arr}`)
console.log(`---`)
//consoe.log调试大法
}
return arr
}
//上面是思考的过程,达到目的的方式
let minIndex = arr => {
let index = 0
for (let i = 1; i < arr.length; i++) {
if (arr[index] > arr[i]) {
index = i
//这里index = i 前面不能有 return
}
}
return index
}
//这是定义minIndex
let swap = (arr, i, j) => {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
//这是定义swap
这种方法就是先遍历出最小值的index,在与当前for下的i比较,如果index !== i则交换arr[index]与arr[i]的值,一直循环到最后两位数进行比较,得到排列后的arr
2.快速排序
快速排序就一个特点,代码简单,容易理解
栗子
let quickSort = arr => {
if (arr.length <= 1) { return arr }
let halfIndex = Math.floor(arr.length / 2)
//找到中间的index
let left = []
let right = []
let half = arr[halfIndex]
arr.splice(halfIndex, 1)
for (let i = 0; i < arr.length; i++) {
if (arr[i] < half) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([half], quickSort(right))
//这一步是精髓,让left,right再次quickSort()循环
将数组均分,得到arr.length/2的值,比这个值小的放到left里面去,比这个值大的放到right里面去,最后的return是精髓,再次用到递归排序。
3.归并排序
归并排序是较难理解的一种递归排序方法
let mergeSort = arr => {
if (arr.length === 1) { return arr }
let left = arr.slice(0, Math.floor(arr.length / 2))
let right = arr.slice(Math.floor(arr.length / 2))
return merge(mergeSort(left), mergeSort(right))
}
let merge = (a, b) => {
if (a.length === 0) { return b }
if (b.length === 0) { return a }
console.log(`${a}::${b}`)
console.log(`---`)
return a[0] > b[0] ? [b[0]].concat(merge(a, b.slice(1))) : [a[0]].concat(merge(a.slice(1), b))
}
也是将数组分成左右两半,左半部分再分左右,右半部分再分左右,直到分成每个组里就一个数字,再merge,难理解的地方也在merge这里,两两比较,取两者第一个数字中小的那个出来,再次循环merge。
4.计数排序
计数排序运用到了哈希表,大大节省了排序时间。
let countSort = arr =>{
let hashTable = {}, max = 0, result = []
for(let i=0; i<arr.length; i++){ // 遍历数组
if(!(arr[i] in hashTable)){
hashTable[arr[i]] = 1
}else{
hashTable[arr[i]] += 1
}
if(arr[i] > max) {max = arr[i]}
}
for(let j=0; j<=max; j++){ // 遍历哈希表
if(j in hashTable){
for(let i = 0; i<hashTable[j]; i++){
result.push(j)
}
}
}
return result
}
5.冒泡排序
排序时间较长,但使用内存较小,排序逻辑也简单。
let arr=[4,3,12,1,3,8,-2]
function bubbleSort(arr){
for(let j=0;j<arr.length-1;j++){
let done=true
for (let i=0;i<arr.length-1-j;i++){
if (arr[i]>arr[i+1]){
const tem=arr[i]
arr[i]=arr[i+1]
arr[i+1]=tem
done=false
}
}
if(done){
break;
}
}
return arr
}
bubbleSort(arr)
console.log(arr)
6.插入排序
插入排序后者小于前者,后者就依次向前排,复杂度与冒泡排序一样。
function insertSort(arr){
for(let i=1;i<arr.length;i++){
let temp=arr[i]
let j
for(j=i;j>0;j--){
if(temp>=arr[j-1]){
break;
}
arr[j]=arr[j-1]
}
arr[j]=temp
}
return arr
}
7.其他排序
这些排序平时用的并不多,就不一一列举了。
数组排序复杂度
**时间复杂度:**是指排序算法所耗时间;
**空间复杂度:**也就是指排序过程中所占用的内存大小;
**稳定性:**所谓稳定性是指待排序的序列中有两元素相等,排序之后它们的先后顺序不变。