leetcode-算法笔记1

160 阅读3分钟

第1题:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

var twoSum = function(nums, target) {
    // 暴力法 
    // for (let i = 0; i < nums.length; i++) {
    //     const index = nums.indexOf(target - nums[i])
    //     if (index !== -1 && index !== i) {
    //         return [i, index]
    //     }
    // }

    // 先遍历一次数组,用map保存 item-index的关系。
    // 再遍历第二次数组,每遍历一项就判断在map中是否存在满足条件的,如果存在则返回
    const map = new Map()

    for (let i = 0; i < nums.length; i++) {
        map.set(nums[i], i)
    }
    for (let j = 0; j < nums.length; j++) {
        const index = map.get(target - nums[j])
        // 如果index有值,并且不和j相等,则说明满足条件
        if (index && index !== j) {
            return [j, index]
        }
    }
};

第二题:无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

var lengthOfLongestSubstring = function(s) {
    // 定义一个set表,用来记录子串
    const set = new Set()
    // 定义左右指针
    let left = 0
    let right = 0
    // 定义maxLength用来保存最长子串长度
    let maxLength = 0
    const length = s.length

    for (; left < length; left++) {
        // 当右指针小于s的长度,并且set中没有right下标的字符
        while (right < length && !set.has(s[right])) {
            // 将s[right]保存到set中
            set.add(s[right++])
        }
        // 比较maxLength和right - left
        maxLength = Math.max(maxLength, right - left)
        // 如果right === length,证明maxLength已经是最大了,不需要继续循环了。
        if (right === length) break
        // 如果不是,删除s[left]
        // 因为如果执行该代码,证明right !== length,那么就意味着上面while循环是因为s[right]已经存在于set中
        // 删除最先进入set的s[left],重新获取子串
        set.delete(s[left])
    }
    return maxLength
};

第三题:回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

var isPalindrome = function(x) {
    // 先变成字符串
    const str = String(x)
    // 将字符串变成数组
    const arr = str.split('')
    // 翻转数组,并将数组切割成字符串判断与str是否相同
    return arr.reverse().join('') === str
};

第四题:最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

var longestCommonPrefix = function(strs) {
    if (strs.length === 1) return strs[0]
    let flag = true
    let index = 0
    while (flag) {
        for (let i = 1; i < strs.length; i++) {
            // 当字符相同
            if (strs[i][index] === strs[i - 1][index]) {
                continue
            } else {
                // 字符不相同,直接跳出循环
                flag = false
                break
            }
        }
        if (flag) {
            index++
            // 防止字符串相同,死循环
            if (index >= strs[0].length) {
                flag = false
            }
        } 
    }

    return strs[0].slice(0, index) === undefined ? '' : strs[0].slice(0, index)
};

第五题:有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。

var isValid = function(s) {
    const stack = []
    for (const i of s) {
        if (['[', '{', '('].includes(i)) {
            stack.push(i)
            continue
        }
        switch (i) {
            case ')':
                if (stack[stack.length - 1] === '(') {
                    stack.pop()
                    continue
                }
                break
            case ']':
                if (stack[stack.length - 1] === '[') {
                    stack.pop()
                    continue
                }
                break
            case '}':
                if (stack[stack.length - 1] === '{') {
                    stack.pop()
                    continue
                }
                break
        }
       
     return false
    }
    if (arr.length === 0) return true
    return false
};

第六题:合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

 

示例 1:

var mergeTwoLists = function(l1, l2) {
    let p1 = l1
    let p2 = l2
    let p3 = new ListNode(null, null)
    let result = p3
    while (p1 && p2) {
        if (p1.val <= p2.val) {
            p3.next = p1
            p3 = p3.next
            p1 = p1.next
        } else {
            p3.next = p2
            p3 = p3.next
            p2 = p2.next
        }
    }
    while (p1) {
        p3.next = p1
        p3 = p3.next
        p1 = p1.next
    }

    while (p2) {
        p3.next = p2
        p3 = p3.next
        p2 = p2.next
    }
    return result.next
};

第七题:删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    // 长度为0,直接返回即可
    if (nums.length === 0) return 0
    // 使用两个指针
    let fast = 1, slow = 1

    while (fast < nums.length) {
        // 因为该数组是有序数组,所以只要相邻两个元素不相等,那么就将fast对应的项赋值给slow
        if (nums[fast] !== nums[fast - 1]) {
            nums[slow] = nums[fast]
            // slow++
            slow++
        }
        // 如果相邻元素相同,直接fast++即可
        fast++
    }

    return slow
};

第八题:最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

/**
 * @param {number[]} nums
 * @return {number}
 */
 
var maxSubArray = function(nums) {
     let pre = 0, max = nums[0]
    for (let i = 0; i < nums.length; i++) {
        pre = Math.max(nums[i], (pre + nums[i]))
        max = Math.max(max, pre)
    }
    return max
};

第九题: 二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。

输入为 非空 字符串且只包含数字 1 和 0

/**
 * @param {string} a
 * @param {string} b
 * @return {string}
 */
var addBinary = function(a, b) {
    let i = a.length - 1
    let j = b.length - 1
    let result = ''
    // 保存进位的值
    let over = 0
    while (i >= 0 && j >= 0) {
        let sum = Number(a[i]) + Number(b[j]) + over
        // sum大于1,也就是说需要进位
        if (sum > 1) {
            over = 1
            sum = sum - 2
        } else {
            // 将over置为0,防止影响后续代码
            over = 0
        }
        result = sum + result
        i--
        j--
    }
    while (i >= 0) {
        let sum = Number(a[i]) + over
        if (sum > 1) {
            over = 1
            sum = sum - 2
        } else {
            over = 0
        }
        result = sum + result
        i--
    }
    while (j >= 0) {
        let sum = Number(b[j]) + over
        if (sum > 1) {
            over = 1
            sum = sum - 2
        } else {
            over = 0
        }
        // +的时候sum要在result前面
        result = sum + result
        j--
    }
    // over要在result前面
    return over === 0 ? result : over + result

};

第十题: 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if (n === 1 || n === 2 || n === 3) {
        return n
    }
    const arr = [2, 3]
    for (let i = 4; i < n; i++) {
        // 将 数组第一位和第二位值进行交换
        [arr[0], arr[1]] = [arr[1], arr[0]]
        // 修改第一位的值
        arr[1] = arr[0] + arr[1]
    }
    return arr[0] + arr[1]
};

第十一题:删除排序链表中的重复元素

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

返回同样按升序排列的结果链表。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var deleteDuplicates = function(head) {
    if (head === null) return head

    let p = head

    while (p.next) {
        if (p.val === p.next.val) {
            p.next = p.next.next
        } else {
            p = p.next
        }
    }
    return head
};

第十二题:合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function(nums1, m, nums2, n) {
    // 先nums1的最后一个数组下标
    let last = m + n - 1
    while (n) {
        if (m === 0 || nums1[m - 1] < nums2[n - 1]) {
            nums1[last--] = nums2[--n] 
        } else {
            nums1[last--] = nums1[--m]
        }
    }
};