LeetCode 160, 139, 41 题解

以下的表达纯属个人理解,如果有不对,欢迎指出 : )。

题解

(LeetCode 160)Intersection of Two Linked Lists

题目:

Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

begin to intersect at node c1.

Notes:

  • If the two linked lists have no intersection at all, return null.

  • The linked lists must retain their original structure after the function returns.

  • You may assume there are no cycles anywhere in the entire linked structure.

  • Your code should preferably run in O(n) time and use only O(1) memory.

大意就是给了两个链表,然后这两个链表的后面有一个段公共部分,让你找到这段公共部分的开始节点。数据保证链表里面没有环。如果两个链表没有公共部分,就返回空。要求在寻找的过程中,必须保持两个链表的原始结构,同时时间和空间复杂度要求为 O(n) 和 O(1)。

题解

其实在很多情况下,实现 O(n) 复杂度的方式,就是多走几遍,只要这个几是常数就可以了。

在这道题里面,由于链表都是单链表,所以只能一直往后走,不能往前走。同时在一个节点上的时候,不能知道这个链表后面的情况(比如说元素的个数)。

对于像这样的链表(或者说是节点指针结构)找公共节点的题,要找公共节点,比较直接能想到的是:如果两个指针本身是上下对齐的(就是说距离公共节点的距离),只要两个指针一起往前走,就肯定会走到同一个节点。

可是现在两个指针是不对齐的,怎么办呢?就让他对齐呗。

按照前面说的多走几遍的套路,看看走一次能得到什么:链表的长度。观察链表:后边的公共部分的长度是相同的,所以长度的差异就在前面,那我们只要把两个链表的长度差 distance 算出来,让指向长链表的指针提前走 distance 步,就可以了同步走了。

 function get_insersection_node(headA, headB)
    distance <- get_length_diff(headA, headB)
    longer_pointer <- get_longer_link_head(headA, headB)
    shorter_pointer <- get_short_link_head(headA, headB)
    move_pointer(longer_poiner, distance)
    
    while (longer_pointer != nil) and (shorter_pointer != nil)
        if (longer_pointer = short_pointer) then
            return longer_pointer
        longer_pointer <- longer_pointer.next
        shorter_pointer <- shorter_pointer.next
        
    // 跑到这里说明两个链表并没有公共部分
    return nil
类似的题

给定一个二叉树,每个节点都有指向他老爸的指针。再给任意两个节点,找到他们的最年轻的公共祖先。

这个题,其实可以用和同上面的题相同的思路解决 : )

(LeetCode 139) Word Break

题目

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.

For example, given
s = "leetcode",
dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".

大意就是给定一个字符串和一个单词列表,判断这个字符串是不是可以完全由单词列表的单词组成。题目保证单词列表里面不会有重复的单词

题解

定义 s[m..n] (m <= n)表示 字符串从第 m 个到第 n 个字符组成的子串。

设 s 的长度为n

观察这个问题,其实这个问题是可以拆分成很多完全相同的字问题的。对于这个问题的定义可以表述成:判断 s[1..n] 能不能完全由单词列表里面的单词组成。

那么,他的子问题(规模较小的问题) 就是: s[1..1], s[1..2], s[1..3], … s[1..n - 1] 能不能完全由单词列表里面的单词组成。

假设存在一个j(1 <= j < n),使得 s[1..j] 是可以完全由单词列表的单词组成的,同时 s[j + 1 .. n] 这个单词又在字典列表里面,那么就说明 s[1..n] 是可以完全由单词列表里面的单词构成的。

也就是说,要解决 s[1..n] 的问题,我们解决了 s[1..1], s[1..2], s[1..3], … s[1..n-1] 这些子问题(因为如果不全部解决的话,就不知道是否存在这么一个 j )。反过来说,只要解决 s[1..1], s[1..2], s[1..3], … s[1..n-1] 这些子问题,我们就能解决 s[1..n] 的问题。

在解决这个问题的过程中,s[1..n] 问题所处的阶段就叫当前阶段,对于当前阶段而言,s[1..1], s[1..2], s[1..3], … s[1..n-1] 问题所处的阶段就是上一阶段

s[1..n] 能否完全由单词列表里的单词组成,就是当前阶段的状态,s[1..1], s[1..2], s[1..3], … s[1..n-1]能否完全由单词列表里面的单词组成,就是上一阶段的状态

好,现在来观察一下上面的分析,就能发现 对于一个 s[1..n] 问题,有可能,从上一阶段的几个状态都能到达当前的状态。也就是说:单词的选择方式不一定是唯一的。但是,在解决 s[1..n] 的时候,并不需要关心 s[1..1],s[1..2],..,s[1..n -1] 问题的解是怎么来的。这就是无后效性

当一个判断性的问题,具有这种子问题性质和无后效性的性质,就可以考虑采用动态规划的方式去做。

状态我们已经定义好了,那么用状态变量 is_beakable[i] 来表示 "s[1..n] 能否完全由单词列表里的单词组成" 这个状态。true 表示可以,false 表示不可以。

从各个 s[1..j] 到 s[1..n] 就称为状态转移,这里的状态转移连接了当前阶段和上一阶段。我们可以考虑用状态转移方程来描述(is_breakable[i] 默认值为 false):

is_breakable[i] = is_breakable[i] Or ((is_breakable[j] = true) And (s[j + 1..n] in dict))

就这样,我们一直算出 is_breakable[n] 就算是将问题求解完了。

function word_break(s, dict)
    is_breakable[0] = false;
    for i <- 1 to n do 
        is_breakable[i] = false;
        for j <- 0 to n do
            is_breakable[i] = is_breakable[i] or ((is_breakable[j] = true) and (s[j + 1..n] in dict))
    return is_breakable[n]

(LeetCode 41) First Missing Positive

题目

Given an unsorted integer array, find the first missing positive integer.

For example,
Given [1,2,0] return 3,
and [3,4,-1,1] return 2.

Your algorithm should run in O(n) time and uses constant space.

大意就是,给定一个没有排过序的数组,里面的数有负有正。正整数序列是:1, 2, 3,… 找到第一个丢失的正整数。要求时间复杂度和空间复杂度是O(n)和O(1)。

题解

这个题,并没有不改变原数组就能在O(n)的空间效率内实现的方法,所以只能考虑改变原有的内存。

观察这个题,知道:

  1. 目标数肯定是个大于0的数。

  2. 目标数的大小,肯定会比数组的长度小。

从这两个信息,我们可以得到:

  1. 能够用正负号来做标记

  2. 能用通过数组的下标来进行标记

  3. 数组里面,小于等于0的和大于数组长度的数都是不重要的数,大于0和小于等于数组长度的数都是重要的数

那么做法就出来了:

  1. 走一遍,把数组里面小于等于零的数和大于数组长度的数都设置成一个比数组长度要大的数

  2. 再走一遍,把数组以重要的数为下标的元素都设置成负的

  3. 最后走一遍,碰到数组里面第一个非负的数,他的下标就是我们的答案

function fisrt_missing_positive(nums, nums_size)
    for i <- 1 to nums_size do
        if (nums[i] <= 0) or (nums[i] > nums_size) then
            nums[i] = nums_size + 1
        
    for i <- 1 to nums_size do 
        if (nums_size + 1 != nums[i]) then
            index = abs(nums[i])
            nums[index] = -1 * abs(nums[index]) 
            
    for i <- 1 to nums_size do 
        if (nums[i] < 0) then
            answer = i
            break
            
    return answer

coordinate35
167 声望16 粉丝

引用和评论

0 条评论