高频算法面试总结

★★★ 冒泡排序

从开始位置两两比较,持续n轮

  • 基础版
    function bubbleSort (arr) {
    // 执行第 i + 1 轮
    for (let i = 0; i < arr.length; i++) {for (let j = 0; j < arr.length - 1; j++) {// 前一个与后一个两两比较if (arr[j] > arr[j + 1]) {// 交换两个变量值let tmp = arr[j]arr[j] = arr[j + 1]arr[j + 1] = tmp}}
    }
    }
    
  • 改进版
    function bubbleSort (arr) {
    // 冒泡每次处理个最大/最小值, i 代表每次最大值的位置
    for (let i = arr.length - 1; i > 0; i--) {for (let j = 0; j < i; j++) {if (arr[j] > arr[j + 1]) {// 交换两个变量值[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]}}
    }
    }
    

★★ 选择排序

每次选出最大/最小值 持续n轮

  • 基础版
function selectSort (arr) {// 每次的最小值的索引for (let i = 0, len_i = arr.length; i < len_i; i++) {// 每轮最小值索引let index = ifor (let j = i, len_j = arr.length; j < len_j; j++) {if (arr[index] > arr[j]) {// 最小值变化index = j}}// 把最小值放到第 i 的索引[arr[i], arr[index]] = [arr[index], arr[i]]}
}
  • 改进版
function selectSort (arr) {// 排出 lenth - 1 个最小值即可for (let i = 0, len_i = arr.length - 1; i < len_i; i++) {let index = ifor (let j = i + 1, len_j = arr.length; j < len_j; j++) {if (arr[index] > arr[j]) {index = j}}// 判断最小索引是否改变if (index !== i) {[arr[i], arr[index]] = [arr[index], arr[i]]}}
}

★★ 插入排序

默认一个已排好序的数组 递增的往这个数组内插入元素

  • 版本一
function insertSort (arr) {// 将要插入的索引for (let i = 1, len_i = arr.length; i < len_i; i++) {let cur = i// 插入到已经排好序的序列中while (cur > 0 && arr[cur] < arr[cur - 1]) {[arr[cur], arr[cur - 1]] = [arr[cur - 1], arr[cur]]cur--}}
}
  • 版本二
function insertSort (arr) {// 将要插入的索引for (let i = 1, len = arr.length; i < len; i++) {let tmp = arr[i]// 插入到已经排好序的序列中for (let j = i - 1; j >= 0 && arr[j] > tmp; j--) {arr[j + 1] = arr[j]}arr[j + 1] = tmp}
}

★★ 希尔排序

增量式的插入排序 正常的插入排序间隔为1 希尔排序默认设置间隔大于1 然后递减为1

function shellSort (arr) {let len = arr.lengthlet step = 1let dis = 3// 设置最大间隔while (step < len / dis) {step = step * dis + 1}// 间隔递减for (; step > 0; step = Math.floor(step / dis)) {// 单个间隔的插入排序for (let i = step; i < len; i++) {let tmp = arr[i]let j = i - stepwhile (j >= 0 && arr[j] > tmp) {arr[j + step] = arr[j]j -= step}arr[j + step] = tmp}}
}

★★ 归并排序

排序之前将数组拆分为两部分排序 然后将排好序的两个部分进行合并排序

function mergeSort (arr) {if (arr.length <= 1) return ;let left = 0,let right = arr.length - 1,let mid = parseInt((left + right) * 0.5)sort(arr, left, mid)sort(arr, mid + 1, right)merge(arr, left, mid, right)
}
function sort (arr, left, right) {// 分解为一个元素if (left >= right) return ;let mid = parseInt((left + right) * 0.5)sort(arr, left, mid)sort(arr, mid + 1, right)merge(arr, left, mid, right)
}
function merge (arr, left, mid, right) {// 合并的[left, mid] [mid + 1, right] 部分 都已经排好序let i = leftlet j = mid + 1let tmp = []// 依次插入连个数组的元素while (i <= mid && j <= right) {// 这里的等于保证了排序算法稳定性(即两个相等的数排序后的位置不会改变)if (arr[i] <= arr[j]) {tmp.push(arr[i])i++} else {tmp.push(arr[j++])}}// 处理剩余的左边/右边元素while (i <= mid) tmp.push(arr[i++])while (j <= right) tmp.push(arr[j++])for (let i = 0, len = tmp.length; i < len; i++) {arr[left + i] = tmp[i]}
}

★★★ 快速排序

排序之前将数组拆按照指定指定基点拆分为两部分 一部分小于等于基点 一部分大于基点

  • 版本一
function quickSort (arr) {if (arr.length <= 1) return ;let left = 0let right = arr.length - 1sort(arr, left, right)
}
function sort (arr, left, right) {if (left >= right) return ;let baseValue = arr[left]let start = leftlet end = rightwhile (start < end) {// 找到右边小于基点的值的索引while (start < end && arr[end] >= baseValue) end--// 找到左边大于基点的值的索引while (start < end && arr[start] <= baseValue) start++if (start < end) {[arr[start], arr[end]] = [arr[end], arr[start]]}}[arr[left], arr[start]] = [arr[start], arr[left]]sort(arr, left, start - 1)sort(arr, start + 1, right)
}
  • 版本二
function quickSort(arr) {if (arr.length <= 1) return ;let left = 0let right = arr.length - 1sort(arr, left, right)
}
function sort (arr, left, right) {if (left >= right) return ;let baseValue = arr[left]let leftArr = []let rightArr = []for (let i = left; i <= right; i++) {if (arr[i] <= baseValue) {leftArr.push(arr[i])} else {rightArr.push(arr[i])}}let mid = left + leftArr.length - 1for (let i = 0, len = leftArr.length; i < len; i++) {arr[left + i] = leftArr[i]}for (let i = 0, len = rightArr.length; i < len; i++) {arr[mid + i + 1] = rightArr[i]}[arr[left], arr[mid]] = [arr[mid], arr[left]]sort(arr, left, mid - 1)sort(arr, mid + 1, right)
}

★★ 堆排序

将数组看作为一个堆结构 其中 index = 0 为根节点

index 指向的节点的子节点有 2index + 1 2index + 2 0 -> 1, 2 1 -> 3, 4 2 -> 5, 6…

实际的操作就是对这个堆进行调整 堆这个数据结构可以去了解一下

参考 https://www.cnblogs.com/chengxiao/p/6129630.html

// 堆排序
function heapSort (arr) {// 从最后一个非叶子节点开始  构建大顶堆   即每次找出最大的一个数for (let i = Math.floor(arr.length * 0.5 - 1); i >= 0; i--) {adjustHeap(arr, i, arr.length)}// 将最大的一个节点放在末尾 等于排好序了 在对剩下的元素构建大顶堆for (j = arr.length - 1; j > 0; j--) {[arr[0], arr[j]] = [arr[j], arr[0]]// 大部分都不需要调整了 所以直接从根节点开始adjustHeap(arr, 0, j)}
}
// 调整堆
function adjustHeap (arr, i, len) {// k = 2 * k + 1 是在交换两个节点时会导致原来的堆结构混乱,需要重新调整子节点let cnt = 0for (let k = 2 * i + 1; k < len; k = 2 * k + 1) {// 找出左子节点与右子节点中最大的节点if (k + 1 < len && arr[k] < arr[k + 1]) k++// 判断是否与子节点进行交换if (arr[k] > arr[i]) {[arr[k], arr[i]] = [arr[i], arr[k]]// 交换完成后要对下一级节点进行重新调整i = k} else {break ;}}
}

★★★ 斐波那契数列

斐波那契数列 0 1 1 2 3 5 8 13 … 第 n 项为第 n-1 与 第 n-2 项的和 首项为0 第二项为1

  • 使用循环
function fibonacci (n) {if (n <= 2) return n - 1let n1 = 0, n2 = 1for (let i = 2; i < n; i++) {[n1, n2] = [n2, n1 + n2]}return n2
}
  • 使用递归

消耗内存空间较大

function fibonacci (n) {return n <= 2 ? n - 1 : Fibonacci(n - 1) + Fibonacci(n - 2)
}// 设置缓存
let cache = {}
function fibonacci (n) {if (n <= 0) return 0if (n <= 2) return n - 1if (cache[n]) return cache[n] return cache[n] = Fibonacci(n - 1) + Fibonacci(n - 2)
}

★★ 汉诺塔问题

汉诺塔规则参照 https://baike.baidu.com/item/%E6%B1%89%E8%AF%BA%E5%A1%94/3468295

  • 递归解决
// 三根柱子分别为 A B C  A 为初始位置
function hanoi (n, current = 'A', temp = 'B', target = 'C') {if (n <= 0) return 0let sum = 1sum += hanoi(n - 1, current, target, temp)console.log(current + ' ---> ' + target)sum += hanoi(n - 1, temp, target, current)return sum
}

★★ 合并两个有序数组

直接参照归并排序的合并 merge方法

function mergeArr (arr1, arr2) {let result = []// p1 与 p2 分别为 arr1 与 arr2 的索引let p1 = p2 = 0let len1 = arr1.lengthlet len2 = arr2.lengthwhile (p1 < len1 && p2 < len2) {arr1[p1] <= arr2[p2] ? result.push(arr1[p1++]) : result.push(arr2[p2++])}while (p1 < len1) result.push(arr1[p1++])while (p2 < len2) result.push(arr2[p2++])return result
}
  • API版
function mergeArr (arr1, arr2) {// sort 自定义排序规则return (arr1.concat(arr2)).sort((a, b) => a - b)
}

★★ 数组中重复的数字

数组去重 可以参考 https://segmentfault.com/a/1190000016418021

  • 简易版
function noRepeat (arr) {return [...new Set(arr)]
}
  • 循环版
function noRepeat (arr) {let result = []// 第 n+1 个不重复的元素for (let i = 0, len_i = arr.length; i < len_i; i++) {let isNoRepeat = truefor (let j = 0, len_j = result.length; j < len_j; j++) {// 若数组中只有基本数据类型可以直接用 === 判断isNoRepeat = !isEqual(arr[i], result[j])if (!isNoRepeat) break;}if (isNoRepeat) result.push(arr[i])}return result;
}// 用到的方法
function isEqual (obj1, obj2) {// 判断类型是否相同let type = getType(obj1)if (type !== getType(obj2)) return false;// 判断基本数据类型值if (typeof obj1 !== 'object') return obj1 === obj2;// 判断地址if (obj1 === obj2) return true;// 判断值是否相同switch (type) {// 对象case '[object Object]':// 取得可遍历属性let keys1 = Object.keys(obj1)let keys2 = Object.keys(obj2)if (keys1.length !== keys.length) return false// 长度相等for (let i = 0, len = keys1.length; i < len; i++) {let key = keys1[i]if (!isEqual(obj1[key], obj2[key])) return false;}break;// Mapcase '[object Map]':// Setcase '[object Set]':// 转为数组obj1 = Array.from(obj1)obj2 = Array.from(obj2)// 数组case '[object Array]':if (obj1.length !== obj2.length) return false;for (let i = 0, len = obj1.length; i < len; i++) {if (!isEqual(obj1[i], obj2[i])) return false;}break;// ...default: break;}return true;
}
function getType (obj) {// 可以判断数组 Map Set数据类型return Object.prototype.toString.call(obj)
}
  • 标记版
function noRepeat (arr) {let result = []// 使用标记  使用 Map 可以判定多种数据类型// 这里也可以使用 WeakMap 弱引用  具体自己去查资料 js WeakMap类型const flag = new Map()for (let i = 0, len = arr.length; i < len; i++) {// 初步判定if (!flag.has(arr[i])) {let isNoRepeat = true// 基本数据类型直接插入// 引用数据类型if (typeof arr[i] === 'object') {for (let j = 0, len_j = result.length; j < len_j; j++) {isNoRepeat = !isEqual(arr[i], result[j])if (!isNoRepeat) break;}}// 没有重复if (isNoRepeat) {result.push(arr[i])flag.set(arr[i], true)}}}return result
}

★★ 两个数组的交集

交集就是相同的部分 没有说是两个集合 所以最终的交集需要保留索引不同的相同的元素

  • 基础版
function intersection (arr1, arr2) {let flag = {}let result = []for (let i = 0, len_i = arr1.length; i < len_i; i++) {let item = arr1[i]// 搜索有没有    使用 indexOf 或 includes 也没问题for (let j = 0, len_j = arr2.length; j < len_j; j++) {// 判断相等建议使用之前封装的 isEqual 方法// isEqual(arr1[i], arr2[i])if (item === arr2[j] && !flag[j]) {result.push(item)flag[j] = truebreak;}}}return result
}
  • Map标记版
// 缺陷 若元素值有引用数据类型可能会出错
function intersection (arr1, arr2) {let map = new Map()let result = []for (let i = 0, len = arr1.length; i < len; i++) {let item = arr1[i]if (map.has(item)) map.set(item, map.get(item) + 1)else map.set(item, 1)}for (let i = 0, len = arr2.length; i < len ;i++) {let item = arr2[i]if (map.has(item)) {result.push(item)map.set(item, map.get(item) - 1)}}return result
}
  • API版本
// 更适合求两个集合的交集
function intersection (arr1, arr2) {// return noRepeat(arr1).filter(v => arr2.includes(v))return [...new Set(arr1)].filter(v => arr2.includes(v))
}

★★ 旋转数组

倒序? 或者 把数组往右旋转k步,要求不返回新的数组,直接改变原数组?

  • 倒序
function reverse (arr) {let len = arr.lengthfor (let i = 0, mid = parseInt(len * 0.5); i < mid; i++) {[arr[i], arr[len - i - 1]] = [arr[len - i - 1], arr[i]]}return arr
}
  • 旋转k步
function rotateArr (arr, k) {let len = arr.lengthlet tmp = [...arr]if (k < 0) k = (k % len) + lenfor (let i = 0; i < len; i++) {let index = (i + k) % lenarr[index] = tmp[i]}return arr
}

★★ 两数之和

给定一个整数数组nums 和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例 nums = [2, 7, 11, 15] target = 9 则结果返回 [0, 1] 即是2 和 7的下标

function twoSum (arr, target) {let map = new Map()for (let i = 0, len = arr.length; i < len; i++) {let diff = target - arr[i]if (map.has(diff)) {return [map.get(diff), i]}map.set(arr[i], i)}return 'not found'
}

★★ 爬台阶的实现思路

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 n = 2 —> 2 两种方法 1阶 + 1阶 2阶

  • 实现思路
// 首先题目要求枚举出所有爬楼梯的台阶 而且可以一次只爬一个台阶(不管怎么爬最后都能只爬一步到达楼顶)
// 这是可以给出一个基本思路  爬一次(1或2) -> 爬剩余的台阶  终止条件是剩余台阶数为0
function climb (n) {if (n === 0) return 1  // 这里返回一次 代表可以爬到楼顶的一种方法climb(n - 1)  // 爬1阶后爬剩余的台阶if (n - 2 >= 0) {climb(n - 2)  // 爬2阶后爬剩余的台阶 这时候要判断剩余台阶数是不是大于等于2      }
}// 改进一下  终止条件设为 n <= 2
function climb (n) {if (n <= 0) return 0    // 直接终止if (n <= 2) return n    // 剩余1阶有一种解法 2阶有两种解法return climb(n - 1) + climb(n - 2)
}// 此时可以发现climb(n - 1)内部又调用了climb(n - 2) 而 climb(n)也调用了climb(n - 2)
// 不妨保存一下计算结果
let cache = {}
function climb (n) {if (n <= 0) return 0    // 直接终止if (n <= 2) return n    // 剩余1阶有一种解法 2阶有两种解法if (cache[n]) return cache[n]return cache[n] = climb(n - 1) + climb(n - 2)
}
// 大功告成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/577311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

date-fns v3 发布——这个由 200 个函数组成的 JavaScript 日期处理套件

date-fns v3 发布——这个由 200 个函数组成的 JavaScript 日期处理套件已经在 TypeScript 中重写&#xff0c;重新引入了 String 日期参数&#xff0c;在 Node 上支持 ESM&#xff0c;并且所有函数现在都可以通过命名导出导出。 经过几个月的开发&#xff0c;v3 终于出来了&a…

力扣经典面试题——搜索旋转排序数组及最小值(二分搜索旋转数组系列一次搞定)

我们先来看看一个常规的二分搜索是如何进行的&#xff1f; 例如要找一个有序数组的某个数 【1&#xff0c;2&#xff0c;4&#xff0c;5&#xff0c;9&#xff0c;11&#xff0c;15&#xff0c;19】 我们要找11&#xff0c;每次我们分割半边判断然后看到底在哪一边。 这里为什么…

Neovim+ctag浏览、编辑源代码

Neovimctag浏览、编辑源代码 一 配置安装vim及 ctags vim应该可以不用装&#xff0c;直接装neovim&#xff0c;这里我是先装了vim再装的neovim Ctags必须装&#xff0c;后面用neovim telescope索引函数时才有效 vim复制系统粘贴板&#xff1a;vim输入模式下&#xff0c;按shi…

【C++PCL】点云处理最小三维圆拟合

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 目录 1.原理介绍 2.代码效果 3.源码展示 4.参数调试

date工具类

package com.hasity.stock.utils;import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat;/*** author by hasity* Date 2021/12/31* Description 日期时间工具类*/ public class DateTimeUtil {/*** 获取指定日期下股票的上一个有效交易日时间* return*/…

linux 内核死锁检测

lockdep是内核提供协助发现死锁问题的功能。 本文首先介绍何为lockdep&#xff0c;然后如何在内核使能lockdep&#xff0c;并简单分析内核lockdep相关代码。 最后构造不同死锁用例&#xff0c;并分析如何根据lockdep输出发现问题根源。 Lockdep介绍 死锁是指两个或多个进程因…

Ubuntu离线安装Docker

一、安装 docker 下载docker离线包 去官网下载docker 安装二进制包&#xff0c;选择适合自己的版本&#xff08;Docker version 20.10.8&#xff09;。 下载地址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/文档名&#xff1a;docker-20.10.8.tgz用…

【Python】Python AI 绘画

本文我们将为大家介绍如何基于一些开源的库来搭建一套自己的 AI 作图工具。 需要使用的开源库为 Stable Diffusion web UI,它是基于 Gradio 库的 Stable Diffusion 浏览器界面 Stable Diffusion web UI GitHub 地址:GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable…

网络编程 day3

TCP多进程并发服务器 #include <stdio.h> #include "/home/ubuntu/head.h"#define IP "192.168.124.85" #define PORT 8888void handler(int sig) {while(waitpid(-1,NULL,WNOHANG)>0); }int main(int argc, const char *argv[]) {//回收僵尸进程…

[kubernetes]Kube-APIServer

API Server API Server是什么 提供集群管理的REST API接口&#xff0c;包括认证授权、数据校验以及集群状态变更等提供其他模块之间的数据交互和通信的枢纽&#xff08;其他模块通过API Server查询或修改数据&#xff0c;只有API Server才直接操作etcd&#xff09; 访问控制…

开源自托管导航页配置服务Dashy本地搭建结合内网穿透远程访问

开源自托管导航页配置服务Dashy本地搭建结合内网穿透远程访问 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一…

Mongodb基础介绍与应用场景

NoSql 解决方案第二种 Mongodb MongoDB 是一款开源 高性能 无模式的文档型数据库 当然 它是NoSql数据库中的一种 是最像关系型数据库的 非关系型数据库 首先 最需要注意的是 无模式的文档型数据库 这个需要后面我们看到它的数据才能明白 其次是 最像关系型数据库的非关系型数据…

RK3588平台开发系列讲解(AI 篇)RKNN 数据结构详解

文章目录 一、rknn_sdk_version二、rknn_input_output_num三、rknn_tensor_attr四、rknn_perf_detail五、rknn_perf_run六、rknn_mem_size七、rknn_tensor_mem八、rknn_input九、rknn_output沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解 RKNN 相关的数…

Autosar CAN开发02(入门Autosar)

Autosar架构 想起当时刚毕业进入公司之后&#xff0c;我的岗位是Autosar Bsw软件工程师。 看着这个什么“Autosar”&#xff0c;真的是一脸懵。 后来才知道&#xff0c;按照我的理解&#xff1a;Autosar就是一个软件架构。它分为ASW和BSW。ASW负责实现应用层功能&#xff08…

“React学习之旅:从入门到精通的点滴感悟“

在探索和学习React的过程中&#xff0c;我逐渐领悟到了前端开发的魅力与挑战。React&#xff0c;作为Facebook推出的开源JavaScript库&#xff0c;以其独特的思维方式和强大的功能&#xff0c;引领着前端开发的潮流。在这篇文章中&#xff0c;我将分享我的React学习心得&#x…

test assert-01-Google Truth 断言

Truth Truth 是用于Java测试的断言框架&#xff0c;灵感来自于FEST&#xff0c;并受到一些可扩展性需求的驱动&#xff0c;几乎完全由谷歌员工在业余时间编写&#xff0c;或者作为Java核心图书馆管理员的身份做出贡献。 作用 作为工程师&#xff0c;我们花费大部分的时间来阅…

《C++避坑神器·二十四》简单搞懂json文件的读写之根据键值对读写Json

c11 json解析库nlohmann/json.hpp文件整个代码由一个头文件组成 json.hpp&#xff0c;没有子项目&#xff0c;没有依赖关系&#xff0c;没有复杂的构建系统&#xff0c;使用起来非常方便。 json.hpp库在文章末尾下载 读写主要有两种方式&#xff0c;第一种根据键值对读写&…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Progress进度条组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Progress进度条组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Progress组件 进度条也是UI开发最常用的组件之一&#xff0c;进度条组件…

OpenAI换血大震动始末:“ChatGPT之父”奥特曼,缘何被“扫地出门”?

近期&#xff0c;AI业界发生了一场“大地震”。作为聊天机器人ChatGPT的开发者&#xff0c;OpenAI无疑是最受关注的人工智能公司之一。就是这家公司的联合创始人、CEO、有“ChatGPT之父”之称的阿尔特曼在“疯狂的5天”里&#xff0c;经历了被闪电免职、加入微软、最终又官复原…

影响嵌入式项目成功的一些要素

选择了嵌入式开发这个技术领域&#xff0c;就相当于选择了一条充满挑战的技术开发道路。 对于工程师而言&#xff0c;项目的成功和失败对他们十分重要。因为一行行代码他们不知道熬了多少个通宵&#xff0c;脑细胞死了多少而写出来的。 如果项目失败了&#xff0c;就意味着辛辛…