【问题标题】:What is wrong with this linked list?这个链表有什么问题?
【发布时间】:2012-06-20 14:12:33
【问题描述】:

任务是编写一个函数来交换列表中的 2 个节点。如果该函数可以不考虑顺序交换节点,则奖励 10%。我认为无论列表中的顺序如何,我的实现都能够交换 2 个元素,但我仍然没有收到奖励分数。我有什么遗漏的吗?

我得到了一个通用节点类,

public class Node<T> {
    public T val;
    public Node<T> next;

    public Node(T val) {
        this.val = val;
        this.next = null;
    }
}

我还得到了一个定义如下的接口,

public interface SwapList<T> {

    public void add(T val);

    /**
     * Swaps two elements in the list, but only if @param val1 comes BEFORE @param
     * val2. Solve the problem regardless of the order, for 10% extra. list: A B
     * C -> swap(A,B) will result in the list B A C list: A B C -> swap(B,A)
     * will not swap. list: A C C -> swap(A, D) will throw a
     * NoSuchElementException list: A B C B -> swap (A, B) will result in the
     * list B A C B list: A B C A B B -> swap (A,B) will result in the list B A
     * C A B B a list with one or zero elements cannot do a swap
     */
    public void swap(T val1, T val2);

    public T get(int i);
}

我有自己的接口实现如下,

import java.util.NoSuchElementException;
public class SwapListImpl<T> implements SwapList<T> {

    private Node<T> head;
    private Node<T> tail;
    private int counter;

    public SwapListImpl() {
        head = null;
        tail = null;
        counter = 0;
    }

    @Override
    public void add(T val) {
        Node<T> node = new Node<T>(val);
        if (head == null) {
            head = node;
            tail = node;
        } else {
            tail.next = node;
            tail = node;
        }

        counter++;
    }

    @Override
    public void swap(T val1, T val2) {

        if (counter < 2 || val1.equals(val2))
            return;

        Node<T> current = head;
        Node<T> currentPrev = null;

        Node<T> first = head;
        Node<T> firstPrev = null;
        Node<T> firstNext = first.next;

        Node<T> second = head;
        Node<T> secondPrev = null;
        Node<T> secondNext = second.next;

        boolean foundFirst = false;
        boolean foundSecond = false;
        boolean inOrder = false;

        while (current != null) {
            if (!foundFirst && current.val.equals(val1)) {

                firstPrev = currentPrev;
                first = current;
                firstNext = current.next;

                if (!foundSecond)
                    inOrder = true;

                foundFirst = true;

            }

            if (!foundSecond && current.val.equals(val2)) {

                secondPrev = currentPrev;
                second = current;
                secondNext = current.next;

                if (foundFirst)
                    inOrder = true;

                foundSecond = true;
            }

            if (foundFirst && foundSecond) {

                if (!inOrder) {
                    Node<T> temp = first;
                    first = second;
                    second = temp;

                    temp = firstPrev;
                    firstPrev = secondPrev;
                    secondPrev = temp;

                    temp = firstNext;
                    firstNext = secondNext;
                    secondNext = temp;
                }

                if (firstPrev == null) {

                    head = second;

                    if (first == secondPrev) {
                        second.next = first;
                        first.next = secondNext;
                    } else {
                        second.next = firstNext;
                        secondPrev.next = first;
                        first.next = secondNext;
                    }
                } else {

                    firstPrev.next = second;
                    first.next = secondNext;

                    if (first == secondPrev) {
                        second.next = first;
                    } else {
                        second.next = firstNext;
                        secondPrev.next = first;
                    }
                }

                break;
            }

            currentPrev = current;
            current = current.next;
        }

        if (!foundFirst || !foundSecond) {
            throw new NoSuchElementException();
        }
    }

    @Override
    public T get(int i) {
        if (i < counter) {
            Node<T> node = head;
            for (int n = 0; n < i; n++) {
                node = node.next;
            }
            return node.val;
        } else {
            throw new IndexOutOfBoundsException();
        }
    }
 }   

【问题讨论】:

  • 到底是什么问题?你怎么知道这段代码不起作用?
  • 是否允许添加到通用节点类?
  • @LouisWasserman 这是由学校自动标记系统标记的
  • @KDiTraglia “无论订单如何,都可以解决问题,额外收取 10%。” - 从接口定义
  • @acattle 原始要求:“交换列表中的两个元素,但前提是 param val1 出现在 param VAL2 之前”;解决以下问题将额外获得 10% {A B C} -> Swap(B, A) -> {B A C}

标签: java linked-list


【解决方案1】:

我认为问题在于交换本身:您忘记设置尾部。

这是针对该问题的一个小测试:

@Test
public void test() {
  SwapListImpl<String> list = new SwapListImpl<String>();
  list.add("A");
  list.add("B");
  list.add("C");

  list.swap("A", "C");

  assertEquals("C", list.get(0));
  assertEquals("C", list.getHead().val);
  assertEquals("B", list.get(1));
  assertEquals("A", list.get(2));
  assertEquals("A", list.getTail().val);

  list.add("D");

  assertEquals("C", list.get(0));
  assertEquals("C", list.getHead().val);
  assertEquals("B", list.get(1));
  assertEquals("A", list.get(2));
  assertEquals("D", list.get(3));
  assertEquals("D", list.getTail().val);

  list.swap("A", "C");

  assertEquals("A", list.get(0));
  assertEquals("A", list.getHead().val);
  assertEquals("B", list.get(1));
  assertEquals("C", list.get(2));
  assertEquals("D", list.get(3));
  assertEquals("D", list.getTail().val);

  list.swap("C", "B");

  assertEquals("A", list.get(0));
  assertEquals("A", list.getHead().val);
  assertEquals("C", list.get(1));
  assertEquals("B", list.get(2));
  assertEquals("D", list.get(3));
  assertEquals("D", list.getTail().val);
}

您看到我在列表中添加了两种方法,用于获取头部和尾部,但这并不重要 - 如果没有显式测试头部和尾部,测试甚至会失败。列表的额外方法非常简单:

  public Node<T> getTail() {
      return this.tail;
    }

    public Node<T> getHead() {
      return this.head;
    }

交换列表的最后一个元素再添加另一个元素时出现不设置tail的问题。

这是实际交换的固定版本:

  if (foundFirst && foundSecond) {

    if (second == this.tail) {
      this.tail = first;
    } else if (first == this.tail) {
      this.tail = second;
    }

    if (first == this.head) {
      this.head = second;
    } else if (second == this.head) {
      this.head = first;
    }

    if (firstPrev == second) {
      first.next = second;
    } else {
      if (firstPrev != null) {
        firstPrev.next = second;
      }
      first.next = secondNext;
    }
    if (secondPrev == first) {
      second.next = first;
    } else {
      if (secondPrev != first && secondPrev != null) {
        secondPrev.next = first;
      }
      second.next = firstNext;
    }
    break;
  }

您看到我没有在您的代码中添加行 - 而是以另一种方式编写代码。我认为它更具可读性,但您也可以尝试以正确的方式设置尾部。但它对我来说太复杂了,所以我降低了代码的复杂性——这就是我重写它的原因。

我建议您将 first 和 second 用于第一次/第二次出现,而不是用于第一次/第二次参数。我认为这将提高该方法的可读性。但这是另一点;-)

希望有所帮助 - 所以恕我直言,顺序不是问题,而是问题。

【讨论】:

  • Bertram 感谢您指出问题。我已经解决了这个问题,但仍然无法获得额外的,但这是完美的。多谢!我已经吸取了教训。你的解决方案比我的要优雅得多。
  • @null 很遗憾您没有获得奖金。也许你应该问你的教授确切的原因。完整的列表仍然可以更加优雅,这里有一些提示:1. 在交换中你不需要 firstNextsecondNext,2. 你不再需要 inOrder,3. 考虑更改first*second* 的内容,正如我在答案末尾和最后 4 中提到的那样。考虑抛出 IOOB-Exc。当get(int) 使用小于零的值调用时也是如此。
  • 谢谢,我刚刚在您的程序中发现了一个错误,imgae {ABCD} 然后交换(C,B),您将“firstPrev.next = second”,这是第二个指向自身。
  • @null 哦,我明白了,是的 - 我更正了上面的代码并扩展了测试用例。
  • 当我遇到这个问题时,我想知道为什么我不能拿出最优雅的解决方案。我觉得我太傻了……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-17
相关资源
最近更新 更多