“这是我参与更文挑战的第3天,活动详情查看: 更文挑战”
theme: 算法时间复杂度以及排序
#什么是时间复杂度? 一个算法流程中 , 常熟操作数量的指标, 这个指标叫做 O, 如果常熟的操作数量表达式中只要高阶项,不要低阶项, 也不要高阶项的系数,剩下的部分记为(fn), 那么时间复杂度就是O(fn)
##例题1: 如何在一个数组[1,2,3,4,5,6,7,8,9]中寻找某一项呢? 一种最简单的方法是循环:
let arr = [1,2,3,4,5,6,7,8,9]
function findNumber(arr, findOption) {
for(let i=1; i< arr.length; i++){
if(arr[i]==findOption){
return i
}
}
}
let findOption = arr[8]
console.log(111111, findNumber(arr, findOption))// 耗费的时间是2.572s
另一种方法是采用的二分法: 注意因为这是一个有序的数组, 所以我们可以先分一半, 然后比较我们要寻找的数字是是在左边的数组还是右边的数组,如果在左边将左边的数组一分为二, 然后继续划分。
let numberIndex= 0
function findNumber(arr,L, R, number){
let mid = Math.floor((L+R)/2)
if(arr[mid]>number){
findNumber(arr,L,mid , number)
}else if(arr[mid]< number){
findNumber(arr,mid+1 ,R, number)
}else{
numberIndex = mid
return mid
}
}
findNumber(arr,0, arr.length-1, 9)
console.log(1111222,numberIndex)//耗时是0.946s
第一种方法的时间复杂度是O(n), 第二种方法的时间复杂度是 O(logN)可以看出对于一个有序数组数量比较多的时候,二分法确实比较优秀一点。 ##例题2: 两个 有序数组, 如何找到其中的相同部分呢 ?let arr1 = [1,2,3,6], let arr2 = [2,3,6,7] 第一种方法是直接两个数组分别循环找到其中的相同部分。 时间复杂度是 O(n*m)
let arr1 = [1,2,3,6]; let arr2 = [2,3,6,7]
function findSame (arr1,arr2) {
let arr = []
for(let i = 0; i< arr1.length; i++){
for(let j = 0 ; j< arr2.length; j++){
if(arr1[i] ==arr2[j ]){
arr.push(arr1[i])
}
}
}
return arr
}
console.log(3333, findSame(arr1,arr2)) //耗时是 1.354s
第二种方法是: 对arr1数组进行循环, 然后在arr2中用二分去查找, 时间复杂度是O(n*logm)
let arr1 = [1,2,3,6]; let arr2 = [2,3,6,7]
function findSame (arr1, arr2){
let finaArr = []
function findNumber(arr,L, R, number){
let mid = Math.floor((L+R)/2)
if(arr[mid]>number){
if(L==mid) return
findNumber(arr,L,mid , number)
}else if(arr[mid]< number){
if(mid+1==R)return
findNumber(arr,mid+1 ,R, number)
}else if(arr[mid] = number){
finaArr.push(arr[mid])
return
}
}
for(let i = 0 ; i< arr1.length; i++){
let number = arr1[i]
let L = 0,R = arr2.length-1;
findNumber(arr2,L, R, number)
}
return finaArr
}
console.log(3333, findSame(arr1,arr2)) //1.106s
第三种方法是 , 如果左边数组比右边的数组小的话 就将左边的数组进行移动 , 如果左边比右边大的话 , 就将右边的数组进行移动 , 如果两个相等的话就共同移动。其中的时间复杂度是O(n+m); 加上continue之后速度快了很多。
let arr1 = [1,2,3,6]; let arr2 = [2,3,6,7]
function findSame (arr1,arr2) {
let arr = []
for(let i = 0, j=0; i< arr1.length&&j<arr2.length;){
if(arr1[i]< arr2[j]){
i++
continue
}else if(arr1[i]==arr2[j]){
arr.push(arr1[i])
i++;
j++
continue
}else {
j++;
continue
}
}
return arr
}
console.log(3333, findSame(arr1,arr2)) //0.952s
介绍完时间复杂度之后我们来看看几种排序方法。 ##冒泡排序:
所谓的冒泡排序就是先将最后一位固定最大的或者是最小的,然后取固定到倒数第二位中去固定[0,length-2]中的最大值或者是最小值然后依次类推。例如我们现在有一个数组是[6,5,1,2,3,4, 7], 我们如果对他进行使用冒泡从小到大开始排序的话应该怎么做呢? 首先排从第一个数字开始排序, 如果当前数字比下一个数字小或者相等的话,那么指针后移,在继续比较下一位,如果是当前数字大于下一位的话就会发生交换,然后指针向后移动。直到最大数字成为最后一位。 整个的时间复杂度是n-1+ n-2+...1
let arr = [6,5,1,2,3,4, 7]
function sortBubble(arr1) {
const arr = JSON.parse(JSON.stringify(arr1))
for(let count = arr.length-1; count>1; count--){
for(let i = 0; i<count; ){
if(arr[i]<= arr[i+1]){
i++;
continue
}else{
let t = arr[i]
arr[i] = arr[i+1];
arr[i+1] = t;
i++
continue
}
}
}
return arr
}
console.log(2222222, sortBubble(arr))
下面介绍一下对数器,所谓的对数器就用你所知道的简单的算法去验证下当前缩写算法的正确性。 我们平时写的算法可以用对数器来验证下 ##插入算法 插入算法的精髓是,外部循环保证从1 的时候, 左边的数组是有序的, 如果是左边的数组没有顺序的,就内部循环发生交换, 比如说我们现在有一个数组是let arr = [8,6,5,2,4,9],我们要保证他从小到大进行排序 外部循环从1开始, 左边的数组是 [8, 6], 没有顺序,左边数组循环交换为[6,8], 然后外部循环的左边就变成了[6,8,5], 然后还是内部循环进行调序, 以此类推,最后的时间复杂度是2+3+4+...n-1
let arr = [8,6,5,2,4,9]
function insertSort(arr) {
for(let i = 1; i< arr.length; i++){
for(let j= i-1; j>=0; j--){
if(arr[j]>arr[j+1]){// 两个数字比较把大的放到右边去
let t = arr[j]
arr[j] = arr[j+1];
arr[j+1] = t
}
}
}
return arr
}
console.log(3333, insertSort(arr))