前端面试题汇总

243 阅读4分钟

算法篇

排序

1、冒泡排序

1.原理:比较两个相邻的元素,将值大的元素交换到右边

2.思路:依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。

    (1)第一次比较:首先比较第一和第二个数,将小数放在前面,将大数放在后面。

    (2)比较第2和第3个数,将小数 放在前面,大数放在后面。

    ......

    (3)如此继续,知道比较到最后的两个数,将小数放在前面,大数放在后面,重复步骤,直至全部排序完成

    (4)在上面一趟比较完成后,最后一个数一定是数组中最大的一个数,所以在比较第二趟的时候,最后一个数是不参加比较的。

    (5)在第二趟比较完成后,倒数第二个数也一定是数组中倒数第二大数,所以在第三趟的比较中,最后两个数是不参与比较的。

    (6)依次类推,每一趟比较次数减少依次

const BubbleSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;
  const length = arr.length;
  for (let i = 0; i < length; i++) {
    for (let j = 1; j < length - i; j++) {
      if (arr[j] < arr[j - 1]) {
        const temp = arr[j];
        arr[j] = arr[j - 1];
        arr[j - 1] = temp;
      }
    }
  }
  return arr
}

2、快速排序

const QuickSort = (arr, low, high) => {
  let x = arr[low]
  let l = low
  let r = high
  while(l < r) {
    while(l < r && arr[r] > x) {
      r--
    }
    while(l < r && arr[l] <= x) {
      l++
    }

    if (l < r) {
      const temp = arr[l]
      arr[l] = arr[r]
      arr[r] = temp
    } else {
      const temp = arr[low]
      arr[low] = arr[l]
      arr[l] = temp
      const tmpArr = arr

      QuickSort(arr, low, l - 1)
      QuickSort(arr, r + 1, high)
    }
  }
}

关于不懂什么是快速排序的可以点击这里去看怎么去实现的。

3、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。(参考百度百科)

const SelectSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;
  const length = arr.length;
  for (let i = 0; i < length - 1; i++) {
    let minIndex = i;

    for (let j = i + 1; j < length; j++) {
      minIndex = arr[minIndex] > arr[j] ? j : minIndex;
    }

    const temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
  }

  return arr;
}

4、归并排序

归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。

  • 分解(Divide):将n个元素分成个含n/2个元素的子序列。
  • 解决(Conquer):用合并排序法对两个子序列递归的排序。
  • 合并(Combine):合并两个已排序的子序列已得到排序结果。

-----------------------------------------------------------------------------------------

const MergeSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  const middle = Math.floor(arr.length / 2);
  const left = arr.slice(0, middle);
  const right = arr.slice(middle);

  return merge(MergeSort(left), MergeSort(right));
}


const 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());
  if (left.length) result = [...result, ...left];

  // while (right.length) result.push(right.shift());
  if (right.length) result = [...result, ...right];

  return result;
}

5、堆排序

const HeapSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  const length = arr.length;
  const stap = Math.floor(length / 2) - 1;
  for (let i = stap; i >= 0; i--) {
    heapify(arr, i, length);
  }
  for (let i = length - 1; i >= 0; i--) {
    const temp = arr[0];
    arr[0] = arr[i];
    arr[i] = temp;

    //调整堆
    heapify(arr, 0, i);
  }
  return arr;
}


const heapify = (arr, i, size) => {
  const left = 2 * i + 1;
  const right = left + 1;
  let maxIndex = i;

  //先判断左节点还否超出
  if (left < size && arr[left] > arr[maxIndex]) maxIndex = left;

  //有节点是否超出 找出最大的子节点
  if (right < size && arr[right] > arr[maxIndex]) maxIndex = right;

  if (maxIndex !== i) {
    const temp = arr[i];
    arr[i] = arr[maxIndex];
    arr[maxIndex] = temp;

    //递归调整
    heapify(arr, maxIndex, size);
  }

  return arr
}

堆排序比较复杂,如果有不理解的可以点击这里

6、插入排序

const InsertionSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  for (let i = 1; i < arr.length; i++) {
    let before = i - 1;
    let after = i;
    
    while (arr[after] < arr[before] && before >= 0) {
      const temp = arr[after];
      arr[after] = arr[before];
      arr[before] = temp;
      const a = arr;
      before--;
      after--;
    }
  }

  return arr
}

7、希尔排序

const ShellSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  const length = arr.length;
  let gap = Math.floor(length / 2);
  while (gap) {
    for (let i = gap; i < length; i++) {
      let before = i - 1;
      let after = i;
      
      while (arr[after] < arr[before] && before >= 0) {
        const temp = arr[after];
        arr[after] = arr[before];
        arr[before] = temp;
        const a = arr;
        before--;
        after--;
      }
    }
    gap = Math.floor(gap / 2);
  }

  return arr;
}

8、基数排序

const RadixSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  const length = arr.length;

  // 找出最大值
  let max = arr[0];
  for (let i = 0; i < length; i++) {
    if (arr[i] > max) max = arr[i];
  }
  //找出最大位数
  let maxDigit = 0;
  while (max != 0) {
    max = parseInt(max / 10);
    maxDigit++;
  }

  let dev = 1, mod = 10, counter = [];
  for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
    for (let j = 0; j < length; j++) {
      let bucket = parseInt((arr[j] % mod) / dev);
      if (counter[bucket] == null) counter[bucket] = [];
      counter[bucket].push(arr[j]);
    }
    
    let pos = 0;
    for (let j = 0; j < counter.length; j++) {
      let value = null;
      if (counter[j] != null) {
        while ((value = counter[j].shift()) != null) {
          arr[pos++] = value;
        }
      }
    }
  }
  return arr;
}

9、计数排序

思想

  • 找出待排序的数组中最大和最小的元素。
  • 统计数组中每个值为 i 的元素出现的次数,存入新数组 countArr 的第 i 项。
  • 对所有的计数累加(从 countArr 中的第一个元素开始,每一项和前一项相加)。
  • 反向填充目标数组:将每个元素 i 放在新数组的第 countArr[i] 项,每放一个元素就将 countArr[i] 减去 1 。

关键在于理解最后反向填充时的操作。

使用条件

  • 只能用在数据范围不大的场景中,若数据范围 k 比要排序的数据 n 大很多,就不适合用计数排序。
  • 计数排序只能给非负整数排序,其他类型需要在不改变相对大小情况下,转换为非负整数。
  • 比如如果考试成绩精确到小数后一位,就需要将所有分数乘以 10,转换为整数

------------------------------------------------------------------------------------------------

const CountSort = (arr) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  const length = arr.length;
  let result = [], count = [], min = arr[0], max = arr[0];
  for (let i = 0; i < length; i++) {
    count[arr[i]] = count[arr[i]] ? count[arr[i]] + 1 : 1;
    min = min <= arr[i] ? min : arr[i];
    max = max >= arr[i] ? max : arr[i];
  }
  for (let i = min; i < max; i++) {
    count[i + 1] = (count[i + 1] || 0) + (count[i] || 0);
  }
  for (let i =length -1; i >= 0; i--) {
    result[count[arr[i]] - 1] = arr[i];
    count[arr[i]]--;
  }
  return result;
}

10、桶排序

const BucketSort = (arr, bucketSize) => {
  if (!(arr instanceof Array)) return 'Is not Array';
  if (arr.length < 2) return arr;

  let i;
  let minValue = arr[0];
  let maxValue = arr[0];
  for (i = 1; i < arr.length; i++) {
    if (arr[i] < minValue) {
      minValue = arr[i];                // 输入数据的最小值
    } else if (arr[i] > maxValue) {
      maxValue = arr[i];                // 输入数据的最大值
    }
  }

  //桶的初始化
  let DEFAULT_BUCKET_SIZE = 5;            // 设置桶的默认数量为5
  bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
  let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;  
  let buckets = new Array(bucketCount);
  for (i = 0; i < buckets.length; i++) {
    buckets[i] = [];
  }

  //利用映射函数将数据分配到各个桶中
  for (i = 0; i < arr.length; i++) {
    buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
  }

  arr.length = 0;
  for (i = 0; i < buckets.length; i++) {
    insertionSort(buckets[i]);                      // 对每个桶进行排序,这里使用了插入排序
    for (let j = 0; j < buckets[i].length; j++) {
      arr.push(buckets[i][j]);                      
    }
  }

  return arr;
}

查找

1、普通查找

2、带有哨兵的普通查找

3、二分查找

未完待续。。。