前端——数据结构与算法之排序和查找算法

710 阅读3分钟

排序和查找

排序算法

排序算法没有优劣之分,在不同的场景中,不同的排序算法执行效率不同。

  1. 选择排序 Selection Sort

一次选择排序,可以将某个区间的最小值排列到该区域的第一位,具体的方式是:

  1. 找出该区域的最小值
  2. 将该值与该区域第一个值交换
  3. 对下一个区域重复上述过程,直到排序完成
var arr = [5,6,2,3,4,7,8,9,1,6,9];
function selectionSort(arr){
  for(let i = 0, len = arr.length; i < len - 1; i ++){
  //搞定 i ~ arr.length-1 区间
  //从该区间中找出最小值,和 第 i 位交换
    let min = arr[i]; //定义一个变量,为该区间的第一个数
    let index = i; //最小值所在的位置
    for(let j = i + 1 ; j < len; j ++){
      if(arr[j] < min){ //循环找到该区域的最小值
        index = j;
        min = arr[j]; 
      }
    }
    [arr[i],arr[index]] = [arr[index],arr[i]]; //交换
  }
}
selectionSort(arr);
  1. 冒泡排序 Bubble Sort (最常见的排序啦)

一次冒泡排序,可以将某个区域序列的最大值排序到该区域的最后一位,具体的方式是:

  1. 将第1位和第2位比较,如果前者比后者大则交换
  2. 将第2位和第3位比较,如果前者比后者大则交换
  3. 依次类推,直到比较到该区域的最后两位
  4. 重复上述过程,直到序列排序完成
var arr = [5,6,2,3,4,7,8,9,1,6,9];
function bubbleSort(arr){
  for(let i = 0,len = arr.length; i < len - 1; i ++){
    //需要经过arr.length-1次的冒泡
    //i:0   循环:0~arr.length-1-i
    //i:1   循环:0~arr.length-1-i
    //i:2   循环: 0~arr.length-1-i
    for(let j = 0; j < len - 1 - i; j ++){
      if(arr[j] > arr[j + 1]){ //如果前者比后者大则交换
        [arr[j],arr[j + 1]] = [arr[j + 1],arr[j]];
      }
    }
  }
}
bubbleSort(arr);
  1. 插入排序 Insertion Sort

将序列分为两个部分,一部分是有序的,一部分是无序的,现在要做的是,就是不断的从无序的部分取出数据,加入到有序的部分,直到整个排序完成

例如:序列[5, 7, 2, 3, 6]

  1. 分为有序的序列和无序的序列 (5) (7 2 3 6)
  2. 不断的扩充有序序列 (5 7) (2 3 6)
  3. 不断的扩充有序序列 (2 5 7) (3 6)
  4. 不断的扩充有序序列 (2 3 5 7) (6)
  5. 不断的扩充有序序列 (2 3 5 6 7)
  6. 排序完成
function insertionSort(arr) {
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] < arr[i - 1]) {
            //将第i位的值加入到前面有序队列的正确位置
            var temp = arr[i];
            for (var j = i; j >= 0; j--) {
                if (j > 0 && arr[j - 1] > temp) {
                    arr[j] = arr[j - 1];
                }
                else {
                    arr[j] = temp;
                    break;
                }
            }
        }
    }
  1. 快速排序 Quick Sort

选择一个数(比如序列的最后一位)作为基准数,将整个序列排序成两部分,一部分比该数小,另一部分比该数大,基准数在中间,然后对剩余的序列做同样的事情,直到排序完成

例如:序列[5, 7, 2, 3, 6, 4]

  1. 选择4作为基准数,排序成为:(3, 2) 4 (7, 6, 5)
  2. 对于3,2, 继续使用该方式排序,得到: (2, 3) 4 (7,6,5)
  3. 对于7,6,5,继续使用该方式排序,得到: (2, 3) 4 (5,6,7)
  4. 排序完成
function quickSort(arr) {
  function _quickSort(arr, start, end) {
      /**
     * 对数组的某个区域进行一次快速排序
     * @param {*} arr 
     * @param {*} start 区域的起始下标
     * @param {*} end 区域的结束下标
     */
    if (start >= end || start >= arr.length - 1) return;
    let low = start,
      high = end;
    let key = arr[end];
    while (low < high) {
      while (low < high && arr[low] <= key) low++;
      arr[high] = arr[low];
      while (low < high && arr[high] >= key) high--;
      arr[low] = arr[high];
    }
    arr[high] = key;
    _quickSort(arr, 0, high - 1);
    _quickSort(arr, high + 1, end);
  }
  return _quickSort(arr, 0, arr.length - 1);
}

查找算法

  1. 顺序查找 Inorder Search

即普通的遍历

function inOrderSearch(arr, target) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return true;
        }
    }
    return false;
}

  1. 二分查找 Binary Search

如果一个序列是一个排序好的序列,则使用二分查找可以极大的缩短查找时间

具体的做法是:

查找该序列中间未知的数据

  1. 相等,找到
  2. 要找的数据较大,则对后续部分的数据做同样的步骤
  3. 要找的数据较小,则对前面部分的数据做同样的步骤
function binarySearch(arr, target) {
    if (arr.length === 0 || target < arr[0] || target > arr[arr.length - 1]) return false;

    var minIndex = 0; //最小下标
    var maxIndex = arr.length - 1; //最大下标
    while (minIndex <= maxIndex) {
        var mid = Math.floor((minIndex + maxIndex) / 2);//中间下标
        if (arr[mid] === target) {
            return true;
        }
        else if (arr[mid] > target) {
            maxIndex = mid - 1;
        }
        else {
            minIndex = mid + 1;
        }
    }
    return false;
}
  1. 插值查找 Interpolation Search

插值查找是对二分查找的进一步改进

如果序列不仅是一个排序好的序列,而且序列的步长大致相同,使用插值查找会更快的找到目标。

插值查找基于如下假设:下标之间的距离比和数据之间的距离比大致相同,即:

(目标下标-最小下标) / (最大下标 - 最小下标) ≈ (目标值 - 最小值) / (最大值 - 最小值)

因此可以算出大致的下标落点:

目标下标 ≈ (目标值 - 最小值) / (最大值 - 最小值) * (最大下标 - 最小下标) + 最小下标

这样就可以计算出大致的下标落点,后续的比较和二分查找一样。

function interpolationSearch(arr, target) {
    if (arr.length === 0 || target < arr[0] || target > arr[arr.length - 1]) return false;
    var minIndex = 0; //最小下标
    var maxIndex = arr.length - 1; //最大下标
    while (minIndex <= maxIndex) {
        var mid = (target - arr[minIndex]) / (arr[maxIndex] - arr[minIndex]) * (maxIndex - minIndex) + minIndex;
        if (arr[mid] === target) {
            return true;
        }
        else if (arr[mid] > target) {
            maxIndex = mid - 1;
        }
        else {
            minIndex = mid + 1;
        }
    }
    return false;
}

Over O(∩_∩)O