- 算法比较弱,每天刷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)
}
我是今阳,如果想要进阶和了解更多的干货,欢迎关注公众号”今阳说“接收我的最新文章