javascript(JS / TS)攻克数据结构 --- 二周计划 JS/TS实现 (已完结)

力扣 – 数据结构二周计划

第一天 – 数组

1、 存在重复元素

image-20220219124817263

/*
	解题思路:创建一个对象,用来存储是否有重复元素,若有重复元素则直接返回true,否则返回false
*/
function containsDuplicate(nums: number[]): boolean {
    let hash:object = {}
    for(let i =0;i<nums.length;i++){
        if(!hash[nums[i]]){
            hash[nums[i]] = true
        }else{
            return true
        }
    }
    return false
};

2、最大子数组和

image-20220219124924306

/*
	解题思路:利用动态规划,每次比较当前元素的前一个元素是否比0大,若比0大就当前元素保存前一个元素与当前元素的最大和
	随后再定义max 动态保存最大的nums【i】 
*/
function maxSubArray(nums: number[]): number {
    let max:number = nums[0]
    let len:number = nums.length
    for(let i =1;i<len;i++){
        nums[i] += Math.max(nums[i-1],0)
        max = Math.max(nums[i],max)
    }
    return max
};

第二天 – 数组

1、两数之和

image-20220220113957990

function twoSum(nums: number[], target: number): number[] {
    let map = new Map()
    let len = nums.length
    /* 用 map 来存储计算过的数字
    	例如 9 - 7 得到的 2 将 7 保存在map里 对象里没有2进行下一步
    	下次计算 9 - 2 得到的7 对象里有存 7 则将结果返回
    */
    for(let i =0;i<len;i++){
        let rest = target - nums[i]
        if(map.has(rest)){
            return [map.get(rest),i]
        }
        map.set(nums[i],i)
    }
};

2、 合并两个有序数组

image-20220220114455568

// 解法一 使用双指针解法
function merge(nums1: number[], m: number, nums2: number[], n: number): void {
    let len:number = nums1.length-1
    m--
    n--
    /* 循环比较 num1 从m--下标 与 num2 从n-- 下标开始谁的值大,
    	大的值就往num1数组的最后面开始往前放
    */
    while(n>=0){
        if(nums1[m] > nums2[n]){
            nums1[len--] = nums1[m--]
        }else{
            nums1[len--] = nums2[n--]
        }
    }
};
// 解法二  暴力合并再排序

function merge(nums1: number[], m: number, nums2: number[], n: number): void {
    nums1.splice(m,nums1.length-m,...nums2)
    nums1.sort((a,b)=>a-b)
};

第三天 – 数组

1、两个数组的交集 II

image-20220223115615557

var intersect = function(nums1, nums2) {
//长短交换判定
    if (nums1.length > nums2.length) {
            return intersect(nums2, nums1)
    }
    const hashmap = new Map()
//将较短的nums1中的元素与个数存入hashmap
    for (let num of nums1) {
        let count = hashmap.get(num) || 0
        hashmap.set(num, count+1)
    }
    let res = [], index = 0
//遍历nums2,遇到重复则count--并更新hashmap
    for (let num of nums2) {
        let count = hashmap.get(num) || 0
        if (count > 0) {
            res[index++] = num
            count--
//更新hashmap,以count状态作为判断条件
            if (count > 0) {
                hashmap.set(num, count)
            } else {
                hashmap.delete(num)
            }
        }
    }
    return res
};

//  双指针做法 + 数组排序

var intersect = function(nums1, nums2) {
    // 两数组从小到大排序
    nums1.sort((a,b)=>a-b)
    nums2.sort((a,b)=>a-b)
    //  创建左右指针,结果数组保存结果,index推进结果的下标
    let res = [],index=0,left=0,right=0
    let len1 = nums1.length,len2 = nums2.length
    // 在两个都没达边界时推进
    while(left<len1 && right < len2){
        // 哪边数组小就推进哪边,直到相等时,即两数组的交集,即把交集的数放进数组里,并推进两数组和结果数组
        if(nums1[left] < nums2[right]){
            left++
        }else if(nums1[left] > nums2[right]){
            right++
        }else{
            // 两边都相等即交集,将结果保存并进行下一波比较
            res[index++] = nums1[left]
            left++
            right++
        }
    }
    return res
};

2、买卖股票的最佳时机

image-20220223122753061

var maxProfit = function(prices) {
    // 创建保存数组中最小值
    let min = prices[0]
    // 记录最大差值
    let max = 0
    let len = prices.length
    for(let i =1;i<len;i++){
        //  在每次循环中记录最小的值
        let cur = prices[i]
        min = Math.min(cur,min)
        //  如果当前值比最小值大则更新最大的差值
        if(cur > min ) max = Math.max(cur - min,max)
    }
    return max
};

第四天 – 数组

1、重塑矩阵

image-20220224115727423

var matrixReshape = function(mat, r, c) {
    let m = mat.length,n = mat[0].length
    //  要是转化的长度不满足直接返回原数组
    if(m * n != r * c) return mat
    let res = []
    //  将数组换为一维的
    mat = mat.flat()
    //  按 c 列的数量,一层一层的添加进结果数组并返回
    for(let i =0;i<r;i++){
        res.push(mat.splice(0,c))
    }
    return res
};

2、杨辉三角

image-20220224120406537

function generate(numRows: number): number[][] {
    let res = []
    for(let i =0 ;i<numRows;i++){
        //  每一层都初始化为1
        const row = new Array(i+1).fill(1)
        for(let j =1;j<row.length-1;j++){
            //  每一层的数都等于其上一层的左右两数之和,从第三层开始
            row[j] = res[i-1][j-1] + res[i-1][j] 
        }
        // 把每一层的数加到结果数组并返回
        res.push(row)
    }
    return res
};

//  用数组的 reduce 方法
var generate = function(numRows) {
    let r = Array(numRows)
    //  初始化数组第一层为1
    r[0] = [1]
    for (let i=1;i<numRows;i++) {
        //初始化i层 为一个数组
        r[i] = []
        //到上一层中累加左右两边的数,并添加到该层
        r[i-1].reduce((p,c) => {
            r[i].push(p+c)
            // 返回右边的数作为下一个pre值
            return c
        },0)
        // 补齐最后一个1添加到数组中
        r[i].push(1)
    }
    // 返回结果数组
    return r
};
/*
	拿第三层举例,到第三层时,上一层为[1,1]
	则reduce 上一层[1,1]得到: [1,2]
	最后再在最后补上一个1:[1,2,1]就为该层的目标数组
*/

第五天 – 数组

1、有效的数独

image-20220225123619519

var isValidSudoku = function(board) {
    // 创建一个保存的集合
    let set = new Set();
    
    for (let i = 0; i < 9; i++) {
        for (let j = 0; j < 9; j++) {
            const number = board[i][j];
            if (number === '.') continue;
            const colStr = `${number}c${i}`;   // 保存每行的元素
            const rowStr = `${number}r${j}`;   //  保存每列的元素
            const sectionStr = `${number}s(${Math.floor(i/3)},${Math.floor(j/3)})`;  //  判断3 * 3 格的元素
            if (set.has(colStr) || set.has(rowStr) || set.has(sectionStr)) return false; //  若有一个不满足有重复就为false
            set.add(colStr);
            set.add(rowStr);
            set.add(sectionStr);
        }
    }
    return true;
};

2、矩阵置零

image-20220225124300759

var setZeroes = function(matrix) {
    let r = matrix.length
    let d = matrix[0].length
    let res = []
    for(let i =0;i<r;i++){
        //  找出每行出现的0,并记录下0的纵坐标
       if(matrix[i].indexOf(0) !== -1){ 
           matrix[i].forEach((item,index,arr)=>{
               if(item == 0) res.push(index)
               //  将该行的元素逐个清0
               arr[index] = 0
           })
           
       }
    }
    let len = res.length
    //  将出现0的纵坐标清0
    for(let i =0;i<r;i++){
        for(let j =0;j<len;j++){
            matrix[i][res[j]] = 0
        }
    }
};

第六天 – 字符串

1、字符串中的第一个唯一字符

image-20220226211257101

var firstUniqChar = function(s) {
    let len = s.length
    for(let i=0;i<len;i++){
        //  如果它从前找的下标 等于 它从后开始的下标说明不是重复的
        if(s.indexOf(s[i]) === s.lastIndexOf(s[i])){
            return i
        }
    }
    return -1
};

2、赎金信

image-20220226211358249

var canConstruct = function(ransomNote, magazine) {
    let hash = {}
    for(let i of magazine){
        //  将大的字符串全部保存下来,重复的就个数+1
        hash[i] = ( hash[i] || 0 )+1
    }
    for(let i of ransomNote){
        if(hash[i]){
            //  若大的只有1个a,小的有两个a的话,第二次遍历到a时hash[a] == 0 则直接return false
            //  或者大的里没有小的字符串,也直接return false
             hash[i]--
        }else{
            return false
        }
    }
    return true
};

3、有效的字母异位词

image-20220226211655304

var isAnagram = function(s, t) {
    // 若两字符长度不等,直接返回false
    if(s.length !== t.length) return false
    let hash = {}
    for(let i of s){
        //  将字符串全部保存下来,重复的就个数+1
        hash[i] = (hash[i] || 0 )+1
    }
    for(let i of t){
        if(hash[i]){
            //  若一个只有1个a,另一个有两个a的话,第二次遍历到a时hash[a] == 0 则直接return false
            //  或者一个里没有另一个的字符串,也直接return false
            hash[i]--
        }
        else return false
    }
    return true
};

第七天 – 链表

1、环形链表

image-20220227105615889

//  第一种使用对象记录每个对象出现的标志,当再次出现时说明有环状返回true
var hasCycle = function(head) {
    let node = head
    while(node){
        if(node.flag) return true
        node.flag = true
        node = node.next
    }
    return false
};
//  第二种使用JSON.stringify ,当对象循环引用时会报错说明链表有环状,否则说明无
var hasCycle = function(head) {
    try{
        JSON.stringify(head)
        return false
    }catch{
        return true
    }
};

2、合并两个有序链表

image-20220227105924589

//  利用双指针循环
var mergeTwoLists = function(list1, list2) {
    let head = new ListNode(-1)
    let preHead = head
    while(list1 && list2){
        if(list1.val < list2.val) 
        {
        preHead.next = list1
        list1 = list1.next
        }
        else{
            preHead.next = list2
            list2 = list2.next
        }
        preHead = preHead.next
    }
    preHead.next = list1 === null?list2:list1
    return head.next
};

3、移除链表元素

image-20220227110049244

//  常规链表解法
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    if(head == null) return head
    //  初始化链表
    let newList = new ListNode(0,head)
    let cur = newList
    // 循环遍历整个链表,并把相同的节点跳过
    while(cur.next){
        if(cur.next.val == val){
            cur.next = cur.next.next
        }else{
            cur = cur.next
        }
    }
    // 返回头节点
    return newList.next
};

第八天 — 链表

1、反转链表

image-20220228091812783

var reverseList = function(head) {
    if(!head) return head
    let node = head
    let pre = null,temp
    while(node){
        temp = node.next
        node.next = pre
        pre = node
        node = temp 
    }
    // 最后的头节点变为pre
    return pre
};

// 第二种递归操作
var reverseList = function(head) {
    if (head == null || head.next == null) {
        return head;
    }
    const newHead = reverseList(head.next);
    //  从倒数第二个节点开始将下一个节点的next指向自己,并将自己的next指向null,完成翻转操作
    head.next.next = head;
    head.next = null;
    //  最后的头节点更换为newHead
    return newHead;
};

2、删除排序链表中的重复元素

image-20220228092550102

var deleteDuplicates = function(head) {
    //  常规的遍历链表操作去除重复的元素
    if(!head) return head
    let node = head
    while(node.next ){
        if(node.val === node.next.val){
            node.next = node.next.next
        }else{
            node = node.next
        }
    }
    return head
};

第九天 — 栈 / 队列

1、有效的括号

image-20220301094622979

var isValid = function(s) {
    if(!s) return true
    let len = s.length
    let str = {
			'{':'}',
			'(':')',
			'[':']'
		}
		let stack = []
		for(let i =0;i<len;i++){
			const cur = s[i]
			if(cur == '(' || cur == '[' || cur == '{')  stack.push(str[cur])
			else{
                //  若栈内没有对应的元素则为false
				if(!stack.length || stack.pop() !== cur) return false
			}
		}
    	// '[' 像这种栈内还有元素的也返回 false
		return !stack.length
};

2、用栈实现队列

image-20220301094832767

var MyQueue = function() {
    this.MyQueue = []
};

/** 
 * @param {number} x
 * @return {void}
 */
MyQueue.prototype.push = function(x) {
    this.MyQueue.push(x)
};

/**
 * @return {number}
 */
MyQueue.prototype.pop = function() {
    return this.MyQueue.shift()
};

/**
 * @return {number}
 */
MyQueue.prototype.peek = function() {
    return this.MyQueue[0]
};

/**
 * @return {boolean}
 */
MyQueue.prototype.empty = function() {
    return this.MyQueue.length == 0
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * var obj = new MyQueue()
 * obj.push(x)
 * var param_2 = obj.pop()
 * var param_3 = obj.peek()
 * var param_4 = obj.empty()
 */

第十天 — 树

1、二叉树的前序遍历

img

image-20220302105724008


// 非递归实现,迭代实现
var preorderTraversal = function(root) {
    if(!root) return []
    let res = []
    let queue = [root]
    while(queue.length){
        let temp = queue.pop()
        res.push(temp.val)
        if(temp.right) queue.push(temp.right)  //  因为栈先进后出所以将right放前
        if(temp.left) queue.push(temp.left)
    }
    return res
};

// 递归实现
var preorderTraversal = function(root,res=[]) {
    if(!root) return []
    res.push(root.val)
    preorderTraversal(root.left,res)
    preorderTraversal(root.right,res)
    return res
};

2、二叉树的中序遍历

image-20220302110453222

img

// 非递归实现
var inorderTraversal = function(root) {
    if(!root) return []
    let res = []
    let stack = []
    while(stack.length || root){
        //  循环遍历左节点
        while(root){
            stack.push(root)
            root = root.left
        }
        let temp = stack.pop()
        res.push(temp.val)
        root = temp.right
    }
    return res
};

//递归实现
var inorderTraversal = function(root,res = []) {
    if(!root) return []
    if(root.left) inorderTraversal(root.left,res)
    res.push(root.val)
    if(root.right) inorderTraversal(root.right,res)
    return res
};

3、 二叉树的后序遍历

image-20220302110709266

img

// 非递归实现,前序遍历是根左右,后序为左右根,将前序实现为根右左,再将数组反转即得后序遍历,左右根
var postorderTraversal = function(root) {
    if(!root) return []
    let res = []
    let stack = [root]
    while(stack.length){
        let temp = stack.pop()
        if(temp.left) stack.push(temp.left)  //  前序根左右调换为,根右左
        if(temp.right) stack.push(temp.right)
        res.push(temp.val)
    }
    return res.reverse()  //  将结果反转即为后序
};

4、二叉树的层序遍历

image-20220302111022274

var levelOrder = function(root) {
    if(!root) return []
    let res = []
    let stack = [root]
    let count = 0  //  记录当前层级
    while(stack.length){
        res[count] = [] //  初始化当前层级
        let countNum = stack.length  //  当前层级的节点数
        while(countNum--){  //  遍历当前层级的节点数
        let temp = stack.shift()  // 从前开始取当前层级的节点
        res[count].push(temp.val)  //  推入每层的节点值
        if(temp.left) stack.push(temp.left)  
        if(temp.right) stack.push(temp.right)
        }
        count++   //  层级+1
    }
    return res
};

第十一天 — 树

1、二叉树的层序遍历

// 同上题解法
var levelOrder = function(root) {
    if(!root) return []
    let count = 0
    let node = [root]
    let res = []
    while(node.length){
        res[count] = []
        let countNum = node.length
        while(countNum--){
            let temp = node.shift()
            res[count].push(temp.val)
            if(temp.left) node.push(temp.left)
            if(temp.right) node.push(temp.right)
        }
        count++
    }
    return res
};

2、二叉树的最大深度

image-20220303111922824

// DFS 深度遍历递归
var maxDepth = function (root) {
let max = 0
const dfs = (root,res = 1) => {
    if (!root) return
    if(!root.left && !root.right) max = 			Math.max(res, max)
    if (root.left) dfs(root.left, res+1)
    if (root.right) dfs(root.right, res+1)
}
dfs(root)
return max
};
// 解法二
var maxDepth = function(root) {
    return root == null?0:Math.max(maxDepth(root.left),maxDepth(root.right)) + 1
};

3、对称二叉树

image-20220303112019915

var isSymmetric = function(root) {
    if(!root) return true
    const isMirror = (left,right)=>{
        //  如果判断到叶子节点则返回true
        if(!left && !right) return true
        // 两个节点都存在,并且值相同,镜像相同,则为true
        if(left && right && left.val === right.val 
        && isMirror(left.left,right.right)
        && isMirror(left.right,right.left)
        ) return true
        // 都不符合就返回false
        return false
    }
    return isMirror(root.left,root.right)
};

第十二天 — 树

1、翻转二叉树

image-20220304105254325

//  迭代解法一
var invertTree = function(root) {
    if(root == null) return root
    let stack = [root]
    while(stack.length){
        let node = stack.shift()
        let temp = node.left
        node.left = node.right
        node.right = temp
        if(node.left) {
            stack.push(node.left)
        }
        if(node.right){
            stack.push(node.right)
        }
    }
    return root
};
// 递归解法二
var inverTree = function(root){
    if(!root) return null
    return {
        val:root.val,
        left:inverTree(root.right),
        right:inverTree(root.left)
    }
}

2、路径总和

image-20220304105427026

// 前序遍历解法一
var hasPathSum = function (root, targetSum) {
    if (!root) return false
    let stack = [[root,root.val]]
    while(stack.length){
        let [node, sum] = stack.pop()
        if(!node.left && !node.right && sum == targetSum) return true
        if(node.right) stack.push([node.right,node.right.val + sum])
        if(node.left) stack.push([node.left,node.left.val + sum])
    }
    return false
};
// 递归解法二
var hasPathSum = function (root, targetSum) {
    if (!root) return false
    let final = false
    const dfs = (node, res) => {
        if (!node.left && !node.right && res == targetSum) final = true
        if(node.left) dfs(node.left, res + node.left.val)
        if(node.right) dfs(node.right, res + node.right.val)
    }
    dfs(root, root.val)
    return final
};

第十三天 — 树

1、二叉搜索树中的搜索

image-20220305092019139

var searchBST = function(root, val) {
    // 递归法
    if(!root) return null
    if(root.val === val) return root
    return root.val > val? searchBST(root.left,val):searchBST(root.right,val)


    // 迭代法
    // while(root){
    //     if(root.val == val){
    //         return root
    //     }
    //     if(root.val > val) root = root.left
    //     else root = root.right
    // }
    // return null
};

2、二叉搜索树中的插入操作

image-20220305092055552

//  递归法最简易,利用二叉搜索树的特性完成插入
var insertIntoBST = function(root, val) {
    if(!root) return new TreeNode(val)
    if(root.val < val){
        if(!root.right) root.right = new TreeNode(val)
        else insertIntoBST(root.right,val)
    }else{
        if(!root.left) root.left = new TreeNode(val)
        else insertIntoBST(root.left,val)
    }
    return root
};

第十四天 — 树

1、验证二叉搜索树

image-20220306115203981

//  递归
var isValidBST = function(root,min=-Infinity,max=Infinity){
    if(!root) return true
    if(root.val <= min || root.val >= max) return false
    return isValidBST(root.left,min,root.val) && isValidBST(root.right,root.val,max)
};
//  利用中序遍历二叉搜索树为一个升序数组
var isValidBST = function(root){
    let res = []
    const dfs = (root)=>{
        if(!root) return false
    root.left && dfs(root.left)
    res.push(root.val)
    root.right && dfs(root.right)
    }
    dfs(root)
    let len = res.length
    for(let i =1;i<len;i++){
        if(res[i-1] >= res[i]) return false
    }
    return true
};

2、两数之和 IV - 输入 BST

image-20220306120747239

// 先遍历元素节点,随后两数之和解法
var findTarget = function (root, k) {
    let res = []
    const dfs = (root) => {
        if (!root) return false
        res.push(root.val)
        if (root.left) dfs(root.left)
        if (root.right) dfs(root.right)
    }
    dfs(root)
    let hash = {}
    for (let i = 0; i < res.length; i++) {
        let rest = k - res[i]
        if (hash[rest]) return true
        hash[res[i]] = true
    }
    return false
};

3、二叉搜索树的最近公共祖先

image-20220306120902431

var lowestCommonAncestor = function (root, p, q) {
    // 递归解法
    if(root.val > p.val && root.val >q.val) return lowestCommonAncestor(root.left,p,q)
    if(root.val < p.val && root.val <q.val) return lowestCommonAncestor(root.right,p,q)
    return root
    
    //  迭代解法,先将p跟q的顺序排除q为小,p为大方便后续计算
    // if(p.val > q.val) [p,q] = [q,p]
    // while (root) {
    //     if (root.val > p.val && root.val < q.val || root === q || root ===p) return root
    //     if(root.val > q.val) root = root.left
    //     if(root.val < p.val) root = root.right
    // }
};
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值