数据结构与算法-排序

92 阅读1分钟
// 交换数组元素位置
function swap(arr, i, j) {
  let temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

// 1。冒泡排序 O(N^2)
function bubbleSort(arr) {
  for (let j = arr.length - 1; j >= 0; j--) {
    for (let i = 0; i < j; i++) {
      if (arr[i] > arr[i + 1]) {
        swap(arr, i, i + 1);
      }
    }
  }
}

// 2.选择排序 O(N^2)
function selectSort(arr) {
  // 假设最小数的索引为0
  for (let j = 0; j < arr.length - 1; j++) {
    let min = j;
    for (let i = min + 1; i < arr.length; i++) {
      if (arr[i] < arr[min]) {
        // 更新最小值的索引
        min = i;
      }
    }
    swap(arr, min, j);
  }
}

// 3.插入排序 O(N^2)
function insertSort(arr) {
  for (let j = 1; j < arr.length; j++) {
    for (let i = j - 1; i >= 0 && arr[i] > arr[i + 1]; i--) {
      swap(arr, i, i + 1);
    }
  }
}

// 4.归并排序 O(NlogN) 额外空间复杂度O(N) 左右两边排好序,两侧指针依次进行比较,放进数组
function mergeSort(arr, l = 0, r = arr.length - 1) {
  // 将数组分成两部分并排序
  if (l == r) return arr[l];
  let mid = l + ((r - l) >> 1);
  mergeSort(arr, l, mid);
  mergeSort(arr, mid + 1, r);
  merge(arr, l, mid, r);
  function merge(arr, l, mid, r) {
    let helper = [];
    let i = 0;
    let p1 = l;
    let p2 = mid + 1;
    while (p1 <= mid && p2 <= r) {
      helper[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= mid) {
      helper[i++] = arr[p1++];
    }
    while (p2 <= mid) {
      helper[i++] = arr[p2++];
    }
    for (i = 0; i < helper.length; i++) {
      arr[l + i] = helper[i];
    }
  }
}

/*
  归并排序的扩展
*/
// 4.1.小和问题,一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和 [1,3,4,2,5] 1 + 1 + 3 + 1 + 1 + 3 + 4 + 2 = 16
function smallSum(arr) {
  if (arr === null || arr.length < 2) {
    return 0;
  }
  return process(arr, 0, arr.length - 1);
}
// arr[L...R]既要排好序,也要求小和
function process(arr, l, r) {
  if (l === r) {
    return 0;
  }
  let mid = l + ((r - l) >> 1);
  return (
    process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r)
  );
}
function merge(arr, l, m, r) {
  let helper = [];
  let i = 0;
  let p1 = l;
  let p2 = m + 1;
  let res = 0;
  while (p1 <= m && p2 <= r) {
    res += arr[p1] < arr[p2] ? (r-p2+1) * arr[p1] : 0
    helper[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
  }
  while (p1 <= m) {
    helper[i++] = arr[p1++];
  }
  while (p2 <= r) {
    helper[i++] = arr[p2++];
  }
  for (i = 0; i < helper.length; i++) {
    arr[l + i] = helper[i];
  }
  return res;
}
console.log(smallSum([1,3,4,2,5]));

// 4.2.逆序对问题 一个数组中,左边的数如果大于右边的数,则这个两个数构成一个逆序对

// 5.快速排序 O(NlogN) 额外空间复杂度O(logN)
function quickSort(arr, l, r) {
  if (l < r) {
    // 随机交换使得快排的时间复杂度从O(N^2) => O(NlogN)
    swap(arr, Math.floor(l+Math.random() * (r-l+1)), r)
    let p = partition(arr, l, r)
    quickSort(arr, l, p[0] - 1) // < 区域
    quickSort(arr, p[1] + 1, r) // > 区域
  }
  // 处理arr[l...r]
  // 默认以arr[r]做划分,arr[r] => p  <p  ==p   >p
  // 返回等于区域(左边界,右边界),所以返回一个长度为2的数组res, res[0], res[1]
  function partition(arr, l, r) {
    let less = l - 1 // <区右边界
    let more = r // >区左边界
    while (l < more) { // l表示当前数的位置 arr[r] => 划分值
      if (arr[l] < arr[r]) { // 当前数 < 划分值
        swap(arr, ++less, l++)
      } else if (arr[l] > arr[r]) { // 当前数 > 划分值
        swap(arr, --more, l)
      } else {
        l++
      }
    }
    swap(arr, more, r)
    return [less + 1, more]
  }
}


/**
 * Test
 * @param {随机数组长度} maxSize
 * @param {随机数组内容} maxValue
 * @returns
 */
// 生成随机数组
function generateRandomArray(maxSize, maxValue) {
  const lens = Math.floor(Math.random() * (maxSize + 1));
  let arr = new Array(lens);
  for (let i = 0; i < lens; i++) {
    arr[i] =
      Math.floor((maxValue + 1) * Math.random()) -
      Math.floor((maxValue + 1) * Math.random());
  }
  return arr;
}
// 复制数组
function copyArray(arr) {
  if (arr === null) return null;
  let res = new Array(arr.length);
  for (let i = 0; i < arr.length; i++) {
    res[i] = arr[i];
  }
  return res;
}
// 对数器 用来与原生排序进行对比,检验结果,检验函数fns,最好不要传太多
function compareArray(testTime, maxSize, maxValue, ...fns) {
  for (let j = 0; j < fns.length; j++) {
    for (let i = 0; i < testTime; i++) {
      // 使用原生排序方法进行排序
      let arr = generateRandomArray(maxSize, maxValue);
      // 复制数组供自定义方法进行排序
      let copyArr = copyArray(arr);
      arr.sort((a, b) => a - b);
      fns[j](copyArr);
      // 判断不同排序方法排序后的数组相等
      if (arr.toString() !== copyArr.toString()) {
        console.log(fns[j].name + "测试未通过!");
        return;
      }
    }
    console.log(fns[j].name + "测试通过!");
  }
}
compareArray(6, 1000, 100, insertSort, selectSort, bubbleSort, mergeSort);