简体   繁体   English

单链接列表删除

[英]Singly-Linked List remove

I was doing Singly-Linked List implementation and I remember Linus Torvalds talking about it here . 我当时正在执行单链列表,并且我记得Linus Torvalds在这里谈论它。

In a singly-linked list in order to remove a node we should have access to the previous node and then change the node it is currently pointing to. 在单链列表中,为了删除节点,我们应该有权访问前一个节点,然后更改其当前指向的节点。

Like this 像这样 在此处输入图片说明

So any way we should have access to the previous node. 因此,我们应该可以使用任何方式访问上一个节点。

But Linus Torvalds removed the special case by using the idea of address in C. So that head also has the 'previous thing' which is the address of head which points to head. 但是莱纳斯·托瓦尔兹(Linus Torvalds)通过使用C语言中的地址概念来删除特殊情况。因此,头也具有“先前的事物”,即头的地址指向头。 So he has used C's feature of pointer and address to remove special case. 因此,他使用了C的指针和地址功能来删除特殊情况。

The normal code with special case 特殊情况下的普通代码 在此处输入图片说明

The code with special case becoming normal case 特殊情况下的代码变成普通情况 在此处输入图片说明

I think this kind of special case removal in singly linked list cannot be done in python, because we don't have the concept of pointers (and hence I will not be able to go one step before head)? 我认为单链列表中的这种特殊情况下的删除无法在python中完成,因为我们没有指针的概念(因此,我将无法走到前面)。 Am I right ? 我对吗 ?

Sure you can do this in Python. 当然,您可以在Python中执行此操作。 What he's saying is that you have some data structure that represents the list itself and points to the head of the list, and you manipulate that just as you would the pointer in a list item when you're dealing with the first list item. 他的意思是,您具有一些数据结构,该数据结构表示列表本身并指向列表的开头,并且您在处理第一个列表项时就像在列表项中使用指针一样对其进行操作。

Now Python is not C so the implementation would be different, but the principle applies. 现在,Python不是C,因此实现会有所不同,但是原理仍然适用。 The list itself is not the same object as its first item, and list items should not have the same methods as the list as a whole, so it makes sense to use separate kinds of objects for them. 列表本身与第一个项目不是同一对象,并且列表项不应该与整个列表具有相同的方法,因此有必要为它们使用不同种类的对象。

Both of them can, however, use an attribute of the same name (eg next ) to point to the next item. 但是,它们两者都可以使用相同名称的属性(例如next )来指向下一项。 So when you iterate through the list, and you are at the first item, the "previous" item is the list itself, and you are manipulating its next attribute if you need to remove the first item. 因此,当您遍历列表时,您位于第一个项目上,“上一个”项目就是列表本身,如果您需要删除第一个项目,则需要操纵它的next属性。

In the real world, of course, you would never write your own Python linked list class except as an exercise. 当然,在现实世界中,除非作为练习,否则您永远不会编写自己的Python链表类。 The built-in list is more efficient. 内置list更加有效。

You cannot use Linus's specific trick in Python, because, as you well know, Python does not have pointers (as such) or an address-of operator. 您不能在Python中使用Linus的特定技巧,因为众所周知,Python没有指针(这样)或地址运算符。 You can still, however, eliminate a special case for the list head by giving the list a dummy head node. 但是,您仍然可以通过为列表赋予虚拟头节点来消除列表头的特殊情况。 You can do that as an inherent part of the design of your list, or you can do it on the fly just by creating an extra node and making it refer to the first data-bearing node as its next node. 您可以将其作为列表设计的固有部分来执行,也可以仅通过创建一个额外的节点并将其引用为第一个数据承载节点作为下一个节点来进行。 Either way, all the nodes you might want to delete are then interior nodes, not special cases. 无论哪种方式,您可能要删除的所有节点都是内部节点,而不是特殊情况。

My first thought upon reading your question was: why would you want to build a singly linked list in python? 在阅读您的问题时,我的第一个想法是:为什么要在python中建立一个单链表? Python offers a wealth of collection types and you can use these without having to worry about whether they are implemented as singly linked list, as double linked list or as some non-recursive data structure (which are usually easier to handle). Python提供了很多集合类型,您可以使用它们而不必担心它们是作为单链表,双链表还是某些非递归数据结构(通常更易于处理)实现的。

But the answer to your question is: Python allows of course to build a singly linked list. 但是,您的问题的答案是:Python当然允许构建单链列表。 For example, the following code does just that: 例如,以下代码就是这样做的:

class Node:
    def __init__(self, x, next):
        self.x = x
        self.next = next

    def __str__(self):
        return "<{}, {!s}>".format(self.x, self.next)

n = Node(1, None)
n = Node(2, n)
n = Node(3, n)
print(n)  # output: <3, <2, <1, None>>>

n.next.next = n.next.next.next
print(n)  # output: <3, <2, None>>

The difference to C is: we did not have to malloc() or work with pointers because python handles memory for us. 与C的不同之处在于:我们不需要malloc()或使用指针,因为python为我们处理了内存。 Python has references instead of pointers, they are similar but much safer and easier to use. Python有引用而不是指针,它们很相似,但更安全,更易于使用。

However, before implementing a linked list, you should consider what your requirements are regarding your collection and maybe you can pick a good one from the built-ins or from the collections module. 但是,在实现链接列表之前,您应该考虑对集合的要求,也许您可​​以从内置插件或集合模块中选择一个合适的集合。

You need two levels of indirection to do it the way Linus suggests, but you can potentially do it in Python perhaps by having a reference to an object which stores a reference to an object or something of this sort (an index to an index?). 您需要按照Linus的建议进行两级间接操作,但是您可能可以在Python中做到这一点,也许是通过对对象的引用来存储对象的引用或此类对象(索引的索引?)。 。 That said, I don't think it maps so elegantly or efficiently to Python and it'd probably be quite wasteful to use an object just to represent a single link in a linked structure. 也就是说,我认为它不能如此优雅或高效地映射到Python,并且仅使用一个对象来表示链接结构中的单个链接可能会非常浪费。

In Python's case I'd just do the additional branching to check for cases where you're removing from the head unless there's some trick I'm missing. 在Python的情况下,我只需要执行额外的分支来检查您是否要从头部移开,除非我缺少一些技巧。

As for implementing linked lists yourself, I actually find many use cases where the standard libraries don't suffice. 至于自己实现链表,我实际上发现了很多用例,其中标准库不足以满足需求。 Here's one example: 这是一个例子:

在此处输入图片说明

... where the grid might have 10,000 cells. ...网格可能有10,000个单元格。 Most linked lists provided by standard libraries aren't optimized to store 10,000+ linked lists in the size of a 32-bit index per list since they're trying to provide interfaces that allow the linked list to be used in isolation (not using a separate backing data structure for storage like an array). 标准库提供的大多数链表都没有经过优化,无法以每个列表32位索引的大小存储10,000+个链表,因为它们试图提供允许隔离使用链表的接口(而不是使用单独的后备数据结构,以像数组一样存储)。 Typically the most efficient use of a linked list is one that doesn't own memory or manage any resources. 通常,最有效地使用链表是不拥有内存或管理任何资源的链表。 It's just linking data in an auxiliary fashion already allocated and managed in another data structure, like this for a 128-bit (16-byte) tree node in an n-ary tree where elements can be stored at any level of the hierarchy: 它只是以已经在另一数据结构中分配和管理的辅助方式链接数据,例如对于n元树中的128位(16字节)树节点的链接,其中元素可以存储在层次结构的任何级别:

struct TreeNode
{
     int32 parent;       // parent index or -1 for no parent
     int32 first_child;  // first child of this node or -1
     int32 next_sibling; // next child for the parent node or -1
     int32 element;      // element data stored in this node or -1
                         // if no data is associated
};

So there's a lot of use cases for implementing your own linked lists and other linked structures which are significantly more efficient for a more narrowly-applicable use case (grid data structures, octrees, quad-trees, graphs, etc), but again, I don't think you can use this trick in languages that don't easily allow you to utilize two or more levels of pointer indirection. 因此,有很多用例可以实现自己的链接列表和其他链接结构,对于更狭窄适用的用例(网格数据结构,八叉树,四叉树,图等),它们的效率要高得多。不要以为您不能在不容易允许您使用两个或多个级别的指针间接性的语言中使用此技巧。 Python inherently only has one for objects -- same with Java and C#. Python本质上只为对象提供一个-与Java和C#相同。 You'd need something like a "reference to a reference to an object" or an "index to an index to an object" or "an index to an object reference to an object" . 您需要类似“对对象的引用的引用” 对对象索引的索引”“对对象的对对象引用的索引”之类的东西

Also linked lists generally aren't so useful in languages that don't allow you to manage where everything is stored in memory since you can end up getting cache misses galore iterating through linked lists otherwise if each list node is fragmented in memory as would often be the case after a GC cycle, eg For linked lists to be really efficient as in the case of the Linux kernel requires you to be able to really have fine control over where each node resides in memory so that list traversal is actually mostly, if not entirely, just iterating through contiguous chunks of memory. 此外,链表通常在不允许您管理所有内容存储在哪里的语言中用处不大,因为您最终可能会在链表中反复遍历缓存,从而导致高速缓存未命中,否则,如果每个列表节点通常都分散在内存中,例如,在GC周期之后,例如,要使链接列表真正有效,就像在Linux内核中那样,则您必须能够对每个节点在内存中的位置进行真正的控制,以便在大多数情况下可以遍历列表不完全是,只是遍历连续的内存块。 Otherwise you're generally better-off using small arrays even if that implies linear-time removals and insertions to/from the middle. 否则,您通常最好使用小型数组,即使这意味着线性时间删除和从中间插入/从中间插入也是如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM