排序算法
一,冒泡排序
思路:
- 对未排序的各元素从头到尾依次比较相邻的两个元素大小关系
- 如果左边的队员高, 则两队员交换位置
- 向右移动一个位置, 比较下面两个队员
- 当走到最右端时, 最高的队员一定被放在了最右边
- 按照这个思路, 从最左端重新开始, 这次走到倒数第二个位置的队员即可.
- 依次类推, 就可以将数据排序完成
时间复杂度:
> 大 O 表示法
>
> - 比较次数:O(N^2)
> - 交换次数:O(N^2)
function bubbleSort(arr){ var length = arr.length; for (var i = arr.length;i > 0;i--){ for (var j = 0;j < i; j++){ if(arr[j] > arr[j+1]){ [arr[j],arr[j+1]]=[arr[j+1],arr[j]]; } } } return arr }
二,选择排序
思路:
- 选定第一个索引位置,然后和后面元素依次比较
- 如果后面的队员, 小于第一个索引位置的队员, 则交换位置
- 经过一轮的比较后, 可以确定第一个位置是最小的
- 然后使用同样的方法把剩下的元素逐个比较即可
- 可以看出选择排序,第一轮会选出最小值,第二轮会选出第二小的值,直到最后
时间复杂度:
> 大 O 表示法
>
> - 比较次数:O(N^2)
> - 交换次数:O(N)
function selectionSort(arr) { var length = arr.length; for (let i = 0; i < length; i++) { var minIndex = i; for (let j = i + 1; j < length; j++) { if (arr[minIndex] > arr[j]) { minIndex = j; } } [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; } return arr; }
三,插入排序
思路:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素 temp,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序的元素)大于新元素 temp,将该元素移到下一位置
- 重复上一个步骤,直到找到已排序的元素小于或者等于新元素 temp 的位置
- 将新元素插入到该位置后, 重复上面的步骤
时间复杂度:
> 大 O 表示法
>
> - 比较次数:O(N^2)
> - 交换次数:O(N)
function insertionSort(arr) { for (var i = 1;i < arr.length;i++) { var tmp = arr[i]; for (var j = i - 1;j >= 0 && arr[j] > tmp;j--) { arr[j + 1] = arr[j]; } arr[j + 1] = tmp; } return arr; }
四,希尔排序
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
选择合适的增量:
- 在希尔排序的原稿中, 他建议的初始间距是 N / 2, 简单的把每趟排序分成两半.
- 也就是说, 对于 N = 100 的数组, 增量间隔序列为: 50, 25, 12, 6, 3, 1.
- 这个方法的好处是不需要在开始排序前为找合适的增量而进行任何的计算.
- 我们先按照这个增量来实现我们的代码.
function shellSort(arr) { var length = arr.length; var gap = Math.floor(length / 2); //增量 while (gap > 0) { // 插入排序 for (let i = gap; i < length; i++) { // 保存临时变量 var j = i; var temp = arr[i]; while (j > gap - 1 && arr[j - gap] > temp) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } gap = Math.floor(gap / 2); //更新增量 } return arr; }
五,快速排序
1. 在数组中选一个基准数(通常为数组第一个);
2. 将数组中小于基准数的数据移到基准数左边,大于基准数的移到右边;
3. 对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
取基准值为数组最左边的数
function quickSort(arr, left = 0, right = arr.length - 1) { if (left >= right) return; var baseId = left, baseVal = arr[baseId], i = left, j = right; while (i < j) { while (j > i && arr[j] >= baseVal) { j--; } while (i < j && arr[i] <= baseVal) { i++; } [arr[i], arr[j]] = [arr[j], arr[i]]; } [arr[baseId], arr[i]] = [arr[i], arr[baseId]]; quickSort(arr, left, i - 1); quickSort(arr, i + 1, right); return arr; }
取基准值为最右边的数
function quickSort(arr, left = 0, right = arr.length - 1) { if (left >= right) return; // 如果左边的索引大于等于右边的索引说明整理完毕 var baseVal = arr[right], // 取无序数组最后一个数为基准值 i = left, j = right; while (i < j) { // 把所有比基准值小的数放在左边,比基准值大的数放在右边 while (i < j && arr[i] < baseVal) { // 找到一个比基准值大的数交换 i++; } arr[j] = arr[i]; // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j) while (j > i && arr[j] > baseVal) { // 找到一个比基准值小的数交换 j--; } arr[i] = arr[j]; // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j) } arr[i] = baseVal; // 将基准值放至中央位置完成一次循环(这时候 j 等于 i ) quickSort(arr, left, i - 1); // 将左边的无序数组重复上面的操作 quickSort(arr, i + 1, right); // 将右边的无序数组重复上面的操作 return arr; }
取基准值为数组最中间的数
方法一:
function quickSort(arr, left, right) { if (left < right) { var mid = arr[parseInt((left + right) / 2)], //枢纽选择的是中间的 l = left, r = right; while (true) { while (arr[l] < mid) { l++; } while (arr[r] > mid) { r--; } if (l >= r) { break; } [arr[l], arr[r]] = [arr[r], arr[l]]; } quickSort(arr, left, l - 1); quickSort(arr, l + 1, right); } return arr; }
方法二:
function quickSort(arr) { if (arr.length <= 1) { return arr; } var pivotIndex = Math.floor(arr.length / 2); var pivot = arr.splice(pivotIndex, 1)[0]; var left = []; var right = []; for (let i = 0; i < arr.length; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return quickSort(left).concat([pivot], quickSort(right)); }
六,并归排序
function mergeSort(arr) { // 采用自上而下的递归方法 var len = arr.length; if (len < 2) { return arr; } var middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right) { var result = []; while (left.length && right.length) { if (left[0] <= right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } while (left.length) result.push(left.shift()); while (right.length) result.push(right.shift()); return result; }
详情→图解并归排序算法
七,堆排序
//生成大顶堆 function adjustHeap(arr, i, len) { //将当前值保存 var temp = arr[i]; //从i结点的左子结点开始,也就是2i+1处开始 for (var j = 2 * i + 1; j < len; j = 2 * j + 1) { //如果左子结点小于右子结点,j指向右子结点 if (j + 1 < len && arr[j] < arr[j + 1]) { j++; } //如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)值和索引都赋值 if (arr[j] > temp) { arr[i] = arr[j]; i = j; } else { break; } } arr[i] = temp; //将temp值放到最终的位置 } function heapSort(data) { //构造大顶堆 //此时我们从最后一个非叶子结点开始,叶结点自然不用调整 ////从第一个非叶子结点从下至上,从右至左调整结构 for (var i = data.length / 2 - 1; i >= 0; i--) { adjustHeap(data, i, data.length); } // console.log(data); //交换堆顶元素与末尾元素;不算最后一个元素,重新调整堆 for (var k = data.length - 1; k > 0; k--) { //将堆顶元素与末尾元素进行交换 [data[0], data[k]] = [data[k], data[0]]; // console.log(data); //不算最后一个元素,重新对堆进行调整 adjustHeap(data, 0, k); } return data; }
详情→图解堆排序算法