为面试作一些算法相关的准备~~
一、时间复杂度
通常使用最差的时间复杂度来衡量一个算法的好坏。
常数时间 O(1) 代表这个操作和数据量没关系,是一个固定时间的操作,比如说四则运算。
对于一个算法来说,可能会计算出如下操作次数 aN + 1,N 代表数据量。那么该算法的时间复杂度就是 O(N)。因为我们在计算时间复杂度的时候,数据量通常是非常大的,这时候低阶项和常数项可以忽略不计。
当然可能会出现两个算法都是 O(N) 的时间复杂度,那么对比两个算法的好坏就要通过对比低阶项和常数项了。
二、位运算
十进制和二进制之间的转换
位运算在算法中很有用,速度可以比四则运算快很多。
十进制33可以看成是32 + 1, 32为2^5,1为2^0。所以就是 100001.
那么二进制 100001同理,首位是 2^5 ,末位是2^0,相加得出 33
左移 <<
10 << 1
// 20
左移就是将二进制全部往左移动,10 在二进制中表示为 1010 ,左移一位后变成 10100 ,转换为十进制也就是 20,所以基本可以把左移看成以下公式a * (2 ^ b)
左移 <<
10 >> 1
// 5
算数右移就是将二进制全部往右移动并去除多余的右边,10 在二进制中表示为 1010 ,右移一位后变成 101 ,转换为十进制也就是 5,所以基本可以把右移看成以下公式int v = a / (2 ^ b)
- 右移的一个用处就是在二分法的时候,计算中间值
13 >> 1 // -> 6
三、排序
两通用函数
function checkArray(arr) {
if(!arr | arr.length <= 2) {
return false
}
return true
}
// 交换数组中的两个值
function swap(array, left, right) {
let rightValue = array[right]
array[right] = array[left]
array[left] = rightValue
}
1、冒泡排序
冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到 length - 1 的位置。
// 冒泡排序
function sort_1 (arr) {
// 外循环决定比较几轮次
for(var i = arr.length-1; i > 0; i--) {
// 内循环决定每一轮最少比较几次
for(var j = 0; j < i; j ++) {
if(arr[j] > arr[j+1]) {
swap(arr, j, j+1);
}
}
}
}
/**
共有 5 元素,
一共比4次。
第一轮 比较 4次
第二轮 3次
第三轮 2次
第四轮 1次
最后一轮 0次
**/
- 冒泡就是,每次循环将该循环中的较大元素往后面放,小的往前面放。O(n*n)
2、插入排序
插入排序原理: 将数组分为两部分,前一部分是已经排好的序列,之后是未排序的序列,我们每次从未排序的序列中拿第一个,根前面已经排好的序列中从头进行比较,找到合适的位置,进行放置。我们最初假设第一次的时候,第一个是排好的。
// 插入排序
function sort_2(arr) {
// 对除第一个元素之外的元素进行向前插入
for(var i = 1 ; i < arr.length; i++){
// 取到未排序的序列的第一个元素,往左侧进行插入。
for(var j = 0; j < i ; j++) {
if(arr[j] > arr[i]) {
swap(arr, j, i);
}
}
}
}
3、选择排序
选择排序原理:每次从待排序的序列中找到最小的放在前面
// 每次从待排序的序列中找到最小的放在前面
function sort_3(arr) {
// 一共需要确定几次最小值 n-1次
for(var i = 0 ; i < arr.length-1 ; i++) {
// 从已经排好的之后开始,找到最小值,放入该序列的最开始,
for(var j = i + 1; j < arr.length; j++) {
if(arr[i] > arr[j]) {
swap(arr, i, j);
}
}
}
}
/**
3 89 72 43 1
3之后的序列中找到比3还小的,然后替换3的位置
1 89 72 43 3
89之后的序列中找比89还小的,然后替换89的位置
1 3 72 43 89
...
**/