指向python

时间:2015-09-25 08:17:26

标签: python c pointers python-3.x

我是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)

7 个答案:

答案 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

您可以将这样的容器类传递给函数,修改它,并隐式地将修改应用于调用框架。对于简单的情况,现有的数据结构(如dictlist也使用引用并且是可变的)更轻量级。

答案 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或汇编编写,但是你应该在尝试之前确保你真的需要这种优化。