冒泡排序
var bubbleSort = function (array) {
for (let i = 0; i < array.length; i++) {
for (let j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
let tmp = array[j];
array[j] = array[j + 1]
array[j + 1] = tmp;
}
}
}
return array;
}
//改进1 记录最后一次交换的位置
var bubbleSort1 = function (array) {
let i = array.length - 1;
while (i > 0) {
let pos = 0;
for (let j = 0; j < i; j++) { //最后一次交换的位置加一 之后有序 减少趟数
if (array[j] > array[j + 1]) {
pos = j;
let tmp = array[j];
array[j] = array[j + 1]
array[j + 1] = tmp;
}
}
i = pos;
}
return array
}
//改进2 正反两个方向冒泡 减少趟数
var bubbleSort2 = function (array) {
let low = 0;
let high = array.length - 1;
while (low < high) {
for (let j = low; j < high; j++) {
console.log(j)
if (array[j] > array[j + 1]) {
let tmp = array[j];
array[j] = array[j + 1]
array[j + 1] = tmp;
}
}
--high; //一趟之后找到最大值 high前移一位
for (let j = high; j > low; j--) {
if (array[j] < array[j - 1]) {
let tmp = array[j];
array[j] = array[j - 1]
array[j - 1] = tmp;
}
}
++low; //一趟之后找到最小值 low后移一位
}
return array
}
var arr = [2, 4, 5, 1, 7, 3]
var bubble = bubbleSort(arr)
console.log(bubble)
算法分析
交换排序 稳定
最佳情况:输入的数组已经是正序了 T(n) = O(n)
最坏情况:输入的数组是反序 T(n) = O(n2)
平均情况: T(n) = O(n2)
选择排序
var selectionSort = function (array) {
let len = array.length;
for (let i = 0; i < len - 1; i++) { //n-1趟
let minindex = i; //该趟找到的最小值应该放置的位置
for (let j = i + 1; j < len; j++) {
if (array[j] < array[minindex]) { //找到最小值的索引
minindex = j
}
}
let tmp = array[i] //交换
array[i] = array[minindex]
array[minindex] = tmp;
}
return array;
}
算法分析
选择排序 不稳定
最佳情况:T(n) = O(n2)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
时间复杂度与输入序列无关
插入排序
var insertionSort = function (array) {
let len = array.length;
for (let i = 1; i < len; i++) { //从下标1开始
let key = array[i]; //记录下一个插入的值
let j = i - 1; //和有序部分的最后一个比较
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j]; //交换
j--;
}
array[j + 1] = key; //空位j+1 就是插入位置
}
return array;
}
//改进 二分法 在有序的部分二分查找
var insertionSort = function (array) {
let len = array.length;
for (let i = 1; i < len; i++) {
let left = 0;
let right = i - 1;
let key = array[i]
while (left <= right) { //有等号
let middle = parseInt((left + right) / 2) //取整数部分
if (key < array[middle]) { //当进行到中间部分 没有等号,否则不稳定
right = middle - 1;
}
else {
left = middle + 1;
}
}
for (let j = i - 1; j <= left; j++) { //从left开始所有元素后移一位 统一移动
array[j + 1] = array[j]
}
array[left] = key; //left就是元素插入的位置
}
return array;
}
算法分析
插入排序 稳定
最佳情况:T(N) = O(n)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
希尔排序
var shellSort = function (array) {
let len = array.length;
let gap = Math.floor(len / 2);
//动态定义间隔??? 这个没搞懂
while (gap < len / 5) {
gap = gap * 5 + 1;
}
for (gap; gap > 0; gap = Math.floor(len / 2)) { //直到gap=1
for (let i = gap; i < len; i++) { // 从第一个gap开始 相当于插入排序
let j = i - gap; //向前找到和i一组的
let key = array[i] //标记一下下标为i的值
while (j >= 0 && array[j] > key) { //如果前边的那个大
array[j + gap] = array[j]; //就往后挪一个
j = j - gap; //找到在前面一个 和i一组的那个元素
}
array[j + gap] = key;
}
}
return array
}
算法分析
不稳定 (相同的元素不在一组,可能被排到前边)
最佳情况:玄学
最坏情况:
平均情况:
归并排序
//分支的思想
var mergeSort = function (array) {
if (array.length < 2) { //最底层的情况
return arr
}
let middle = Math.floor(array.length / 2);
let left = array.slice(0, middle)
let right = array.slice(middle, array.length)
return merge(mergeSort(left), mergeSort(right))
}
function merge (left, right) {
let 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
}
算法分析
归并排序 稳定 分治的思想
最佳情况:T(n) = O(nlogn)
最坏情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)
归并排序的时间复杂度和选择排序一样,不受输入数组序列影响
时间复杂度降低,代价是需要额外的空间,最大需要O(n)的空间。
快速排序
//方法1
var quickSort = function (array, left, right) {
if (left < right) {
let pivot = array[right]; //基准为最后一个数
let i = left - 1; //i指向left之前
for (let j = left; j <= right; j++) { //从left开始遍历到right
if (array[j] <= pivot) { //当遍历到的数小于等于pivot时
i++; //挪到i的位置 把这个数和array[i]交换 这样i的位置上的数是上一次找到的比pivot小的数
let temp = array[j]; //最后j=right时,满足array[j]=pivot 就把pivot的位置找到了,放在i的位置上
array[j] = array[i]; //此时iz左面的数都比pivot小,i右边的数都比pivot大
array[i] = temp;
}
}
quickSort(array, left, i - 1) //再对左边和右边分别快排
quickSort(array, i + 1, right)
}
return array;
}
//方法2
var quickSort = function (array) {
if (array.length < 2) { //出口
return array
}
let pivotindex = Math.floor(array.length / 2)
let pivot = array.splice(pivotindex, 1)[0]; //把基准从数组里面取出来
let left = [];
let right = [];
for (let i = 0; i < array.length; i++) { //遍历每一个元素 把小的放left,大的放右边right
if (array[i] < pivot) {
left.push(array[i])
}
else {
right.push(array[i])
}
}
return quickSort(left).concat([pivot], quickSort(right)) //左 pivot 右相连
}
算法分析
不稳定
最佳情况:T(n) = O(nlogn) 分成logn组
最坏情况:T(n) = O(n2) 数组已经有序 分成n组
平均情况:T(n) = O(nlogn)
堆排序
var heapSort = function (array) {
var heapsize = array.length;
//建堆
for (let i = Math.floor(heapsize / 2) - 1; i >= 0; i--) { //从第一个非叶子节点开始
heapify(array, i, heapsize);
}
for (let j = heapsize - 1; j >= 0; j--) {
let temp = array[0]
array[0] = array[j]
array[j] = temp;
heapify(array, 0, --heapsize) //从顶部开始维护堆的性质 同时堆的大小自减一
}
return array;
}
function heapify (array, x, len) {
let l = 2 * x + 1, r = 2 * x + 2, largest = x, temp; //找到左右孩子
if (l < len && array[l] > array[largest]) { //将较大的孩子设为largest
largest = l;
}
if (r < len && array[r] > array[l]) {
largest = r
}
if (x != largest) { //如果找到了更大的 ,交换
temp = array[x];
array[x] = array[largest];
array[largest] = temp;
heapify(arr, largest, len) //交换后继续向下维护 如果进行到了叶子节点,就维护完了,什么也不会做
}
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(heapSort(arr))
算法分析
不稳定
最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)