交换单个链表的头部和尾部

时间:2018-06-07 04:31:13

标签: python linked-list

我被困在这一半半天。我是否可以获得一些关于如何交换链表的头尾的提示或指示(反转整个列表)而不复制他们的数据?

class myNode:

    def __init__(data, next):
         self.data = data
         self.next = next


class MyLinkedList:

    def __init__(self):
         self.head = None
         self.tail = None

如果我能看到代码并对其进行解释,那就太棒了!

编辑:

基本上,我解决这个问题的最初方法是。

  1. 通过列表识别以找到尾部(当node.next为None时停止while循环,意味着我已经到达终点)
  2. 然后将尾部链接到head.next

    1. 通过列表进行识别,找到列表的第二个最后一个节点并将其链接到头部。

    2. 将原始头部链接为null,因为现在头部应该交换到尾部。

4 个答案:

答案 0 :(得分:1)

有一些边缘情况最好单独处理以保持代码简单。它们是大小为零,一和二的列表。

对于前两个,列表根本不会改变。对于大小为2,只有头部和尾部会改变,没有内部节点(因为 没有内部节点)。

在这种情况下,左侧的列表变为右侧的列表,(|表示空指针,h/t是头尾指针):

h    t               h    t
A -> B -> |          B -> A -> |

with(这是伪代码而不是Python,.n指定next指针):

                     h=A  t=B  A.n=B  B.n=|
tail.next = head     h=A  t=B  A.n=B  B.n=A
head.next = null     h=A  t=B  A.n=|  B.n=A
swap head, tail      h=B  t=A  A.n=|  B.n=A

对于所有其他情况(三个或更多节点),倒数第二个节点必须将其指针更改为指向新尾部。上面的双节点情况可以更容易,因为它知道头部是倒数第二个。下图显示了第四个大小写的情况(我用p标记了倒数第二个节点):

h         p    t          h         p    t
A -> B -> C -> D -> |     D -> B -> C -> A -> |

这可以通过以下操作实现:

                          h=A  t=D  p=C A.n=B  B.n=C C.n=D  D.n=|
tail.next = head.next     h=A  t=D  p=C A.n=B  B.n=C C.n=D  D.n=B
head.next = None          h=A  t=D  p=C A.n=|  B.n=C C.n=D  D.n=B
penu.next = head          h=A  t=D  p=C A.n=|  B.n=C C.n=A  D.n=B
swap head, tail           h=D  t=A  p=C A.n=|  B.n=C C.n=A  D.n=B

因此,就将其放入Python而言,以下程序包含方法的测试工具,以便您可以在操作中看到它:

class MyNode:
    def __init__(self, data, next = None):
         self.data = data
         self.next = next

class MyList:
    def __init__(self):
         self.head = None
         self.tail = None

    def push_back(self, data):
        if self.tail is None:
            self.head = MyNode(data)
            self.tail = self.head
        else:
            self.tail.next = MyNode(data)
            self.tail = self.tail.next

    def dump(self, desc):
        print(desc, end=' ')
        node = self.head
        while node is not None:
            print(node.data, end=' -> ')
            node = node.next
        print('|')

    def swap(self):
        # For empty and size 1, no change.

        if self.head is None: return
        if self.head.next is None: return

        # For size 2, easy swap.

        if self.head.next.next is None:
            self.tail.next = self.head
            self.head.next = None
            self.head, self.tail = self.tail, self.head
            return

        # For size 3+, little more complex, need the
        # penultimate node as well as head and tail.

        penu = self.head
        while penu.next != self.tail:
            penu = penu.next

        self.tail.next = self.head.next
        self.head.next = None
        penu.next = self.head
        self.head, self.tail = self.tail, self.head

首先是节点类,根据Hans的建议进行修改,以默认下一个指针。然后是列表类本身,按照原始问题进行初始化。

它还有一个push_back方法,因为如果你不能在其中添加任何内容,并且使用dump方法,列表几乎没用,这样我们就可以看到每个操作后列表的样子

该类的唯一其他部分是swap方法,这个逻辑在本答案的前面部分已经介绍过了。

当然,没有某种测试代码,可以存在什么样的自尊级别。以下将测试不同大小的列表,以便您可以看到swap操作按预期工作:

x = MyList()

# Do various sizes.

for i in range(6):
    # Add an element except for first time (so we check empty list).

    if i > 0:
        x.push_back(i)

    # Show before and after figures, making sure we restore
    # list to original state.

    x.dump('Size = %d, before:'%(i))
    x.swap()
    x.dump('Size = %d, after :'%(i))
    x.swap()
    print()

运行此代码会导致:

Size = 0, before: |
Size = 0, after : |

Size = 1, before: 1 -> |
Size = 1, after : 1 -> |

Size = 2, before: 1 -> 2 -> |
Size = 2, after : 2 -> 1 -> |

Size = 3, before: 1 -> 2 -> 3 -> |
Size = 3, after : 3 -> 2 -> 1 -> |

Size = 4, before: 1 -> 2 -> 3 -> 4 -> |
Size = 4, after : 4 -> 2 -> 3 -> 1 -> |

Size = 5, before: 1 -> 2 -> 3 -> 4 -> 5 -> |
Size = 5, after : 5 -> 2 -> 3 -> 4 -> 1 -> |

一旦您对此工作感到满意,您可能需要考虑将penu(倒数第二个节点指针)变成一个成员,就像headtail一样,然后设置它如果大小为2或更小,则为None。这样,您不必每次都想要交换节点时进行搜索。每当调用push_back(或任何其他改变列表大小的方法)时,都应该相对容易地更新它。

答案 1 :(得分:0)

在查看代码@Waffle链接后,以下似乎是重要的类。第一个表示链表中的节点,第二个表示节点的容器类,允许轻松表示空列表。目标是以一种使所代表的列表就地变异的方式实现LinkedList.swap_front_back()

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

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def swap_front_back(self):
        """TODO: Magic happens here"""

由于Python具有None的隐式返回,因此使用返回作为一种打破函数的方式,即使它被认为"也不会返回任何内容"是一个安全的退出策略。因此,以下几行消除了最小的边缘情况:

if self.size < 2:
    return

我提出的用于交换列表的策略涉及倒数第二个元素,如果碰巧是第一个元素,那么会发生各种坏事。换句话说,遗憾的是,我们必须处理大小为2的链表的情况作为特例(对于此实现)。

if self.size == 2:
    self.head, self.tail = self.tail, self.head
    self.head.next = self.tail
    self.tail.next = None
    return

在一般情况下,我们需要更改链接列表的倒数第二个元素的位置。如果没有发生这种情况,链表就会成为一个循环,并且各种不好的事情会发生在期望列表结束的算法中。

current = self.head
while current.next.next is not None:
    current = current.next

一旦找到倒数第二个元素,我们就可以完成实际的指针操作。

current.next.next = self.head.next
# Now the former last element points to the second element
# A->B->C->D->B

current.next = self.head
# Now the second-to-last element points to the previous head
# A->B->C->A   D->B

self.head.next = None
# We don't want circles, so we eliminate A being first and last
# D->B->C->A

self.head, self.tail = self.tail, self.head
# Even after the internal manipulations are done, the
# LinkedList needs to be updated to reference the appropriate
# locations.

总而言之,我们可以创建所需的方法:

def swap_front_back(self):
    if self.size < 2:
        return

    if self.size == 2:
        self.head, self.tail = self.tail, self.head
        self.head.next = self.tail
        self.tail.next = None
        return

    current = self.head
    while current.next.next is not None:
        current = current.next

    self.tail.next = self.head.next
    current.next = self.head
    self.head.next = None
    self.head, self.tail= self.tail, self.head

如果LinkedList非常小以至于不需要交换,则会提前返回,它会将转置作为特殊情况处理,否则它首先找到倒数第二个节点,然后使用它来调整必需指针。

答案 2 :(得分:0)

通常,掉期依赖第三个变量来存储要反转的值之一。例如,给定列表[3, 4, 1, 6, 5],可以像这样完成交换第一个和最后一个:

l = [3, 4, 1, 6, 5]
_temp = l[0] #first value
l[0] = l[-1]
l[-1] = _temp
#[5, 4, 1, 6, 3]

下面的实现使用默认参数,该参数将存储对列表中第一个节点的引用。一旦到达列表的末尾,将进行简单的交换操作,类似于上面的交换:

class LinkedList:
  def __init__(self, head=None):
    self.head = head
    self._next = None
  def insert_node(self, val):
    if self.head is None:
      self.head = val
    else:
      getattr(self._next, 'insert_node', lambda x:setattr(self, '_next', LinkedList(x)))(val)
  def swap(self, first = None, count = 0):
    if not self._next and self.head is not None:
       _head = getattr(self, 'head')
       if first:
         self.head = getattr(first, 'head')
         setattr(first, 'head', _head)
    else:
       if self.head:
         self._next.swap(first = self if not count else first, count = 1)
  @classmethod
  def load_list(cls, length = 5):
    _l = cls()
    import random
    for _ in range(length):
      _l.insert_node(random.randint(1, 100))
    return _l
  def flatten(self):
    if self.head is not None:
      return [self.head, *getattr(self._next, 'flatten', lambda :'')()]
    return ''
  def __repr__(self):
    return ', '.join(map(str, self.flatten()))

这个简单的递归适用于任何大小的列表:

for i in range(10):
   l = LinkedList.load_list(length = i)
   _s = repr(l)
   l.swap()
   print(f'{_s} -> {repr(l)}')

输出:

 -> 
14 -> 14
80, 57 -> 57, 80
83, 44, 80 -> 80, 44, 83
94, 10, 42, 81 -> 81, 10, 42, 94
25, 26, 32, 31, 55 -> 55, 26, 32, 31, 25
8, 25, 25, 84, 83, 49 -> 49, 25, 25, 84, 83, 8
19, 95, 3, 33, 4, 56, 33 -> 33, 95, 3, 33, 4, 56, 19
97, 92, 37, 100, 27, 24, 33, 17 -> 17, 92, 37, 100, 27, 24, 33, 97
72, 24, 56, 18, 9, 64, 82, 85, 97 -> 97, 24, 56, 18, 9, 64, 82, 85, 72

答案 3 :(得分:-1)

最初说你有A - &gt; B - &gt; C - &gt; D - &gt;无头:尾巴:D

你想要D - &gt; B - &gt; C - &gt; A - &gt;无头:D,尾巴:A

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(outletname) 
                    from tbloutlet
                    group by outletname
                    order by outletname
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT category,' + @cols + ' from 
             (
                SELECT c.category, o.outletname, SUM(t.amt) as amt
                FROM tblcategory c
                INNER JOIN tbltran t ON t.catid = c.id
                INNER JOIN tbloutlet o ON o.id = t.outletid
                GROUP BY c.category, o.outletname
            ) x
            pivot 
            (
                sum(amt)
                for outletname in (' + @cols + ')
            ) p '

execute(@query);