算法学习

b 站教学视频:https://www.bilibili.com/video/BV1Ry4y1L7CR?p=2

1.认识复杂度和简单排序

常数

常数操作+ - * / >> << 数组的寻址:int a = arr[i] 都属于常数
非常数操作:链表的寻址:int b = list.get(i)

时间复杂度

选择排序: 从 0 的位置循环对比 将最小的放到 0 的位置, 然后从 1 的位置循环对比 将最小的放到 1 的位置,... 一直到第 N 项

function selectSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    let mianIndex = i;
    for (let j = i + 1; j < arr.length; j++) {
      mianIndex = arr[mianIndex] < arr[j] ? mianIndex : j;
    }
    swap(arr, i, mianIndex);
  }
  return arr;
}
function swap(arr, i, j) {
  let temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

时间复杂度 O(N^2)

首先估计 需要进行的常数操作
写出常数操作的公式

 看: N-1 + N-2 + N-3 ...
 比较:N-1 + N-2 + N-3 ...
 交换:N
= aN^2 + bN + c

取最复杂的情况就是aN^2,去掉常数系数a, 使用O()包上就是时间复杂度 O(N^2)

O(N)优与O(N^2)

分析一个算法的好坏、先对比时间复杂度指标、指标相同的情况对比实际运行时间(常数操作时间)

分类:

O():代表最差情况的常数操作

θ():代表平均情况

Ω():代表最好情况

冒泡排序:

从 0 的位置对比 1 的位置,如果 0 的位置>1 的位置,两个数做交换、继续从 1 的位置对比 2 的位置,...一直到第 N -1 项

function bubbleSort(arr) {
  for (let e = arr.length - 1; e > 0; e--) {
    for (let i = 0; i < e; i++) {
      if (arr[i] > arr[i + 1]) {
        swap(arr, i, i + 1);
      }
    }
  }
  return arr;
}
function swap(arr, i, j) {
  let temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

时间复杂度 O(N^2)

异或运算

性质
性质 1 0^a =a ; a^a=0;
性质 2 a^b === b^a; (ab)c === a(bc);
性质 3 同一组数异或,结果不受先后影响, 一堆数怎么先后异或结果都相同

使用异或实现 swap 方法(互换两个变量的值)

function swap(arr, i, j) {
  arr[i] = arr[i] ^ arr[i];
  arr[j] = arr[i] ^ arr[i];
  arr[i] = arr[i] ^ arr[i];
}

注意:这种方式操作的前提要保证 a 和 b 在为内存中两个独立的区域,如果 a 和 b 相同 a 和 b 都会变成 0,为了程序的严谨性不要使用这种方式进行两个变量的交换

实现原理:

int a=甲;
int b=乙;
a=a^b;    //a=甲^乙           b=乙
b=a^b;    //a=甲^乙           b=乙^甲^乙=0^甲=甲
a=a^b;    //a=甲^乙^甲=0^乙=乙 b=甲

面试题

给出一个数组,该数组全部为 int 型数据,
( 时间复杂度为 O(N),空间复杂度为 O(1) )
(1)数组中有一种数为奇数次,其他数为偶数次,求奇数次的数字

思路:把所有数异或 ,最后剩下的肯定是奇数次的数

(2)数组中有两种数为奇数次,其他数为偶数次,求奇数次的数字分别是什么
思路:
在整型数的 32 位中,他们必然有 1 位不一样,而这位就是它们异或
后结果为 1 的那位,我们通过这一位将整个数组分成两份,使其中一份
异或就可以得到,两个数其中一个数。再异或它俩的异或结果就可以得
到另一个数

// 有两个奇数次的变量 a 和 b
function diffTwo(arr) {
  let eor = 0;
  for (let item of arr) {
    eor ^= item;
  }
  // eor = a ^ b
  // eor != 0
  // eor 必然有一个位置为1
  let rightOne = eor & (~eor + 1); // 取出最右侧的1   比如:00000000001
  let onlyOne = 0;
  for (let item of arr) {
    // item  与 00000000001 & 出来的肯定 是最后一位为1的 否则是0
    if ((item & rightOne) == 1) {
      // onlyOne 不是 a 就是 b
      onlyOne ^= item;
    }
  }
  // a,b 或者 b,a
  return [onlyOne, eor ^ onlyOne];
}

插入排序

类似捋牌,从左边第 2 张牌开始,从右往左划到合适的位置插入进去

1-2 是否正确排序:1 的位置和 2 的位置对比是否需要换位,

  • 需要换位那就换,然后从 1 向左比较看是否需要换位(因为 1 左没有了所以进入 next)
    • 需要换位就换位继续向左比较
    • 否者 next
  • 否者 nex

2-3 是否正确排序:那从 2 对比 3 的位置,是否需要换位,

  • 需要换位那就换,然后从 2 向左比较看是否需要换位
    • 需要换位就换位继续向左比较
    • 否者 next
  • 否者 next

重复操作直到数组最后一位

// 原始代码
function insertSort(arr) {
  // 0 不用管,i负责将指针往右挪
  for (let i = 1; i < arr.length; i++) {
    // j负责将指针往左挪进行对比替换, 一旦i-1的位置大于i位置就替换,并且j-- 继续往左比
    for (let j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
      swap(arr, j, j + 1);
    }
  }
  return arr;
}
// 好理解的
function insertSort(arr) {
  // 0 不用管,i负责将指针往右挪
  for (let i = 1; i < arr.length; i++) {
    // j负责将指针往左挪进行对比替换, 一旦j - 1的位置大于j位置就替换,并且j-- 继续往左比
    for (let j = i; j > 0 && arr[j - 1] > arr[j]; j--) {
      swap(arr, j, j - 1);
    }
  }
  return arr;
}

时间复杂度 O(N^2),但在数比较顺的情况,性能优于 冒泡和选择

2 分法

在一个有序数组中看某个数是否存在,

正常遍历:时间复杂度 O(N)

2 分法: 时间复杂度 O(log2N)

从数组中知道中间位置的数字 与 目标数对比,

小于就在左侧数组中继续使用中间数对比

大于就在右侧数组中继续使用中间数对比

等于就直接返回

log2N 代表 N 的对数: N = 8 2^3=N log2N = 3 相当于只有 3 次常数操作 比 O(N) 更小

在一个有序数组中看某个数的最左侧的位置

2 分法:

使用中间位置对比目标数

大于或等于都往左找同时使用变量记录下标

小于就回到上回定位的下标的位置往右中间数继续对比

最后剩下两个数后取左边的

局部最小值

一个无序数组、相邻两数肯定不相等、求 局部最小值,也就是是 i - 1 , i , i + 1 这三个位置上的最小值

2 分法:直接用 2 分一直往左找一定能找到最小值

2 分法判断场景:存在左右两侧可以甩掉一边的场景就可以用 2 分

对数器

将已有成熟算法跟自己的算法进行 50 万次结果对比看是否都对

function randomArray(maxSize, maxValue) {
  let arr = Array.from({ length: parseInt((maxSize + 1) * Math.random()) });
  arr.map((item) => (maxValue + 1) * Math.random() - maxValue * Math.random());
  return arr;
}

function comparison() {
  let testTime = 50000;
  let maxSize = 100;
  let maxValue = 100;
  let flag = true;
  for (let i = 0; i < testTime; i++) {
    let arr1 = randomArray(maxSize, maxValue);
    let arr2 = arr1.slice();
    if (arr1.toString() !== arr2.toString()) {
      flag = false;
      break;
    }
  }
  if (flag) {
    console.log("comparison OK!");
  } else {
    console.log("comparison error!");
  }
}
posted @ 2021-10-11 13:34  __Bowen  阅读(32)  评论(0编辑  收藏  举报