元旦匆匆而过,2020年的春节又接踵而来,大家除了忙的提着裤子加班、年底冲冲冲外,还有着对于明年的迷茫和期待!2019年有多少苦涩心酸,2020年就有更多幸福美好,加油,奥利给!怀着一颗积极向上的心,来面对未来每一天的挑战!
所谓“兵马未动,粮草先行”,我们打响明天的战役也需要精神食粮来做后勤保障才是。在此我整理了多位从业者和我在2019年底至2020年初的一厂面试精选题,希望对磨砺锋芒、奋发向上的小伙伴有所帮助,祝你早日剑指大厂,扬帆起航,奥利给!
有跨站脚本攻击(XSS)、跨站请求伪造(CSRF)、点击劫持、SQL注入、DDOS攻击、DNS劫持
永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双"-"进行转换等。
永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。
xss:跨站点攻击。xss攻击的主要目的是想办法获取目标攻击网站的cookie,因为有了cookie相当于有了session,有了这些信息就可以在任意能接进互联网的PC登陆该网站,并以其他人的身份登陆做破坏。预防措施防止下发界面显示html标签,把>等符号转义。
csrf:跨站点伪装请求。csrf攻击的主要目的是让用户在不知情的情况下攻击自己已登录的一个系统,类似于钓鱼。如用户当前已经登陆了邮箱或bbs,同时用户又在使用另外一个,已经被你控制的网站,我们姑且叫它钓鱼网站。这个网站上面可能因为某个图片吸引你,你去点击一下,此时可能就会触发一个js的点击事件,构造一个bbs发帖的请求,去往你的bbs发帖,由于当前你的浏览器状态已经是登陆状态,所以session登陆cookie信息都会跟正常的请求一样,纯天然的利用当前的登陆状态,让用户在不知情的情况下,帮你发帖或干其他事情。预防措施,请求加入随机数,让钓鱼网站无法正常伪造请求。
DNS劫持 DNS劫持就是通过劫持了DNS服务器,通过某些手段取得某域名的解析记录控制权,进而修改此域名的解析结果,导致对该域名的访问由原IP地址转入到修改后的指定IP。DNS劫持通过篡改DNS服务器上的数据返回给用户一个错误的查询结果来实现的。
DNS污染 DNS污染,指的是用户访问一个地址,国内的服务器(非DNS)监控到用户访问的已经被标记地址时,服务器伪装成DNS服务器向用户发回错误的地址的行为。范例,访问Youtube、Facebook之类网站等出现的状况。
1.DNS解析
2.发送tcp连接
3.发送http请求
4.服务器处理请求并返回http报文
5.浏览器解析渲染界面
6.连接结束
参考地址:https://www.cnblogs.com/yuanzhiguo/p/8119470.html
我们通常认为浏览器开始渲染 标签或者解析完
标签的时刻就是页面白屏结束的时间点。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>白屏title>
<script type="text/javascript">
// 不兼容performance.timing 的浏览器,如IE8
window.pageStartTime = Date.now();
script>
<link rel="stylesheet" href="common.css">
<link rel="stylesheet" href="page.css">
<script type="text/javascript">
// 白屏时间结束点
window.firstPaint = Date.now();
script>
head>
<body>
body>
html>
因此白屏时间则可以这样计算出:
可使用Performance API时
白屏时间 = firstPaint - performance.timing.navigationStart;
不可使用Performance API时
白屏时间 = firstPaint - pageStartTime
参考地址: https://segmentfault.com/p/1210000011237223/read
什么是xss攻击:
攻击者通过篡改网页,嵌入恶意js代码,当用户访问网页时,被嵌入的js代码会被执行,从而达到恶意攻击用户的一种方式;
如何防范 XSS 攻击:
1、对输入的字符串做长度限制;
2、对用户的输入进行过滤,如对& < > " ’ /等进行转义;
3、获取用户的输入,不用innerHtml,用innerText.
CSRF攻击的原理:
跨站请求伪造,攻击者构造某个网站后台接口的请求地址,诱导用户去点击或者用特殊的方法让 该请求地址自动加载,用户在登陆的情况下,这个请求被服务器端接收后误以为是用户合法的操作,
对于get形式的接口跨域轻易被攻击,对于psot形式的接口也不是100%安全,攻击者可诱导用户带from表单可用post形式提交参数的页面。
如何防范CSRF攻击:
1、验证 HTTP Referer 字段;
2、在请求地址中添加 token 并验证;
3、在 HTTP 头中自定义属性并验证。
合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title要有所不同;description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同;keywords列举出重要关键词即可
语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
重要内容不要用js输出:爬虫不会执行js获取内容
少用iframe:搜索引擎不会抓取iframe中的内容
非装饰性图片必须加alt
提高网站速度:网站速度是搜索引擎排序的一个重要指标
Hybrid App主要以JS+Native两者相互调用为主,从开发层面实现“一次开发,多处运行”的机制,成为真正适合跨平台的开发。Hybrid App兼具了Native App良好用户体验的优势,也兼具了Web App使用HTML5跨平台开发低成本的优势。
目前已经有众多Hybrid App开发成功应用,比如美团、爱奇艺、微信等知名移动应用,都是采用Hybrid App开发模式。
移动应用开发的方式,目前主要有三种:
Native App: 本地应用程序(原生App),一般依托于操作系统,有很强的交互,是一个完整的App,可拓展性强,需要用户下载安装使用。(简单来说,原生应用是特别为某种操作系统开发的,比如iOS、Android、黑莓等等,它们是在各自的移动设备上运行的)
该模式通常是由“云服务器数据+APP应用客户端”两部份构成,APP应用所有的UI元素、数据内容、逻辑框架均安装在手机终端上。
原生应用程序是某一个移动平台(比如iOS或安卓)所特有的,使用相应平台支持的开发工具和语言(比如iOS平台支持Xcode和Objective-C,安卓平台支持Eclipse和Java)。原生应用程序看起来(外观)和运行起来(性能)是最佳的。
Web App:网页应用程序(移动web)指采用Html5语言写出的App,不需要下载安装。类似于现在所说的轻应用。生存在浏览器中的应用,基本上可以说是触屏版的网页应用。(Web应用本质上是为移动浏览器设计的基于Web的应用,它们是用普通Web开发语言开发的,可以在各种智能手机浏览器上运行)
Web App开发即是一种框架型APP开发模式(HTML5 APP 框架开发模式),该开发具有跨平台的优势,该模式通常由“HTML5云网站+APP应用客户端”两部份构成,APP应用客户端只需安装应用的框架部份,而应用的数据则是每次打开APP的时候,去云端取数据呈现给手机用户。
HTML5应用程序使用标准的Web技术,通常是HTML5、JavaScript和CSS。这种只编写一次、可到处运行的移动开发方法构建的跨平台移动应用程序可以在多个设备上运行。虽然开发人员单单使用HTML5和JavaScript就能构建功能复杂的应用程序,但仍然存在一些重大的局限性,具体包括会话管理、安全离线存储以及访问原生设备功能(摄像头、日历和地理位置等)。
Hybrid App:混合应用程序(混合App)指的是半原生半Web的混合类App。需要下载安装,看上去类似Native App,但只有很少的UI Web View,访问的内容是 Web 。
混合应用程序让开发人员可以把HTML5应用程序嵌入到一个细薄的原生容器里面,集原生应用程序和HTML5应用程序的优点(及缺点)于一体。
混合应用大家都知道是原生应用和Web应用的结合体,采用了原生应用的一部分、Web应用的一部分,所以必须在部分在设备上运行、部分在Web上运行。不过混合应用中比例很自由,比如Web 占90%,原生占10%;或者各占50%。
1、meta:viewport 防止浏览器自动缩放
<meta name="viewport" content="width=device-width , user-scalable=no , initial-scale=1.0 , maximum-scale=1.0 , minimum-scale=1.0">
2、响应式布局(responsive)
响应式布局可使网站在不同的设备上浏览时对应不同分辨率皆有适合的呈现
其布局通过媒体查询@media实现,
新闻及门户网站可以用响应式布局,但大型网站媒体查询有其局限性
实际上除了响应式,网站通常会采用:1.提供两套html由后端根据用户设备来切换 2.提供两个完全不同的url由后端根据用户设备来跳转
3、通过动态rem实现
css常见的单位有: px,em,rem ,vh ,vw
font-size
一样需要注意:
<script>
document.write(` `)
script>
参考地址:https://www.jianshu.com/p/c6d82db7ad62
Git回滚代码到某个commit
回退命令:
git reset --hard HEAD^ 回退到上个版本
git reset --hard HEAD~3 回退到前3次提交之前,以此类推,回退到n次提交之前
git reset --hard commit_id 退到/进到,指定commit的哈希码(这次提交之前或之后的提交都会回滚)
参考地址 :https://www.jianshu.com/p/57f0626a1432
设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
}
// 代理实现单例模式
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 测试单体模式的实例
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的
console.log(a === b); //true
/*策略类*/
var levelOBJ = {
"A": function(money) {
return money * 4;
},
"B" : function(money) {
return money * 3;
},
"C" : function(money) {
return money * 2;
}
};
/*环境类*/
var calculateBouns =function(level,money) {
return levelOBJ[level](money);
};
console.log(calculateBouns('A',10000)); // 40000
var imgFunc = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image();
img.onload = function() {
imgFunc.setSrc(this.src);
}
return {
setSrc: function(src) {
imgFunc.setSrc('./loading,gif');
img.src = src;
}
}
})();
proxyImage.setSrc('./pic.png');
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
参考: https://www.cnblogs.com/bopo/p/9255654.html
给定一个长度为N的数组,找出一个最长的单调递增子序列,子序列不一定连续,但初始顺序不能乱。
例如:给定一个长度为6的数组A{4, 5, 7, 1,3, 9},则其最长的单调递增子序列为{4,5,7,9},长度为4。
//找出最长子序列的长度
int LargestListFind()
{
int vec[6] = {4,5,7,1,3,9};
int d[6] = {1};
for(unsigned int i = 0; i < 6; i++)
{
d[i] = 1;
for(unsigned int j = 0; j < i; j++)
{
if(vec[j] < vec[i] && d[j] >= d[i])
d[i] = d[j] + 1;
}
}
int Max = -1;
for(unsigned int i = 0; i < 6; i++)
if(Max < d[i])
Max = d[i];
return Max;
}
参考 : https://blog.csdn.net/love20165104027/article/details/79618367
我们先来整理一下什么是平衡二叉树?
满足以下两点的就是平衡二叉树:
1.左右子树的高度差不能超过1
2.左右子树也是平衡二叉树
int IsBalance(BNode *root,int *pHeight)
{
if (root == NULL)
{
*pHeight = 0;
return 1;
}
int leftHeight;
int rightHeight;
int leftBalance = IsBalance(root->left, &leftHeight);
int rightBalance = IsBalance(root->right, &leftHeight);
*pHeight = MAX(leftHeight, rightHeight) + 1;
if (leftBalance == 0 || rightBalance == 0)
{
return 0;
}
int diff = leftHeight - rightHeight;
if (diff < -1 || diff>1)
{
return 0;
}
else {
return 1;
}
}
参考 :https://blog.csdn.net/WYH19951220/article/details/88891672
示例:
给定 “abcabcbb” ,没有重复字符的最长子串是 “abc” ,那么长度就是3。
给定 “bbbbb” ,最长的子串就是 “b” ,长度是1。
给定 “pwwkew” ,最长子串是 “wke” ,长度是3。请注意答案必须是一个子串,“pwke” 是 子序列 而不是子串。
public int LengthOfLongestSubstring(string str)
{
int resLength = 0;
int strLength = str.Length;
int i = 0, j = 0;
HashSet<string> hashSet = new HashSet<string>();
while (i < strLength && j < strLength)
{
string oneStrJ = str.Substring(j,1);
if (!hashSet.Contains(oneStrJ))
{
hashSet.Add(oneStrJ);
j++;
resLength = Math.Max(resLength,j-i);
} else {
string oneStrI = str.Substring(i, 1);
hashSet.Remove(oneStrI);
i++;
}
}
return resLength;
}
参考:https://blog.csdn.net/xc121566/article/details/80827129
判断是否存在一条直线,让所有线段在直线上的投影存在一个公共点。这个问题可以转化为这样的问题:
是否存在一条直线可以穿过所有线段,进而转化成经过某两个线段端点的直线能否穿过所有线段。(具体证明可以自行百度)
现在问题简化了,只需要枚举两个端点(包括同一条线段的)判断是否相交就行了。
坑:枚举的两个端点可能重合
#include <cstdio>
#include <iostream>
#include <cmath>
const double eps=1e-8;
using namespace std;
struct Point {
double x, y;
Point(double x=0, double y=0) : x(x),y(y) {}
};
int n,T;
Point p[1005][2];
typedef Point Vector;
Vector operator + (Vector A,Vector B) {return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B) {return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p) {return Vector(A.x*p,A.y*p);}
Vector operator / (Vector A,double p) {return Vector(A.x/p,A.y/p);}
bool operator < (const Point& a,const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
int dcmp(double x) {
if (fabs(x) < eps) return 0;
else return x<0?-1:1;
}
bool operator == (const Point &a,const Point &b) {
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
double Cross(Vector A,Vector B) {
return A.x*B.y - A.y*B.x;
}
// 判断直线与线段是否相交
bool SegLineInt(Point a1,Point a2,Point b1,Point b2) {
double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1);
return dcmp(c1)*dcmp(c2)<=0;
}
// 判断一条直线是否满足条件
bool judge(Point A,Point B)
{
// 一个坑,选择的两个点不能重合
if (A==B) return false;
for (int i=1;i<=n;i++)
if (!SegLineInt(A,B,p[i][0],p[i][1]))
return false;
return true;
}
int main()
{
cin >> T;
while(T--)
{
cin >> n;
for (int i=1;i<=n;i++)
scanf("%lf%lf%lf%lf",&p[i][0].x,&p[i][0].y,&p[i][1].x,&p[i][1].y);
if (n==1) {printf("Yes!\n"); continue;}
bool flag=0;
// 枚举两个端点
for (int i=1;i<=n&&!flag;i++)
{
for (int j=i+1;j<=n;j++)
{
flag|=judge(p[i][0],p[j][0]);
flag|=judge(p[i][0],p[j][1]);
flag|=judge(p[i][1],p[j][0]);
flag|=judge(p[i][1],p[j][1]);
if (flag) break;
}
}
if (flag) printf("Yes!\n");
else printf("No!\n");
}
return 0;
}
参考:https://blog.csdn.net/radium_1209/article/details/89520666
参考:https://blog.csdn.net/baidu_38621657/article/details/88369398
参考:https://blog.csdn.net/ValDC_Morning/article/details/76615752
https://www.cnblogs.com/clarke157/p/6808951.html
插入排序算法是基于某序列已经有序排列的情况下,通过一次插入一个元素的方式按照原有排序方式增加元素。这种比较是从该有序序列的最末端开始执行,即要插入序列中的元素最先和有序序列中最大的元素比较,若其大于该最大元素,则可直接插入最大元素的后面即可,否则再向前一位比较查找直至找到应该插入的位置为止。插入排序的基本思想是,每次将1个待排序的记录按其关键字大小插入到前面已经排好序的子序列中,寻找最适当的位置,直至全部记录插入完毕。执行过程中,若遇到和插入元素相等的位置,则将要插人的元素放在该相等元素的后面,因此插入该元素后并未改变原序列的前后顺序。我们认为插入排序也是一种稳定的排序方法。插入排序分直接插入排序、折半插入排序和希尔排序3类。
冒泡排序算法是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。冒泡排序的基本思想是,首先将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换,再对第2个和第3个记录的关键字进行比较,依次类推,重复进行上述计算,直至完成第(n一1)个和第n个记录的关键字之间的比较,此后,再按照上述过程进行第2次、第3次排序,直至整个序列有序为止。排序过程中要特别注意的是,当相邻两个元素大小一致时,这一步操作就不需要交换位置,因此也说明冒泡排序是一种严格的稳定排序算法,它不改变序列中相同元素之间的相对位置关系。
选择排序算法的基本思路是为每一个位置选择当前最小的元素。选择排序的基本思想是,基于直接选择排序和堆排序这两种基本的简单排序方法。首先从第1个位置开始对全部元素进行选择,选出全部元素中最小的给该位置,再对第2个位置进行选择,在剩余元素中选择最小的给该位置即可;以此类推,重复进行“最小元素”的选择,直至完成第(n-1)个位置的元素选择,则第n个位置就只剩唯一的最大元素,此时不需再进行选择。使用这种排序时,要注意其中一个不同于冒泡法的细节。举例说明:序列58539.我们知道第一遍选择第1个元素“5”会和元素“3”交换,那么原序列中的两个相同元素“5”之间的前后相对顺序就发生了改变。因此,我们说选择排序不是稳定的排序算法,它在计算过程中会破坏稳定性。
快速排序的基本思想是:通过一趟排序算法把所需要排序的序列的元素分割成两大块,其中,一部分的元素都要小于或等于另外一部分的序列元素,然后仍根据该种方法对划分后的这两块序列的元素分别再次实行快速排序算法,排序实现的整个过程可以是递归的来进行调用,最终能够实现将所需排序的无序序列元素变为一个有序的序列。
归并排序算法就是把序列递归划分成为一个个短序列,以其中只有1个元素的直接序列或者只有2个元素的序列作为短序列的递归出口,再将全部有序的短序列按照一定的规则进行排序为长序列。归并排序融合了分治策略,即将含有n个记录的初始序列中的每个记录均视为长度为1的子序列,再将这n个子序列两两合并得到n/2个长度为2(当凡为奇数时会出现长度为l的情况)的有序子序列;将上述步骤重复操作,直至得到1个长度为n的有序长序列。需要注意的是,在进行元素比较和交换时,若两个元素大小相等则不必刻意交换位置,因此该算法不会破坏序列的稳定性,即归并排序也是稳定的排序算法。
参考: https://blog.csdn.net/hanxiaoran906/article/details/81488232
写一个程序创建一棵二叉树,并按照一定规则,输出二叉树根节点到叶子节点的路径。
规则如下:
1、从最顶端的根结点,到最下面的叶子节点,计算路径通过的所有节点的和,如果与设置的某一值的相同,那么输出这条路径上的所有节点。
2、从根节点遍历树时,请请按照左到右遍历,即优先访问左子树的节点。
二叉树创建规则:从上到下一层一层的,按照从左到右的顺序进行构造
import java.util.Scanner;
public class Main {
public static int counter = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = Integer.valueOf(sc.nextLine());
String line = sc.nextLine();
compute(N, line);
}
public static void compute(int N, String line){
String[] arr = line.split(",");
int len = arr.length;
//从index=1开始存数据
Node[] nodeArr = new Node[len + 1];
for(int i = 0; i < len; i++){
int val = Integer.valueOf(arr[i]);
nodeArr[i + 1] = new Node(val);
}
//构建二叉树
Node root = nodeArr[1];
for(int i = 1; i < len + 1; i++){
if(i * 2 < len + 1){
nodeArr[i].left = nodeArr[2 * i];
}
if(i * 2 + 1 < len + 1){
nodeArr[i].right = nodeArr[2 * i + 1];
}
}
// printTree(root);
printPaths(root, len, N);
}
public static void printTree(Node root){
if(root == null){
return;
}
System.out.println(root.val);
if(root.left != null){
printTree(root.left);
}
if(root.right != null){
printTree(root.right);
}
}
public static void printPaths(Node root, int n, int N) {
int[] path = new int[n];
printPaths(root, path, 0, N);
if(counter == 0){
System.out.println("error");
}
}
public static void printPaths(Node root, int[] path, int pathLen, int N) {
if (root == null) return;
path[pathLen++] = root.val;
if (root.left == null && root.right == null) {
printArray(path, pathLen, N);
}
else {
printPaths(root.left, path, pathLen, N);
printPaths(root.right, path, pathLen, N);
}
}
public static void printArray(int[] ints, int len, int N) {
int total = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
sb.append(ints[i] + ",");
total += ints[i];
}
if(total == N){
System.out.println(sb.toString().substring(0, sb.toString().length() - 1));
counter++;
}
}
}
class Node{
public int val;
public Node left;
public Node right;
public Node(int val){
this.val = val;
}
}
给定一个链表,要求每隔k个元素反转 即链表为1->2->3->4->5->6->7->8
当k=2时,链表为2->1->4->3->6->5->8->7
当k=5时,链表5->4->3->2->1->6->7->8
class Node<T> {
public T data;
public Node<T> next;
Node(T dataPortion) {
data = dataPortion;
next = null;
}
Node(T dataPortion, Node<T> nextNode) {
data = dataPortion;
next = nextNode;
}
}
public class ListKReverse {
public static void main(String[] args) {
ListKReverse s = new ListKReverse();
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
Node n5 = new Node(5);
Node n6 = new Node(6);
Node n7 = new Node(7);
Node n8 = new Node(8);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
n6.next = n7;
n7.next = n8;
Node head = s.ReverseInGroups(n1, 4);
while (head != null) {
System.out.print(head.data+" ");
head = head.next;
}
System.out.println();
}
public Node ReverseInGroups(Node current, int k) {
if (current == null || current.next == null ) return current;
int n=0;
Node oldHead=current;
while(current!=null)
{
current=current.next;
n++;
}
System.out.println(n);
int reverseNum=n/k;
current=oldHead;
Node newHead = current;
Node previousGroupTail = null;
int count = 1;
int num=0;
while (current != null&&num<reverseNum) {
Node groupTail = current;
Node prev = null;
Node next = null;
for (int i = 1; i <= k && current != null; i++) {
next = current.next;
current.next = prev;
prev = current;
current = next;
}
if (count == 1) {
newHead = prev;
count++;
}
if (previousGroupTail != null) {
previousGroupTail.next = prev;
}
previousGroupTail = groupTail;
num++;
}
if(current!=null)
if (previousGroupTail != null)
previousGroupTail.next = current;
return newHead;
}
}
递归算法:
f(0) = 0 f(1) = 1 f(2) = 2 其次,当n=3时,青蛙的到达第三阶的前第位置有两种情况,阶一,阶二,所以递推f(3)=f(2)+f(1).即n>=3时,f(n)=f(n-1)+f(n-2)。
long facinabo(int n)
{
if(n==0)
{
return 0;
}
if(n==1)
{
return 1;
}
if(n==2)
{
return 2;
}
else
{
return facinabo(n-1)+facinabo(n-2);
}
}
复杂度:O(n*n)。
非递归算法
long facinabo(int n)
{
int sum=0;
int q1=1;
int q2=2;
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
if(n==2) {
return 2;
}
for(int i=3;i<=n;i++){
sum = q1+q2;
q1=q2;
q2=sum;
}
return sum;
}
时间复杂度:O(n)。
var ans,res,len;
var dfs=function(index,sum,candidates,target){
if(sum===target){
var tmp=res.map(function(item){
return item;
})
ans.push(tmp);
// console.log(res,ans);
return ;
}
for(var i=index;itarget)
continue;
res.push(candidates[i]);
dfs(i,sum+candidates[i],candidates,target);
res.pop();
}
}
var combinationSum = function(candidates, target) {
ans=[];
len=candidates.length;
candidates.sort((a,b)=>a-b);
for(var i=0;i
方法一:正则实现
//正则实现
function format (num) {
var reg=/\d{1,3}(?=(\d{3})+$)/g;
return num.toString().replace(reg, '$&,');
}
//基础
function format(num){
num+='';
var str="";
for(var i=num.length-1,j=1;i>=0;i--,j++){
if(j%3===0 & i!=0){
str+=num[i]+',';
}else{
str+=num[i];
}
}
return str.split('').reverse().join('');
}
方法二:for循环正常思维算法
function format(num){
num=num+'';//数字转字符串
var str="";//字符串累加
for(var i=num.length- 1,j=1;i>=0;i--,j++){
if(j%3==0 && i!=0){//每隔三位加逗号,过滤正好在第一个数字的情况
str+=num[i]+",";//加千分位逗号
continue;
}
str+=num[i];//倒着累加数字
}
return str.split('').reverse().join("");//字符串=>数组=>反转=>字符串
}
方法三:slice+while循环
function format(num) {
var arr = [],
str = num + '',
count = str.length;
while (count >= 3) {
arr.unshift(str.slice(count - 3, count));
count -= 3;
}
// 如果是不是3的倍数就另外追加到上去
str.length % 3 && arr.unshift(str.slice(0, str.length % 3));
return arr.toString();
}
方法四:reduce版
function format(num) {
var str = num+'';
// ["8", "7", "6", "5", "4", "3", "2", "1"]
return str.split("").reverse().reduce((prev, next, index) => {
return ((index % 3) ? next : (next + ',')) + prev;
})
}
console.log(format(12345678));
设三只变色龙分别为x y z当任意两只相遇边恒另外一只时,其变换成课简化为:(x-n)(y-n) (z+zn) n= 0,1,2,…
其中n =0 代表没有穿绳变化的情况。那么只要变化后满足下述三种情形之一,则代表可以变成同一种颜色:
x-n = y-n
x-n = z+2n
y-n = z+2n
(即,有两种颜色的变色龙数量相等时,可全部变为第三种颜色)
解答方程可得:
上述三种情况实质上可以用一种情况代表:
x-y = 3n (n =0,1,2,…)
即,三种变色龙中,任意两种的数量相乘为3的倍数时,即可变成为同一种颜色的变色龙。
参考地址:https://blog.csdn.net/qq_32657025/article/details/79599954
对前端未来有什么看法
简单介绍下自己
讲讲项目,遇见比较有趣的点,遇到的困难 (快手)
你还面过哪些公司呢?有offer吗?
用三句话简单评价一下你自己
如何看待加班
未来职业规划
在与后台沟通过程中,遇见过什么困难?如何联调?
自我介绍
印象最深刻的一个项目,为什么,这个项目给你的最大的收获是啥?
近期遇到的最大的困难是啥?如何解决?期间是如何努力的?
最近印象最深刻的事情是啥?
学习能力强的原因是啥?
有什么业余爱好?
最近去哪些地方玩过?
如果老板提一个很难实现的需求,如何解决?
银行和互联网的区别,为啥想要到银行工作?
如果银行和互联网同时给你offer,怎么选?
为什么不留原公司,原公司有哪些地方让你想吐槽?如何解决这些问题?会找领导主动反馈吗?
觉得原公司有啥不合理的
有别的offer吗? 如果面临多个offer选择,你会怎么考虑?(腾讯)
讲讲项目,遇见比较有趣的点,遇到的困难(快手)
此处没有标准答案,因为每个人的情况不同,大家多面几次这些问题都应该不是问题了。
还有2件事拜托大家
一:求赞 求收藏 求分享 求留言,让更多的人看到这篇内容
二:欢迎添加我的个人微信
备注“资料”, 300多篇原创技术文章,海量的视频资料即可获得
备注“加群”,我会拉你进技术交流群,群里大牛学霸具在,哪怕您做个潜水鱼也会学到很多东西
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)下
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)中
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)上
11道浏览器原理面试题
这儿有20道大厂面试题等你查收
2020 前端面试 | “HTML + CSS + JS”专题
图解浏览器的工作原理(史上最全)
BAT前端经典面试问题:史上最最最详细的手写Promise教程