算法

头像
Juanha
    阅读 17 分钟

    数组

    快速排序

    function quickSort(arr, left, right) {
        if (left >= right) return
        const base = arr[left]
        let p = left, q = right
        while (p < q) {
            while (arr[q] > base && q >= p) {
                q--
            }
            while (arr[p] < base && p <= q) {
                p++
            }
            const temp = arr[q]
            arr[q] = arr[p]
            arr[p] = temp
        }
        arr[p] = base
    
        quickSort(arr, left, p)
        quickSort(arr, p + 1, right)
    }

    归并排序

    function mergeSort(arr) {
        if (arr.length <= 1) return arr
        const mid = Math.floor(arr.length / 2)
        const left = arr.slice(0, mid)
        const right = arr.slice(mid)
    
        const leftArr = mergeSort(left)
        const rightArr = mergeSort(right)
    
        const result = []
    
        while (leftArr.length > 0 && rightArr.length > 0) {
            if (leftArr[0] < rightArr[0]) {
                result.push(leftArr.shift())
            } else {
                result.push(rightArr.shift())
            }
        }
        return result.concat(leftArr).concat(rightArr)
    }

    两数之和

    var twoSum = function (nums, target) {
        let hash = {};
        for (let i = 0; i < nums.length; i++) {
          if (hash[target - nums[i]] !== undefined) {
            return [i, hash[target - nums[i]]];
          }
          hash[nums[i]] = i;
        }
        return [];
      };

    三数之和

    var threeSum = function(nums) {
        const len = nums.length;
        if(len < 3) return [];
        nums.sort((a, b) => a - b);
        const resSet = new Set();
        for(let i = 0; i < len - 2; i++) {
            if(nums[i] > 0) break;
            let l = i + 1, r = len - 1;
            while(l < r) {
                const sum = nums[i] + nums[l] + nums[r];
                if(sum < 0) { l++; continue };
                if(sum > 0) { r--; continue };
                resSet.add(`${nums[i]},${nums[l]},${nums[r]}`);
                l++;
                r--;
            }
        }
        return Array.from(resSet).map(i => i.split(","));
    };

    二分查找

    var search = function(nums, target) {
        let left = 0, right = nums.length - 1;
        // 使用左闭右闭区间
        while (left <= right) {
            let mid = left + Math.floor((right - left)/2);
            if (nums[mid] > target) {
                right = mid - 1;  // 去左面闭区间寻找
            } else if (nums[mid] < target) {
                left = mid + 1;   // 去右面闭区间寻找
            } else {
                return mid;
            }
        }
        return -1;
    };

    螺旋矩阵遍历

    var generateMatrix = function(n) {
        let startX = startY = 0;   // 起始位置
        let loop = Math.floor(n/2);   // 旋转圈数
        let mid = Math.floor(n/2);    // 中间位置
        let offset = 1;    // 控制每一层填充元素个数
        let count = 1;     // 更新填充数字
        let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
    
        while (loop--) {
            let row = startX, col = startY;
            // 上行从左到右(左闭右开)
            for (; col < startY + n - offset; col++) {
                res[row][col] = count++;
            }
            // 右列从上到下(左闭右开)
            for (; row < startX + n - offset; row++) {
                res[row][col] = count++;
            }
            // 下行从右到左(左闭右开)
            for (; col > startX; col--) {
                res[row][col] = count++;
            }
            // 左列做下到上(左闭右开)
            for (; row > startY; row--) {
                res[row][col] = count++;
            }
    
            // 更新起始位置
            startX++;
            startY++;
    
            // 更新offset
            offset += 2;
        }
        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2 === 1) {
            res[mid][mid] = count;
        }
        return res;
    };

    合并两个有序数组

    var merge = function(nums1, m, nums2, n) {
        let p1 = m - 1, p2 = n - 1;
        let tail = m + n - 1;
        var cur;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 === -1) {
                cur = nums2[p2--];
            } else if (p2 === -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    };

    随机排序

      function shuffle(arr) {
        var i, j,  temp;
        for (i = arr.length - 1; i > 0; i--) {
          j = Math.floor(Math.random() * (i + 1));
          temp = arr[i];
          arr[i] = arr[j];
          arr[j] = temp;
        }
        return arr;
    }
    // 这个实现有问题
      function shuffle(arr){
        return arr.sort(function(){
            return Math.random() - 0.5;
        });
    }

    两个有序数组的中位数 时间复杂度(log(m+n)

    var findMedianSortedArrays = function(nums1, nums2) {
    
        if (nums1.length > nums2.length) {
            [nums1, nums2] = [nums2, nums1]
        }
        let m = nums1.length, n = nums2.length
        let left = 0, right = m
    
        while(left <= right) {
            let i = left + Math.floor((right - left) / 2)
            let j = Math.floor( (m + n + 1) / 2) - i
    
            let leftiMax = i === 0 ? -Infinity: nums1[i - 1]
            let rightiMin = i === m ? Infinity: nums1[i]
            let leftjMax = j === 0 ? -Infinity: nums2[j - 1]
            let rightjMin = j === n ? Infinity: nums2[j]
    
            if (leftiMax<=rightjMin && leftjMax<=rightiMin) {
                if ((m+n)%2 === 1) {
                    return Math.max(leftiMax, leftjMax)
                } else {
                    return (Math.max(leftiMax, leftjMax) + Math.min(rightiMin, rightjMin) )/ 2
                }
            } else if (leftiMax>rightjMin) {
                right = i - 1
            } else {
                left = left + 1
            }
        }
    
    };

    数组中的第K个最大元素(215)

    // 堆排序
    var findKthLargest = function(nums, k) {
       let heapSize=nums.length
        buildMaxHeap(nums,heapSize) // 构建好了一个大顶堆
        // 进行下沉 大顶堆是最大元素下沉到末尾
        for(let i=nums.length-1;i>=nums.length-k+1;i--){
            swap(nums,0,i)
            --heapSize // 下沉后的元素不参与到大顶堆的调整
            // 重新调整大顶堆
             maxHeapify(nums, 0, heapSize);
        }
        return nums[0]
       // 自下而上构建一颗大顶堆
       function buildMaxHeap(nums,heapSize){
         for(let i=Math.floor(heapSize/2)-1;i>=0;i--){
            maxHeapify(nums,i,heapSize)
         }
       }
       // 从左向右,自上而下的调整节点
       function maxHeapify(nums,i,heapSize){
           let l=i*2+1
           let r=i*2+2
           let largest=i
           if(l < heapSize && nums[l] > nums[largest]){
               largest=l
           }
           if(r < heapSize && nums[r] > nums[largest]){
               largest=r
           }
           if(largest!==i){
               swap(nums,i,largest) // 进行节点调整
               // 继续调整下面的非叶子节点
               maxHeapify(nums,largest,heapSize)
           }
       }
       function swap(a,  i,  j){
            let temp = a[i];
            a[i] = a[j];
            a[j] = temp;
       }
    };
    // 快排
    var findKthLargest = function (nums, k) {
      const len = nums.length;
      const targetIndex = len - k;
      let left = 0,
        right = len - 1;
    
      while (left < right) {
        const index = partition(nums, left, right);
        if (index === targetIndex) {
          return nums[index];
        } else if (index < targetIndex) {
          left = index + 1;
        } else {
          right = index - 1;
        }
      }
    
      return nums[left];
    };
    
    function partition(nums, start, end) {
      const povit = nums[start];
      while (start < end) {
        while (start < end && nums[end] >= povit) {
          end--;
        }
        nums[start] = nums[end];
        while (start < end && nums[start] < povit) {
          start++;
        }
        nums[end] = nums[start];
      }
      nums[start] = povit;
      return start;
    }
    

    字符串

    两数之和

    var addStrings = function (num1, num2) {
        const result = []
        let p = num1.length - 1
        let q = num2.length - 1
        let add = 0
        while (p >= 0 || q >= 0 || add) {
            const x = p >= 0 ? Number(num1[p]) : 0
            const y = q >= 0 ? Number(num2[q]) : 0
            const sum = x + y + add
            result.unshift(sum % 10)
            add = Math.floor(sum / 10)
            p--
            q--
        }
    
        return result.join('')
    };

    千分符格式化

    var thousandSeparator = function(n) {
        let res = ''
        let count = 0
        const str = n.toString()
        const len = str.length
    
        for(let i = len - 1; i >= 0; i--) {
            count++
            if (count % 3 === 0 && i !== 0) {
                res = '.' + str[i] + res
            } else {
               res = str[i] + res
            }
        }
    
        return res
    
    };
    var thousandSeparator = function(n) {
      return n.toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,")
    }

    有效括号

    var isValid = function(s) {
        const n = s.length;
        if (n % 2 === 1) {
            return false;
        }
        const pairs = new Map([
            [')', '('],
            [']', '['],
            ['}', '{']
        ]);
        const stk = [];
        for (let ch of s){
            if (pairs.has(ch)) {
                if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
                    return false;
                }
                stk.pop();
            } 
            else {
                stk.push(ch);
            }
        };
        return !stk.length;
    
    };
    

    字符转整数

    /**
     * @param {string} str
     * @return {number}
     */
    var myAtoi = function(str) {
      // 自动机类
      class Automaton{
        constructor() {
          // 执行阶段,默认处于开始执行阶段
          this.state = 'start';
          // 正负符号,默认是正数
          this.sign = 1;
          // 数值,默认是0
          this.answer = 0;
          /*
          关键点:
          状态和执行阶段的对应表
          含义如下:
          [执行阶段, [空格, 正负, 数值, 其他]]
          */
          this.map = new Map([
            ['start', ['start', 'signed', 'in_number', 'end']],
            ['signed', ['end', 'end', 'in_number', 'end']],
            ['in_number', ['end', 'end', 'in_number', 'end']],
            ['end', ['end', 'end', 'end', 'end']]
          ])
        }
    
        // 获取状态的索引
        getIndex(char) {
          if (char === ' ') {
            // 空格判断
            return 0;
          } else if (char === '-' || char === '+') {
            // 正负判断
            return 1;
          } else if (typeof Number(char) === 'number' && !isNaN(char)) {
            // 数值判断
            return 2;
          } else {
            // 其他情况
            return 3;
          }
        }
    
        /*
        关键点:
        字符转换执行函数
        */
        get(char) {
          /*
          易错点:
          每次传入字符时,都要变更自动机的执行阶段
          */
          this.state = this.map.get(this.state)[this.getIndex(char)];
    
          if(this.state === 'in_number') {
            /*
            小技巧:
            在JS中,对字符串类型进行减法操作,可以将得到一个数值型(Number)的值
    
            易错点:
            本处需要利用括号来提高四则运算的优先级
            */
            this.answer = this.answer * 10 + (char - 0);
    
            /*
            易错点:
            在进行负数比较时,需要将INT_MIN变为正数
            */
            this.answer = this.sign === 1 ? Math.min(this.answer, Math.pow(2, 31) - 1) : Math.min(this.answer, -Math.pow(-2, 31));
          } else if (this.state === 'signed') {
            /*
            优化点:
            对于一个整数来说,非正即负,
            所以正负号的判断,只需要一次。
            故,可以降低其判断的优先级
            */
            this.sign = char === '+' ? 1 : -1;
          }
        }
      }
    
      // 生成自动机实例
      let automaton = new Automaton();
    
      // 遍历每个字符
      for(let char of str) {
        // 依次进行转换
        automaton.get(char);
      }
    
      // 返回值,整数 = 正负 * 数值
      return automaton.sign * automaton.answer;
    };
    
    var myAtoi = function (s) {
      let res = 0,
        negativeSymbol = 1;
      s = s.trim();
      for (let i = 0; i < s.length; i++) {
        if (i == 0 && s[i] == "-") {
          negativeSymbol = -1;
          continue;
        } else if (i == 0 && s[i] == "+") continue;
        if (s[i] >= 0 && s[i] <= 9 && s[i] != " ") {
          res = res * 10 + (s[i] - 0);
          if (res * negativeSymbol <= -2147483648) return -2147483648;
          else if (res * negativeSymbol >= 2147483647) return 2147483647;
        } else break;
      }
      return res * negativeSymbol;
    };
    
    

    链表

    反转链表

    var reverseList = function(head) {
        let pre = null
        let cur = head
    
        while(cur !== null) {
            const temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        }
    
        return pre
    
    };

    删除链表倒数第N个结点

    var removeNthFromEnd = function(head, n) {
        let ret = new ListNode()
        ret.next = head
        let p = ret, q = ret
    
        // p 先前进N
        while(n--) {
            p = p.next
        }
        // p 遍历到尾结点
        while(p.next !== null) {
            p = p.next
            q = q.next
        }
        q.next = q.next.next
        return ret.next
    };

    合并两个有序链表

    var mergeTwoLists = function(l1, l2) {
        const prehead = new ListNode(-1);
    
        let prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
    
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = l1 === null ? l2 : l1;
    
        return prehead.next;
    };
    

    二叉树

    深度优先遍历(递归和迭代)

    // 递归
    var preorderTraversal = function(root) {
        let res=[];
        const dfs=function(root){
            if(root===null)return ;
            //先序遍历所以从父节点开始
            res.push(root.val);
            //递归左子树
            dfs(root.left);
            //递归右子树
            dfs(root.right);
        }
        //只使用一个参数 使用闭包进行存储结果
        dfs(root);
        return res;
    };
    // 迭代
    var preorderTraversal = function(root, res = []) {
        if(!root) return res;
        const stack = [root];
        let cur = null;
        while(stack.length) {
            cur = stack.pop();
            res.push(cur.val);
            cur.right && stack.push(cur.right);
            cur.left && stack.push(cur.left);
        }
        return res;
    };

    层次遍历

    var levelOrder = function(root) {
        //二叉树的层序遍历
        let res=[],queue=[];
        queue.push(root);
        if(root===null){
            return res;
        }
        while(queue.length!==0){
            // 记录当前层级节点数
            let length=queue.length;
            //存放每一层的节点 
            let curLevel=[];
            for(let i=0;i<length;i++){
                let node=queue.shift();
                curLevel.push(node.val);
                // 存放当前层下一层的节点
                node.left&&queue.push(node.left);
                node.right&&queue.push(node.right);
            }
            //把每一层的结果放到结果数组
            res.push(curLevel);
        }
        return res;
    };

    最大深度

    var maxdepth = function(root) {
        if (!root) return root
        return 1 + math.max(maxdepth(root.left), maxdepth(root.right))
    };

    最近公共祖先

    var lowestCommonAncestor = function(root, p, q) {
        // 使用递归的方法
        // 需要从下到上,所以使用后序遍历
        // 1. 确定递归的函数
        const travelTree = function(root,p,q) {
            // 2. 确定递归终止条件
            if(root === null || root === p||root === q) {
                return root;
            }
            // 3. 确定递归单层逻辑
            let left = travelTree(root.left,p,q);
            let right = travelTree(root.right,p,q);
            if(left !== null&&right !== null) {
                return root;
            }
            if(left ===null) {
                return right;
            }
            return left;
        }
       return  travelTree(root,p,q);
    };

    对称二叉树

    var isSymmetric = function(root) {
        //使用递归遍历左右子树 递归三部曲
        // 1. 确定递归的参数 root.left root.right和返回值true false 
        const compareNode=function(left,right){
            //2. 确定终止条件 空的情况
            if(left===null&&right!==null||left!==null&&right===null){
                return false;
            }else if(left===null&&right===null){
                return true;
            }else if(left.val!==right.val){
                return false;
            }
            //3. 确定单层递归逻辑
            let outSide=compareNode(left.left,right.right);
            let inSide=compareNode(left.right,right.left);
            return outSide&&inSide;
        }
        if(root===null){
            return true;
        }
        return compareNode(root.left,root.right);
    };
    

    动态规划

    最长重复子数组

    // 输入: A: [1,2,3,2,1] B: [3,2,1,4,7] 输出:3 解释: 长度最长的公共子数组是 [3, 2, 1] 。
    const findLength = (A, B) => {
        // A、B数组的长度
        const [m, n] = [A.length, B.length];
        // dp数组初始化,都初始化为0
        const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
        // 初始化最大长度为0
        let res = 0;
        for (let i = 1; i <= m; i++) {
            for (let j = 1; j <= n; j++) {
                // 遇到A[i - 1] === B[j - 1],则更新dp数组
                if (A[i - 1] === B[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                // 更新res
                res = dp[i][j] > res ? dp[i][j] : res;
            }
        }
        // 遍历完成,返回res
        return res;
    };

    最长公共子序列

    // 输入:text1 = "abcde", text2 = "ace" 输出:3 解释:最长公共子序列是 "ace",它的长度为 3。
    const longestCommonSubsequence = (text1, text2) => {
        let dp = Array.from(Array(text1.length+1), () => Array(text2.length+1).fill(0));
    
        for(let i = 1; i <= text1.length; i++) {
            for(let j = 1; j <= text2.length; j++) {
                if(text1[i-1] === text2[j-1]) {
                    dp[i][j] = dp[i-1][j-1] +1;;
                } else {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])
                }
            }
        }
    
        return dp[text1.length][text2.length];
    };

    最长连续递增序列

    const findLengthOfLCIS = (nums) => {
        let dp = Array(nums.length).fill(1);
    
    
        for(let i = 0; i < nums.length - 1; i++) {
            if(nums[i+1] > nums[i]) {
                dp[i+1] = dp[i]+ 1;
            }
        }
    
        return Math.max(...dp);
    };

    最大回文子串

    var longestPalindrome = function (s) {
        const len = s.length
        if (len < 2) return s 
        let max = 0
        let str = s[0]
        const dp = Array.from(new Array(len), () => new Array(len))
      
      
        for(let j=0; j< len; j++) {
          for(let i=0; i<j; i++) {
            dp[i][j] = s[i] === s[j] && (j - i < 3 || dp[i+1][j-1])
            if(dp[i][j] && j -i + 1 > max) {
              max = j - i + 1
              str = s.substring(i, j+1)
            }
          }
        }
        return str
      }

    最长回文子序列

      const longestPalindromeSubseq = (s) => {
        const strLen = s.length;
        let dp = Array.from(Array(strLen), () => Array(strLen).fill(0));
        
        for(let i = 0; i < strLen; i++) {
            dp[i][i] = 1;
        }
    
        for(let i = strLen - 1; i >= 0; i--) {
            for(let j = i + 1; j < strLen; j++) {
                if(s[i] === s[j]) {
                    dp[i][j] = dp[i+1][j-1] + 2;
                } else {
                    dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }
    
        return dp[0][strLen - 1];
    };

    无重复子数组最大长度 (滑动窗口)

    function sub(arr) {
        let max = 0
        let base = 0
        const has = new Map()
        for(let i = 0; i < arr.length; i++) {
            const cur = arr[i]
            if (hash.hash(cur)) {
                base = Math.max(base, has.get(cur) + 1)
            }
            hast.set(cur, i)
            max = Math.max(max, i - base + 1)
        }
        return max
    }

    子数组乘积最大

    function largestSum(arr) {
        let max = Number.MIN_SAFE_INTEGER, fmin = arr[0], fmax= arr[0]
    
        for(let num of arr) {
            if (num < 0) {
                const temp = fmax
                fmax = fmin
                fmin = temp
            }
    
            fmin = Math.min(fmin*num, num)
            fmax = Math.max(fmax*num, num)
            max = Math.max(max, fmax)
        }
        return max
    }

    最小费用

    function minCount(days, cost) {
        const len = days.length
        let maxDay = days[len - 1], minDay = days[0]
        let dp = new Array(maxDay + 31).fill(0)
    
        for(let d = maxDay, i = len -1; d >= minDay; d-- ) {
            if (days[i] === d) {
                dp[d] = Math.min(dp[d + 1] + cost[0], dp[d+7] + cost[1], dp[d+30]+cost[2])
                i--
            } else {
                dp[d] = dp[d+1]
            }
        }
        return dp[minDay]
    }
    

    岛屿数量

    const numIslands = (grid) => {
      let count = 0
      for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {
          if (grid[i][j] === '1') {
            count++
            turnZero(i, j, grid)
          }
        }
      }
      return count
    }
    function turnZero(i, j, grid) {
      if (i < 0 || i >= grid.length || j < 0 
           || j >= grid[0].length || grid[i][j] === '0') return
      grid[i][j] = '0'
      turnZero(i, j + 1, grid)
      turnZero(i, j - 1, grid)
      turnZero(i + 1, j, grid)
      turnZero(i - 1, j, grid)
    }
    

    二叉树中和为某一值的路径

    var pathSum = function(root, target) {
        const res = []
    
        function dfs(root, path, sum) {
            if (!root) return
            path.push(root.val)
            sum -= root.val
            if (!root.left && !root.right) {
                if (sum === 0) {
                    res.push([...path])
                }
            }
            dfs(root.left, path, sum)
            dfs(root.right, path, sum)
            path.pop()
     
        }
    
        dfs(root, [], target)
    
        return res
    
    };

    Juanha
    0 声望0 粉丝

    « 上一篇
    手写