算法笔记

122 阅读3分钟

二分法查找

二分法查找

    function search(nums, target) {
        let left = 0
        let right = nums.length - 1
        while(left <= right) {
            let mid = left + (right -left)/2
            if(nums[mid] < target) {
                left = mid + 1
            } else  {
                right = mid - 1 
            }
            return left
        }
    }

双指针

平方数排序

function sortSqurt(nums) {
    let left = 0, right = nums.length-1
    let res = []
    while(left<= right) {
        let l = nums[left] * nums[left]
        let r = nums[right] * nums[right]
        if(l< r) {
            res.unshift(r)
            r--
        } else {
            res.unshift(l)
            l++
        }
    }
    return res
}
sortSqurt([-9,-1,0,3,6])

数组轮转

输入: nums = [1,2,3,4,5,6,7], k = 3

输出: [5,6,7,1,2,3,4]

解释: 向右轮转 1 步: [7,1,2,3,4,5,6]

向右轮转 2 步: [6,7,1,2,3,4,5]

向右轮转 3 步: [5,6,7,1,2,3,4]

// 利用数组的倒置
funtion rotate(nums, k) {
    let reserve = function(start, end) {
        while(start< end) {
            let temp = nums[start]
            nums[start] = nums[end]
            nums[end] = temp
            start++ 
            end--
        }
    }
    k %= nums.length;
    reserve(0, nums.length-1)
    reverve(0,k-1)
    reverer(k, nums.length-1)
    return nums
}

移动零

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
// 快慢指针
// 快指针遇到非零数值后,慢指针跟进赋值
// 当快指针指到尾部时,满指针收集了所有的非零数,之后慢指针补齐0
function solution(nums) {
    let fast = 0
    let slow = 0
    while( fast < nums.length ) {
        if(nums[fast] !=0) {
            nums[slow++] = nums[fast]
        }
        fast ++
    }
    while ( slow < nums.length ) {
        nums[slow++] = 0
    }
    return nums
}
javascipt 
function move0(nums) {
    let temp = []
    for(let i = 0; i< nums.length; i++) {
        if(nums[i] != 0) {
            temp.push(i)
        }
    }
    for(let j= nums.length - temp.length; j< nums.length; j++) {
        nums[j] = 0
    }
    return nums
}

两数之和

        //双循环查找
 // function twoSum(nums, target) {
    //     for(let fast = 0; fast< nums.length; fast++) {
    //         let temp = nums[fast]
    //         for(let slow = fast + 1; slow < nums.length; slow ++) {
    //             console.log(slow, fast)
    //             if(nums[slow] == target - temp) {
    //                 return [fast+1, slow+1]
    //             }
    //         }
    //     }
    // }
var twoSum = function(numbers, target) {
    let left = 0
    let right = numbers.length -1
    while(left <= right) {
        let temp = numbers[left] + numbers[right]
        if(temp < target) {
            left ++
        } else if (temp > target) {
            right --
        } else {
            return [left+1, right+1]
        }
    }
};

字符串反转

 var reverseString = function (s) {
        let left = 0
        let right = s.length - 1
        let swap = function (i, j) {
            let temp = i
            i = j
            j = temp
        }
        while (left < right) {
            [s[left], s[right]] = [s[right], s[left]];
            left++
            right--
        }
        return s
    };

反转句子中单词

输入: s = "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"
     // 反转文字
    var reverseString = function (s) {
        let left = 0
        let right = s.length - 1
        while (left < right) {
            [s[left], s[right]] = [s[right], s[left]];
            left++
            right--
        }
        return s
    };
    // 反转句子中的单词
    var reverseWords = function (s) {
        let left = 0
        let right = s.length - 1
        let arr = s.split(' ')
        for (var i = 0; i < arr.length; i++) {
            arr[i] = reverseString(arr[i].split('')).join('')
        }
        reverseString(arr)
        return arr.join(' ')
    };

查找链表中的中间节点

// 双指针-快慢指针
function midnode(head) {
    let fast = slow = head
    while(fast && fast.next) {
        fast = fast.next.next
        slow = slow.next
    }
    return slow
}
// 第一次循环获得链表长度,第二次循环到N/2返回结果
function midnode(head) {
    let len = 0
    let listnode1 = listnode2 = head
    while(listnode1) {
        listnode1 = listnode1.next
        len++
    }
    let temp = 0
    while(temp< Math.trunc(len/2)) {
        temp++
        listnode2 = head.next
    }
    return listnode2
}

删除链表中倒数N的节点

// 利用栈stack
   function deleteNnode(head, n) {
       let stack = new Array()
       let listnode1 = head
       while(listnode1) {
           stack.push(listnode1)
           listnode1 = listnode1.next
       }
       let k = n
       while(k>0) {
           k--
           stack.pop()
       }
       let temp = stack.pop()
       temp.next = temp.next.next
       return temp
   }
   
    // 第一次循环得出长度,第二次在头部插入0形成新的链表在倒数n的位置之前循环
    // 每次取next, 在利用引用类型的数据特点,操作temp相当于操作listNode
    function deleteNnode(head, n) {
        let cur = head
        let length = 0
        let listNode = new ListNode(0, head);
        while (cur != null) {
            cur = cur.next
            length++
        }
        let k = 0
        let temp = listNode
        while (k != length - n) {
            temp = temp.next
            k++
        }
        temp.next = temp.next.next
        return listNode.next
    }

滑动窗口

查询字符串的最长子串

示例 1:

输入: s = "abcabcbb" 输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb" 输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3:

输入: s = "pwwkew" 输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。   请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

解法思路 定义一个set类型来接收,每次循环后set长度进行比较 取出最长的值就是答案 每一次循环,判断第i-1位置开始,直到遇到重复的值为止的set长度,同时删除上一次set的头部

function lengthOfLongestSubstring(s) {
    let len = s.length
    let i = 0, rk = -1
    let set = new Set()
    let ans = 0
    for(i; i< len; i++) {
        if(i!=0) {
            set.delete(s.charAt(i))
        }
        while(rk< n -1 && !set.has(s.charAt(rk + 1 ))) {}
            set.add(s.charAt(rk+1))
            rk ++ 
        }
        ans = Math.max(ans, set.length)
    }
    return ans
}

判断字符串的排列

var checkInclusion = function(s1, s2) {
    const n = s1.length, m = s2.length;
    if (n > m) {
        return false;
    }
    const cnt1 = new Array(26).fill(0);
    const cnt2 = new Array(26).fill(0);
    for (let i = 0; i < n; ++i) {
        ++cnt1[s1[i].charCodeAt() - 'a'.charCodeAt()];
        ++cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()];
    }
    console.log(cnt1.toString(), cnt2.toString())
    // if (cnt1.toString() === cnt2.toString()) {
    //     return true;
    // }
    // for (let i = n; i < m; ++i) {
    //     ++cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()];
    //     --cnt2[s2[i - n].charCodeAt() - 'a'.charCodeAt()];
    //     if (cnt1.toString() === cnt2.toString()) {
    //         return true;
    //     }
    // }
    // return false;
};

深度优先、广度优先

图像渲染

// 深度优先渲染
function colorFill(image, sr, sc, color) {
    let m = image.length
    let n = image[0].length
    let oldColor = image[sr][sc]
    const fill = (i,j) => {
        image[i][j] = color 
        if(i<0|| j<0 || i>=m|| j>=n || image[i][j]!=oldColor) {
            return 
        }
        fill(i-1,j)
        fill(i,j-1)
        fill(i+1,j)
        fill(i,j+1)
    }
    fill(sr,sc)
    return image
}
function fill(image, sr, sr, color) {
    let m = image.length
    let n = image[0].length
    let oldColor = image[sr][sc]
    let queue = [[sr,sc]]
    if(oldColor == color) retrun image
    while(queue.length) {
        let [i,j] = queue.shift()
        image[i][j] = color
        if(i-1>=0 && image[i-1,j]== oldcolor) queue.push([i-1,j]);
        if(i+1<=m && image[i+1,j]== oldcolor) queue.push([i+1,j]);
        if(j-1>=0 && image[i,j-1]== oldcolor) queue.push([i,j-1]);
        if(j+1>=0 && image[i,j+1]== oldcolor) queue.push([i,j+1]);
    }
    return image
}

计算岛屿最大面积

image.png

深度优先思路:

  1. 取出一点(i,j) 判断边界和值 如果超出边界或者为0 返回
  2. 通过1得出这个点是岛屿点,将这个点赋值为0
  3. 定义一个ans 通过递归调用从(i,j)点开始收集所有岛屿并令ans+1
  4. 遍历图 返回结果
var maxAreaOfIsland = function (grid) {
    let m = grid.length
    let n = grid[0].length
    function dfs(i, j) {
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == 0) return 0
        grid[i][j] = 0
        let ans = 1, dm = [1,-1,0,0], dn = [0,0,1,-1]
        for(let k = 0; k< dm.length; k++) {
            ans = ans + dfs(i + dm[k], j + dn[k])
        }
        return ans
    }
    let res = 0
    for(let i = 0; i< m; i++) {
        for(let j =0 ; j< n; j++) {
            res = Math.max(dfs(i,j), res)
        }
    }
    return res
};

广度优先思路

var maxAreaOfIsland = function (grid) {
    let m = grid.length 
    let n = grid[0].length
    let res = 0
    let dx= [1,-1,0,0], dy= [0,0,1,-1]
    for(let i = 0; i< m; i++) { 
        for(let j =0 ; j< n; j++) { 
            let queue = [[i,j]]
            let temp = 0
            while(queue.length) {
                let [x,y] = queue.shift()
                if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] === 0) continue;
                if(grid[x][y] == 1) {
                    grid[x][y] = 0
                    temp ++
                    for(let k = 0; k< dx.length; k++) { 
                        queue.push([x+dx[k],y+dy[k]])
                    }
                }
            }
            ans = Math.max(ans, temp)
        } 
    }
    return ans
}

合并二叉树

// dfs 递归调用

function mergeTree(root1, root2) {
    if(root1 == null) return root2
    if(root2 == null) return root1
    return new treeNode(
        root1.val + root2.val, 
        mergeTrees(root1.left, root2.left),
        mergeTrees(root1.right, root2.right)
    ) 
}
// bfs
/**
 * @param {TreeNode} root1
 * @param {TreeNode} root2
 * @return {TreeNode}
 */
var mergeTrees = function (root1, root2) {
  // 如果只要有一个二叉树为空,则合并后的二叉树为另一个非空的二叉树
  if (root1 == null) return root2;
  if (root2 == null) return root1;
  // 两个二叉树都不为空的情况
  let queue = [merged], queue1 = [root1], queue2 = [root2];
  while(queue1.length && queue2.length) {
    let node = queue.shift(),
      node1 = queue1.shift(),
      node2 = queue2.shift();
    let left1 = node1.left,
      left2 = node2.left,
      right1 = node1.right,
      right2 = node2.right;
    if (left1 || left2) {
      // 要合并俩节点有左子节点都非空
      if (left1 && left2) {
        // 合并并赋值为左子树
        let left = new TreeNode(left1.val + left2.val);
        node.left = left;
        // 齐头并进入队列
        queue.push(left);
        queue1.push(left1);
        queue2.push(left2);
      } else if (left1) node.left = left1;
      else if (left2) node.left = left2;
    }
    if (right1 || right2) {
      if (right1 && right2) {
        let right = new TreeNode(right1.val + right2.val);
        node.right = right;
        queue.push(right);
        queue1.push(right1);
        queue2.push(right2);
      } else if (right1) node.right = right1;
      else if (right2) node.right = right2;
    }
  }
};