字符串
最长公共子串(LSC)
//方法一:普通的方法,将较短长度的串赋值给str1,然后在短串中
//取它的子串,判断该子串是否在长串中,在的话maxlen+1,将该
//子串赋值给maxstr,i往后移;如果不在i往后移
function LCS(str1,str2){
if(!str1 || !str2) return '';
let maxlen=0;
let maxstr='';
if(str1.length>str2.length) [str1,str2]=[str2,str1];
for(let i=0;i<str1.length;i++){
if(str2.indexOf(str1.slice(i-maxlen,i+1))!=-1){
maxstr=str1.slice(i-maxlen,i+1);
maxlen+=1;
}
}
return maxstr;
}
console.log(LCS("1AB2345CD","12345EF"));
//方法二,动态规划
//动态规划
//思路:首先用一个全为0的二维数组dp(l1*l2)
//来保存结果,最后结果最大的那个就是最长公共子串的长度
//在str1[i]==str2[j]时修改dp[i][j]的值,注意边界
//(i==1 || j==1),如果不是边界直接为左上的值+1,最后
//最大的那个数就是最大公共子串的长度
function LCS(str1,str2){
if(!str1 || !str2) return '';
let l1=str1.length,l2=str2.length;
let dp=[];
let endIndex=0,maxlen=0;
//初始化二维数组
for(let i=0;i<l1;i++){
let temp=[];
for(let j=0;j<l2;j++){
temp[j]=0;
}
dp.push(temp);
}
//动态规划
for(let i=0;i<l1;i++){
for(let j=0;j<l2;j++){
if(str1[i]==str2[j]){
// 处理边界
if(i==0 || j==0) dp[i][j]=1;
else dp[i][j]=dp[i-1][j-1]+1;
}
if(dp[i][j]>maxlen){
endIndex=i;
maxlen=dp[i][j];
}
}
}
return str1.substr(endIndex-maxlen+1,maxlen);
}
最长公共前缀
//思路将strs排序,将strs的第一个字符串first
//和最后一个字符串last进行比较,如果first[i]===last[i]
//,就将该字符添加到结果字符串中,否则直接返回res。
function longestFront(strs){
if(!strs || !strs.length) return '';
strs=strs.sort();
let first=strs[0];
let last=strs[strs.length-1];
let res='';
for(let i=0;i<first.length;i++){
if(first[i]===last[i]){
res+=first[i];
}
else return res;
}
return res;
}
console.log(longestFront(['flower','flight','flow']));
最长无重复字符子串
//思路:定义两个指针i和j,初始化都为0,substr表示临时子串,
//maxlength表示最大长度,flag标识最长子串的起始位置。
//在i<ste.legth&&j<str.length时循环,如果substr中不存在
//str[j],则在substr中添加str[j],j++;如果存在,则i++,
//j=i,substr=‘’,
//每次循环都判断maxlength和substr.length的大小,并记录flag
function maxLength(str){
let i=0,j=i;
let substr='';
let maxLength=0;
let flag;
while(i<str.length && j<str.length){
if(substr.indexOf(str[j])===-1){
substr+=str[j];
j++;
}
else{
i++;
j=i;
substr='';
}
if (maxLength<substr.length) {
flag=i;
maxLength=substr.length;
}
}
return str.substr(flag,maxLength);
}
console.log(maxLength('absdaqws'))
括号匹配
//思路:使用栈,遇到左括号就进站,遇到右括号就出栈,并判断出栈是否为对应的左括号,
//如果不是直接返回false,最后判断栈中是否还有元素,有的话返回false,最后返回true
/*
给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。
*/
function isValid(str){
let stack=[];
for(let i=0;i<str.length;i++){
if (str[i]==='(' || str[i]==='[' || str[i]==='{') {
stack.push(str[i]);
}
else if(str[i]===')' && stack.pop(str[i])!=='(') return false;
else if(str[i]===']' && stack.pop(str[i])!=='[') return false;
else if(str[i]==='}' && stack.pop(str[i])!=='{') return false;
}
if(stack.length>0) return false;
return true;
}
console.log(isValid('{2+[2+(2+2)]}')) //true
最长括号子串
function longValid(s){
// write code here
let res = 0;
let stack = [-1];
for(let i=0; i<s.length; i++){
if(s[i] === '('){
stack.push(i);
} else {
stack.pop();
if(stack.length){
res = Math.max(res, i-stack[stack.length-1]);
} else {
stack.push(i);
}
}
}
return res;
}
console.log(longValid('((())'))
判断回文
// 判断回文
function isHuiWen(str){
// 方法一,前后各指针,同步走的数是否一致
// for(let i=0,j=str.length-1;i<str.length,j>=0;i++,j--){
// if(str[i]===str[j]) return true;
// else return false;
// }
// 方法二
return str===str.split('').reverse().join('');
//判断一个数是否是回文
var isPalindrome = function(x) {
if ( x < 0 ) return false
let str = '' + x
return Array.from(str).reverse().join('') === str
};
}
console.log(isHuiWen('abbac'))
最长回文子串
//方法一:暴力破解,首先求出所有子串,判断它们是否为回文,
//如果是,找出最大的那个并记录下来返回
//这里判断回文和求所有的子串函数省略,时间复杂度为o(n^3)
function longestHuiWen(str){
let res=allSubstr(str);
let maxlength=0;
let maxstr=''
for(let i=0;i<res.length;i++){
if(isHuiWen(res[i])){
if(maxlength<res[i].length){
maxlength=res[i].length;
maxstr=res[i];
}
}
}
return maxstr;
}
console.log(longestHuiWen('cbbd'))
方法二:动态规划
//首先考虑如果字符串长度为1,那么答案就是其本身
//如果字符串长度等于2,那么如果s[i] == s[j] 则说明该字符串为回文
//那么如果长度大于2呢?s[i] == s[j]的情况下s[i + 1] == s[j-1],也说明该字符串为回文
var longestPalindrome = function(s) {
if (s.length == 1) {
// 长度1,返回本身
return s;
}
// 创建二阶数组存储从j到i是否是回文数组,0为不回文,1为回文
let arr = [];
for (let i = 0; i < s.length; i ++) {
arr[i] = [];
};
// 存储最长回文子串的起始位置
let begin = 0;
// 存储最长子串的长度
let max = 0;
for(let i = 0; i < s.length; i ++) {
let j = 0;
while (j <= i) {
// 如果 i-j <= 1 时,说明i位置和j位置要么是重合的,要么是相邻的,即为最后一次查找
// 否则继续查询[j + 1]到[i - 1]是否为回文
if( s[j] == s[i] && (i - j <= 1 || arr[j+1][i-1] ) ) {
// 如果符合上述条件,说明j到i是回文
arr[j][i] = 1
if (i - j + 1 > max) {
// 如果当前子串大于存储的子串长度,则替换之
begin = j;
// 注意+1,比如从3到5的长度为3 = 5 - 3 + 1
max = i - j + 1;
}
}
j ++;
}
}
return s.substr(begin, max);
}
全排列
//思路:把str分为两部分,第一部分为第一个字母str[0],
//第二部分为剩余字符串str.slice(1),递归后面剩余的字符串,
//求他们的全排列,并保存到rest数组中,然后将第一个字符插进去。
//最后再去重排序
function Permutation(str){
if(str.length == 0){
return [];
}
var result = [];
if(str.length == 1){
return [str];
}else{//把str分为两部分,第一部分为第一个字母str[0],第二部分为剩余的字符串str.slice(1),把Permutation(str.slice(1))作为一个已知量。
var rest = Permutation(str.slice(1));//递归,Permutation(str.slice(1))表示剩余部分字符串的全排列。
for (var j = 0; j < rest.length; j++) {//插空
for (var k = 0; k < rest[j].length+1; k++) {
var temp = rest[j].slice(0,k)+str[0]+rest[j].slice(k);
result.push(temp);
}
}
//去掉result中重复的元素
var res = [];
for(var k = 0;k<result.length;k++){
if(res.indexOf(result[k]) === -1){
res.push(result[k]);
}
}
return res.sort();
}
}
大数加法
function sumStrings(a,b){
//res表示结果字符串,c表示进位
var res='', c=0;
a = a.split('');
b = b.split('');
while (a.length || b.length || c){
//末尾相加,两个取反表示将a.pop()的字符串转化为同等大小的数字
//为什么不用类型转换?因为Number(undefined)=NaN,而~~undefined=0
c += ~~a.pop() + ~~b.pop();
res = c % 10 + res;
c = c>9?1:0;
}
return res.replace(/^0+/,'');
}
var a = '87349238473285973856723867325';
var b = '000034324382582347583275834758437853843853445';
console.log(sumStrings(a,b));
大数乘法
function solve( s , t ) {
// write code here
if(!s || !t){
return 0
}
let m=s.length,
n=t.length,
res=new Array(m+n).fill(0);
for(let i=m-1;i>=0;i--){
let a=s[i]-'0'
for(let j=n-1;j>=0;j--){
let b=t[j]-'0'
let cur=a*b+res[i+j+1]
res[i+j+1]=cur%10
res[i+j]+=(Math.floor(cur/10))
}
}
while(res[0]==0){
res.shift()
}
if(res.length==0){
return '0'
}
return res.join('')
}
console.log(solve('11','99'));
驼峰命名法
function cssStyle2DomStyle(sName) {
let strList=sName.split('-');
if (strList[0]==='') return '';
else{
for(let i=1;i<strList.length;i++){
strList[i]=strList[i].charAt(0).toUpperCase()+strList[i].slice(1);
}
return strList.join('');
}
}
let str='font-size';
console.log(cssStyle2DomStyle(str));
字符串中每个字符统计
function count(str) {
let strObj={};
for(let i=0;i<str.length;i++){
if(str[i]===' ') continue;
if(!strObj[str[i]]){
strObj[str[i]]=1;
}else{
strObj[str[i]]+=1;
}
}
return strObj;
}
let str='hello world!';
console.log(count(str));
反转字符串
function slove(str){
return str.split('').reverse().join('');
}
console.log(slove('abcd'));
字符串中所有子串
function getAllSubstr(str){
let res=[];
for(let i=0;i<str.length;i++){
for(let j=i;j<str.length;j++){
let temp=str.substring(i,j+1);
res.push(temp);
}
}
return res;
}
console.log(getAllSubstr('abc'))
数组
查找数组中重复元素
function duplicates(arr) {
//1.indexOf和lastIndexOf,第一次出现且不是最后一次出现
/*let temp=[];
for(let i=0;i<arr.length;i++){
if(arr.indexOf(arr[i])===i && arr.lastIndexOf(arr[i])!==i){
temp.push(arr[i]);
}
}
return temp;*/
//2.遍历数组,将数组的元素和出现的次数分别作为对象属性和值,遍历对象属性次数大于1的即可
let obj={};
let repeatList=[];
//遍历数组,将数组的值作为obj的索引,出现次数为值
arr.forEach(function(item){
if(obj[item]) obj[item]+=1;
else obj[item]=1;
});
let keyList=Object.keys(obj);
keyList.forEach(function(item){
if(obj[item]>1){
repeatList.push(item);
}
});
return repeatList;
}
arr=[1,2,4,3,2,1];
console.log(duplicates(arr));
数组去重
//方法一:set去重
function unique(arr){
return Array.from(new Set(arr))
//return [...new Set(arr)] 也可以
}
//方法二:indexOf去重
function unique(arr){
var array=[]
for(var i=0;i<arr.length;i++){
if (array.indexOf(arr[i])===-1) {
array.push(arr[i])
}
}
return array
}
//方法三,利用对象属性名唯一
function unique(arr){
let h={};
let temp=[];
for(let i=0;i<arr.length;i++){
if (!h[arr[i]]) {
h[arr[i]]=true;
temp.push(arr[i]);
}
}
return temp;
}
排序
var arr = [1, 2, 1, 3, 5, 12, 12, 3, 12];
/*
// 数组本身的sort方法,本身是按照字符串的顺序进行排序
console.log(arr.sort((a,b)=>a-b)); //升序
console.log(arr.sort((a,b)=>b-a)); //降序
*/
/*
// 1.冒泡排序
function bubble(arr) {
var temp = null;
for (var i = 0; i < arr.length - 1; i++) {
var flag = true;
for (var j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
}
if (flag) {
break;
}
}
return arr;
}
*/
/*
// 2.插入排序
function insertSort(arr) {
var temp; //temp变量用于临时存储待插入元素
for (var i = 1; i < arr.length; i++) {
temp = arr[i];
//从前往后查找插入位置
for (var j = i; j > 0 && arr[j - 1] > temp; j--) {
arr[j] = arr[j - 1]; //将大于temp的arr[j]元素后移
}
arr[j] = temp;
}
return arr;
}
*/
// 3.快排
function quickSort(num, left, right) {
if (left >= right) return; // 若左右指针相遇,待排序数组长度小宇1,即递归的终点,return(注意不能写成left==right,这里left是有可能大于right的)。
var i = left, j = right, flag = left; // 定义可移动的左右指针 i,j,定义flag为基数下标。
while (i < j) { // 在i<j时不断循环,i一旦与j碰头,则跳出循环。
while (num[j] >= num[flag] && j > flag) j--; // j不断左移,找到在num[flag]右侧且比它大的数。
if (i >= j) {
break; // 由于j可能已被改变,需再次判断i与j是否碰头。
}
while (num[i] <= num[flag] && i < j) i++; // i不断右移,找到且比基数小的数,且i不能与j碰头。(由于两次交换已合并,此处不需要使得i在flag左侧)
// num[flag] num[j] num[i]三者换位,可用ES6语法糖[num[flag],num[j],num[i]] = [num[j],num[i],num[flag]];
let temp = num[flag];
num[flag] = num[j];
num[j] = num[i];
num[i] = temp
flag = i; // 基数已经在原num[i]的位置,flag同时也要赋值成i。
}
quickSort(num, left, flag - 1); // 将flag左边数组作为待排序数组,递归调用。
quickSort(num, flag + 1, right); // 将flag右边数组作为待排序数组,递归调用。
}
quickSort(arr,0,arr.length-1)
console.log(arr)
//快排非递归
function _quickSort(num, left, right) {
var list = [[left, right]]; // 将[left,right]存入数组中,类似于递归入栈
while (list.length > 0) { // 若list不为空,循环弹出list最后一个数组进行快排
var now = list.pop(); // 弹出list末尾。(也可用list.shift()取出list第一个数组,但在数据量较大时,这种方式效率较低)
if (now[0] >= now[1]) { // 若左右指针相遇,待排序数组长度小宇1,则无需进行快排(注意不能写成now[0]==now[1],这里now[0]是有可能大于now[1]的
continue;
}
var i = now[0], j = now[1], flag = now[0]; // 以下与递归方法相同,请参考上面的递归详解
while (i < j) {
while (num[j] >= num[flag] && j > flag) j--;
if (i >= j) {
break;
}
while (num[i] <= num[flag] && i < j) i++;
let temp = num[flag];
num[flag] = num[j];
num[j] = num[i];
num[i] = temp;
flag = i;
}
list.push([now[0], flag - 1]); // 将flag左边数组作为待排序数组,只需将左右指针放入list即可。
list.push([flag + 1, now[1]]); // 将flag右边数组作为待排序数组,只需将左右指针放入list即可。
}
}
// 4.选择排序
function select(arr) {
var len = arr.length,
i,j,temp,k;
for (i=0;i<len;i++){ //遍历数组的每一个值,并于其后的值比较找出最小值之后互换
k=i;
for (j=i+1;j<len;j++){
if (arr[j]<arr[k])k=j;
}
if (k!=i){ //如果arr[i]已经是最小的则不需要互换
temp=arr[k];
arr[k]=arr[i];
arr[i]=temp;
}
}
return arr;
}
console.log(select(arr))
合并两个有序数组
思路:双指针,从后面开始往前移动,取较大的赋值给a[l]
function merge(a,b,m,n){
let l1=m-1;
let l2=n-1;
let l=m+n-1;
while(l1>=0 && l2>=0){
if(a[l1]>b[l2]){
a[l]=a[l1];
l1--;
}else{
a[l]=b[l2];
l2--;
}
l--;
}
while(l2>=0){
a[l]=b[l2];
l--;
l2--;
}
return a;
}
console.log(merge([1,2,5,7],[3,4,6],4,3));
查找两数之和等于目标值
//1.使用对象
function twoSum(numbers,target){
let obj={};
let res=[]
for(let i=0;i<numbers.length;i++){
let temp=target-numbers[i];
if(obj[temp]){
res.push([obj[temp],i+1]);
}
obj[numbers[i]]=i+1;
}
return res;
}
console.log(twoSum([3,2,4,3],6))
//2.使用双循环
function twoSum(numbers,target){
let res=[];
for(let i=0;i<numbers.length;i++){
for(let j=i+1;j<numbers.length;j++){
if(numbers[i]+numbers[j]===target){
res.push([i+1,j+1]);
}
}
}
return res;
}
数组中三数相加等于0
/*注意:
三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
解集中不能包含重复的三元组。
*/
function threeSumZero(num){
let res=[];
if(num.length<3) return [];
num=num.sort((a,b)=>a-b);
for(let i=0;i<num.length;i++){
if(num[i]>0) break;
if(i>0 && num[i]===num[i-1]) continue;
let left=i+1;
let right=num.length-1;
while(left<right){
let sum=num[i]+num[left]+num[right];
if(sum==0){
res.push([num[i],num[left],num[right]]);
while(num[left]==num[left+1]){
left++;
}
while(num[right]==num[right-1]){
right--;
}
left++;
right--;
}
else if(sum<0) left++;
else right--;
}
}
return res;
}
console.log(threeSumZero([-10,0,10,20,-10,-40]));
加起来和为目标值的组合
function combinationSum2( num , target ) {
// write code here
if(!num.length) return [];
let res = [];
num.sort((a,b)=>a-b);
function toback(start, curArr, sum){
if(sum == target){
res.push([...curArr]);
}else if(sum < target){
for(let i = start;i < num.length;i++){
if(i-1>=start&&num[i]==num[i-1]){
continue;
}
curArr.push(num[i]);
toback(i+1,curArr,sum+num[i]);
curArr.pop()
}
}
}
toback(0,[], 0);
return res
}
console.log(combinationSum2([100,10,20,70,60,10,50],80));
螺旋矩阵输出
function luoxuanConsole(matrix){
let res = [];
if (matrix.length == 0) return res;
let rowBegin = 0;
let rowEnd = matrix.length - 1;
let colBegin = 0;
let colEnd = matrix[0].length - 1;
while (rowBegin <= rowEnd && colBegin <= colEnd) {
for (let j = colBegin; j <= colEnd; j++) {
res.push(matrix[rowBegin][j]);
}
rowBegin++;
// Traverse Down
for (let j = rowBegin; j <= rowEnd; j++) {
res.push(matrix[j][colEnd]);
}
colEnd--;
if (rowBegin <= rowEnd) {
// Traverse Left
for (let j = colEnd; j >= colBegin; j--) {
res.push(matrix[rowEnd][j]);
}
}
rowEnd--;
if (colBegin <= colEnd) {
// Traverse Up
for (let j = rowEnd; j >= rowBegin; j--) {
res.push(matrix[j][colBegin]);
}
}
colBegin++;
}
return res;
}
console.log(luoxuanConsole([
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]))
合并区间
/*
给出一组区间,请合并所有重叠的区间。
例如,
给出[10,30],[20,60],[80,100],[150,180],
返回[10,60],[80,100],[150,180].
*/
function merge( intervals ) {
intervals.sort((a,b)=>a-b)
if(intervals.length<2){
return intervals
}
//pre为合并的临时区间,cur是永远intervals的第一个元素
let res=[],
pre=intervals.shift();
while(intervals.length!=0){
let cur=intervals.shift()
if(cur[0]<=pre[1]){
pre[1]=Math.max(pre[1],cur[1])
}else{
res.push(pre)
pre=cur
}
}
res.push(pre)
return res
}
console.log(merge([[10,30],[20,60],[80,100],[150,180]]))
矩阵的最小路径和
//思路:采用动态规划的思想,当i==0 && j!=0时,它的上一步只能是matrix[i][j-1],
//i!=0 && j==0时,它的上一步只能是matrix[i-1][j],如果均不为0时,就选上面两个的
//最小值,最后返回matrix[i-1][j-1]就是最小路径和
function smallestPath(matrix){
for(var i=0;i<matrix.length;i++){
for(var j=0;j<matrix[0].length;j++){
if(i==0 && j==0) continue;
else if(i==0 && j!=0){
matrix[i][j]+=matrix[i][j-1];
}
else if(i!=0 && j==0){
matrix[i][j]+=matrix[i-1][j];
}
else{
matrix[i][j]+=Math.min(matrix[i-1][j],matrix[i][j-1]);
}
}
}
return matrix[i-1][j-1];
}
console.log(smallestPath([[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]));
数组中的最长连续(数字连续,位置不连续)子序列
给定无序数组arr,返回其中最长的连续序列的长度(要求值连续,位置可以不连续,例如 3,4,5,6为连续的自然数)
//首先排序,然后去判断该元素是否等于前一个元素+1,
//如果是maxlength+1,指针后移,否则只是指针+1
function MLS( arr ) {
arr=arr.sort((a,b)=>a-b);
let maxlength=1;
let i=1;
while(i<arr.length){
if(arr[i]===arr[i-1]+1){
maxlength++;
i++;
}else{
i++;
}
}
return maxlength;
}
console.log(MLS([100,4,200,1,3,2,5]));
给定一个无序的整数数组,找到其中最长递增子序列的长度。
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
/**
* @param {number[]} nums
* @return {number}
*/
var lengthOfLIS = function(nums) {
if (nums.length < 2) return nums.length;
const d = [1];
let maxAns = 1;
for (let i=1; i<nums.length; i++) {
let len = 0;
for (let j=0;j<d.length;j++) {
if (nums[i] > nums[j]) {
len = Math.max(len, d[j]);
}
}
d[i] = len + 1;
maxAns = Math.max(maxAns, d[i])
}
return maxAns;
};
最长递增子序列给定一个无序的整数数组,找到其中最长递增子序列。
function LIS( arr ) {
// write code here
let length = arr.length;
let end = new Array(length+1);//用来保存最长递增子序列元素
let dp = new Array(length);//保存子序列长度
let n = 1;
end[1] = arr[0];
dp[0] = 1;
for(let i=0;i<length;i++) {
if(end[n] < arr[i]) {
end[++n] = arr[i];
dp[i] = n;
} else {
//二分法查找end中小于arr[i]的元素的位置
let left = 0;
let right = n;
while(left<=right) {
let mid = left + Math.floor((right-left)/2);
if(end[mid] >= arr[i]) {
right = mid-1;
} else {
left = mid + 1;
}
}
//找到了就把它放到正确的位置
end[left] = arr[i];
//更新这个递增子序列的长度
dp[i] = left;
}
}
//下面是将其中字典序最小的返回,如果需要所有的最长递增子序列,直接查找dp中长度为n的。
let res = new Array(n);
for(let i=length-1;i>=0;i--) {
if(dp[i] === n) {
res[--n] = arr[i];
}
}
return res;
}
连续子数组的最大和
function FindGreatestSumOfSubArray(array)
{
// write code here
let sum = array[0]
let res = array[0]
for(let i=1;i<array.length;i++){
if(sum>0){
sum = sum+array[i]
}else{
sum = array[i]
}
res = Math.max(res,sum)
}
return res
}
连续子数组的最大乘积
//思路:imax,imin分别来记录前一个数结尾的子数组最大乘积和最小乘积
//当arr[i]>0时,imax=Math.max(arr[i],arr[i]*imax);imin=Math.min(arr[i],arr[i]*imin);
//当arr[i]<0时,imax=Math.max(arr[i],arr[i]*imin);imin=Math.min(arr[i],arr[i]*imax);
//也就是交换imax和imin即可,负负等正
//最后取max和imax的最大值
function maxMuplicate(arr){
let max=arr[0],imax=arr[0],imin=arr[0];
for(let i=1;i<arr.length;i++){
if(arr[i]<0) {
[imin, imax] = [imax, imin];
}
imax = Math.max(arr[i] , arr[i]*imax);
imin = Math.min(arr[i] , arr[i]*imin);
max = Math.max(max, imax);
}
return max;
}
console.log(maxMuplicate([-2.5,4,0,3,0.5,8,-1]));
数组扁平化
let arr=[
[1,2,2],
[3,4,5,5],
[6,7,8,9,[11,12,[12,13,[14]]]],10
]
/*
// 1.es6,单独的在js文件中使用node可能会出错,版本低了
arr=arr.flat(Infinity)
*/
/*
// 2.toString转化为字符串,元素之前逗号分隔,split分隔为字符串数组
arr=arr.toString().split(',').map(item=>parseFloat(item));
*/
// 3.some来实现
while(arr.some(item=>Array.isArray(item))){
arr=[].concat(...arr)
}
console.log(arr)
斐波拉契数列
// 斐波拉契数列:[1,1,2,3,5,8,13,21...]
// 递归
function fb1(n){
if(n <= 2){
return 1;
}else{
return fb1(n-1) + fb1(n-2);
}
}
// 非递归
function fibonacci (num) {
var n1 = 1,
n2 = 1,
n = 1;
for (var i = 3; i <= num; i++) {
n = n1 + n2;
n1 = n2;
n2 = n;
}
return n ;
}
// 3.闭包
const fb3 = function(){
var mem = [0,1];
var f = function(n){
var res = mem[n];
if(typeof res !== 'number'){
mem[n] = f(n-1) + f(n-2);
res = mem[n];
}
return res;
}
return f;
}()
输入一个正数N,输出所有和为N的连续正数序列
/*
输入一个正数N,输出所有和为N的连续正数序列
例如:输入15
返回:[[1,2,3,4,5],[4,5,6],[7,8]]
*/
function createArr(n, len) {
let arr = new Array(len).fill(null),
temp = [];
arr[0] = n;
arr = arr.map((item, index) => {
if (item === null) {
item = temp[index - 1] + 1;
}
temp.push(item);
return item
});
return arr;
}
function fn(count) {
let result = [];
// 求出中间值(只取中间值以下的)
let middle = Math.ceil(count / 2);
// 从1开始累加
for (let i = 1; i <= middle; i++) {
// 控制累加多少次
for (let j = 2; ; j++) {
// 求出累加多次的和
let total = (i + (i + j - 1)) * (j / 2);
if (total > count) {
break;
} else if (total === count) {
result.push(createArr(i, j));
break;
}
}
}
return result;
}
console.log(fn(15));
js判断数组中是否包含某个元素
//1.array.indexOf()
var arr=[1,2,3,4];
var index=arr.indexOf(3);
console.log(index);
//2.array.includes(searcElement[,fromIndex])
var arr=[1,2,3,4];
if(arr.includes(3))
console.log("存在");
else
console.log("不存在");
//3.array.find(callback[,thisArg])
var arr=[1,2,3,4];
var result = arr.find(item =>{
return item > 3
});
console.log(result);
//4.array.findIndex()
var arr=[1,2,3,4];
var result = arr.findIndex(item =>{
return item > 3
});
console.log(result);
//5.for循环
树
二叉树的先中后序遍历
/*
* function TreeNode(x) {
* this.val = x;
* this.left = null;
* this.right = null;
* }
*/
//先序
function preOrder(root,path){
if(!root) return;
path.push(root.val);
preOrder(root.left,path);
preOrder(root.right,path);
}
//中序
function minOrder(root,path){
if(!root) return;
minOrder(root.left,path);
path.push(root.val);
minOrder(root.right,path);
}
//后续
function postOrder(root,path){
if(!root) return;
postOrder(root.left,path);
postOrder(root.right,path);
path.push(root.val);
}
function threeOrders( root ) {
// write code here
let res=[],pre=[],min=[],post=[];
preOrder(root,pre);
minOrder(root,min);
postOrder(root,post);
res.push(pre);
res.push(min);
res.push(post);
return res;
}
二叉树的层次遍历
//思路:使用队列来实现层次遍历
//根节点入队,队不空时用while循环:在内部用for循环
//1.根出队,访问根,2.左孩子不为空,左孩子入队,
//3.右孩子不为空,右孩子入队
function levelOrder( root ) {
// write code here
if(!root) return [];
let queen=[root];
let result=[];
let temp=[];
let len=0;
while(queen.length>0){
len=queen.length;
for(let i=0;i<len;i++){
let node=queen.shift();
temp.push(node.val);
if(node.left) queen.push(node.left);
if(node.right) queen.push(node.right);
}
result.push([...temp]);
temp=[];
}
return result;
}
二叉树的之字形层次遍历
//层次遍历的基础上,定义一个level表示层级,如果level%2==0,就翻转那一层的数据。
function zigzagLevelOrder( root ) {
// write code here
if(!root) return [];
let queen=[root];
let result=[];
let temp=[];
let len=0;
let level=0;
while(queen.length>0){
len=queen.length;
for(let i=0;i<len;i++){
let node=queen.shift();
temp.push(node.val);
if(node.left) queen.push(node.left);
if(node.right) queen.push(node.right);
}
level++;
if(level%2==0) result.push([...temp.reverse()]);
else result.push([...temp]);
temp=[];
}
return result;
}
根据先序中序遍历结果构建二叉树
//思路:首先将先序遍历的第一个数赋值给根节点,
//然后在中序遍历中找到根节点的位置,
//最后递归得到左子树和右子树
function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
}
function reConstructBinaryTree(pre, vin)
{
if(!pre.length || !pre.length) return null;
//根节点为先序遍历的第一个节点
let rootVal=pre[0];
//根据第一个数字创建节点
let node=new TreeNode(rootVal);
//寻找根节点在中序遍历中的下标
//i的两个含义:根节点在中序遍历中的下标;当前左子树的节点个数
let i=vin.indexOf(rootVal);
//左子树
node.left=reConstructBinaryTree(pre.slice(1,i+1),vin.slice(0,i));
//右子树
node.right=reConstructBinaryTree(pre.slice(i+1),vin.slice(i+1));
return node;
}
根据中序后序遍历结果构建二叉树
function buildTree(inorder, postorder) {
if(!inorder.length || !postorder.length){
return null
}
let node = postorder.pop()//头节点
let index = inorder.indexOf(node) //头节点在中序遍历数组中的位置
let root = new TreeNode(node) //构造一棵树
root.left = buildTree(inorder.slice(0,index),postorder.slice(0,index))
root.right = buildTree(inorder.slice(index+1),postorder.slice(index))
return root
};
求二叉树的右视图
//方法一:层次遍历二叉树,每一层的最后一个数添加到结果数组中,这里不做说明
//方法二:深度优先搜索,
//当数组长度等于当前深度时,把当前的值加入到数组,先遍历右子树,右边没了再遍历左子树
function DFS(root,depth,res){
if(root){
if(res.length === depth){// 当数组长度等于当前 深度 时, 把当前的值加入数组
res.push(root.val);
}
DFS(root.right,depth+1,res);// 先从右边开始, 当右边没了, 再轮到左边
DFS(root.left,depth+1,res);
}
}
/*测试使用
var rightSideView = function(root) {
if(!root)
return [];
let arrList =[];
DFS(root,0,arrList);
return arrList;
};
*/
二叉树的镜像
//方法一:递归
//思路,交换左右孩子,然后递归左子树和右子树
function Mirror(root)
{
// write code here
if(!root) return null;
[root.left,root.right]=[root.right,root.left];
Mirror(root.left);
Mirror(root.right);
return root;
}
//方法二:非递归
//借助于栈,在左右子树有一个或者两个不为空时交换左右子树
//左子树不为空,入栈,右子树不为空,入栈。再求完一棵子树的镜像后再求还有一棵子树的镜像(纵向,深度优先)
//(如果要使用广度优先的话,使用队列,然后让temp=队首就行,再出队)
function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
}
function Mirror(root)
{
// write code here
if(!root) return null;
let stack=[root];
while(stack.length>0){
let temp=stack.pop();
if(temp.left || temp.right){
[temp.left,temp.right]=[temp.right,temp.left]
}
if(temp.left)
stack.push(temp.left);
if(temp.right)
stack.push(temp.right);
}
}
判断二叉树是否对称
function similar(left, right){
if(!left && !right){
return true;
}else if(!left || !right){
return false;
}
if(left.val === right.val){
return similar(left.left, right.right) && similar(left.right, right.left)
}else{
return false;
}
}
function isSymmetric( root ) {
if(!root) return true;
return similar(root.left, root.right);
}
在二叉树中找到两个节点的最近公共祖先
//思路:如果root为空,或者根节点的val等于o1/o2,直接返回root.val,
//递归在左边和右边查找两个节点的最近公共祖先,
//如果left为空,返回right,如果right为空,返回left
//最后返回root.val
/*
* function TreeNode(x) {
* this.val = x;
* this.left = null;
* this.right = null;
* }
*/
/**
*
* @param root TreeNode类
* @param o1 int整型
* @param o2 int整型
* @return int整型
*/
function lowestCommonAncestor( root , o1 , o2 ) {
// write code here
if(!root) return null;
if(root.val==o1 || root.val==o2) return root.val;
let left=lowestCommonAncestor(root.left,o1,o2);
let right=lowestCommonAncestor(root.right,o1,o2);
if(!left) return right;
if(!right) return left;
return root.val;
}
二叉树的高度
function maxDepth( root ) {
// write code here
if(!root) return 0;
let llength=maxDepth(root.left);
let rlength=maxDepth(root.right);
return Math.max(llength,rlength)+1;
}
二叉树中的总节点数
function nodeNum( head ) {
if (!head) return 0
return 1 + nodeNum(head.left) + nodeNum(head.right)
}
二叉搜索树的第k个节点
二叉搜索树指的是如果有左孩子,左孩子的值永远小于根节点的值,如果有右孩子,右孩子的值永远大于根节点
//二叉搜索树的中序遍历就是升序排列的序列,第k个就是path[k-1]
function inOrder(root,path){
if(root){
inOrder(root.left,path);
path.push(root);
inOrder(root.right,path);
}
}
function KthNode(pRoot, k)
{
let path=[];
inOrder(pRoot,path);
if(k>=1 && path.length>=k) return path[k-1];
return null;
}
二叉树的最大路径和
function maxPathSum( root ) {
// write code here
var maxSum = -Infinity
function getMax(root){
if(!root) return 0;
let leftSum = Math.max(0,getMax(root.left))
let rightSum = Math.max(0,getMax(root.right))
maxSum = Math.max(maxSum,leftSum + rightSum + root.val)
return Math.max(0,Math.max(leftSum,rightSum)+root.val)
}
getMax(root)
return maxSum
}
根节点到叶子节点的所有路径
/*给定一个仅包含数字\ 0-9 0−9 的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。
例如根节点到叶子节点的一条路径是1\to 2\to 31→2→3,那么这条路径就用\ 123 123 来代替。
找出根节点到叶子节点的所有路径表示的数字之和*/
//思路:深度优先搜索
function dfs(root,sum){
if(!root) return 0;
sum=sum*10+root.val;
if(!root.left && !root.right) return sum;
return dfs(root.left,sum)+dfs(root.right,sum);
}
function sumNumbers( root ) {
// write code here
let res=0;
if(!root) return res;
return dfs(root,0);
}
二叉树根节点到叶子节点是否有路径等于指定值
function hasPathSum( root , sum ) {
if(root ===null)return false;
if(root!=null && root.left ==null
&& root.right ==null
&& sum -root.val==0)return true;
return hasPathSum( root.left , sum-root.val )
||hasPathSum( root.right , sum-root.val )
}
二叉树根节点到叶子节点的和等于指定值的路径
//思路:将当前根节点的值push到path中,如果currentSum==指定值sum,
//将该条路径push到结果数组中,退出该次递归
//如果左孩子不为空,就继续往下遍历,直到叶子节点
function pathSum( root , sum ) {
if(!root) return [];
let result=[];
dfs(root,0,[]);
return result;
function dfs(root,currentSum,path){
if(!root) return;
currentSum+=root.val;
path.push(root.val);
if(currentSum==sum && !root.left && !root.right){
result.push(path);
return;
}
if(root.left) dfs(root.left,currentSum,[...path]);
if(root.right) dfs(root.right,currentSum,[...path]);
}
}
判断平衡二叉树
平衡二叉树:要么是一个空树,要么左右孩子的高度差的绝对值不超过1
//思路:定义一个求高度的函数getHeight,求左右子树的高度,
//如果高度之差的绝对值大于1,返回false,否则返回true
//递归左子树和右子树,全部返回true即为平衡二叉树
function getHeight(root){
if(!root) return 0;
return Math.max(getHeight(root.left),getHeight(root.right))+1;
}
function IsBalanced_Solution(pRoot)
{
if(!pRoot) return true;
let left=getHeight(pRoot.left);
let right=getHeight(pRoot.right);
if(Math.abs(left-right)>1) return false;
else return true;
return IsBalanced_Solution(pRoot.left) && IsBalanced_Solution(pRoot.right);
}
判断完全二叉树
完全二叉树的每一层节点都是满的 ,最后一层节点 空节点的右侧没有节点 一个节点有左节点才能有右节点
function isCompleteTree(root){
if(!root || !root.left&&!root.right){
return true;
}else if(root.left && root.right){
return isCompleteTree(root.left)
&& isCompleteTree(root.right);
}else if(!root.left && root.right){
return false;
}else if(root.left && !root.right){
return !root.left.left
&& !root.left.right;
}
}
判断搜索二叉树
//根节点的值小于最小值或者大于最大值都返回false
//如果上述条件为true,递归左子树和右子树
function isSearchTree(root, min, max){
if(!root){
return true;
}
if(root.val<=min || root.val>=max){
return false;
}else{
//遍历左子树的时候max是root.val,遍历右子树的时候min是root.val
return isSearchTree(root.left, min, root.val)
&& isSearchTree(root.right, root.val, max);
}
}
合并二叉树
已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替
function mergeTrees( t1 , t2 ) {
// write code here
if(!t1 && !t2) return null
if(!t1) return t2
if(!t2) return t1
t1.val += t2.val
t1.left = mergeTrees(t1.left,t2.left)
t1.right = mergeTrees(t1.right,t2.right)
return t1
}
链表
反转链表,输出新链表的表头
/*
* function ListNode(x){
* this.val = x;
* this.next = null;
* }
*/
function ReverseList(head)
{
let prev = null // 尾随cur的prev指针,开始时指向null
let cur = head // 推进指针,开始时指向头结点
while (cur) { // cur指针推进到null节点,则退出循环
let next = cur.next // 暂存cur的下一节点
cur.next = prev // 将cur的next指针指向prev
prev = cur // 将prev更新为cur节点
cur = next // 将cur指针推进一个节点
//[cur.next,prev,cur]=[prev,cur,cur.next]
} // 退出while时,cur指向null,prev指向原链的尾节点
return prev
};
链表中的节点每k个一组翻转
function reverseKGroup( head , k ) {
let pre=null;
let cur=head;
let p=head;
//查找长度是否满足反转的数量
for(let i=0;i<k;i++){
if(!p) return head;
p=p.next;
}
//对k个链表元素进行反转
for(let j=0;j<k;j++){
//下面四行是反转链表的操作
let temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
//递归反转
head.next=reverseKGroup(cur,k);
return pre;
}
链表在指定区间反转
//思路,先找到需要反转的位置,再反转
function ListNode(x){
this.val = x;
this.next = null;
}
function reverseBetween( head , m , n ) {
let preHead=new ListNode(0);
preHead.next=head;
let pre=preHead;
let cur=head;
//先找到需要反转的位置
for(let i=0;i<m-1;i++){
pre=pre.next;
cur=cur.next;
}
//反转
for(let j=0;j<n-m;j++){
let temp=cur.next;
cur.next=temp.next;
temp.next=pre.next;
pre.next=temp;
}
return preHead.next;
}
判断链表中是否有环
//思路定义快慢指针,快指针走一步,慢指针走两步,如果有环总会相遇的
function hasCycle( head ) {
if(!head) return false;
let slow=head;
let fast=head;
while(fast && fast.next){
slow=slow.next;
fast=fast.next.next;
if(slow==fast) return true;
}
return false;
}
链表中环的入口节点
//思路:快慢指针相遇之后,快慢指针中的一个指向头,
//快慢指针再次都以一步的速度前进,再次相遇就是环的入口
function detectCycle( head ) {
if(!head ||!head.next) return null;
let slow=head;
let fast=head;
while(fast && fast.next){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
fast=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
return null;
}
删除链表的倒数第n个节点
//设置双指针,p走到第n个位置,q才开始走,
//等到p都走完了,q也就走到了需要删除的地方了
//(要删除的节点是q.next)
function removeNthFromEnd( head , n ) {
let p=head;//前指针
let q=head;//后指针
for(let i=0;i<n;i++){
if(!p.next) return head.next;
p=p.next;
}
while(p.next){
p=p.next;
q=q.next;
}
//删除倒数第n个节点
q.next=q.next.next;
return head;
}
链表中的倒数第n个节点
function FindKthToTail(head, k) {
if (head == null) {
return null;
}
var p = head;
for (let i = 0; i < k; i++) {
if (p == null) {
return null;
}
p = p.next;
}
let q = head;
while (p) {
q = q.next;
p = p.next;
}
return q;
}
合并两个有序链表
//非递归法
function mergeTwoLists( l1 , l2 ) {
if(!l1) return l2;
if(!l2) return l1;
let head;
//确定新链表的表头
if(l1.val<l2.val){
head=l1;
l1=l1.next;
}else{
head=l2;
l2=l2.next;
}
let cur=head;
while(l1 && l2){
//选择小的节点作为新节点
if(l1.val<l2.val){
cur.next=l1;
cur=cur.next;
l1=l1.next;
}else{
cur.next=l2;
cur=cur.next;
l2=l2.next;
}
}
//处理不为空链表的剩余节点
if(l1) cur.next=l1;
if(l2) cur.next=l2;
return head;
}
//递归法
function mergeTwoLists( l1 , l2 ) {
if(l1 === null) return l2
if(l2 === null) return l1
if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next, l2)
return l1
}else{
l2.next = mergeTwoLists(l1, l2.next)
return l2
}
}
合并k个已排序的链表
//思路:列表中的链表依次进行合并,上一次的结果与下一个链表进行合并
function mergeTwoLists(l1, l2){
if(!l1) return l2
if(!l2) return l1
if(l1.val > l2.val) {
l2.next = mergeTwoLists(l2.next, l1)
return l2
} else {
l1.next = mergeTwoLists(l1.next, l2)
return l1
}
}
function mergeKLists( lists ) {
if(!lists.length) return null
let firstList = lists[0];
for(let i=1, len=lists.length; i<len; i++){
firstList = mergeTwoLists(firstList, lists[i]);
}
return firstList;
}
//改进合并速度,将lists分为长度相同的两部分,分别合并,
//最后合并左右两个大链表即可
function mergeKLists( lists ) {
let n=lists.length;
if(n===0) return null;
function merge(left,right){
if(left==right) return lists[left];
let mid=(left+right)>>1;
let l1=merge(left,mid);
let l2=merge(mid+1,right);
return mergeTwoLists(l1,l2);
}
return merge(0,n-1)
}
两个链表生成相加链表
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
//思路:利用栈,从末尾开始相加两数,
function ListNode(x){
this.val = x;
this.next = null;
}
function addInList( head1 , head2 ) {
let arr1=[],arr2=[];
let head=null;
let carry=0;
if(!head1) return head2;
if(!head2) return head1;
while(head1){
arr1.push(head1.val);
head1=head1.next;
}
while(head2){
arr2.push(head2.val);
head2=head2.next;
}
while(arr1.length || arr2.length || carry){
let num1=arr1.length?arr1.pop():0;
let num2=arr2.length?arr2.pop():0;
let sum=num1+num2+carry;
carry=Math.floor(sum/10);//floor向下取整
let node=new ListNode(sum%10);
//使用头插法将新节点插入到链表中
node.next=head;
head=node;
}
return head;
}
两个链表的第一个公共节点
//思路:两个链表如果长度一致的话,直接定义两个指针,同时往后移,第一个相同的节点就是结果
//如果长度不一致,a+b=b+a,拼接两个链表,长度就一致了,在使用上面的方法。
function FindFirstCommonNode(pHead1, pHead2)
{
if(!pHead1 || !pHead2) return null;
let p1=pHead1;
let p2=pHead2;
while(p1!=p2){
p1=p1.next;
p2=p2.next;
if(p1!=p2){
//p1为空,拼接pHead2
if(!p1) p1=pHead2;
//p2为空,拼接pHead1
if(!p2) p2=pHead1;
}
}
return p1;
}
单链表的选择排序
//思路:直接将链表的所有数装到数组中,利用sort方法排序
//然后依次选出元素链接到原链表中
function sortInList( head ) {
if(!head) return;
let arr=[];
let node=head;
while(node){
arr.push(node.val);
node=node.next;
}
arr.sort((a,b)=>a-b);
node=head;
for(let item of arr){
node.val=item;
node=node.next;
}
return head;
}
链表的奇偶重排
function oddEvenList( head ) {
let oHead=new ListNode(0);
let eHead=new ListNode(0);
let count=0;
let op=oHead;//奇数指针
let ep=eHead;//偶数指针
while(head){
count++;
//偶数,将偶数序号的节点链接到ep后面
if(count%2==0){
ep.next=head;
ep=ep.next;
}else{
op.next=head;
op=op.next;
}
head=head.next;
}
//拼接,先奇数后偶数
ep.next=null;
op.next=eHead.next;
return oHead.next;
}
判断是否是回文链表
//方法一,利用数组
//思路:先将链表中的值存入数组,判断数组是否是回文结构即可
function isPail( head ) {
if(!head || !head.next) return true;
let arr=[];
while(head){
arr.push(head.val);
head=head.next;
}
let left=0;
let right=arr.length-1;
while(left<right){
if(arr[left]!=arr[right]) return false;
left++;
right--;
}
return true;
}
//方法二:快慢指针
//首先快慢指针先找到链表中间的位置,
//然后反转后半部分的链表
//再顺序比较这两个链表
function isPail( head ) {
if (head == null)
return false;
let slow = head;
let fast = head;
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
let q = head;
let p = reverse(slow);
while(p) {
if (q.val != p.val)
return false;
q = q.next;
p = p.next;
}
return true;
}
function reverse(head) {
let pNode = head;
let pHead = new ListNode(null);
while(pNode) {
const cNode = pNode;
pNode = pNode.next;
cNode.next = pHead.next;
pHead.next = cNode;
}
return pHead.next;
}
删除链表中重复的元素,只保留出现一次的元素(链表本身有序)
给出的链表为1->2->3->3->4->4->5返回1-> 2->5
function deleteDuplicates( head ) {
let res=new ListNode(null);
res.next=head;
let pre=res;
let next=head;
while(next){
let val=next.val;
let temp=next;
next=next.next;
//找到不为val的节点
while(next && next.val==val){
next=next.next;
pre.next=next;
}
if(temp.next==next){
pre=temp;
}
}
return res.next;
}
删除链表中重复的元素,所有元素只出现一次(链表本身有序)
function deleteDuplicates( head ) {
if(head ==null) return head;
let temp = head;
while(temp.next != null){
if(temp.val == temp.next.val){
temp.next = temp.next.next;
}else{
temp = temp.next;
}
}
return head;
}
约瑟夫问题
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。
n 表示环形链表的长度, m 表示每次报数到 m 就自杀。
function ysf( n , m ) {
var arr = [];
if(n < 1 || m < 1){
return;
}
for(let i = 1; i<=n; i++){
arr.push(i)
}
let index = 0
while( arr.length > 1){
index = (index + m -1) % arr.length;
arr.splice(index, 1)
}
return arr[0]
}
重排链表
原链表:l1-l2-l3-l4
新链表:l1-l4-l2-l3
function reorderList( head ) {
let arr = [];
let cur = head;
while (cur) {
arr.push(cur);
cur = cur.next;
}
let length = arr.length;
let i = 0;
let j = length - 1;
while (i < j) {
// 最后一对
if (i === j - 1) {
break;
}
arr[i].next = arr[j];
arr[j-1].next = null;
arr[j].next = arr[i+1];
i++;
j--;
}
return arr[0];
}
其他
二分查找有序数组
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
function upper_bound_( n , v , a ) {
if(v>a[n-1]) return n+1;
let low=0,high=n-1;
while(low<high){
let mid=Math.floor((low+high)/2);//向下取整
if(a[mid]<v) low=mid+1;
else high=mid;
}
return low+1;
}
滑动窗口的最大值
如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
function maxInWindows(num, size)
{
// write code here
if(!num.length||size>num.length||size<=0){return []}
if(size===1){return num}
let ans=[];
for(let i=0;i<num.length;i++){
if(i+size>num.length){break}
ans.push(Math.max(...num.slice(i,i+size)))
}
return ans;
}
寻找第k大
直接排序,然后返回arr[n-k]
JS
用闭包实现一个计数器,每调用一次+1
function counter(){
var count=0;
return function(){
return ++count;
};
}
var A=counter()
console.log(A()); //1
console.log(A()); //2
console.log(A()); //3
计时器,每隔100ms输出一个数
实现一个打点计时器,要求
1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1
2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作
3、第一个数需要立即输出
//使用setInterval
function count(start, end) {
//立即输出第一个值
console.log(start++);
var timer = setInterval(function(){
if(start <= end){
console.log(start++);
}else{
clearInterval(timer);
}
},100);
//返回一个对象
return {
cancel : function(){
clearInterval(timer);
}
};
}
/*
//使用setTimeout
function count(start, end) {
if(start <= end){
console.log(start);
start++;
st = setTimeout(function(){count(start, end)}, 100);
}
return {
cancel: function(){clearTimeout(st);}
}
}
*/
浅克隆
展开运算符、slice、concat可以实现浅克隆
let obj={
a:100,
b:[10,20,30],
c:{
x:10
},
d:/^\d+$/
};
let obj1={...obj}; //用展开符来进行浅克隆
console.log(obj1===obj) //false
深克隆
//JSON.strinify变为字符串,但是这个方法会把正则变为空的对象
//JSON.parse再将字符串变为对象,
//JSON方法的弊端就是会将正则、函数复制出错
let obj2=JSON.parse(JSON.stringify(obj));
//递归
function deepClone(obj) {
// 过滤特殊情况
if (obj === null) return null
if (typeof obj !== 'object') return obj
if (obj instanceof RegExp) {
return new RegExp(obj)
}
if (obj instanceof Date) {
return new Date(obj)
}
// 不直接创建空对象的目的:克隆的结果和之前保持相同的所属类
let newObj = new obj.constructor;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}