【问题标题】:Determine if two arrays are a rotated version of each other确定两个数组是否是彼此的旋转版本
【发布时间】:2020-03-01 00:24:25
【问题描述】:

在 JAVA 中提出了类似的问题,但有人可以帮助我改进代码:并解释我的代码的时间复杂度和空间复杂度是多少。我的代码检查两个数组是否相互旋转:

list1 = [1, 2, 3, 4, 5, 6, 7]

list2b = [4, 5, 6, 7, 1, 2, 3]

is_rotation(list1, list2b) 应该返回 True。

list2c = [4, 5, 6, 9, 1, 2, 3]

is_rotation(list1, list2c) 应该返回 False。

我的代码:

def is_rotation (list1,list2):

    i = 0
    j = 0
    k = 0

    result = True

    if len(list1) != len(list2):
        return False  

    while i < len(list1) -1 and j < len(list1) -1:

        if list1[i] == list2[j]:
            i = i +1
            j = j +1
            break
        elif list1[i] > list2[j]:
            j = j +1
        else:
            i = i +1
    else:
        return False

    for l in range(i,len(list1)):

        if i == j:
            if list1[i] != list2[j]: 
                return False
        elif list1[i] != list2[j] or list2[i] != list1[k]:
            return False
        else:
            i = i +1
            j = j +1
            k = k +1

    return result

【问题讨论】:

  • 一些 cmets 来描述循环应该完成的任务会有所帮助
  • 如果值是唯一的,请考虑以下简化:i2 = l2 中 l1[0] 的索引;旋转 iff,对于 x in 0 到 len l1,其中 l1[x] == l2[(i2 + x) % len l1] - 可以写成两个简单的循环(或更少)。也就是说,使用一组唯一的值,它只是比较给定先前选择的第二个列表开始的“偏差”的序列相等性。
  • 可以通过尝试将 l2 中所有出现的 l1[0] 作为“偏差”,直到一个匹配,修改上述内容以使用非唯一值。
  • 一种快速的方法,O(n),是使用一些快速算法进行精确的字符串匹配,如 Knuth-Morris-Pratt,但应用于列表项而不是字母。跨度>
  • 一个简单的方法是查看所有可能的旋转。列表a 通过k 位置的轮换只是a[k:] + a[:k]

标签: python python-3.x algorithm


【解决方案1】:

有点hacky的方式:

def is_rotation(lst1, lst2):
    if(len(lst1)==len(lst2)):
        return (str(lst1)[1:-1] in str(lst2+lst2)) & (str(lst2)[1:-1] in str(lst1+lst1)) 
    else:
        return False

它是如何工作的:

(1) 检查两个列表的长度是否相同,如果不一样则返回False

(2) 如果他们这样做,将第一个列表转换为string,删除最外面的括号(通过删除第一个和最后一个字符 - 你可以在那里做任何括号,不仅是方括号,它可以是 tuple也)。

(3) lst2+lst2 将返回按顺序重复的lst2 的所有元素(所以一个lst2 一个接一个)。然后转换为字符串它只会返回其字符串格式list

(4) 根据 cmets - 为了处理极端情况 - 我们应该检查两种方式,因为如果 lst1lst2 的旋转版本,那么 lst2lst1 的旋转版本

测试

print(is_rotation([561, 1, 1, 1, 135], [1, 1, 1, 1, 1]))
#outputs False
print(is_rotation([[1,2,3,4], 2, 3, 4], [1, 2,3,4]))
#outputs False
print(is_rotation([1, 2, 3, 4, 5], [4, 5, 1, 2, 3]))
#outputs True

【讨论】:

  • 不完全正确:例如,[1, 1, 1, 1, 1] 将被错误地视为 [54321, 1, 1, 1, 12345] 的旋转。
  • @Amadan 为什么这是最好的方法?
  • 绝对不是最好的方法:某些值模式失败(正如@Gassa 指出的那样),需要对字符串进行昂贵的转换,甚至比复制整个列表所需的内存更多。
  • 谢谢大家-现在请看一下-这应该可以解决您提到的情况。
【解决方案2】:

对于一种更明确的方法,您可以使用 itertools 一个一个地生成给定列表的所有旋转:

import itertools as it
def get_rotations(lst1):
    foo = it.cycle(lst1)
    for y in range(len(lst1)):
        result = []
        for x in range(len(lst1)):
            result.append(next(foo))
        yield result
        next foo

那么你可以这样做:

def is_rotated(L1, L2) -> bool:
    bar = get_rotations(L1)
    While True:
        try:
            if next(bar) == L2;
                return True
        except StopIteration:
                return False

顺便说一句:我认为这打破了所有关于使用异常来控制程序逻辑的正常流程的传统观点,但是......感觉不对,不知何故(C++ 的背景,我们被反复告知永远不要这样做事物。)。它是 Pythonic 的吗?

【讨论】:

  • 您可以简单地遍历far,而不是try-except,并使用else语句代替except来返回false。
【解决方案3】:

您可以使用 itertools 中的循环来优化比较次数并避免创建额外的数据副本。 (即 O(n) 时间的 O(1) 空间)

这是一个函数示例,如果两个列表相互旋转,则返回旋转偏移量;如果不匹配,则返回 None。逻辑永远不需要超过 2N 次比较来确定偏移量。

from itertools import cycle
def getRotation(listA,listB):
    if len(listA) != len(listB): return None
    unmatched,offset = len(listA),0
    iterA,iterB      = cycle(listA),cycle(listB)
    a = next(iterA)
    while unmatched and offset<len(listA):
        b = next(iterB)
        if a==b:
            unmatched -= 1
            a = next(iterA)
        else:
            unmatched = len(listA)
            offset   += 1
    if unmatched: return None
    return offset

输出:

list1 = [1, 2, 3, 4, 5, 6, 7]
list2b = [4, 5, 6, 7, 1, 2, 3]
print(getRotation(list1,list2b)) # 4 (rotation to the right)

如果需要,您可以将其调整为简单地返回 True 或 False。

它也可以很容易地适应检查是否可以通过循环生成一个列表。 (例如 [2,3,1,2,3,1,2,3,1,2] 可以通过循环 [1,2,3] 产生)。 我没有在示例中包含该功能以避免混淆问题。

【讨论】:

    【解决方案4】:

    我虽然以另一种方式做到这一点:

    def is_rotation (list1,list2):
        if len(list1) != len(list2):
            return False
    
        for i in range(len(list1)):
            if list1[i:] + list1[:i] == list2:
                return True
    
        return False
    

    1) 测试两者是否具有相同的长度。

    2) 循环只会旋转列表中的所有可能性,并检查其中一个是否相等..

    我不知道这是否是最好的方法,但很容易理解;)

    【讨论】:

    • 测试列表元素是否是任务的一部分?
    • 可能不会。我会编辑这个删除测试部分。
    猜你喜欢
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 2013-03-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多