Ops on SinglyList:removeMiddle and reverse


这几天在成都校招,计算机及相关专业本科/硕士,笔试30分钟,其中有两个代码算法题,任选其一:

  • 移除单链表的中间元素
  • 反转单链表,递归和非递归两种方式

这是很简单的题,网上一搜一把,目的是考察学生有基础编码能力,不要求编译测试通过,只要写清楚思路和注意EDGE CASE都算过,但很可惜,通过率比较低。为什么呢?

如果自己构造数据结构,代码稍多一些,我们也当然希望看到学生能多表现自己:

package com.dunkcoder;

public class SinglyList<T> {

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

    public SinglyList(Node<T> head) {
        this.head = head;
        this.tail = head;
    }

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

    public void add(Node<T> node) {
        this.tail.setNext(node);
        this.tail = node;
    }

    public void remove(Node<T> node) {
        // TODO:
    }

    public static class Node<T> {
        private Node<T> next;
        private T object;

        public Node(T object) {
            this.object = object;
        }

        // getter() setter()

        public Node<T> next() {
            return next;
        }

        public void setNext(Node<T> next) {
            this.next = next;
        }

        // toString() hashCode() equals() ...
    }
}

下面是算法和测试代码:

package com.dunkcoder;

public class TestSinglyList {

    public static <T> SinglyList<T> removeMiddle(SinglyList<T> list) {
        SinglyList.Node<T> current = list.head();
        int index = 0;
        SinglyList.Node<T> middle = list.head();

        // 定义2个指针:current和middle
        // current的步速是middle的2倍,当current走到尾巴的时候,middle刚好指在中间元素
        while (current.next() != null) {
            index++;
            if (index % 2 == 0) {
                middle = middle.next();
            }
            current = current.next();
        }

        // 考虑链表长度为偶数和奇数情况
        String stringTmpl = "middle:%s of length:%s removed";
        if (index % 2 == 1) {
            // 奇数个元素
            middle = middle.next();
            list.remove(middle);
            System.out.println(String.format(stringTmpl, middle, index));
        } else {
            // 偶数个元素,middle指向左边
            SinglyList.Node<T> middleRight = middle.next();
            list.remove(middle);
            System.out.println(String.format(stringTmpl, middle, index));
            list.remove(middleRight);
            System.out.println(String.format(stringTmpl, middleRight, index));
        }

        return list;
    }

    // 递归
    public static <T> SinglyList.Node<T> recursivelyReverse(
            SinglyList.Node<T> head) {
        SinglyList.Node<T> forwardNode = head.next();
        if (forwardNode == null)
            return head;
        SinglyList.Node<T> newHead = recursivelyReverse(forwardNode);
        forwardNode.setNext(head);
        head.setNext(null);
        return newHead;
    }

    // 循环迭代
    public static <T> SinglyList.Node<T> iterativelyReverse(
            SinglyList.Node<T> head) {
        SinglyList.Node<T> tempNode = null;
        SinglyList.Node<T> traversedNode = null;

        while (head != null) {
            tempNode = head.next();

            head.setNext(traversedNode);
            traversedNode = head;

            head = tempNode;
        }
        head = traversedNode;
        return head;
    }

    public static void main(String[] args) {

        SinglyList.Node<String> head = new SinglyList.Node<String>("head");
        SinglyList<String> list = new SinglyList<String>(head);
        list.add(new SinglyList.Node<String>("1"));
        list.add(new SinglyList.Node<String>("2"));
        list.add(new SinglyList.Node<String>("3"));
        list.add(new SinglyList.Node<String>("4"));
        // list.add(new SinglyList.Node<String>("5"));

        removeMiddle(list);

        SinglyList.Node<String> reversedHead = recursivelyReverse(head);
        // SinglyList.Node<String> reversedHead = iterativelyReverse(head);
        System.out.println(reversedHead);
    }

}

如果基于LinkedList或ArrayList写也是可以的,说明对JDK比较熟悉,只是LinkedList是双向链表:

package com.dunkcoder;

import java.util.ArrayList;
import java.util.ListIterator;

public class TestArrayList {

    public static <T> ArrayList<T> removeMiddle(ArrayList<T> list) {
        if (list == null || (list != null && list.size() <= 2)) {
            return list;
        } else {
            ListIterator<T> middle = list.listIterator();
            ListIterator<T> current = list.listIterator();
            T t = null;
            int index = 0;
            while (true) {
                // 2步走
                t = current.next();
                if (t != null) {
                    index++;
                    if (index % 2 == 0) {
                        // 1步走
                        t = middle.next();
                    }
                    // 没有下一个元素
                    if (!current.hasNext()) {
                        if (index % 2 == 0) {
                            // 偶数个元素,此时afterStepItr指向Middle的左边
                            int pos = index / 2 - 1;
                            list.remove(pos);// 删除Middle的左边
                            list.remove(pos);// 删除Middle的右边
                        } else {
                            // 奇数个元素,此时afterStepItr少移一次
                            int pos = index / 2 + 1 - 1;
                            list.remove(pos);// 删除Middle
                        }
                        break;
                    }
                }

            }
            return list;
        }
    }

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        // list.add("5");
        removeMiddle(list);
    }

    // LinkedList双向,ArrayList则可以考虑使用Collections.reverse()方法
    public static <T> LinkedList<T> iterativelyReverse(LinkedList<T> head) {
        LinkedList<T> reversedList = new LinkedList<T>();
        for( Iterator<T > itr = head.descendingIterator(); itr.hasNext(); ) {
            T t = itr.next();
            reversedList.add(t);
        }
        return reversedList;
    }
}

就酱,您有任何问题或建议,请给我写邮件


Yinwer /
Published under (CC) BY-NC-SA