常见题

115 阅读6分钟

算法

是指解决问题的方案准确而完整的描述,是解决一系列解决问题的清晰指令,算法代表着:用系统的方法解决问题的策略机制

是解决问题的方法和步骤

我们用不同的方法解决相同的问题,算法的成本是不同的.总体上来讲,一个优秀的算法去追求两个目标

1.在程序中,占用最少的内存空间区完成需求
⒉.花最少的时间去完成需求
结论:我们的算法研究的如何花最少的时间和占用最少的内存空间去完成相同的需求

算法是一种解决问题的方法。我们需要将:时间占用和空间占用量化。有关算法的时间耗费分析,我们称为算法的时间复杂度分析,算法空间的耗费分析,我们称为空间的复杂度分析

1. 时间维度:是指执行当前算法所消耗的时间,时间越短,算法越好
        算法的时间复杂度说的是:'一个算法的执行时间根据数据规模增长的一个趋势'
        40亿的数据要求搜索,线性查找法:最坏的情况,40亿条数据,40亿次操作二分查找: 32次操作就可以完成
        算法的时间复杂度:即算法的时间度量
        T(n) = o(f(n))//大o表示法
        O后面的括号,括号中是一个函数。指明某个算法耗时与数据增长量之间的关系。
        n:代表的是输入数据的量
        T:代表的是算法需要执行的总时间
        大O表示法:表示代码执行时间随数据规模增长的变化趋势,'时间复杂度表示的是变化趋势',并不是真正的执行时间。|
    分析算法时间复杂度
        1.算法完成时间最少需要的基本操作,最优的时间复杂度
        2.算法完成时间最多需要的基本操作,最坏的时间复杂度
        3.算法完成时间平均需要的基本操作,平均时间复杂度
        对于最优的时间复杂度意义不大,因为没有提供有用的信息,他反应最乐观最理想的情况,没有提供参考价值
        '最坏的时间复杂度',提供了一种保证,表明算法在程序的基本操作当中一定可以完成需求
        平均时间复杂度,是对算法的一个比较全面的评价,不是每一个计算都能够在这个基本操作中完成
        '常见的时间复杂度'
            1.常数阶:O(1),T(n)=O(1),表示无论n的规模如何增长,它的执行时间基本不变,常数阶是最低的时间复杂度
            2.线性阶:O(n),T(n)=O(n),代表数据量增大几倍,耗时也会增大几倍,常见的算法:遍历
            3.平方阶:o(n^2),T(n)=O(n^2),代表的是数据量增大n倍时,耗时增大n的平方倍,两层循环
            4.立方阶:o(n^3),T(n)=O(n^3),代表的是数据量增大n倍时,耗时增大n的立方倍,三层循环
            5.对数阶:O(logN),T(n)=O(logN),当数据增大n倍时,耗时增大logN倍,二分查找,n以倍数的规模递减 n=>n/2=>n/4
            6.线性对数阶:O(logN),T(n)=O(nlogN),就是n*logN,当数据增大n倍时,耗时增大n*logN倍
            7.指数阶:O(2^N)当数据增大n的倍数时候,耗时增大2^n倍
        '计算时间复杂度'
            a.基本操作,及只有常数项,认为时间复杂度就是O(1)
            b.顺序结构,时间复杂度按加法
            c.循环结构,时间复杂度按乘法
            d.分支结构,时间复杂度取最大值。
            判断一个算法的效率的时候,往往只需要取关注操作数量的最高次项。
            优劣对比:O(1)< O(logN)<O(n)< O(nlogN)< o(n^2)< o(n^3)<o(2^n)
            越小表示算法的执行时间越短,算法效率越高
2. 空间维度:是指执行当前算法所消耗的内存空间
    'S(n)=O(f(n))'
    空间复杂度是对一个算法在运行中临时占用存储空间大小的度量。表示的是:算法存储空间与输入值之间的关系
    通常来讲,只要我们的算法不涉及到动态的空间,递归,栈,空间复杂度通常为常数阶O(1)
    算法的空间复杂度,并不是计算实际占用的空间,而是计算整个算法的辅助空间单元(变量)的个数,与问题的规模没有关系,空间复杂度主要看新开辟的变量个数


1

如果 a+b+c=1000 , 并且 a^2+b^2=c^2 求所有 a b c的组合

console.time('耗时')
for (let a = 0; a < 1000; a++) {
    for (let b = 0; b < 1000; b++) {
        let c = 1000 - a - b
        if (Math.pow(a, 2) + Math.pow(b, 2) == Math.pow(c, 2)) {
            console.log(`a:${a}\nb:${b}\nc:${c}`);
            console.log('--------');
        }
    }
}
console.timeEnd('耗时')

console.time('耗时')
for (let a = 0; a < 1000; a++) {
    for (let b = 0; b < 1000; b++) {
        let c = 1000 - a - b
        if (Math.pow(a, 2) + Math.pow(b, 2) == Math.pow(c, 2)) {
            console.log(`a:${a}\nb:${b}\nc:${c}`);
            console.log('--------');
        }
    }
}
console.timeEnd('耗时')

快排

let arr = [20, 10, 4, 5, 8, 31, 0, 50]
function aw(arr){
    if(arr.length<=1) return arr
    const index=Math.floor(arr.length/2)
    //基准元素
    const proid=arr.splice(index,1)[0]
    let left=[]
    let right=[]
    arr.map((item)=>{
        if(item<proid){
            left.push(item)
        }else{
            right.push(item)
        }
    })
    return aw(left).concat(proid,aw(right))
}
console.log(aw(arr));

归并排序

let arr = [20, 10, 4, 5, 8, 31, 0, 50]
const mer = (left, right) => {
    console.log(left,right);
    let temp = []
    while (left.length > 0 && right.length > 0) {
        if (left[0] < right[0]) {
            temp.push(left.shift())
        } else {
            temp.push(right.shift())
           
        }
    }
    console.log(temp.concat(left,right),'temp');
    return temp.concat(left,right)
}
const mergeSort = (arrs) => {
    if (arrs.length == 1) return arrs
    let mid = Math.floor(arrs.length / 2)
    let left = arrs.slice(0, mid)
    let right = arrs.slice(mid)
    return mer(mergeSort(left), mergeSort(right))
}
console.log(mergeSort(arr),'asdas');

插入排序

let arr = [20, 10, 4, 5, 8, 31, 0, 50, 1];

const inserSort = (arr) => {
    let temp = []
    for (let j = 0; j < arr.length; j++) {
        let i = j
        temp = arr[j]
        while(i>0&&arr[i-1]>temp){
         arr[i]=arr[i-1]
         i--
        }
        arr[i]=temp
    }
    console.log(arr);
}
inserSort(arr)

插入排序

let arr = [20, 10, 4, 5, 8, 31, 0, 50, 1];

const inserSort = (arr) => {
    let hand = []
    hand.push(arr[0])
    for (let i = 1; i < arr.length; i++) {
        let A = arr[i]
        for (let j = hand.length - 1; j >= 0; j--) {
            let B = hand[j]
            if (A > B) {
                hand.splice(j + 1, 0, A)
                break;
            }
            if (j === 0) {
                hand.unshift(A)
            }
        }

    }
    console.log(hand);
}
inserSort(arr)

选择排序

let arr = [20, 10, 4, 5, 8, 31, 0, 50, 1];
const selection = (arr) => {
    let min, temp;
    for (let i = 0; i < arr.length; i++) {
        min = i
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                min = j
            }
        }
        temp = arr[i]
        arr[i] = arr[min]
        arr[min] = temp
    }
    return arr
}
console.log(selection(arr));

冒泡排序

let arr = [12, 8, 24, 16, 1]
for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length; j++) {
            if(arr[i]>arr[j]){
               let temp=arr[i]
               arr[i]=arr[j]
               arr[j]=temp
            }
    }
}
console.log(arr);

字符串拆分对象

    let a = `
name,age,parent
Bob,30,David
David,60,
Anna,10,Bob
`
    let b = a.split('\n').map(item => item.replace("  ", "")).filter((item) => item !== '')
    let title = b[0].split(',')
    let ary = []
    b.forEach((item, index) => {
        if (index !== 0) {
            let obj = {};
            item.split(',').forEach((ite, i) => {
                obj[title[i] === 'parent' ? ('_' + title[i]) : title[i]] = (title[i] === 'age' ? Number(ite) : ite)
            })
            ary.push(obj)
        }
    })
    let obj = {}
  ary.forEach((item)=>{//以name为键名构建对应对象
    obj[item[title[0]]] = item
  })

  ary.forEach((item)=>{//为当前键名对应对象添加children和parent(如果存在的话)
    if(item['_parent']!==""){
      if(!obj[item['_parent']].children){
        obj[item['_parent']].children = []
      }
      obj[item['_parent']].children[obj[item['_parent']].children.length] = item
      if(!obj[item.name].parent){
        obj[item.name].parent = []
      }
      obj[item.name].parent[obj[item.name].parent.length] = obj[item['_parent']]
    }
  })
  let res = Object.values(obj).map((item)=>{//删除关联字段
    delete item['_parent']
    return item
  }).find((item)=>!item.parent)//找出没有parent的对象(一级对象)
  console.log(res);

js正序数组打乱

var a = [1, 2, 3, 4, 5];

a.sort(randomSort);
console.log(a);
function randomSort(a, b) { return Math.random() > 0.5 ? -1 : 1; }

js千分位

function format(num) {
    num = String(num);//数字转字符串
    console.log(num);
    let str = '';//字符串累加
    for (let i = num.length - 1, j = 1; i >= 0; i--, j++) {
        if (j % 3 == 0 && i != 0) { //每隔三位加逗号,过滤正好在第一个数字的情况
            str = ',' + num[i] + str; //加千分位逗号
            continue;
        }
        str = num[i] + str; //累加数字
    }
    console.log(str);
    return str;
}
let num = 1234567890;
format(num); //"1,234,567,890"

防抖


function debounce(fn, wait) {
    let timeout = null
    return args => {
        if (timeout) {
            clearTimeout(timeout)
        }
        timeout = setTimeout(fn, wait)
    }
}

节流

function throttle(fn, delpay) {
    let oldTime = new Date().getTime()
    let time = null
    return function () {
        let newTime = new Date().getTime()
        clearTimeout(time)
        if (newTime - oldTime >= delpay) {
            fn()
            oldTime = newTime
        } else {
            time = setTimeout(() => {
                fn()
                oldTime = newTime
            }, delpay)
        }
    }
}

数组扁平化

 function flatten(data,index) {
    var resultArr = [];
    var len = data.length;
     if(index>0){
        for (var i = 0; i < len; i ++) {
            if (Array.isArray(data[i])) {
                resultArr = resultArr.concat(flatten(data[i],--index));
            } else {
              resultArr.push(data[i]);
            }
          }
     }
     console.log(resultArr);
    return resultArr;
  }
  var arr=[1,2,3,[4,5,'hello',['world',9,666]]]
  console.log(flatten(arr,2));

数组中最接近的数字

    var arr = [
        500,320, 400,  200, 1, 10, 8, 100, 130, 120, 135, 140, 180, 190, 170, -1,
        -2, -4,
    ]; // 原有数组
    var targetNum = 360; // 目标数值
    // 新数组相加之后的值取最小值后找下标  由于新数组与原数组下标对应 即是原数组最小值下标
    function findCloseNum(arr, num) {
        let a= arr.sort((a,b)=>b-a)
            .map((item) => Math.abs(num - item))
            .findIndex(
                (item) => item === Math.min(...arr.map((item) => Math.abs(num - item)))
            );
            console.log(arr,a);
            return arr[a]
    }

数组转树

  function toTree(data) {
    data=JSON.parse(JSON.stringify(data))
    let result = []
    let map = {}
    data.forEach((item) => {
        map[item.id] = item
    })
    data.forEach(item => {
        let parent = map[item.pid]
        if (parent) {
            (parent.children || (parent.children = [])).push(item)
        } else {
            result.push(item)
        }
    })
    return result
}

树转数组

let tree = {
        id: 1,
        children: [
            {
                id: 2, children: [
                    { id: 3 }
                ]
            },
            {
                id: 4,
                children: [
                    { id: 5 }
                ]
            }
        ]
    }
function toArr(data) {
    let arr = []
    function treeToArray(res, parent = null) {
        res.forEach(item => {
            arr.push({ id: item.id, pid: parent })
            if (item.children) {
                treeToArray(item.children, item.id)
            } 
        })
    }
    treeToArray(data)
    return arr
}

判断对象是否为promise

let obj=new Promise((resolve,reject)=>{
    resolve(13)
})

function isPromise(obj) {
  return obj  //有实际含义的变量才执行方法,变量null,undefined和''空串都为false
  	&& (typeof obj === 'object' || typeof obj === 'function') // 初始promise 或 promise.then返回的
  	&& typeof obj.then === 'function';
}
console.log(isPromise(obj));

相同字符串组成新的数组

let arr = ["abc", "bca", "qwe", "ewq", 'acb', 'wqe', 'xyz', 'tre']
function q() {
    let a = []
    let b = []
    for (var i = 0; i < arr.length; i++) {
        if (b.indexOf(i) == -1) {
            b.push(i)
            let s = [arr[i]]
            for (var j = i + 1; j < arr.length; j++) {
                if (arr[i].split('').sort().join('') == arr[j].split('').sort().join('') && b.indexOf(j) == -1) {
                    b.push(j)
                    s.push(arr[j])
                }
            }
            a.push(s)

        }
    }
    console.log(a)
}

地址栏拼接

let url = "https://apis.map.qq.com/uri/v1/routeplan?"
let data = { referer: 'CQPBZ-QRWCU-2RGVJ-44CGR-5WCY6-WVB5Q', };
data.type = "drive"
data.from = "我的位置"
data.fromcoord = "我的位置经纬度"
data.to = "目的地"
data.tocoord = "目的地经纬度"
Object.keys(data).map((key) => {
    url += key + '=' + data[key] + '&';
})
// 此处生成的 url 末尾多一个 & , .substring 截取一下 
url = url.substring(0, url.length - 1)
console.log(url);

url解析成对象

function geturl(url){
    let arr = url.split('?');
    let res = arr[1].split('&');
    let items = {};
    for(let i=0;i<res.length;i++){
    let a = res[i].split('=');
    items[a[0]] = a[1];
    }
    console.log(items);
    return items
}