数据结构--单向链表

292 阅读2分钟

每天都在学习框架,抽时间来巩固一下基础,记录下链表的实现过程。


1. 单向链表


单向链表的接口比较简单,包含两个元素,element数据本体,next指针。

一个单向链表中的头部和尾部是两个特殊的部分。有了头结点就能遍历获取到整个链表的内容,当遍历到指针为Null的节点时,说明到达了尾部,整个链表遍历结束。

@Data
public class Node<T> {
    private T element;

    private Node next;

    public boolean hasNext() {
        return next != null;
    }

    public Node() {
    }

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

    public Node(T element, Node next) {
        this.element = element;
        this.next = next;
    }

}

1.1. 增加节点

测试用例:

    @Test
    public void addTest() {
        SinglyLinkedList<String> linked = new SinglyLinkedList<>();
        linked.add("a");
        linked.add("b");
        linked.add("c");
        assertThat(linked.getLength(), is(3));
        SinglyLinkedList<Integer> intLinked = new SinglyLinkedList<>(4);
        intLinked.add(3);
        intLinked.add(2);
        intLinked.add(1);
        assertNotNull(intLinked.toString());
        assertThat(intLinked.getLength(), is(4));
    }

实现代码:

public class SinglyLinkedList<T> {

    @Getter
    private Node head;

    /**
     * 链表长度
     */
    private Integer length;

    public SinglyLinkedList() {
        length = 0;
    }

    public SinglyLinkedList(T data) {
        this.head = new Node<>(data);
        length = 1;
    }

    public void add(T data) {
        Node<T> newHead = new Node<>(data);
        if (head == null) {
            head = newHead;
        } else {
            newHead.setNext(head);
            head = newHead;
        }
        length++;
    }


    public int getLength() {
        return this.length;
    }

    @Override
    public String toString() {
        if (head == null) {
            return "";
        }
        Node temp = head;
        StringBuilder stringBuilder = new StringBuilder();
        while (temp.hasNext()) {
            stringBuilder.append(temp.getElement()).append(",");
            temp = temp.getNext();
        }

        return stringBuilder.append(temp.getElement()).toString();
    }

}

1.2. 获取索引数据

测试用例:

    @Test(expected = IllegalArgumentException.class)
    public void getValueTest() {
        SinglyLinkedList<String> stringLinked = new SinglyLinkedList<>();
        stringLinked.add("a");
        stringLinked.add("b");
        stringLinked.add("c");


        log.info(stringLinked.toString());
        assertThat(stringLinked.getValue(0), is("c"));
        assertThat(stringLinked.getValue(2), is("a"));

        stringLinked.getValue(3);
    }

实现代码:

    public T getValue(Integer index) {
        if (index > length - 1 || index < 0) {
            throw new IllegalArgumentException("错误的索引值");
        }
        Node<T> result = head;
        for (int i = 0; i < index; i++) {
            result = result.getNext();
        }
        return result.getElement();
    }

1.3. 链表翻转

链表翻转在实现的过程中总感觉特别的绕,出现了好几次递归的情况,所以画图梳理了一下逻辑。

通过操作翻转前链表数据,每次取出current和next节点,创建新的节点设置翻转关系后,重复逻辑进行下一个节点的设置。


测试用例:

    @Test
    public void reverseTest() {
        assertReverse(1);
        assertReverse(0);
        assertReverse(2);
        assertReverse(10);
    }


    private void assertReverse(int num) {
        SinglyLinkedList<Integer> linked = new SinglyLinkedList<>();
        for (int i = 0; i < num; i++) {
            linked.add(i);
        }
        log.info("翻转前:{}", linked);
        linked.reverse();
        log.info("翻转后:{}", linked);
        Node head = linked.getHead();
        for (int i = 0; i < num; i++) {
            Integer element = (Integer) head.getElement();
            assertThat(element, is(i));
            if (head.hasNext()) {
                head = head.getNext();
            }
        }
    }

实现代码:

    /**
     * 链表翻转
     */
    public void reverse() {
        if (head == null || !head.hasNext()) {
            return;
        }
        Node newNode = null;
        while (head.hasNext()) {
            Node current = head;
            Node next = current.getNext();
            head = head.getNext();
            if (newNode == null) {
                newNode = new Node();
                newNode.setElement(next.getElement());
                newNode.setNext(new Node(current.getElement()));
            } else {
                newNode = new Node(next.getElement(), newNode);
            }
        }
        this.head = newNode;
    }


(未完待续...)