算法-特殊

2 阅读24分钟
11. 螺旋矩阵
var spiralOrder = function(matrix) {
    const result = [];
    let top=0,bottom=matrix.length-1;
    let left=0,right=matrix[0].length-1;
    while (left<=right&&top<=bottom){
        for(let i=left;i<=right;i++){
            result.push(matrix[top][i])
        }
        top++
        for(let i=top;i<=bottom;i++){
            result.push(matrix[i][right])
        }
        right--
				
        // top++ 可能大于 bottom;最后一行数据时边界
        if(bottom>=top){
            for(let i=right;i>=left;i--){
                result.push(matrix[bottom][i])
            }
            bottom--
        }
        // 最后一列时边界
        if(right>=left){
            for(let i=bottom;i>=top;i--){
                result.push(matrix[i][left])
            }
            left++
        }
    }
    return result;
};
12. 买卖股票的最佳时机
var maxProfit = function(prices) {
    // 买入最大值  初始化时候
    let inPrice = Number.MAX_VALUE;
    let result = 0;
    for(let i= 0; i<prices.length;i++){
      	// 比买入价便宜就继续买
        if(inPrice>prices[i]){
            inPrice = prices[i]
        }
        result = Math.max(result, prices[i]-inPrice)
    }
    return result
};
18. 岛屿数量

发现一个岛屿 把与其接壤的全部设为0 接壤的岛屿算一个

var numIslands = function(grid) {
    let count = 0;
    for(let i=0;i<grid.length;i++){
        for(let j=0;j<grid[i].length;j++){
            if(grid[i][j]==='1'){
                count++;
                defs(i,j, grid)
            }
        }
    }
  	// 挨着的岛算是一个岛屿
    function defs(i,j){
        //超出边界 或者不是岛屿直接返回
        if(i<0||i>=grid.length||j<0||j>=grid[0].length||grid[i][j]==='0'){
            return
        }
        // 是岛屿已经计数 标记为0  把岛屿周围的1都标记为0
        grid[i][j] = '0'
        defs(i-1,j)
        defs(i+1,j)
        defs(i,j-1)
        defs(i,j+1)
    }
    return count;
    
};

22. 岛屿的最大面积

找到岛屿计算其附近岛屿累加 「当前岛屿置为0;避免重复计算」与最大值比较

var maxAreaOfIsland = function(grid) {
    let result = 0,bottom=grid.length-1,right=grid[0].length-1;
    for(let i=0;i<=bottom;i++){
        for(let j=0;j<=right;j++){
            if(grid[i][j] === 1){
              	// 找到一个岛屿 累加周围的
                result = Math.max(result, getOther(i,j))
            }
        }
    }
    function getOther(i,j){
       // 边界或者不是岛屿的 返回0
        if(i<0||i>bottom||j<0||j>right||grid[i][j]===0){
            return 0
        }
        // 计数 标记
        let num = 1
        grid[i][j] = 0
        // 四个方向去找
        num += getOther(i-1,j)
        num += getOther(i+1,j)
        num += getOther(i,j-1)
        num += getOther(i,j+1)
        return num;
    }
    return result;
};
40. 被围绕的区域
var solve = function(board) {
    const rows = board.length;
    const cols = board[0].length;

    function dfs(x,y){
        if(x<0||y<0||x>=rows||y>=cols||board[x][y]!='O')return;
      	// 是0的标记为你/  没包裹着
        board[x][y] = '/'
        dfs(x+1,y)
        dfs(x-1,y)
        dfs(x,y+1)
        dfs(x,y-1)
    }
  	// 四个边发起 为0的则没被包裹
    for(let i=0;i<rows;i++){
        dfs(i,0)
        dfs(i,cols-1)
    }
    for(let i=0;i<cols;i++){
        dfs(0,i)
        dfs(rows-1,i)
    }
    console.log(board)
    for(let i=0;i<rows;i++){
        for(let j=0;j<cols;j++){
            if (board[i][j] === 'O') board[i][j] = 'X';
            if (board[i][j] === '/') board[i][j] = 'O';
        }
    }
    return board;
};
20. 接雨水

哪边底从哪边开始。接的雨水 最大值-当前值

var trap = function(height) {
    let left = 0;
    let right = height.length-1;
    let leftMax = 0
    let rightMax = 0
    let result = 0;
    while (left<right){
        // 哪边低从哪边开始
        if(height[left]<height[right]){
            if(height[left]>leftMax){
                leftMax = height[left]
            }else{
                result +=(leftMax - height[left])
            }
            left ++
        }else{
            if(height[right]> rightMax){
                rightMax = height[right]
            }else {
                result +=(rightMax - height[right])
            }
            right--;
        }
    }
    return result;
};
23. 爬楼梯

后边的雨前面的数据有关系。基本用这种方法;获取了前几个值 后边的循环计算

var climbStairs = function(n) {
    const dep =[1,2]
    //后一次是前两次的和
    for(let i=2;i<n;i++){
        dep[i] = dep[i-1] + dep[i-2]
    }
    return dep[n-1]
};
81.整数拆分。7 dp[i]= dp[i-3]*3
// 动态规划
var integerBreak = function(n) {
  const dp = [];
    dp[2] = 1;
    dp[3] = 2;
    dp[4] = 4;
    dp[5] = 6;
    dp[6] = 9;
    for(let i = 7; i <= n ; i++){
        dp[i] = dp[i - 3] * 3;
    }
    return dp[n];
};
// 找规律
var integerBreak = function(n) {
        if(n == 2) return 1;
        if(n == 3) return 2;
        let result = 1;
        while(n > 4){
            n -= 3;
            result *= 3;
        }
        return (n * result);
};
48. 外观数列
var countAndSay = function(n) {
  if(n==1) return '1';
  let prev = '1'
  	// *****i<=n  有n个数
   for(let i=2;i<=n;i++){
      let curr = ''
      let j=0;
      // 遍历pre
      while (j<prev.length){
         let count = 1
         // 累加相同
         while (j<prev.length&&prev[j]===prev[j+1]){
            count++;
            j++
         }
         // j++
         curr += count + prev[j]
         j++
      }
      prev = curr

   }
   return prev;
};
33. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

知道前两个的时候 第三个偷不偷 值看前面两个的值 如果。三个情况只能选 1 3 或者2

var rob = function(nums) {
    // 一家都没有 返回0
    if(nums.length==0)return 0;
    if(nums.length ==1)return nums[0]
    // 动态规划   只有一家 只有两家的情况
    let dep = [nums[0], Math.max(nums[0],nums[1])];
    for(let i=2;i<nums.length;i++){
        // 每一家如果偷 得到钱的情况 就是这一家加上这家的上上家 如果不偷就是前一家 偷了得到的钱 ****
        dep[i] = Math.max(dep[i-1],dep[i-2]+nums[i])
    }
    return dep.pop()
};
56. 不同路径
var uniquePaths = function(m, n) {
    const dp = Array(m).fill(Array(n).fill(0));
    // 初始化第一行和第一列
    for(let i=0;i<m;i++){
        dp[i][0] = 1
    }
    for(let i=0;i<n;i++){
        dp[0][i] = 1
    }

    // 循环在1开始
    for(let i=1;i<m;i++){
        for(let j=1;j<n;j++){
            dp[i][j] = dp[i][j-1]+dp[i-1][j]
        }
    }
    return dp[m-1][n-1]

};
34 零钱兑换 II

dp 每种零钱可以找的数量; dp[i - coin] 前面找零需要的数量

function coinChange(coins, amount) {
    const dp = new Array(amount + 1).fill(amount + 1);
  	// 0的时候不用找
    dp[0] = 0;
    for (let i = 1; i <= amount; i++) {
        for (let coin of coins) {
          	// 零钱大于要找的钱的时候
            if (i >= coin) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount];
}
44. 柠檬水找零
var lemonadeChange = function(bills) {
    let five = 0, ten =0;
    for(let bill of bills){
        if(bill === 5){
            five++
        }
        if(bill === 10){
            if(five<=0)return false;
            ten++;
            five--;
        }
        if(bill === 20){
            if(ten>0 && five>0){
                ten--;
                five--;
                continue;
            }
            if(five>2){
                five-=3
                continue;
            }
            return  false
        }
    }
    return true;

};
35. 寻找旋转排序数组中的最小值 II
var findMin = function(nums) {
    // 升序螺旋 如果旋转了  旋转点一定小于前一个值;如果旋转后和原来一样  第一个就是最小值
    for(let i =1;i<nums.length;i++){
       if(nums[i]<nums[i-1]){
           return nums[i]
       }
    }
    return nums[0]
};
36. 移掉 K 位数字 使剩下的数字最小

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

var removeKdigits = function(num, k) {
    let stack = []
    for(let step of num){
        // 如果后边的值比前面的值小就 用后边的值把前面的值删除掉;  删除掉k个之后不再删除
        while(stack.length>0&&k>0&&stack.slice(-1)>step){
            stack.pop()
            k--;
        }
        stack.push(step)
    }
    // 如果上边没删完 删除后边的
    while (k>0){
        stack.pop()
        k--
    }
    // 如果第一个值是0的情况处理下
    while (stack[0] == '0'){
        stack.shift()
    }
    return stack.join('')||'0'
};
39. 括号生成
var generateParenthesis = function(n) {
 		const result = []
    function backTrack(s,left, right){
        if(s.length == 2*n){
            result.push(s)
            return
        }
        if(left<n){
            backTrack(s+'(', left+1, right)
        }
        // 右边小于左边
        if(right<left){
            backTrack(s+')', left, right+1)
        }
    }
    backTrack('',0,0)
    return result;

};
42. 逆波兰表达式求值
var evalRPN = function(tokens) {
    const stack = [];
    for (let token of tokens) {
      	// isFinite 判断是数 还是操作符。是数直接压入栈中
        if (isFinite(token)) {
            stack.push(parseInt(token, 10)); // 明确指定十进制
        } else {
          	// 符号时候 栈中弹出两个数做为计算;  计算的结果压入栈中
            let right = stack.pop();
            let left = stack.pop();
            switch (token) {
                case '+':
                    stack.push(left + right);
                    break;
                case '-':
                    stack.push(left - right);
                    break;
                case '*':
                    stack.push(left * right);
                    break;
                case '/':
                    // 结果应该向零取整
                    stack.push(parseInt(left / right, 10));
                    break;
            }
        }
    }
    return stack.pop();
};
46. 移动零
var moveZeroes = function(nums) {
    let insert = 0
    for(let i=0;i<nums.length;i++){
        if(nums[i]!=0){
            nums[insert] = nums[i]
            insert++
        }
    }
    while (insert<nums.length){
        nums[insert] = 0;
        insert++;
    }
    return nums;
};


var moveZeroes = function(nums) {
    let slow = 0;
    for (let fast = 0; fast < nums.length; fast++) {
        if (nums[fast] !== 0) {
            // 把非零元素移动到slow的位置
            nums[slow] = nums[fast];
            // 当fast和slow不相等时,说明中间有零存在,需要将当前fast的位置设置为0
            if (fast !== slow) {
                nums[fast] = 0;
            }
            slow++;
        }
    }
};
47. 最后一块石头的重量
var lastStoneWeight = function(stones) {
    while (stones.length>1){
      // 排序很重要
        stones.sort((a,b)=>{return a-b})
        const y = stones.pop()
        const x = stones.pop()
        if(y>x){
            stones.push(y-x)
        }
    }
    return stones[0]||0;
};
50. 矩形重叠
右上角大于左下角。左下角小于右上角  则有重叠。相当于大的大于小的。小的小于大的则有重叠。  重叠和完全包裹不一样要注意  
var isRectangleOverlap = function(rec1, rec2) {
  
    // 解构赋值,获取矩形的坐标
    let [x1, y1, x2, y2] = rec1;
    let [x3, y3, x4, y4] = rec2;

    // 检查是否满足重叠的条件
    return x2 > x3 && x1 < x4 && y2 > y3 && y1 < y4;

};
LCR 54. 最小栈
var MinStack = function() {
    this.stack = [];
    this.minStack = [];
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    this.stack.push(x);
    if (this.minStack.length === 0 || x <= this.minStack[this.minStack.length - 1]) {
        this.minStack.push(x);
    }
};
/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    const popped = this.stack.pop();
    if (popped === this.minStack[this.minStack.length - 1]) {
        this.minStack.pop();
    }
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    return this.stack[this.stack.length - 1];
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
    return this.minStack[this.minStack.length - 1];
};

// 类的写法
// class MinStack {
//     constructor() {
//         this.stack = [];
//         this.minStack = [];
//     }
//
//     push(x) {
//         this.stack.push(x);
//         if (this.minStack.length === 0 || x <= this.minStack[this.minStack.length - 1]) {
//             this.minStack.push(x);
//         }
//     }
//
//     pop() {
//         const popped = this.stack.pop();
//         if (popped === this.minStack[this.minStack.length - 1]) {
//             this.minStack.pop();
//         }
//     }
//
//     top() {
//         return this.stack[this.stack.length - 1];
//     }
//
//     getMin() {
//         return this.minStack[this.minStack.length - 1];
//     }
// }
63. 前 K 个高频元素
var topKFrequent = function(nums, k) {
    let map = new Map();
    let res = [];
    for(let i = 0; i < nums.length; i++) {
        if(map.has(nums[i])) {
            map.set(nums[i], map.get(nums[i]) + 1);
        } else {
            map.set(nums[i], 1);
        }
    }
    //返回一个按出现次数降序的二维数组
    let sortArray = Array.from(map).sort((a, b) => b[1] - a[1]);
    for(let i = 0; i < k; i++) {
        res.push(sortArray[i][0]);
    }
    return res;
};

64. 克隆图
var cloneGraph = function(node) {
    // 记录复制的节点
    let map = new Map()
    function dfs(node){
        if(!node)return node;
        if(map.has(node)) return map.get(node);
        // clone当前节点
        let cloneNode = new Node(node.val);
        map.set(node, cloneNode);
        // 当前节点的相邻节点
        for(let neighbor of node.neighbors){
            cloneNode.neighbors.push(dfs(neighbor))
        }
        return cloneNode
    }
    return dfs(node)
};

65.斐波那契数列


// 1.最简单递归
var fib = function(n) {
    if(n<2){
        return n
    }
    return fib(n-1) + fib(n-2)
}
// 2.递归加记忆化
var fib = function(n,stateMap=[]) {
    if(n<2){
        return n
    }
    if(!stateMap[n]){
        stateMap[n] = fib(n-1,stateMap) + fib(n-2,stateMap)
    }
    return stateMap[n]% 1000000007
};
// 动态规划
var fib = function(n) {
    if(n<2){
        return n
    }
    let p = 0
    let c = 1
    let rt = 0
    let i = 1
    while (i++ < n) {
        rt = (p + c) % 1000000007
        p = c
        c = rt
    }
    return rt
};
66.搜索二维矩阵 II
var searchMatrix = function(matrix, target) {
    for(let i=0,len=matrix.length;i<len;i++){
        if(matrix[i][0]>target){
            continue
        }
        for(let j=0,lens=matrix[i].length;j<lens;j++){
            if(matrix[i][j]>target){
                break
            }
            if(matrix[i][j]==target){
                return true
            }
        }
    }
    return false
};

// 根据数的规律在右上角开始  左边的比他小 下边的比他大
var searchMatrix = function(matrix, target) {
    let len = matrix.length
    let r = matrix[0].length - 1
    let t = 0
    while (r>=0 && t<len) {
        if(matrix[t][r] > target){
            r--
        } else if(matrix[t][r] < target){
            t++
        } else {
            return true
        }
    }
    return false
}
68.二进制中1的个数

flg一直左移动。32位后就没了

var hammingWeight = function(n) {
    let flg = 1;
    let cont = 0
    while (flg) {
        if(flg & n){
            cont ++
        }
        flg = flg << 1
    }
    return cont
};
73.扑克牌中的顺子 没重复 最大值-最小值<=4

// 思路:

  1. 顺子的最小最大值差 <=4
  2. 顺子没对子
var isStraight = function(nums) {
    let rt = {}
    // 这俩默认值很关键 用边界 不能直接用nums[0]  第一个可能是 0
    // 如果有对子肯定不是顺子。在没有对子的情况下 只要最大-最小 <=4 就是顺子
    let min = 14
    let max = 1
    for(let i=0;i<nums.length;i++){
        if(nums[i]!= 0){
            if(rt[nums[i]]){
                return false
            }
            rt[nums[i]] = true
            min = Math.min(min,nums[i])
            max = Math.max(max,nums[i])
        }
    }
    if(max-min <=4){
        return true
    }else{
        return false
    }
};
74.数组中出现次数超过一半的数字
var majorityElement = function(nums) {
    let len = nums.length
    let rt = {}
    for(let i=0;i<len;i++){
        let temp = nums[i]
        if(rt[temp]){
            rt[temp]++
        } else{
            rt[temp] = 1
        }
        if(rt[temp]>(len/2)){
            return temp
        }
    }
};
// [摩尔投票】数组中出现次数超过一半的数字; 一个与其他比
var majorityElement1 = function(nums) {
    let ans = 0, count = 0;
    for(let i = 0; i < nums.length; i++){
        if(!count) {
            ans = nums[i];
            count++;
        } 
        if(nums[i] === ans) {
            count +=  1
        } else {
            count +=  -1;
        }
    }
    return ans;
};
let nums = [ 1,2,1,3,1,4,1,5,6]
let rt = majorityElement1(nums)
console.log(rt)
83.高度检查器

学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。

请你返回能让所有学生以 非递减 高度排列的最小必要移动人数。

注意,当一组学生被选中时,他们之间可以以任何可能的方式重新排序,而未被选中的学生应该保持不动。

高度不在顺序中的拿出来

// 排序后比较不同
var heightChecker = function(heights) {
  let temp = [...heights]
  heights.sort((a,b)=>{return a-b})
  let rt = temp.filter((item, index)=>{
    return item != heights[i]
  })
  return rt.length
};
let heights = [1,1,4,2,1,3]

let rt = heightChecker(heights)
84.同构字符串

给定两个字符串 s 和 t,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

//直接记录每个映射关系;如果映射关系重复了直接判断不是同构字符串
var isIsomorphic = function(s, t) {
  let mapS = {}
  let mapT={}
  for(let i=0,len=s.length;i<len;i++){
    let valueS = s[i]
    let valueT = t[i]
    
    if(!mapS[valueS]){
      // 如果不存在映射就直接赋值
      mapS[valueS] = valueT
    } else if(mapS[valueS] !== valueT){
      // 如果存在映射但是不相等 就返回false
      return false
    }

    if(!mapT[valueT]){
      mapT[valueT] = valueS
    } else if(mapT[valueT] != valueS){
      return false
    }
  }
  return true;
};
87.猜数字游戏

你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:

你写出一个秘密数字,并请朋友猜这个数字是多少。 朋友每猜测一次,你就会给他一个提示,告诉他的猜测数字中有多少位属于数字和确切位置都猜对了(称为“Bulls”, 公牛),有多少位属于数字猜对了但是位置不对(称为“Cows”, 奶牛)。 朋友根据提示继续猜,直到猜出秘密数字。 请写出一个根据秘密数字和朋友的猜测数返回提示的函数,返回字符串的格式为 xAyB ,x 和 y 都是数字,A 表示公牛,用 B 表示奶牛。

xA 表示有 x 位数字出现在秘密数字中,且位置都与秘密数字一致。 yB 表示有 y 位数字出现在秘密数字中,但位置与秘密数字不一致。 请注意秘密数字和朋友的猜测数都可能含有重复数字,每位数字只能统计一次。

示例 1:

输入: secret = "1807", guess = "7810" 输出: "1A3B" 解释: 1 公牛和 3 奶牛。公牛是 8,奶牛是 0, 1 和 7。

var getHint = function(secret, guess) {
  var bull = 0;
  var cow = 0;
  // 存储secret[n] guess[n]数字不同的元素
  var skeep = []
  var gkeep = []
  
  // 先判断位置数字都一样,剩下的就用sk gk来存储存
  for(var i in guess){
    // 位置数字都一样 bull++
    if(secret[i] == guess[i]){
      bull++
    } else {
      skeep.push(secret[i])
      gkeep.push(guess[i])
    }
  }
  // 因为bull已经处理过;这边只要gkeep内的元素出现在skeep内 代表一个cow
  for(var j in gkeep){
    var findIndex = skeep.indexOf(gkeep[j]);
    if(findIndex !=-1){
      cow++;
      skeep[findIndex] = null
    }
  }
  return bull + "A" + cow + "B"
};
89.Nim 游戏](leetcode-cn.com/problems/ni…)

你和你的朋友,两个人一起玩 Nim 游戏:

桌子上有一堆石头。 你们轮流进行自己的回合,你作为先手。 每一回合,轮到的人拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。 假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

示例 1:

输入:n = 4 输出:false 解释:如果堆中有 4 块石头,那么你永远不会赢得比赛; 因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。

// 找规律
var canWinNim = function(n) {
    if(n<4){
        return true 
    }
    if(n%4 == 0){
        return false
    }
    return true
};
90.计数质数

统计所有小于非负整数 n 的质数的数量。

var countPrimes = function(n) {
    let cont = 0;
    let state = {2:false}// 2是质数
    for(let i=2;i<n;i++){
        if(!state[i]){
            cont++
          	质数的平方肯定不是质数。合数的 几倍肯定还是合数。   从小到达累计标记
            for(let j=i*i;j<n;j+=i){
                state[j] = true
            }
        }
    }
    return cont;
};
91.旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

var rotate = function(nums, k) {
    let step = nums.length % k// 旋转整圈算是不动 看余数 直接把前面的移动到后边即可
    let temp = nums.splice(0,nums.length - step)
    nums.push(...temp)
};

var rotate = function(nums, k) {
    nums.push(...(nums.splice(0,nums.length-k%nums.length)))
};
95.圆圈中最后剩下的数字 ans = (ans + m) % i;

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

var lastRemaining = function(n, m) {
    let ans = 0;
    for (let i = 2; i <= n; i++) {
        ans = (ans + m) % i;
    }
    return ans;
};
102.机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

// 广度优先遍历
var movingCount = function(m, n, k) {
    // 位数和
    function getSum(num) {
        let sum = 0;
        while(num) {
            sum += num % 10;
            num = Math.floor(num / 10);
        }
        return sum;
    } 
    // 方向数组
    const directionAry = [
        [-1, 0], // 上
        [0, 1], // 右
        [1, 0], // 下
        [0, -1] // 左
    ];
    // 已经走过的坐标
    let set = new Set(['0,0']);
    // 将遍历的坐标队列,题意要求从[0,0]开始走
    let queue = [[0, 0]];
    // 遍历队列中的坐标
    while(queue.length) {
        // 移除队首坐标
        let [x, y] = queue.shift();
        // 遍历方向
        for(let i = 0; i < 4; i++) {
          	每个方向x y有一对值
            let offsetX = x + directionAry[i][0];
            let offsetY = y + directionAry[i][1];
            // 临界值判断  注意 m n 都是长度 offsetX offsetY 是坐标
            if(offsetX < 0 || offsetX >= m || offsetY < 0 || offsetY >= n || getSum(offsetX) + getSum(offsetY) > k || set.has(`${offsetX},${offsetY}`)) {
                continue;
            }
            // 走过的格子就不再纳入统计
            set.add(`${offsetX},${offsetY}`);
            // 将该坐标加入队列(因为这个坐标的四周没有走过,需要纳入下次的遍历)
            queue.push([offsetX, offsetY]);
        }
    }
    // 走过坐标的个数就是可以到达的格子数
    return set.size;
};


103.把数组排成最小的数 并不是数字从小到大排序即可 字符串排序

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

var minNumber = function(nums) {
    nums.sort((a, b)=>{
        return (a+''+b) - (b+''+a)
    })
    return nums.join('')
}

104.加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 注意点: 满10 进位

function plusOne(digits) {
    let len = digits.length-1
    digits[len] = digits[len] + 1
  // 从后往前检查  看可不可以进位置
    for(let i=len;i>=0;i--){
        let r = Math.floor(digits[i]/10)
        if(r){
          	进位 看前面是否有值 有值直接进位 没有值 直接添加一位 unshift
            digits[i] = digits[i]%10
          	// 如果前一位有值
            if(digits[i-1]){
                digits[i-1] = digits[i-1] + r
            } else {
                // 如果前一位没有值了 就把进位 加在数组的第一位
                digits.unshift(r)
            }
        } else {
            break
        }
    }
    return digits
}
var digits= [9]
var rt = plusOne(digits)
console.log(rt)
105.二进制求和

二进制求和 给你两个二进制字符串,返回它们的和(用二进制表示)。 输入为 非空 字符串且只包含数字 1 和 0。

示例 1: 输入: a = "11", b = "1" 输出: "100"

function addBinary(a, b){
    let i = a.length-1 
    let j = b.length-1
    let rt = ''
    let c = 0
    a = a.split('')
    b = b.split('')
    while (i>=0||j>=0||c) { // 边界处理 总位数可能比任意两个长
        let s = (a[i]||0)*1 + (b[j]||0)*1 + c*1  // 加完后的值
        // 主要就是判断加完后的值 然后给当前位置赋值 保留进位的值 留给前一位 一直累积到最后
        switch (s) {
          case 0:
            rt = 0 + rt
            c = 0
            break;
          case 1:
            rt = 1 + rt
            c = 0
            break;
          case 2:
            rt = 0 + rt
            c=1
            break;
          case 3:
            rt = 1 + rt
            c=1
            break;
          default:
            if(c == 1){
              rt = 1 + rt
              c = 0
            }
            break;
        }
        i--
        j--
    }
    return rt
}

let a = "11", b = "1"
let rt = addBinary(a,b)
console.log(rt)
106.平方根 二分

实现 int sqrt(int x) 函数。 计算并返回 x 的平方根,其中 x 是非负整数。 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

var mySqrt = function(x) {
    if(x < 0) return NaN;
    if(x == 0) return 0;
    let min = 1;
    let max = x;
    // 1 的情况 避免死循环
    while(max - min > 1){
        let mid = Math.ceil((max + min)/2);
      	// 二分 mid*mid  如果等于x 就是他的平方根;  和二分查找一样
        if (mid*mid < x ) {
            min = mid;
        } else if (mid*mid > x) {
            max = mid;
        } else {
            return mid
        }
    };
  	// 如果一直没找到就用最小的
      return min;
  };

  let rt = mySqrt(2)
107.快速排序
let arr = [1,2,3,4,9,8,7,6,5]

let quickSort = function(arr){
    let len = arr.length;
    if(len<2)return arr; // 限制
    let pivot = Math.floor(len/2)
    let mid = arr[pivot];
    let minArr = []
    let maxArr = []
    for(let i=0;i<len;i++){
        // 临界值 
        if(arr[i] <= mid && i !=pivot){
            minArr.push(arr[i])
        } else if(arr[i] > mid){
            maxArr.push(arr[i])
        }
    }
    return [...quickSort(minArr),mid,...quickSort(maxArr)]//*****
}
let rt = quickSort(arr)
console.log(111, rt)
108.字符串大小写转换 charCodeAt fromCharCode a:97 A:32。A~a = 32

写个方法 将字符串 ‘abCDEg’ 转换成 ‘ABcdeG’;大小写转换

// charCodeAt fromCharCode  97 32
function resetChart(str) {
  let rtStr = ''
  for(let i=0;i<str.length;i++){
    let rcode = str[i].charCodeAt()>=97?str[i].charCodeAt()-32:str[i].charCodeAt()+32
    rtStr += String.fromCharCode(rcode)
  }
  return rtStr;
}
// toUpperCase  toLowerCase         
function resetChart2(str) {
  let rtStr = ''
  for(let i=0;i<str.length;i++){
    rtStr += str[i].charCodeAt()>=97?str[i].toUpperCase():str[i].toLowerCase()
  }
  return rtStr;
}

109.数组拍平并去重复

如 将数组[[1,2,3], [4,5,6], [7,8], [6,7,9],[3,2,1],[1,1,1]] 转换为 [1,2,3,4,5,6,7,8,9]

 let resetArr = function (arr){
    let tempArr = [] // 存储不重复值
    let rtArr = arr.reduce((rt,item)=>{
        return rt.concat(item)
    }).filter((item)=>{
        if(!tempArr.includes(item)){
            tempArr.push(item)
            return true
        }
    })
    return rtArr
 }
// 直接循环等方法  用key不唯一的方式。
let resetArr1 = function (arr){
    let rtArr = []
    let arrMap = {}
    // 用set存 更好些
    arr.forEach(item => {
        item.forEach((subItem)=>{
            arrMap[subItem] = ''
        })
    });
    rtArr = Object.keys(arrMap)
    return rtArr
}
let arr = [[1,2,3], [4,5,6], [7,8], [6,7,9],[3,2,1],[1,1,1]]
resetArr(arr)
110.求两个数组的交集

如 arr1 = [1, 2, 2, 1],arr2 = [2, 2],返回 [2, 2]

// 一个数组上拿出值 看另一个数组是否存在。重点是删除操作 保证不重复
let intersect2 = function(arr1, arr2) {
    let rtArr =[]
    while (arr1.length) {
        let item = arr1.pop()  
        let index = arr2.indexOf (item)
        if (index != -1) {
            // 确保重复的不被漏掉
            arr2.splice(index, 1)
            rtArr.push(item)
        }
    }
    return rtArr
}

// 哈希表存储一个数组的值 另一个循环直接比较
let intersect3 = function(arr1, arr2) {
    let rtArr =[]
    const map = {} 
    for (let n of arr1) {
        // 哈希表 存储每个值。 有相同的++
        if (map[n]) {
            map[n]++
        } else {
            map[n] = 1
        }
    }
    
    for (let n of arr2) {
        if (map[n] > 0) {
            rtArr.push(n)
            // 获取到一个相同的 --
            map[n]--
        }
    }
    return rtArr
}
let arr1 = [1, 2, 2, 1],arr2 = [2, 2]
intersect(arr1, arr2)
111.逆序number

将number类型的数转换为逆序后的字符串。如:输入整型 1234,返回字符串“4321”

// 转换成字符串直接循环
function numberReverse2(num){
        // 直接转字符串循环
        let strNum = String(num)
        let strLen = strNum.length
        let rtStr = ''
        for(let i=strLen-1;i>=0;i--){
            rtStr +=strNum[i]
        }
        return rtStr
}
// 用取余和相除做处理
function numberReverse3 (num){
    let rtStr = ''
    while (num!=0) {
        // 利用 取余 和取整
        rtStr += num%10
        num = Math.floor(num/10)
    }
    return rtStr
}

let num = 1234
numberReverse(num)
112.实现findStrIndex算法

从长度为 n 的字符串 s 中,查找是否存在字符串 t,t 的长度是 m,存在返回位置,不存在返回-1。

let findStrIndex2 = function (s, n, t, m) {
    let strMap = {},i=0;
  	// 循环直接把t的长度的值存起来当key  
    while (i<=n-m) {
        // 把所有可能性都存起来 m个长度一个key
        strMap[String(s.slice(i,i+m))] = i
        i++
    }
    return strMap[t]||-1;
}

let s = 'abcdqwertyui'
let n = 12
let t = 'bc'
let m = 2
findStrIndex(s, n, t, m)
113.文档的相似度(交集并集长度)

两个(具有不同单词的)文档的交集(intersection)中元素的个数除以并集(union)中元素的个数,就是这两个文档的相似度。例如,{1, 5, 3} 和 {1, 7, 2, 3} 的相似度是 0.4,其中,交集的元素有 2 个,并集的元素有 5 个。给定一系列的长篇文档,每个文档元素各不相同,并与一个 ID 相关联。它们的相似度非常“稀疏”,也就是说任选 2 个文档,相似度都很接近 0。请设计一个算法返回每对文档的 ID 及其相似度。只需输出相似度大于 0 的组合,保留4位小数。请忽略空文档。为简单起见,可以假定每个文档由一个含有不同整数的数组表示。

输入: 
[
  [14, 15, 100, 9, 3],
  [32, 1, 9, 3, 5],
  [15, 29, 2, 6, 8, 7],
  [7, 10]
]
输出:
[
  "0,1: 0.2500",
  "0,2: 0.1000",
  "2,3: 0.1429"
]
let computeSimilarities1 = function(arr){
    let rt = []
    let len = arr.length
    for(let i=0;i<len-1;i++){
        for(let j=i+1;j<len;j++){
          	// 获取交集
            let union= Array.from(new Set(arr[i].concat(arr[j])))
            let iLenth = arr[i].length 
            let jLenth = arr[j].length
            // 知道并集 交集的长度 可直接确定;
            // 交集的长度 = 数组1长度 + 数组2长度 - 数组1数组2并集长度 ****
            let similarities = (iLenth + jLenth - union.length)/union.length
            if(similarities > 0.0001){
                rt.push(i + ',' + j + ': ' + (similarities.toFixed(4)))
            }
        }
    }
    return rt
}
114.搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。
var searchMatrix = function(matrix, target) {
  // 如果每行第一个都比target大 就继续下一行;如果不比target大 就循环这一行的每一个比较
  for(let i=0,len=matrix.length;i<len;i++){
    if(matrix[i][0]>target){
      continue
    }
    for(let j=0,lens=matrix[i].length;j<lens;j++){
      if(matrix[i][j]>target){
        break
      }
      if(matrix[i][j]==target){
        return true
      }
    }
  }
  return false
};

// 转换成字符串 直接找
var searchMatrix1 = function(matrix, target) {
  return matrix.toString().split(',').indexOf(target + '')==-1?false:true
};


115.月份对象转数组

某公司 1 到 12 月份的销售额存在一个对象里面

如下:{1:222, 2:123, 5:888},

请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]。

 
 let setMounthData1 = function(data){
   let rtData = new Array(12).fill(null).map((item, index)=>{
     return  data[index+1] || null
   })
   return rtData
 }
 

116.求两个数的最大公约数 辗转相除法 更相减损术(出自九章算术)

求两个数的最大公约数(GCD,Greatest Common Divisor)的一个经典算法是欧几里得算法(Euclidean algorithm)。这个算法基于一个重要的原理:两个整数的最大公约数与它们的差的最大公约数相同。更具体地说,对于任意两个整数 ab(假设 a>b),它们的最大公约数等于 aab 的最大公约数。欧几里得算法通过重复应用这个原理,逐步减小整数的大小,直到找到最大公约数。

欧几里得算法的一个更实用的形式是使用余数替代差值,这样可以更快地减小数的大小。具体算法如下:

  1. 若 a=0b=0,则最大公约数为 a
  2. 否则,计算 a 除以 b 的余数r
  3. a 设置为 b,将b 设置为 r
  4. 重复步骤 2 和 3,直到 a=0b=0,此时的 a 即为两数的最大公约数。
function gcd(a,b){
  while(b!=0){
    let temp = b
    b = a%b
    a = temp
  }
  return a
}
117. 的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

二进制 的2 102-1 014100 4-1 11。 

对于任何大于 0 的整数 *n*,如果 *n* 是22 的幂次方,那么它的二进制表示中有且仅有一个 1,并且在这个 1 之后全是 00

下面是按位与运算的基本逻辑:如果有0就是0
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

按位或的基本逻辑: 如果有1就是1
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

var isPowerOfTwo = function(n) {
    if(n<=0){return false}
    return (n&n-1) == 0
};