本文都是使用javascript书写,学习用时两个月,希望能帮助大家。
1.冒泡排序
冒泡排序比较相邻元素,每次都将较大值放在后面,每轮循环结束可以找出一个最大值 平均时间复杂度O(n^2)
let arr = [9,7,8,2,1,4,5]
//使用冒泡排序实现从小到大
let temp
let flag
for(let i = 0; i < arr.length - 1; i++) {
flag = true
for(let j = 0; j < arr.length -1 - i; j++) {
//相邻元素进行比较,将较大的元素放在后面
if(arr[j] > arr[j+1]) {
flag = false
temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
//可以优化之处,如果某一轮循环结束,没有出现元素交换顺序,则代表此数组已经有序
if(flag) {
break
}
}
2.选择排序
在每一轮循环中,找出一个最大值,标记该最大值,循环结束后进行交换。 与冒泡排序法进行对比,此排序方式不再每一次发现一个更大值就立即交换。 平均时间复杂的O(n^2)
let arr = [9,7,8,6,4,3,2,5]
let max,maxIndex
for(let i = 0; i < arr.length - 1; i++) {
max = arr[i]
maxIndex = i
for(let j = i + 1; j < arr.length; j++) {
if(arr[j] > max) {
max = arr[j]
maxIndex = j
}
}
arr[maxIndex] = arr[i]
arr[i] = max
}
3.插入排序
将无序队列插入有序队列,先取第一个元素,让第二个元素与之前元素比较排序成为有序队列。 然后取第三个元素插入到前两个元素组成的有序队列中 平均时间复杂的O(n^2)
let arr = [1,3,0,5,-2,15,10]
let insertSort = (arr) => {
for(let i = 1; i < arr.length; i++) {
let insertVal = arr[i] //待插入元素值
let insertIndex = i - 1 //待插入索引
//插入一个元素,即将它后面的元素向后移动
while(insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex]
insertIndex--
}
arr[insertIndex + 1] = insertVal
}
}
insertSort(arr)
4.希尔排序
相对于插入排序进行比较,如果是升序排列,当最小的数要插入数组中时,他要将原来已经有序的队列每一个元素往后移动一位。针对这种情况,希尔排序可以优化,希尔排序把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 平均时间复杂的O(nlogn)
let arr = [7,8,9,4,5,6,1,2,3,0]
for(let gap = Math.floor(arr.length / 2); gap > 0; gap = Math.floor(gap / 2)) {
for(i = gap; i < arr.length; i++) {
let inseartVal = arr[i]
let inseartIndex = i - gap
while(inseartIndex >= 0 && arr[inseartIndex] > inseartVal) {
arr[inseartIndex + gap] = arr[inseartIndex]
inseartIndex -= gap
}
arr[inseartIndex + gap] = inseartVal
}
}
5.快速排序
快速排序是对冒泡排序的一种优化,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列 平均时间复杂的O(nlogn)
let arr = [8,7,9,5,6,4,2,3,1,0,7,5,0]
let temp
let quickSort = (arr,left,right) => {
let pivot = arr[Math.floor((left + right)/2)]
let l = left
let r = right
while(l < r) {
while(arr[l] < pivot) {
l++
}
while(arr[r] > pivot) {
r--
}
if(l >= r) {
break
}
temp = arr[l]
arr[l] = arr[r]
arr[r] = temp
if(arr[l] == pivot) {
r--
}
if(arr[r] == pivot) {
l++
}
}
if(l === r) {
l += 1
r -= 1
}
if(left < r) {
quickSort(arr,left,r)
}
if(l < right) {
quickSort(arr,l,right)
}
}
quickSort(arr,0,arr.length-1)
6.归并排序
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之) 平均时间复杂的O(nlogn)
let arr = [8,7,9,6,2,1,3,4,5,0]
let temp = []
//归并排序,将问题分解然后再组合回去。
let merge = (arr,left,mid,right,temp) => {
let i = left
let j = mid + 1
let t = 0
//将某一轮,左右两边有序的队列合并成一个有序队列并返回
while(i <= mid && j <= right) {
if(arr[i] < arr[j]) {
temp[t] = arr[i]
t++
i++
} else {
temp[t] = arr[j]
t++
j++
}
}
while(i <= mid) {
temp[t] = arr[i]
t++
i++
}
while(j <= right) {
temp[t] = arr[j]
t++
j++
}
//将数组返回,这里非常关键! 并不是将整个数组进行返回,而是将当前合并的两个数组进行返回
let leftIndex = left
t = 0
while(leftIndex <= right) {
arr[leftIndex] = temp[t]
t++
leftIndex++
}
}
let mergeSort = (arr,left, right, temp) => {
let mid = Math.floor((left + right) / 2)
if(left < right) {
mergeSort(arr,left,mid,temp)
mergeSort(arr,mid + 1, right,temp)
merge(arr,left,mid,right,temp)
}
}
mergeSort(arr,0,arr.length -1,temp)
7.桶排序
将所有待比较数据统一为同样的数位长度,数位较短的数前面补0,从最低位开始依次排序 平均时间复杂的O(nlogn)
let arr = [53, 3, 542, 748, 14, 214]
//确定进行多少轮,由此数组最大值数据长度决定
let max = arr[0]
for(let i = 1; i < arr.length - 1; i++) {
if(max < arr[i]) {
max = arr[i]
}
}
let length = max.toString().length //确定大循环次数
let tongs
let tongsIndex
let t
for(let i = 0, n = 1; i < length; i++, n*=10) {
tongs = []
tongsIndex = []
for(let i = 0; i < 10; i++) {
tongs[i] = []
tongsIndex[i] = 0
}
//将元素放入桶中
for(let i = 0; i<arr.length; i++) {
let num = Math.floor(arr[i]/n) % 10
tongs[num].push(arr[i])
tongsIndex[num]++
}
//将元素从桶中取出
t = 0
for(let i = 0; i < tongs.length; i++) {
if(tongsIndex[i] != 0) {
for(let j =0; j< tongsIndex[i];j++) {
arr[t] = tongs[i][j]
t++
}
}
}
}
8.堆排序
堆排序是利用堆这种数据结构进行排序 平均时间复杂的O(nlogn)
let arr = [4,6,8,5,9]
let adjustHeap = (arr, i, length) => {
let temp = arr[i];//先取出当前元素的值,保存在临时变量
//开始调整
//说明
//1. k = i * 2 + 1 k 是 i结点的左子结点
for(let k = i * 2 + 1; k < length; k = k * 2 + 1) {
if(k+1 < length && arr[k] < arr[k+1]) { //说明左子结点的值小于右子结点的值
k++; // k 指向右子结点
}
if(arr[k] > temp) { //如果子结点大于父结点
arr[i] = arr[k]; //把较大的值赋给当前结点
i = k; //!!! i 指向 k,继续循环比较
} else {
break;//!
}
}
//当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
arr[i] = temp;//将temp值放到调整后的位置
}
let heapSort = (arr) => {
let temp
//完成我们最终代码
//将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
for(let i = Math.floor(arr.length / 2)-1; i >=0; i--) {
adjustHeap(arr, i, arr.length);
}
/*
* 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
*/
for(let j = arr.length-1;j >0; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, j);
}
}
heapSort(arr)