每日LeetCode力扣(1~5)

651 阅读4分钟
  • 算法比较弱,每天刷5道leetcode吧,用kotlin实现

1. 两数之和

  • 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
  • 解题: 这道 Two Sum 的题目作为 LeetCode 的开篇之题,乃是经典中的经典,正所谓‘ 平生不识 TwoSum,刷尽 LeetCode 也枉然
fun _0001_twoSum() {
    println("--------_001_twoSum-------")
    val nums = intArrayOf(1, 2, 11, 7, 15)
    println("_001_AddTwoNumber-->")
    for (i in twoSum(nums, 9)!!) {
        println(i)
    }
}

fun twoSum(nums: IntArray, target: Int): IntArray? {
    val map = HashMap<Int, Int>()
    for (i in nums.indices) {
        if (map.containsKey(target - nums[i])) {
            return intArrayOf(map[target - nums[i]]!!, i)
        }
        map[nums[i]] = i
    }
    return null
}

2. 两数相加

  • 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
  • 解题 建立一个新链表,然后把输入的两个链表从头往后撸,每两个相加,添加一个新节点到新链表后面。 为了避免两个输入链表同时为空,我们建立一个 dummy 结点,将两个结点相加生成的新结点按顺序加到 dummy 结点之后, 由于 dummy 结点本身不能变,所以用一个cur 来指向新链表的最后一个结点。
fun _0002_addTwoNumbers() {
    println("--------_002_addTwoNumbers-------")
    addTwoNumbers(
        ListNode(2, ListNode(4, ListNode(3, ListNode(1)))),
        ListNode(5, ListNode(6, ListNode(4, ListNode(5))))
    )?.print()
    println()
}


fun addTwoNumbers(l1: ListNode?, l2: ListNode?): ListNode? {
    var l1 = l1
    var l2 = l2
    val dummy = ListNode(-1)
    var cur: ListNode? = dummy
    var carry = 0
    while (l1 != null || l2 != null) {
        val d1 = l1?.data ?: 0
        val d2 = l2?.data ?: 0
        val sum = d1 + d2 + carry
        carry = if (sum >= 10) 1 else 0
        cur?.next = ListNode(sum % 10)
        cur = cur?.next
        if (l1 != null) {
            l1 = l1.next
        }
        if (l2 != null) {
            l2 = l2.next
        }
    }
    if (carry == 1) cur?.next = ListNode(1)
    return dummy.next
}

class ListNode {
    var data = 0
    var next: ListNode? = null

    internal constructor(data: Int) {
        this.data = data
    }

    internal constructor(data: Int, next: ListNode?) {
        this.data = data
        this.next = next
    }

    fun print() {
        print("-->$data")
        if (next != null) {
            next?.print()
        } else {
            println()
        }

    }
}

3. 无重复字符的最长子串

  • 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
  • 解题 使用 HashSet 把出现过的字符都放入 HashSet 中,遇到 HashSet 中没有的字符就加入 HashSet 中并更新结果 res,如果遇到重复的,则从左边开始删字符,直到删到重复的字符停止:
fun _0003_lengthOfLongestSubstring() {
    println("--------_003_lengthOfLongestSubstring-------")
    lengthOfLongestSubstring("abcabcbb")
    lengthOfLongestSubstring("bbbbb")
    lengthOfLongestSubstring("pwwkew")
    lengthOfLongestSubstring("pwwkewabcdefghijkabcabc")
}
fun lengthOfLongestSubstring(s: String) {
    var maxLen = 0
    var left = 0
    var right = 0
    val temp = HashSet<Char>()
    while (right < s.length) {
        if (!temp.contains(s[right])) {
            temp.add(s[right++])
            maxLen = Math.max(maxLen, temp.size)
        } else {
            temp.remove(s[left++])
        }
    }
    println("len:$maxLen")
}

4. 寻找两个正序数组的中位数

  • 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
  • 进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
示例 3:
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000
示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
  • 解题 用迭代形式的二分搜索法来解,是所谓的中位数,换一种角度去看,其实就是把一个有序数组分为长度相等的两段, 中位数就是前半段的最大值和后半段的最小值的平均数,也就是离分割点相邻的两个数字的平均值。 比如说对于偶数个数组 [1 3 5 7],那么分割开来就是 [1 3 / 5 7],其中 '/' 表示分割点,中位数就是3和5的平均值。对于奇数个数组 [1 3 4 5 7],可以分割为 [1 3 4 / 4 5 7],可以发现左右两边都有个4,则中位数是两个4的平均数,还是4。
fun _0004_findMedianSortedArrays() {
    println("--------_004_findMedianSortedArrays-------")
    println(findMedianSortedArrays(intArrayOf(1, 3), intArrayOf(2)))
    println(findMedianSortedArrays(intArrayOf(1, 2), intArrayOf(3, 4)))
    println(findMedianSortedArrays(intArrayOf(0, 0), intArrayOf(0, 0)))
    println(findMedianSortedArrays(intArrayOf(), intArrayOf(1)))
    println(findMedianSortedArrays(intArrayOf(2), intArrayOf()))
    println(findMedianSortedArrays(intArrayOf(1, 2, 3, 4, 5), intArrayOf(3, 4, 5, 6)))
}

fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double {
    var m = nums1.size
    var n = nums2.size
    if (m < n) return findMedianSortedArrays(nums2, nums1)
    if (n == 0) {
        return (nums1[(m - 1) / 2] + nums1[m / 2]) / 2.0
    }
    var left = 0
    var right = 2 * n
    while (left <= right) {
        var mid2 = (left + right) / 2
        var mid1 = m + n - mid2
        var l1: Double = if (mid1 == 0) Double.MIN_VALUE else nums1[(mid1 - 1) / 2].toDouble()
        var l2: Double = if (mid2 == 0) Double.MIN_VALUE else nums2[(mid2 - 1) / 2].toDouble()
        var r1: Double = if (mid1 == m * 2) Double.MAX_VALUE else nums1[mid1 / 2].toDouble()
        var r2: Double = if (mid2 == n * 2) Double.MAX_VALUE else nums2[mid2 / 2].toDouble()
        if (l1 > r2) left = mid2 + 1
        else if (l2 > r1) right = mid2 - 1
        else return (Math.max(l1, l2) + Math.min(r1, r2)) / 2
    }
    return -1.0
}

5. 最长回文子串

  • 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
  • 解题 马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性。
fun _0005_longestPalindrome() {
    println("--------_005_longestPalindrome-------")
    println(longestPalindrome("babad"))
    println(longestPalindrome("cbbd"))
    println(longestPalindrome("aaaadcbabcdfabcccccc"))
}

fun longestPalindrome(s: String): String? {
    if (s.length < 2) {
        return s
    }
    var temp = "$"
    for (element in s) {
        temp += "#$element"
    }
    temp += "#@"
    val n = temp.length
    val p = IntArray(n)
    var id = 0
    var mx = 0
    var index = 0
    var maxLen = -1
    for (i in 1 until n - 1) {
        p[i] = if (mx > i) Math.min(p[2 * id - i], mx - i) else 1
        while (temp[i + p[i]] == temp[i - p[i]]) {
            p[i]++
        }
        if (mx < i + p[i]) {
            mx = i + p[i]
            id = i
        }
        if (maxLen < p[i] - 1) {
            maxLen = p[i] - 1
            index = i
        }
    }
    val start = (index - maxLen) / 2
    return s.substring(start, start + maxLen)
}

我是今阳,如果想要进阶和了解更多的干货,欢迎关注公众号”今阳说“接收我的最新文章