js手撕代码

文章目录

字符串

最长公共子串(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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值