1

线性链表是一组节点。这是一个节点的定义方式(为了简单起见,我们不区分节点和列表):

class Node{
    Object data;
    Node link;

    public Node(Object pData, Node pLink){
        this.data = pData;
        this.link = pLink;
    }

    public String toString(){
        if(this.link != null){
            return this.data.toString() + this.link.toString();
        }else{
            return this.data.toString() ;
        }
    }

    public void inc(){
        this.data = new Integer((Integer)this.data + 1);
    }

    public void lappend(Node list){
        Node child = this.link;
        while(child != null){
            child = child.link;
        }
        child.link = list;
    }

    public Node copy(){
        if(this.link != null){
            return new Node(new Integer((Integer)this.data), this.link.copy());
        }else{
            return new Node(new Integer((Integer)this.data), null);
        }
    }

    public Node invert(){
        Node child = this.link;
        while(child != null){
            child = child.link;
        }
        child.link = this;....
    }
}

我能够制作列表的深层副本。现在我想反转列表,以便第一个节点是最后一个,最后一个是第一个。倒排列表必须是深拷贝。

我开始开发反转功能,但我不确定。有任何想法吗?

更新:也许有一种递归方式,因为线性链表是一种递归数据结构。

我会取第一个元素,遍历列表,直到我到达一个没有子节点的节点并附加第一个元素,我会在第二个、第三个......

4

8 回答 8

4

有时候面试的时候会问这个问题...

我不建议使用递归解决方案,或者使用堆栈来解决这个问题。为这样的任务分配O(n)内存是没有意义的。

这是一个简单的O(1)解决方案(我现在没有运行它,所以如果需要更正,我深表歉意)。

Node reverse (Node current) {

    Node prev = null;

    while (current != null) {
        Node nextNode = current.next;
        current.next = prev;
        prev = current;
        current = nextNode;
    }

    return prev;
}

顺便说一句:该lappend方法有效吗?似乎它总是会抛出一个NullReferenceException.

于 2011-01-13T22:43:50.727 回答
3

基于以下观察,这个问题有一个很好的递归解决方案:

  1. 空列表的反面是空列表。
  2. 单例列表的反面就是它本身。
  3. 节点 N 后跟列表 L 的列表的反向是列表 L 后跟节点 N 的反向。

因此,您可以使用伪代码沿这些行实现反向函数:

void reverseList(Node node) {
    if (node == null) return;      // Reverse of empty list is itself.
    if (node.next == null) return; // Reverse of singleton list is itself.

    reverseList(node.next); // Reverse the rest of the list
    appendNodeToList(node, node.next); // Append the new value.
}

该算法的简单实现在 O(n 2 ) 中运行,因为每次反转都需要追加,这需要对列表的其余部分进行 O(n) 扫描。但是,您实际上可以通过巧妙的观察在 O(n) 中完成这项工作。假设您有一个如下所示的链表:

n1 --> n2 --> [rest of the list]

如果您反转从 n2 开始的列表,那么您最终会得到以下设置:

n1       [reverse of rest of the list] --> n2
 |                                          ^
 +------------------------------------------+

因此,您可以通过设置将 n1 附加到列表其余部分的反向n1.next.next = n1,这会更改n2反向列表的新结尾,以指向 n1:

[reverse of the rest of the list] --> n2 --> n1

而你是金色的!还有更多的伪代码:

void reverseList(Node node) {
    if (node == null) return;      // Reverse of empty list is itself.
    if (node.next == null) return; // Reverse of singleton list is itself.

    reverseList(node.next); // Reverse the rest of the list
    node.next.next = node; // Append the new value.
}

编辑:正如 Ran 所指出的,这使用调用堆栈作为其存储空间,因此存在堆栈溢出的风险。如果你想使用显式堆栈,你可以这样做:

void reverseList(Node node) {
    /* Make a stack of the reverse of the nodes. */
    Stack<Node> s = new Stack<Node>();
    for (Node curr = node; node != null; node = node.next)
        s.push(curr);

    /* Start unwinding it. */
    Node curr = null;
    while (!s.empty()) {
        Node top = s.pop();

        /* If there is no node in the list yet, set it to the current node. */
        if (curr == null)
            curr = top;
        /* Otherwise, have the current node point to this next node. */
        else
            curr.next = top;

        /* Update the current pointer to be this new node. */
        curr = top;
    }
}

我相信这同样会反转链表元素。

于 2011-01-13T22:37:49.860 回答
2

我会将当前列表视为堆栈(这是我的伪代码):

Node x = copyOf(list.head);
x.link = null;
foreach(node in list){
    Node temp = copyOf(list.head);
    temp.link = x;
    x = temp;        
}

最后x将是反向列表的头部。

于 2011-01-13T22:37:21.393 回答
1

我比较熟悉whit C,但还是让我试试。(我只是不确定这是否在 Java 中运行,但它应该)

node n = (well first one)
node prev = NULL;
node t;
while(n != NULL)
{
     t = n.next;
     n.next = prev;
     prev = n;
     n = t;
}
于 2011-01-13T23:05:54.157 回答
1

反转单链表是一个经典的问题。它也在这里得到了回答(并且得到了很好的回答),它不需要递归也不需要额外的内存,除了一个寄存器(或 2 个)用于保持参考。但是对于 OP,我想这是一个学校项目/家庭作业和一些建议,如果您曾经使用单链表进行一些真实的数据存储,请考虑使用尾节点。(到目前为止,单链表几乎已经绝迹,不过想到了 HashMap 存储桶)。除非您必须在“添加”期间检查所有节点的某些情况,否则尾部是一个相当大的改进。下面是一些具有反向方法和尾节点的代码。

package t1;

public class SList {
    Node head = new Node(); 
    Node tail = head;

    private static class Node{
        Node link;
        int data;           
    }


    void add(int i){
        Node n = new Node();
        n.data = i;
        tail = tail.link =n;        
    }

    void reverse(){
        tail = head;
        head = reverse(head);
        tail.link = null;//former head still links back, so clear it
    }

    private static Node reverse(Node head){            
        for (Node n=head.link, link; n!=null; n=link){//essentially replace head w/ the next and relink
            link = n.link;
            n.link = head;
            head = n;           
        }
        return head;
    }

    void print(){
        for (Node n=head; n!=null;n=n.link){
            System.out.println(n.data);
        }       
    }

    public static void main(String[] args) {
        SList l = new SList();
        l.add(1);l.add(2);l.add(3);l.add(4);
        l.print();
        System.out.println("==");
        l.reverse();
        l.print();
    }
}
于 2011-01-14T01:53:04.047 回答
0

我想知道类似的事情(我没有测试它,所以):

invert(){
  m(firstNode, null);
}

m(Node v, Node bef){
  if(v.link != null)
   m(v.link,v);
  else
   v.link=bef;
}
于 2011-01-13T22:41:56.493 回答
0

无需太多测试,

Node head = this;
Node middle = null;
Node trail = null;
while (head != null) {
    trail = middle;
    middle = head;
    head = head.link;
    middle.link = trail;
}
head = middle;
return head;
于 2011-01-13T22:51:24.377 回答
0
public ListNode Reverse(ListNode list)
{
    if (list == null) return null; 

    if (list.next == null) return list; 

    ListNode secondElem = list.next;

    ListNode reverseRest = Reverse(secondElem);

    secondElem.Next = list;

    return reverseRest;
}

希望这可以帮助。

于 2014-04-21T16:39:37.957 回答