我是python的新手,我正在尝试理解如何将Python与C / C ++联系起来。考虑问题:检查给定的链接列表是否是回文。从source开始,我找到了一个非常有效的解决方案:
// Initial parameters to this function are &head and head
bool isPalindromeUtil(struct node **left, struct node *right)
{
/* stop recursion when right becomes NULL */
if (right == NULL)
return true;
/* If sub-list is not palindrome then no need to
check for current left and right, return false */
bool isp = isPalindromeUtil(left, right->next);
if (isp == false)
return false;
/* Check values at current left and right */
bool isp1 = (right->data == (*left)->data);
/* Move left to next node */
*left = (*left)->next;
return isp1;
}
// A wrapper over isPalindromeUtil()
bool isPalindrome(struct node *head)
{
isPalindromeUtil(&head, head);
}
基本上,指向指针的指针被分配给前面(左指针),指针被指定给列表的末尾(右指针)。一旦指针到达各自的位置,它们就会递归并找到匹配值。这里,* left维持指向指针的状态,指针的值在递归循环中持续存在。 python中的一个解决方案是将左指针传递回布尔值。但是,如果有多个指向指针的指针,则返回元素的数量会爆炸!糟糕的设计。还有其他办法吗?
编辑:
非常感谢您的回复!我忘了提到我不是想在这里解决回文问题。我试图了解是否有一个使用Pythonic等效的方法来处理指针。我想要理解的是,如果向我提出一个使用链表等数据结构的问题,我是否应该尝试将其转换为其他数据结构(如列表)或者如果我需要创造性并使用问题解决问题相同的数据结构。由于指向指针是链接列表和BST中非常重要的概念,是否有相应的解决方案或解决此概念?
编辑#2: 我提出的粗略代码如下。但是,left_ptr仍然停留在相同的位置,因为它是一个指针,而不是指向指针的指针:
def palindrome_utils(self, left_ptr, right_ptr):
if right_ptr is None:
return True
prev_result = self.palindrome_utils(left_ptr, right_ptr.get_link())
cur_result = left_ptr.get_data() == right_ptr.get_data()
left_ptr = left_ptr.get_link()
return cur_result and prev_result
def palindrome(self):
return self.palindrome_utils(self.head, self.head)
答案 0 :(得分:5)
编辑:将l == l[::-1]
添加为is_palindrome5
,这是非常快速的,也是目前最具可读性和pythonic的。
我可以检查回收的最快速度是这个单线:
def is_palindrome1(l):
return l[:len(l) / 2] == l[(len(l)+1) / 2:][::-1]
在您的问题中对c ++函数的python转换检查非回文最快:
def is_palindrome2(l):
left = 0
right = len(l) - 1
for i in xrange(0,len(l) / 2):
if l[right] != l[left]:
return False
left += 1
right -= 1
return True
这是因为在开始比较元素之前,此函数不会创建任何新列表。 请注意,我使用了第一个和最后一个元素不同的非回文,因此检查在第一次比较后立即返回(对于非回文并非总是如此)。
我还测试了mistermiyagi(is_palindrome3
)的答案和PM 2Ring(is_palindrome4
)的答案:
import itertools
def create_palindrome(n):
return range(1,n) + range(n,0,-1)
def is_palindrome1(l):
return l[:len(l) / 2] == l[(len(l)+1) / 2:][::-1]
def is_palindrome2(l):
left = 0
right = len(l) - 1
for i in xrange(0,len(l) / 2):
if l[right] != l[left]:
return False
left += 1
right -= 1
return True
def is_palindrome3(seq):
for l, r in itertools.izip(iter(seq), reversed(seq)):
if l != r:
return False
return True
def is_palindrome4(seq):
return all(l == r
for _, l, r in itertools.izip(xrange((1 + len(seq)) // 2),
iter(seq), reversed(seq)))
def is_palindrome5(l):
return l == l[::-1]
if __name__ == '__main__':
import timeit
setup_palindrome = "from __main__ import create_palindrome, %s; l = create_palindrome(%i)"
setup_non_palindrome = "from __main__ import %s; l=range(%i)"
def test(f, n):
return (timeit.timeit("%s(l)" % f, setup=setup_palindrome % (f, n), number=100000),
timeit.timeit("%s(l)" % f, setup=setup_non_palindrome % (f, n), number=100000))
small = 5
big = 1000
for f in is_palindrome1, is_palindrome2, is_palindrome3, is_palindrome4:
print("%s small list: palindrome: %f non-palindrome: %f" % ((f.__name__,) + test(f.__name__, small)))
for f in is_palindrome1, is_palindrome2, is_palindrome3, is_palindrome4:
print("%s big list: palindrome: %f non-palindrome: %f" % ((f.__name__,) + test(f.__name__, big)))
结果:
is_palindrome1 small list: palindrome: 0.093779 non-palindrome: 0.073669
is_palindrome2 small list: palindrome: 0.087658 non-palindrome: 0.048855
is_palindrome3 small list: palindrome: 0.085866 non-palindrome: 0.056385
is_palindrome4 small list: palindrome: 0.139685 non-palindrome: 0.123519
is_palindrome5 small list: palindrome: 0.021798 non-palindrome: 0.022422
is_palindrome1 big list: palindrome: 1.589591 non-palindrome: 0.432679
is_palindrome2 big list: palindrome: 9.414250 non-palindrome: 0.043481
is_palindrome3 big list: palindrome: 7.315568 non-palindrome: 0.056859
is_palindrome4 big list: palindrome: 6.655833 non-palindrome: 0.128915
is_palindrome5 big list: palindrome: 2.095099 non-palindrome: 0.283472
答案 1 :(得分:2)
以下是MisterMiyagi答案中的一个变体,它没有对每对进行两次测试:
import itertools
def is_palindrome(seq):
maxi = (1 + len(seq))//2
for i, l, r in itertools.izip(xrange(maxi), iter(seq), reversed(seq)):
#print l, r
if l != r:
return False
return True
data = ('abcba', '123321', 'ABCDBA')
for seq in data:
print seq, is_palindrome(seq)
a = list(seq)
print a, is_palindrome(a)
<强>输出强>
abcba True
['a', 'b', 'c', 'b', 'a'] True
123321 True
['1', '2', '3', '3', '2', '1'] True
ABCDBA False
['A', 'B', 'C', 'D', 'B', 'A'] False
或者,作为一个班轮:
def is_palindrome(seq):
return all(l == r
for _, l, r in itertools.izip(xrange((1 + len(seq))//2),
iter(seq), reversed(seq)))
请注意all()
短路,因此一旦检测到不匹配就会挽救。
答案 2 :(得分:1)
你可以像C ++代码那样做。
您有一个Node节点,其中包含两个成员“data”和“next”,用于制作自制的链式列表。
而不是指向节点的指针只使用对节点的引用。 而不是指向指针的指针使用包含一个Node元素的列表。
以下是可以成为代码相关部分的内容
# Recursive call
isp = isPalindromeUtil(left, right.next);
# A wrapper over isPalindromeUtil()
def isPalindrome(head)
return isPalindromeUtil([head], head)
# Move left to next node
left[0] = left[0].next
这样,它就像在C ++代码中那样工作,就是当你“向左移动到下一个节点”时 在函数中,它也将被移动到函数的调用者。
这不是推荐的递归调用方法。这个例子不是一个好的设计。它的评论也很差:“如果子列表不是回文,则无需检查当前左右,返回false”。这是错的。如果list是“ABCDEF”,则递归的最内层调用将测试A和F,而不是C和D. 它还会测试两次:A-F,B-E,C-D,D-C,E-B,F-A,这不是最有效的方法。
答案 3 :(得分:0)
Python没有像C / C ++那样的指针概念,所以当然没有双指针。尽管所有对象(除了基本类型之外的所有对象)都是引用(非常量)。如果你有一个Node类,你可以用非常类似的方式编写算法。
答案 4 :(得分:0)
pythonic方式,只要你的数据结构有适当的接口实现,就不用担心序列细节和使用
import itertools
def is_palindrome(seq):
for l, r in itertools.izip(iter(seq), reversed(seq)):
if l!=r:
return False
return True
当然,如果你因为性能原因而想要一路走(你可能最终会变慢......),你可以做与C ++完全相同的事情。
对于任意迭代,您可以使用与指针相同的迭代器item(seq)
和reversed(seq)
。他们甚至可以方便地为您提供next
方法,这完全相同。
对于不提供可迭代接口的自定义数据结构,您可以创建自己的指针式结构。虽然python没有每个看到的指针,但它有绑定到名称的引用,可以绑定到持久对象。
class pointer(object):
def __init__(self, obj):
self.obj = obj
您可以将这样的容器类传递给函数,修改它,并隐式地将修改应用于调用框架。对于简单的情况,现有的数据结构(如dict
或list
也使用引用并且是可变的)更轻量级。
答案 5 :(得分:0)
如果您想要递归,可以对序列进行切片:
def is_palindrome(seq):
if len(seq) < 2:
return True
return seq[0] == seq[-1] and is_palindrome(seq[1:-1])
但是,不鼓励Python中的递归。 Python doesn't support tail call optimisation, and probably never will
答案 6 :(得分:0)
Python回文检查器的最高度优化版本如下:
def is_palindrome(s):
s = ''.join(s) # just in case a deque is passed
return s==s[::-1]
当然,我正在优化编写,调试和维护代码所需的时间。如果你想优化内存或处理时间,你可以做很多事情,包括用C或汇编编写,但是你应该在尝试之前确保你真的需要这种优化。