New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript数据结构及算法——排序 #12

Open
axuebin opened this Issue Oct 21, 2017 · 0 comments

Comments

Projects
None yet
1 participant
@axuebin
Copy link
Owner

axuebin commented Oct 21, 2017

本文主要记录的是JavaScript实现常用的排序算法,冒泡排序、快速排序、归并排序等。


前言

用JavaScript写算法是种怎么样的体验?不喜欢算法的我最近也对数据结构和算法有点兴趣。。。所以,将会有这些:

现阶段我对于数据结构、算法的理解还很浅,希望各位大佬多多指导。

排序

介绍排序算法

冒泡排序

说到冒泡排序,大家都很熟悉,顾名思义,是一种“冒泡”的过程。

主要思想:比较任何两个相邻的项,如果第一个比第二个大,则交换它们。

时间复杂度:O(n2)

空间复杂度:O(1)

如何实现呢?是不是遍历所有需要排序的数据,然后将它和所有数比较一次,然后就可以了?

道理是有的,我们试试看:

function bubbleSort(arr) {
  const len = arr.length; // 声明一个len来存储数组的长度
  let temp = 0;
  for (let i = 0; i < len; i += 1) { // 外循环遍历数组
    for (let j = 0 ; j < len - 1 ; j += 1) { // 内循环执行当前项和下一项进行比较
      if (arr[j] > arr[j + 1]) {  // 如果当前项比下一项大,则交换它们
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
      console.log(arr);
    }
  }
  return arr;
}

我们通过输出数组来看一下整个流程:

[5, 4, 3, 2, 1] 
[4, 5, 3, 2, 1] // 5>4,交换
[4, 3, 5, 2, 1] // 5>3,交换
[4, 3, 2, 5, 1] // 5>2,交换
[4, 3, 2, 1, 5] // 5>1,交换
[3, 4, 2, 1, 5] // 4>3,交换
[3, 2, 4, 1, 5] // 4>2,交换
[3, 2, 1, 4, 5] // 4>1,交换
[3, 2, 1, 4, 5] // 4<5,不交换
[2, 3, 1, 4, 5] // 3>2,交换
[2, 1, 3, 4, 5] // 3>1,交换
[2, 1, 3, 4, 5] // 3<4,不交换
[2, 1, 3, 4, 5] // 4<5,不交换
[1, 2, 3, 4, 5] // 2>1,交换
[1, 2, 3, 4, 5] // 2<3,不交换
[1, 2, 3, 4, 5] // 3<4,不交换
[1, 2, 3, 4, 5] // 4<5,不交换
[1, 2, 3, 4, 5] // 1<2,不交换
[1, 2, 3, 4, 5] // 2<3,不交换
[1, 2, 3, 4, 5] // 3<4,不交换
[1, 2, 3, 4, 5] // 4<5,不交换

排序确实是排好了,但是我们发现,有很多的不必要的比较,我们应该想办法避免这些。想一想,这些都是在内循环中对已经排序过的数进行比较,所以我们可以稍稍改进一下代码:

function bubbleSort(arr) {
  const len = arr.length;
  let temp = 0;
  for (let i = 0; i < len; i += 1) {
    for (let j = 0 ; j < len - 1 - i ; j += 1) {
      if (arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
      console.log(arr);
    }
  }
  return arr;
}

在内循环中,我们另 j 的取值到 len-1-i 为止,因为再往后的数已经排序好了。同样地,我们来看看流程:

[5, 4, 3, 2, 1] 
[4, 5, 3, 2, 1] // 5>4,交换
[4, 3, 5, 2, 1] // 5>3,交换
[4, 3, 2, 5, 1] // 5>2,交换
[4, 3, 2, 1, 5] // 5>1,交换
[3, 4, 2, 1, 5] // 4>3,交换
[3, 2, 4, 1, 5] // 4>2,交换
[3, 2, 1, 4, 5] // 4>1,交换
[2, 3, 1, 4, 5] // 3>2,交换
[2, 1, 3, 4, 5] // 3>1,交换
[1, 2, 3, 4, 5] // 2>1,交换

nice,没必要的比较已经完全没有了。

选择排序

主要思想:找到数组中的最小值然后将其放置在第一位,接着第二位第三位。。。

时间复杂度:O(n2)

空间复杂度:O(1)

直接看代码吧:

function selectionSort(arr) {
  const len = arr.length; // 用len存储数组长度
  let indexMin = 0; // 最小值索引
  let temp = 0;
  for (let i = 0; i < len - 1; i += 1) { //外循环遍历数组
    indexMin = i; // 先假设这一轮循环的第一个值是最小的
    for (let j = i; j < len; j += 1) { // 比较i时候会比它之后的数小,如果小,则令indexMin存储这个更小值的索引
       if (arr[indexMin] > arr[j]) {
        indexMin = j;
      }
    }
    if (i !== indexMin) { // 执行完内循环之后判断当前值i是否是最小的,如果不是,就要交换
      temp = arr[i];
      arr[i] = arr[indexMin];
      arr[indexMin] = temp;
    }
    console.log(arr);
  }
  return arr;
}
[5, 4, 3, 2, 1] 
[1, 4, 3, 2, 5] // 寻找最小值1,交换1和5
[1, 2, 3, 4, 5] // 寻找最小值2,交换2和4
[1, 2, 3, 4, 5] // 寻找最小值3,不交换
[1, 2, 3, 4, 5] // 寻找最小值4,不交换
[1, 2, 3, 4, 5] // 寻找最小值5,不交换

是不是很酷,然而它的时间复杂度其实还是 O(n2)

插入排序

主要思想:每次将一个元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。

时间复杂度:O(n2)

空间复杂度:O(1)

直接看代码吧:

function insertionSort(arr) {
  const len = arr.length; // 数组长度
  let j = 0; // 使用的辅助变量
  let temp = 0;
  for (let i = 1; i < len; i++) { // 外循环,从1开始
    j = i; // 当前索引赋给j
    temp = arr[i]; // 当前值存在temp
    while (j > 0 && arr[j - 1] > temp) { // 如果j前面的数比它大,就往前移,直到第一位
      arry[j] = arr[j - 1];
      j--;
    }
    arr[j] = temp; // temp是要排序的那个数,放到正确的j的位置上
  }
  return arr;
}

归并排序

主要思想:思想主要是分治。将原始数组划分成较小的数组,直到每个小数组只有一个位置,然后将小数组归并成较大的数组。

时间复杂度:O(nlogn)

空间复杂度:O(n)

直接看代码吧:

//
function mergeSort(arr) {
  const len = arr.length;
  if (len === 1) {
    return arr;
  }
  const mid = Math.floor(len / 2);
  const left = arr.slice(0, mid);
  const right = arr.slice(mid, len);
  return merge(mergeSort(left), mergeSort(right));
}
 
//
function merge(left, right) {
  const result = [];
  let il = 0;
  let ir = 0;
  while (il < left.length && ir < right.length) {
    if (left[il] < right[ir]) {
      result.push(left[il++]);
    } else {
      result.push(right[ir++]);
    }
  }
  while (il < left.length) {
    result.push(left[il++]);
  }
  while (ir < right.length) {
    result.push(right[ir++]);
  }
  return result;
}

快速排序

来看看面试中最喜欢考察的快速排序。

主要思想:每次将一个元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。

时间复杂度:O(nlogn)

空间复杂度:O(logn)

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const pivotIndex = Math.floor(arr.length / 2);
  const pivot = arr.splice(pivotIndex, 1)[0]; // 将这个元素取出并从原数组中删除
  const left = [];
  const 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));
}

@axuebin axuebin changed the title 用JavaScript实现常见的排序算法 JavaScript数据结构及算法——排序 Oct 22, 2017

@axuebin axuebin added blog and removed writing labels Oct 30, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment