如果是 Promise 内部报错,reject 抛出错误后,由于就近原则,then 的第二个参数会先捕获到异常,catch 则无法获取异常。但如果是 then 的第一个参数抛出错误,then 的第二个参数会捕获不到,只有 catch 能捕获。都用catch就可以了
finally(callback){
return this.then(value=>{
return MyPromise.resolve(callback()).then(()=>value)
},reason=>{
return MyPromise.resolve(callback()).then(()=>{throw reason})
})
}
使用场景:试想一下,你有一个组件包通过 npm 发布后,你的10个业务项目引用这个组件包。当这个组件包更新了版本,你的10个项目想要使用最新功能就必须一一升级版本、编译打包、部署,这很繁琐。但是模块联邦让组件包利用CDN的方式共享给其他项目,这样一来,当你到组件包更新了,你的10个项目中的组件也自然更新了。
他和利用 npm 发包来实现的方案的区别在于,npm 发布的组件库从 1.0.1 升级到 1.0.2 的时候,必须要把业务线项目重新构建,打包,发布才能使用到最新的特性,而模块联邦可以实现实时动态更新而无需打包业务线项目。
模块联邦是webpack的内置模块,使用起来也是相当的简单,做好相关配置就可以了,首先要保障项目webpack是5.0及以上。然后在对应的项目的webpack.config.js进行配置,ModuleFederationPlugin有几个重要的参数:
1、name: 当前应用的名称,需要唯一性;
2、exposes: 需要导出的模块,用于提供给外部其他项目进行使用;
3、remotes: 需要依赖的远程模块,用于引入外部其他模块;
4、filename: 入口文件名称,用于对外提供模块时候的入口文件名;
5、shared: 配置共享的组件,一般是对第三方库做共享使用;
// webpack 配置
new ModuleFederationPlugin({
name: "main_app",
filename: "remoteEntry.js",
exposes: {
"./search": "./src/search/search.vue"
},
remotes: {
lib_remote: "lib_remote@http://localhost:8085/remoteEntry.js",
},
shared: {
vue: {
eager: true,
singleton: true,
}
}
})
JavaScript是单线程的语言,如果在浏览器中需要执行一些大数据量的计算,页面上的其他操作就会因为来不及响应而出现卡顿的情况,Web Worker的出现为js提供了一个多线程的环境,让更多耗时的复杂计算拿到另一个环境中去完成,计算完之后再提供给主进程使用,前端页面可以只负责界面渲染,让用户体验更流畅。
使用限制
webworker的使用
import React, { Component } from 'react';
import worker_script from './worker-script';
export default class PageHome extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
const worker = new Worker(worker_script);
console.log(worker);
worker.onmessage = function (event) {
console.log(`Received message ${event.data}`);
};
// worker.postMessage('dadada')
}
const workercode = () => {
self.onmessage = function (e) {
console.log('Message received from main script');
let workerResult = `Received from main: ${e.data}`;
console.log('Posting message back to main script');
self.postMessage(workerResult);
};
self.postMessage('workerResult');
};
let code = workercode.toString();
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
const blob = new Blob([code], { type: 'application/javascript' });
const worker_script = URL.createObjectURL(blob);
export default worker_script;
ES 6 引入了一个新的数据类型 Symbol,创建独一无二的值
ref可以用来获取真实dom,来操作dom节点。ref还可以用来获取React组件的实例,ref不能直接应用于函数组件,ref 经常用来做dom操作以及子组件向父组件通信,还可以作为内部数据存储使用。
forwardRef用于解决上面函数组件无法直接使用ref的问题,使用forwardRef可以使得函数组件也可以使用ref做上面的操作。
useRef 函数接收一个初始值,并返回一个可变的(mutable) ref 对象,也就是我们的变量refContainer。这个对象上面会存在一个属性——current,它的默认值就是initalValue。这里有个比较重要的点就是useRef 创建返回的对象是可变的,并且它会在组件的整个生命周期中都存在,这也就以为着无论你在何时何地访问这个对象,它的值永远都是最新的值。
扩展下createRef和useRef是不一样的,
hooks 和函数组件对应的 fiber 对象建立起关联,将 useRef 产生的 ref 对象挂到函数组件对应的 fiber 上,函数组件每次执行,只要组件不被销毁,函数组件对应的 fiber 对象一直存在,所以 ref 等信息就会被保存下来。
缓存的四个位置按照优先级顺序依次为Service Worker、Memory Cache、Disk Cache和Push Cache。
聊一聊浏览器缓存
我们都知道,传统web的架构模型是单线程架构,其渲染线程和脚本线程是互斥的,这也就是说为什么长时间的脚本运行可能会使页面失去响应,而小程序的架构模型有别于传统web,小程序为双线程架构,其渲染线程和脚本线程是分开运行的。
传统web开发者可以使用各种浏览器暴露出来的DOM API来进行DOM操作,但由于小程序的逻辑层和渲染层是分开的,逻辑层并没有一个完整的浏览器对象,因而缺少相关的DOM API和BOM API
传统web开发者需要面对的是各式各样的浏览器,PC 端需要面对 IE、Chrome、火狐浏览器等,在移动端需要面对Safari、Chrome以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具。
通信使用websocket
今天是学习前端的第一天之小程序的双线程架构
【代码题】
深拷贝const deepCopy = (obj, hash = new WeekMap()) => {
if(obj instanceof Date) {
return new Date(obj)
}
if(obj instanceof RepExp) {
return new RegExp(obj)
}
if(hash.has(obj) {
return hash.get(obj)
}
let property = Object.getOwnPropertyDescriptors(obj)
let cloneObj = Object,create(Object.getPrototypeOf(obj), property)
hash.set(obj, cloneObj)
for(let key of Reflect.ownKeys(obj)) {
if(obj[key] && typeof obj[key] === 'object') {
cloneObj[key] = deepCopy(obj[key], hash)
} else {
cloneObj[key] = obj[key]
}
}
return cloneObj
}
const deepClone = (obj, m) => {
if(obj ??'' === '') return obj;
if(obj === document) return;
if(!Object.prototype.isPrototypeOf(m)) {
if(HTMLElement.prototype.isProtoTypeOf(source)) {
target = document.createElement(source);
} else if(source.constructor === RegExp) {
target = new RegExp(source.source, source.flags);
} else if(source.constructor === Date) {
target = new Date(source);
} else if(source.constructor === Function) {
var arr = source.toString().replace(/\n|\r/g, "").trim().match(/\((.*?)\)\s*\{(.*)\}/).slice(1); //进行source处理
target = new Function(arr[0].trim(), arr[1]);
} else if(source.constructor === Set) {
target = new Set(deepClone(Array.from(source.values())));
} else if(source.constructor === Map) {
target = new Map();
for(var [key, value] of source.entries()) {
if(Object.prototype.isPrototypeOf(key)) {
target.set(deepClone(key), deepClone(value));
} else {
target.set(deepClone(key), value);
}
} else {
if(Obejct.prototype.isPrototypeOf(value)) {
target.set(key, deepClone(value));
} else {
target.set(key, value);
}
}
}
} else {
target = new source.constructor();
}
var names = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source));
for(var i = 0; i < names.length; i++) {
if(source.constructor === Function && names[i] === 'prototype') continue;
var desc = Object.getownPropertyDescriptor(source, names[i]);
if(Object.prototype.isPrototypeOf(desc.value)) {
Object.defineProperty(target, names[i], {
enumerable: desc.enumerable,
configurable: desc.configurable,
writable: desc.writable,
value: deepClone(desc.value)
});
} else {
Object.defineProperty(taget, names[i], desc);
}
}
return target;
};
【代码题】
二叉树光照,输出能被光照到的节点, dfs能否解决?输入: [1,2,3,null,5,null,4]
输出: [1,3,4]
/**
* @param {TreeNode} root
* @return {number[]}
*/
function exposedElement(root) {
};
[外链图片转存中…(img-TLxlzkps-1667398190178)]
// 将list转化为树结构
class TreeNode {
constructor(val, left, right) {
this.val = (val === undefined ? 0 : val);
this.left = (left === undefined ? 0 : left);
this.right = (right === undefined ? 0 : right);
}
}
function buildTree(arr) {
if (!arr || arr.length === 0) {
return null;
}
let root = new TreeNode(arr.shift());
let nodeQueue = [root];
while (arr.length > 0) {
let node = nodeQueue.shift();
if (!arr.length) {
break;
}
let left = new TreeNode(arr.shift());
node.left = left;
nodeQueue.push(left);
if (!arr.length) {
break;
}
// 左侧叶子拼完,右边一样的操作
let right = new TreeNode(arr.shift());
node.right = right;
nodeQueue.push(right);
}
// 最后返回根结点,通过根结点就能得到整个二叉树的结构
return root;
}
const rightSideView = (root) => {
const res = [];
const dfs = (node, level) => {
if(!node) return;
res[level] = node.val;
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
dfs(root, 0);
return res;
}
setTimeout(function () {
console.log(1);
}, 100);
new Promise(function (resolve) {
console.log(2);
resolve();
console.log(3);
}).then(function () {
console.log(4);
new Promise((resove, reject) => {
console.log(5);
setTimeout(() => {
console.log(6);
}, 10);
})
});
console.log(7);
console.log(8);
// 23784561
【代码题】
作用域var a=3;
function c(){
alert(a);
}
(function(){
var a=4;
c();
})();
// 3 c 定义在全局
【代码题】
输出题function Foo() {
Foo.a = function(){
console.log(1);
}
this.a = function(){
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3);
}
Foo.a = function() {
console.log(4);
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
// 4 2 1
// 补充 Foo.a 为 3
教你如何捕获前端错误
window.addEventListener('rejectionhandled', event => { // 错误的详细信息在reason字段 // demo:settimeout error console.log(event.reason); });
请求和响应拦截器在接口报错时进行处理
经常在处理对象时或者数组长度时,因为类型或者找不到改对象会导致页面崩溃等问题,可以使用ts类型检查 以及 es6的?.方法来避免页面崩溃
实现一个前端监控系统,应该考虑什么?如何去实现
通过埋点来监控整个前端的稳定性,例如首屏加载速度,性能监控等
首屏加载时间:times = (performance.timing.domComplete - performance.timing.navigationStart) / 1000
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime
// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
name: "first-contentful-paint",
entryType: "paint",
startTime: 507.80000002123415,
duration: 0,
};
var elements= {
image: document.getElementById('111');
}
elements.image = null;
【代码题】
好多请求, 耗时不同, 按照顺序输出, 尽可能保证快, 写一个函数.同:手写题: 实现一个批量请求函数, 能够限制并发量?
const promiseList = [
new Promise((resolve) => {
setTimeout(resolve, 1000)
}),
new Promise((resolve) => {
setTimeout(resolve, 1000)
}),
new Promise((resolve) => {
setTimeout(resolve, 1000)
})
]
fn(promiseList);
【代码题】
一个数组的全排列输入一个数组 arr = [1,2,3,4]
输出全排列
[[1], [2], [3], [1, 2], [1, 3], ...., [1,2,3], [1,2,4] ....]
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(data) {
let res = [];
let group = [];
let index = 0;
res.push(data[index]);
for(var i = 0; i < group.length; i++) {
res.push(group[i] + data[index]);
}
group.push.apply(group, res);
if(index + 1 >= data.length) return group;
else return getGroup(data, index + 1, group);
};
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
function permute(nums) {
const res = [], path = [];
const used = new Array(nums.length).fill(false);
const dfs =()=> {
if (path.length === nums.length) {
res.push(path.slice());
return;
}
for (let i = 0; i < nums.length; i++) {
if (used[i]) continue; // 如果之前已经出现过了,直接跳
path.push(nums[i]);
used[i] = true; // 表示这个位置已经用过了
dfs(); // 递归 回溯
path.pop(); // 回溯的过程中,将当前的节点从 path 中删除
used[i] = false;
}
}
dfs();
return res;
}
const obj = {
fn1: () => console.log(this),
fn2: function() {console.log(this)}
}
obj.fn1();
obj.fn2();
const x = new obj.fn1(); // undefined/window
const y = new obj.fn2(); // obj
promise的特性
1.对象的状态不受外界影响。promise对象代表一个异步操作,有3种状态: pending(初始状态),fulfilled(成功状态),rejected(失败状态)。只有异步操作的结果才能决定当前的状态,任何其它操作都不能改变这个状态。
2.一旦状态改变就不会再变,任何时候都可以得到这个结果。promise状态的改变只有两种情况: pending 到 fulfillted, pending 到 rejected。
promise优缺点
优点:有了promise对象,就可以把异步操作以同步操作的流程表达出来,避免层层嵌套回调函数,此外promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:一旦新建就无法取消,会立即执行。如果不设置回调函数,promise 内部抛出的错误,不会反应到外部。当前处于pending状态,无法得知进行到哪一步了(刚刚开始还是即将结束)。
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
created(在模板渲染成html前调用) => computed => mounted(在模板渲染成html后调用)
同上
CSP 同上
HTTP协议:最常用的应用层
最重要的传输层
??做权限
is
关键字一般用于函数返回值类型中,判断参数是否属于某一类型,并根据结果返回对应的布尔类型。用了is,ts就可以根据is后面的类型做类型判断。比如is Element, 就可以对参数用element的属性和方法,querySelector之类的。
【代码题】
多叉树, 获取每一层的节点之和const res = {
value: 2,
children: [
{ value: 6, children: [ { value: 1 } ] },
{ value: 3, children: [ { value: 2 }, { value: 3 }, { value: 4 } ] },
{ value: 5, children: [ { value: 7 }, { value: 8 } ] }
]
};
const layerSum = function(root) {
let result = [], index = 0;
const level = (root, index) => {
if (!root) return;
if (!result[index]) result[index] = 0;
result[index] += root.value;
if(root.children) root.children.forEach(child => level(child, index+1))
};
level(root, index);
return result;
};
console.log(levelOrder(res));
【代码题】
虚拟dom转真实domconst vnode = {
tag: 'DIV',
attrs: {
id: 'app'
},
children: [{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
}]
},
{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
},
{
tag: 'A',
children: []
}
]
}
]
}
function render ( vnode, container ){
return container.appendChild( _render( vnode ) );
}
function _render( vnode ){
if ( typeof vnode === 'number' ) {
vnode = String( vnode );
}
//处理文本节点
if( typeof vnode === 'string'){
const textNode = document.createTextNode( vnode )
return textNode;
}
//处理组件
if ( typeof vnode.tag === 'function' ) {
const component = createComponent( vnode.tag, vnode.attrs );
setComponentProps( component, vnode.attrs );
return component.base;
}
//普通的dom
const dom = document.createElement( vnode.tag );
if( vnode.attrs ){
Object.keys( vnode.attrs ).forEach( key => {
const value = vnode.attrs[ key ];
setAttribute( dom, key, value ); // 设置属性
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
return dom ; // 返回虚拟dom为真正的DOM
}
//实现dom挂载到页面某个元素
const ReactDOM = {
render: ( vnode, container ) => {
container.innerHTML = '';
return render( vnode, container );
}
}
try_files 解决的是:当 nginx 找不到客户端需要的资源时该怎么办的问题。以 history 路由为例:假如你的页面 url 是 http://www.example.com/post
,你的 nginx 配置如下:
location / {
root local/web/dist
}
当你在 post 路由下刷新页面时,nginx 会返回 404。这是什么原因呢?因为我们没有告诉nginx找不到某个文件时该怎么做。root 指定了 / 对应的单页静态资源目录,从而使url映射到dist目录下。
这个配置可以让你项目的 css,js 被顺利加载,但是碰到上面的 URL,nginx 就不知所措了。因为我们的 dist 文件夹下面并没有 post 这个文件或者文件夹,所以 nginx 会给你个 404 页面。try_files 就是为了解决这个问题的,try_files 语法如下:
server {
listen 4010;
server_name localhost;
location / {
root '/root/static';
index /index.html;
try_files $uri $uri/ /index.html;
}
}
$uri 会匹配到 post
,nginx 发现 dist 目录下下面没有 post 这个文件,也没有 post 这个文件夹,所以最后会返回 dist 目录下的 index.html。这样,index.html 被浏览器加载之后,前端路由就会工作,将用户需要的资源加载出来。而我们 build 出来的 css,js 文件,由于可以被 nginx 正确找到,则不会受到影响。
【代码题】
有序数组原地去重// 快慢指针
const res = [0,0,1,1,2,2,2,2,4,4,5,5,6]
const removeDuplicates = (nums) => {
let slow = 0, fast = 1;
if(nums.length === 0) return
while (fast < nums.length) {
if (nums[fast] !== nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
fast++;
}
return nums.splice(0, slow + 1);
}
井盖和什么相关,增加不同的权重,通过一两个区去统计推算整个城市的井盖数量。
知乎答案
【代码题】
二叉树层序遍历, 每层的节点放到一个数组里给定一个二叉树,返回该二叉树层序遍历的结果,(从左到右,一层一层地遍历)
例如:
给定的二叉树是{3,9,20,#,#,15,7},
该二叉树层序遍历的结果是[[3],[9,20],[15,7]]
var levelOrder = function(root) {
let res = [];
if(root === null) return res;
let list = [];
list.push(root);
while(list.length) {
let curLen = list.length;//上一轮剩下的节点,全属于下一层
let newLevel = [];
for(let i = 0; i < curLen; i++) {//同层所有节点
let node = list.shift();//出列
newLevel.push(node.val);//push进newLevel数组
//左右子节点push进队列
if(node.left) list.push(node.left);
if(node.right) list.push(node.right);
}
res.push(newLevel);//push到res数组
};
return res;
};
console.log(levelOrder(res));
【代码题】
实现一个函数, fetchWithRetry 会自动重试3次,任意一次成功直接返回const fetchWithRetry = async (
url,
options,
retryCount = 0,
) => {
const { MAXRET = 3, ...remainingOptions } = options;
try {
return await fetch(url, remainingOptions);
} catch (error) {
// 如果重试次数没有超过,那么重新调用
if (retryCount < maxRetries) {
return fetchWithRetry(url, options, retryCount + 1);
}
// 超过最大重试次数
throw error;
}
}
// 补充超时和取消
// 创建一个 reject 的 promise
// `timeout` 毫秒
const throwOnTimeout = (timeout) =>
new Promise((_, reject) =>
setTimeout(() =>
reject(new Error("Timeout")),
timeout
),
);
const fetchWithTimeout = (
url,
options = {},
) => {
const { timeout, ...remainingOptions } = options;
// 如果超时选项被指定,那么 fetch 调用和超时通过 Promise.race 竞争
if (timeout) {
return Promise.race([
fetch(url, remainingOptions),
throwOnTimeout(timeout),
]);
}
return fetch(url, remainingOptions);
}
// 取消
const fetchWithCancel = (url, options = {}) => {
const controller = new AbortController();
const call = fetch(
url,
{ ...options, signal: controller.signal },
);
const cancel = () => controller.abort();
return [call, cancel];
};
【代码题】
链表中环的入口节点/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function (head) {
var hashSet = new Set()
while (head) {
if(hashSet.has(head)){
return head
}
hashSet.add(head)
head = head.next
}
return null
};
// 快慢指针
var detectCycle = function(head) {
if(!head) return null;
let fast = head, slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast === slow){
fast = head;
while(fast !== slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
};
快慢指针解析
将dom转换为canvas
puppeteer html2canvas dom-to-image等工具
初探JavaScript的截屏实现
js实现纯前端截屏
RT(Response Time): 1个请求所完成的时间
QPS(Query Per Second): 1秒钟内所完成的请求数量
TPS(Transactions Pre Second):tps为事务每秒的请求次数
在集群化的架构下,可以采用池化(内存池,连接池,线程池),分布式缓存,分布式消息队列,流控技术(服务降级,应用拆分,限流)和数据库高并发(分库分表,读写分离等)提高并发能力
使用缓存 & 为高量级的数据读取做缓存预加载机制目的:利用内存读取替代磁盘IO,减少磁盘IO次数,从而降低读取耗时
读写分离 目的:通过对用户访问的数据分析,一定是读数据库的量要远远大于写数据库的量,这时读就成为瓶颈。读请求一定程度上会阻塞写请求。
而读写的可靠性级别也是不一样的,写的可靠性要求会更高,针对读写的不同的要求,进行数据库的读写分离
分库分表
水平分表: 业务并发量不大, 单表数据大,检索数据慢
垂直分表: 业务并发量不大, 大表,存大文本
水平分库:业务并发量大,单表数据大
垂直分库: 业务并发量大, 业务模块分库,单模块业务量大
百万 QPS 前端性能监控系统设计与实现
首先要了解google图片或者百度图片是什么样的。他是自动填充满每一行的宽度,但是没张图片并不是固定宽度,同时支持无限下拉以及图片懒加载。还有一点刷新后仍然是之前的排列效果
那么我们一定是要对图片进行筛选才能达到我们想要的效果,从数据中筛选出符合条件的图片排列返回(涉及到算法)
看这个: 图像布局算法 - Google Plus
css grid布局实现瀑布流
vue 基于原生JS实现等宽瀑布流布局
无限下拉其实就是分页查询
懒加载
var arr = [{url: ''} , ...]
const div = document.querySelector('div')
arr.forEach(function (item ) {
const imgTag = new Image()
imgTag.dataset.src = item.url
imgTag.dataset.load = 0
imgTag.style.height = '300px'
imgTag.style.width = '300px'
imgTag.style.display = 'block'
div.appendChild(imgTag)
})
function lazyload() {
for (let i = 0; i < div.children.length; i++) {
if (div.children[i].dataset.load == 1) continue
if (div.children[i].offsetTop <= document.documentElement.scrollTop + document.documentElement.clientHeight) {
div.children[i].src = div.children[i].dataset.src
div.children[i].dataset.load = 1
}
}
}
window.onscroll = function () {
lazyload()
}
Js 有个新类型BigInt
借助第三方库实现
将数字转换为字符串处理
来源: 力扣 Riddle
// 最简洁
const getLeastNumbers = (arr, k) => {
return arr.sort((a, b) => a - b).slice(0, k)
};
// 快排
const getLeastNumbers = (arr, k) => {
function qSort(arr, low, height) {
if (low >= height) return
let flag = arr[low]
let left = low, right = height
while (left < right) {
while (arr[right] >= flag && left < right) right--
while (arr[left] <= flag && left < right) left++
if (left < right) {
let temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
}
}
arr[low] = arr[right]
arr[right] = flag
qSort(arr, low, right - 1)
qSort(arr, right + 1, height)
}
qSort(arr, 0, arr.length - 1)
return arr.slice(0, k)
};
// 优化版
const getLeastNumbers = (arr, k) => {
if (k >= arr.length) return arr;
function qSort(arr, low, height) {
if (low >= height) return
let flag = arr[low]
let left = low, right = height
while (left < right) {
while (arr[right] >= flag && left < right) right--
while (arr[left] <= flag && left < right) left++
if (left < right) {
let temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
}
}
arr[low] = arr[right]
arr[right] = flag
if (right == k) return
if (k < right) qSort(arr, low, right - 1)
else qSort(arr, right + 1, height)
}
qSort(arr, 0, arr.length - 1)
return arr.slice(0, k)
};
首先分开跑8次,这样就取到了32位剩下的运动员,将每一组的第一组在一起跑一次,剩下四个,分别叫老大,老二,老三,老四
这样淘汰掉后四名第一次所在的组,剩下16人。
可以得出结论第二轮跑第一的人一定是第一。
那么还剩3个名额,可见老四所在的组除了老四其他人都被pass,老三所在的组除了老三和小老三其他都被pass,老二所在的组除了老二,小老二,小小老二 其他pass,老大在的组前四个都留下。剩下9人
除了老大 老二剩下人一起跑(因为小老二和老三一定没老二快),在这次比赛中,如果小老二或者老三排名第一或者第二,那从该次比赛中取出前两个,和老大,老二,一起作为前4名。如果小老二或者老三没排名或者排在第三或者第四,那么将老大组下的三兄弟和老二比赛一次,取前三名和老大一起作为前4名。剩下4人
经典面试题:64匹马,8个赛道,找出前4名最少比赛多少场?
[面经] 5年前端 - 历时1个月收获7个offer