动态数据结构基础01:链表

动态数据结构

动态数组、栈和队列的底层仍依托静态数组来实现,通过定义一个resize()方法解决固定容量问题

而链接是真正的动态数据结构,学习的同时也可以更深入的理解引用(指针)和递归

单链表的实现

数据存储在“节点”(Node)中,每个节点拥有value和next两个属性,next为指向下一个节点的指针

优点:链表是真正的动态数据结构,不需要进行扩缩容

缺点:链表丧失了随机访问的能力,每次操作必须从头节点开始依次往后找,不适合索引有语意的情况

public class Algorithm {

    public static void main(String[] args) {

        LinkedList<Integer> linkedList = new LinkedList<>();

        for (int i = 0; i < 5; i++) {

            linkedList.addFirst(i);
            System.out.println(linkedList);
        }

        linkedList.add(666, 3);
        linkedList.remove(1);
        System.out.println(linkedList);

        System.out.println(linkedList.contains(0));
        System.out.println(linkedList.contains(4));

        linkedList.set(888, 4);
        System.out.println(linkedList);
        System.out.println(linkedList.get(4));
    }
}

class LinkedList<E>{

    /**
     * 定义节点类Node,链表的底层采用Node类实现
     */
    private class Node {

        /**
         * 属性设置为public,便于外部类调用链表
         */
        public E e;
        public Node next;

        public Node(E e, Node next){

            this.e = e;
            this.next = next;
        }

        public Node(){

            this.next = null;
        }
    }

    /**
     * 链表操作必须要知道前一个节点,为了方便实现,给头节点也定义一个虚拟前节点,称为虚拟头节点,这样所有的节点都可以使用同样的方法
     * 虚拟头节点始终指向头节点前面的一位,链表为空时也会存在,其初始值为null,指向的也是null
     */
    private Node dummyHead;
    private int size;

    public LinkedList(){

        dummyHead = new Node(null, null);
        size = 0;
    }

    public int getSize(){

        return size;
    }

    public boolean isEmpty(){

        return size == 0;
    }

    /**
     * 链表所有操作都需要先找到前一个节点,因此可以定义一个返回任意索引前一个节点的方法
     */
    public Node getPrevNode(int index){

        if (index < 0 || index >= size){
            throw new IllegalArgumentException("索引值非法");
        }

        /**
         * 从头节点的前一个节点,也就是虚拟头节点开始
         */
        Node prev = dummyHead;

        /**
         * 循环index - 1次,prev代表的就是第index - 1个节点,也就是index的前一个节点
         */
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        return prev;
    }

    /**
     * 在链表中插入节点
     */
    public void add(E e, int index) {

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("索引值非法");
        }

        /**
         * add()方法不能复用getPrevNode()方法,因为add()方法可以取到size
         */
        Node prev = dummyHead;

        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        /**
         * 新建一个节点存放要添加的数据,让其指向prev节点的下一个节点,然后让prev节点指向新节点,完成节点的插入
         */
        prev.next = new Node(e, prev.next);
        size++;
    }

    /**
     * 在链表头部添加节点
     * 和数组相反,链表从头部添加元素是最方便的
     */
    public void addFirst(E e){

        add(e, 0);
    }

    public void addLast(E e){

        add(e, size);
    }

    /**
     * 删除链表元素
     */
    public E remove(int index){

        Node prev = getPrevNode(index);

        Node tem = prev.next;
        prev.next = tem.next;
        tem.next = null;
        size--;

        return tem.e;
    }

    public E removeFirst(){

        return remove(0);
    }

    public E removeLast(){

        return remove(size - 1);
    }

    /**
     * 修改链表的元素
     */
    public void set(E e, int index){

        Node prev = getPrevNode(index);

        prev.next.e = e;
    }

    /**
     * 查询链表的元素
     */
    public E get(int index){

        Node prev = getPrevNode(index);

        return prev.next.e;
    }

    public E getFirst(){

        return get(0);
    }

    public E getLast(){

        return get(size - 1);
    }

    public boolean contains(E e){

        for (Node prev = dummyHead; prev.next != null; prev = prev.next){

            /**
             * 只是比较是否相等用equals(),如果是比较大小排序,则要实现Comparable接口和重写compareTo()方法
             */
            if (prev.next.e.equals(e)){
                return true;
            }
        }

        return false;
    }

    @Override
    public String toString(){

        StringBuilder str = new StringBuilder();
        Node prev = dummyHead;
        System.out.println("链表元素个数为:" + getSize());

        /**
         * 和contains()方法的for循环作用一样
         */
        while (prev.next != null){

            str.append(prev.next.e + "——>");
            prev = prev.next;
        }

        str.append("null");

        return str.toString();
    }
}

复杂度分析

单链表增、删、改、查都需要遍历整个链表,时间复杂度是O(n)

但对单链表头进行操作时,复杂度仅为O(1),因此单链表可以实现只在一端进行操作的数据结构——栈

单链表实现栈

public class Algorithm {

    public static void main(String[] args) {

        LinkedListStack<Integer> stack = new LinkedListStack<>();

        for (int i = 0; i < 5; i++) {

            stack.push(i);
            System.out.println(stack);
        }

        System.out.println(stack.getSize());
        System.out.println(stack.peek());

        stack.pop();
        System.out.println(stack);

        System.out.println(stack.isEmpty());
    }
}

interface Stack<E> {

    int getSize();
    boolean isEmpty();
    void push(E element);
    E pop();
    E peek();
}

class LinkedListStack<E> implements Stack<E> {

    LinkedList<E> stack;

    public LinkedListStack(){

        stack = new LinkedList<>();
    }

    @Override
    public void push(E element){

        stack.addFirst(element);
    }

    @Override
    public E pop(){

        return stack.removeFirst();
    }

    @Override
    public E peek(){

        return stack.getFirst();
    }

    @Override
    public int getSize(){

        return stack.getSize();
    }

    @Override
    public boolean isEmpty(){

        return stack.isEmpty();
    }

    @Override
    public String toString(){

        StringBuilder str = new StringBuilder();
        str.append("top [");
        System.out.println("栈的元素个数为:" + getSize());

        for (int i = 0; i < getSize(); i++) {

            str.append(stack.get(i));

            if (i != stack.getSize() - 1){
                str.append(", ");
            }
        }

        str.append("]");

        return str.toString();
    }
}

class LinkedList<E>{

    private class Node {

        public E e;
        public Node next;

        public Node(E e, Node next){

            this.e = e;
            this.next = next;
        }

        public Node(){

            this.next = null;
        }
    }

    private Node dummyHead;
    private int size;

    public LinkedList(){

        dummyHead = new Node(null, null);
        size = 0;
    }

    public int getSize(){

        return size;
    }

    public boolean isEmpty(){

        return size == 0;
    }

    public Node getPrevNode(int index){

        if (index < 0 || index >= size){
            throw new IllegalArgumentException("索引值非法");
        }

        Node prev = dummyHead;

        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        return prev;
    }

    public void add(E e, int index) {

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("索引值非法");
        }

        Node prev = dummyHead;

        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        prev.next = new Node(e, prev.next);
        size++;
    }

    public void addFirst(E e){

        add(e, 0);
    }

    public void addLast(E e){

        add(e, size);
    }

    public E remove(int index){

        Node prev = getPrevNode(index);

        Node tem = prev.next;
        prev.next = tem.next;
        tem.next = null;
        size--;

        return tem.e;
    }

    public E removeFirst(){

        return remove(0);
    }

    public E removeLast(){

        return remove(size - 1);
    }

    public void set(E e, int index){

        Node prev = getPrevNode(index);

        prev.next.e = e;
    }

    public E get(int index){

        Node prev = getPrevNode(index);

        return prev.next.e;
    }

    public E getFirst(){

        return get(0);
    }

    public E getLast(){

        return get(size - 1);
    }

    public boolean contains(E e){

        for (Node prev = dummyHead; prev.next != null; prev = prev.next){

            if (prev.next.e.equals(e)){
                return true;
            }
        }

        return false;
    }
}

数组栈和单链表栈性能比较

import java.util.Random;

public class Algorithm {

    public static void main(String[] args) {

        int count = 1000000;

        ArrayStack<Integer> arrayStack = new ArrayStack<>();
        System.out.println("数组栈用时:" + test(arrayStack, count) + "秒");

        LinkedListStack<Integer> linkedListStack = new LinkedListStack<>();
        System.out.println("链表栈用时:" + test(linkedListStack, count) + "秒");
    }

    public static double test(Stack<Integer> name, int count){

        Random random = new Random();

        long startTime = System.nanoTime();

        for (int i = 0; i < count; i++) {
            name.push(random.nextInt(count));
        }

        for (int i = 0; i < count; i++) {
            name.pop();
        }

        long endTime = System.nanoTime();

        return (endTime - startTime) / 1000000000.0;
    }
}

interface Stack<E> {

    int getSize();
    boolean isEmpty();
    void push(E element);
    E pop();
    E peek();
}

class LinkedListStack<E> implements Stack<E> {

    LinkedList<E> stack;

    public LinkedListStack(){

        stack = new LinkedList<>();
    }

    @Override
    public void push(E element){

        stack.addFirst(element);
    }

    @Override
    public E pop(){

        return stack.removeFirst();
    }

    @Override
    public E peek(){

        return stack.getFirst();
    }

    @Override
    public int getSize(){

        return stack.getSize();
    }

    @Override
    public boolean isEmpty(){

        return stack.isEmpty();
    }

    @Override
    public String toString(){

        StringBuilder str = new StringBuilder();
        str.append("top [");
        System.out.println("栈的元素个数为:" + getSize());

        for (int i = 0; i < getSize(); i++) {

            str.append(stack.get(i));

            if (i != stack.getSize() - 1){
                str.append(", ");
            }
        }

        str.append("]");

        return str.toString();
    }
}

class LinkedList<E>{

    private class Node {

        public E e;
        public Node next;

        public Node(E e, Node next){

            this.e = e;
            this.next = next;
        }

        public Node(){

            this.next = null;
        }
    }

    private Node dummyHead;
    private int size;

    public LinkedList(){

        dummyHead = new Node(null, null);
        size = 0;
    }

    public int getSize(){

        return size;
    }

    public boolean isEmpty(){

        return size == 0;
    }

    public Node getPrevNode(int index){

        if (index < 0 || index >= size){
            throw new IllegalArgumentException("索引值非法");
        }

        Node prev = dummyHead;

        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        return prev;
    }

    public void add(E e, int index) {

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("索引值非法");
        }

        Node prev = dummyHead;

        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        prev.next = new Node(e, prev.next);
        size++;
    }

    public void addFirst(E e){

        add(e, 0);
    }

    public void addLast(E e){

        add(e, size);
    }

    public E remove(int index){

        Node prev = getPrevNode(index);

        Node tem = prev.next;
        prev.next = tem.next;
        tem.next = null;
        size--;

        return tem.e;
    }

    public E removeFirst(){

        return remove(0);
    }

    public E removeLast(){

        return remove(size - 1);
    }

    public void set(E e, int index){

        Node prev = getPrevNode(index);

        prev.next.e = e;
    }

    public E get(int index){

        Node prev = getPrevNode(index);

        return prev.next.e;
    }

    public E getFirst(){

        return get(0);
    }

    public E getLast(){

        return get(size - 1);
    }

    public boolean contains(E e){

        for (Node prev = dummyHead; prev.next != null; prev = prev.next){

            if (prev.next.e.equals(e)){
                return true;
            }
        }

        return false;
    }
}

class ArrayStack<E> implements Stack<E>{

    Array<E> array;

    public ArrayStack(int capacity){

        array = new Array<>(capacity);
    }

    public ArrayStack(){

        array = new Array<>();
    }

    @Override
    public int getSize() {

        return array.getSize();
    }

    @Override
    public boolean isEmpty() {

        return array.isEmpty();
    }

    @Override
    public void push(E element) {

        array.addLast(element);
    }

    @Override
    public E pop() {

        return array.removeLast();
    }

    @Override
    public E peek() {

        return array.getLast();
    }

    @Override
    public String toString() {

        StringBuilder str = new StringBuilder();

        str.append("Stack: [");

        for (int i = 0; i < array.getSize(); i++) {

            str.append(array.get(i));

            if (i != array.getSize() - 1){
                str.append(", ");
            }
        }
        str.append("] top");

        return str.toString();
    }
}

class Array<E> {

    private E[] data;
    private int size;

    public Array(int capacity){

        data = (E[]) new Object[capacity];
        size = 0;
    }

    public Array(){

        this(10);
    }

    public void add(int index, E element){

        if (index < 0 || index > size){
            throw new IllegalArgumentException("索引值非法");
        }

        if (size == data.length){
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = element;
        size++;
    }

    public void addLast(E element){

        add(size, element);
    }

    public E remove(int index){

        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("索引值非法");
        }

        E target = data[index];

        for (int i = index; i < size - 1; i++) {
            data[i] = data[i + 1];
        }

        size--;
        data[size] = null;

        if (size == data.length / 4 && data.length / 2 != 0){
            resize(data.length / 2);
        }

        return target;
    }

    public E removeLast(){

        return remove(size - 1);
    }

    public int getSize(){

        return size;
    }

    public E get(int index){

        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("索引值非法");
        }

        return data[index];
    }

    public E getLast(){

        return get(size - 1);
    }

    private void resize(int newCapacity){

        E[] newData = (E[]) new Object[newCapacity];

        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }

        data = newData;
    }

    public boolean isEmpty(){

        return size == 0;
    }
}

链表中包含很多的new Node()操作,会不停的开辟新的空间,因此在大规模时耗时很长

但在时间复杂度上没有量级的差异,可能只是相差了几倍

用带尾指针单链表实现队列

单链表在尾部添加或删除元素的时间复杂度是O(n),因此不能直接用来实现队列

可以再设置一个尾节点tail,此时在尾部添加节点也很容易,但是删除节点还是很麻烦,因为尾节点无法向前寻找前一个节点的位置

所以用带尾指针单链表实现队列,要从tail端入队,从haed端出队

public class Algorithm {

    public static void main(String[] args) {

        LinkedListQueue<Integer> queue = new LinkedListQueue<>();

        for (int i = 0; i < 5; i++) {

            queue.enqueue(i);
            System.out.println(queue);
        }

        for (int i = 0; i < 4; i++) {

            queue.dequeue();
            System.out.println(queue);
        }
    }
}

interface Queue<E> {

    int getSize();
    boolean isEmpty();
    void enqueue(E element);
    E dequeue();
    E getFront();
}

class LinkedListQueue<E> implements Queue<E>{

    private class Node{

        public E element;
        public Node next;

        public Node(E element){

            this.element = element;
            this.next = null;
        }

        public Node(E element, Node next){

            this.element = element;
            this.next = next;
        }
    }

    /**
     * 入队出队都不需要遍历链表中间,因此可以不用虚拟头节点
     */
    private Node head;
    private Node tail;
    private int size;

    public LinkedListQueue(){

        head = null;
        tail = null;
        size = 0;
    }

    @Override
    public int getSize() {

        return size;
    }

    @Override
    public boolean isEmpty() {

        return size == 0;
    }

    @Override
    public void enqueue(E element) {

        /**
         * 当添加第一个节点时,head和tail会指向同一个节点
         */
        if (tail == null){

            tail = new Node(element);
            head = tail;
        }
        else {

            tail.next = new Node(element);
            tail = tail.next;
        }

        size++;
    }

    @Override
    public E dequeue() {

        if (size == 0){
            throw new IllegalArgumentException("队列为空");
        }

        Node tem = head;
        head = head.next;
        tem.next = null;

        size--;

        /**
         * 当最后一个节点出队的时候,tail也要指向null
         */
        if (size == 0) {
            tail = null;
        }

        return tem.element;
    }

    @Override
    public E getFront() {

        if (size == 0){
            throw new IllegalArgumentException("队列为空");
        }

        return head.element;
    }

    @Override
    public String toString(){

        StringBuilder str = new StringBuilder();
        System.out.print("队列的元素个数为:" + getSize() + "\nhead [");

        for (Node cur = head; cur != null; cur = cur.next) {

            str.append(cur.element);

            if (cur.next != null){
                str.append(", ");
            }
        }

        str.append("] tail");

        return str.toString();
    }
}

数组队列、循环队列、单链表队列性能对比

import java.util.Random;

public class Algorithm {

    public static void main(String[] args) {

        int count = 100000;

        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
        System.out.println("数组队列用时:" + test(arrayQueue, count) + "秒");

        LoopQueue<Integer> loopQueue = new LoopQueue<>();
        System.out.println("循环队列用时:" + test(loopQueue, count) + "秒");

        LinkedListQueue<Integer> linkedListQueue = new LinkedListQueue<>();
        System.out.println("链表队列用时:" + test(linkedListQueue, count) + "秒");
    }

    public static double test(Queue<Integer> name, int count){

        Random random = new Random();

        long startTime = System.nanoTime();

        for (int i = 0; i < count; i++) {
            name.enqueue(random.nextInt(count));
        }

        for (int i = 0; i < count; i++) {
            name.dequeue();
        }

        long endTime = System.nanoTime();

        return (endTime - startTime) / 1000000000.0;
    }
}

interface Queue<E> {

    int getSize();
    boolean isEmpty();
    void enqueue(E element);
    E dequeue();
    E getFront();
}

class LinkedListQueue<E> implements Queue<E>{

    private class Node{

        public E element;
        public Node next;

        public Node(E element){

            this.element = element;
            this.next = null;
        }

        public Node(E element, Node next){

            this.element = element;
            this.next = next;
        }
    }

    private Node head;
    private Node tail;
    private int size;

    public LinkedListQueue(){

        head = null;
        tail = null;
        size = 0;
    }

    @Override
    public int getSize() {

        return size;
    }

    @Override
    public boolean isEmpty() {

        return size == 0;
    }

    @Override
    public void enqueue(E element) {

        if (tail == null){

            tail = new Node(element);
            head = tail;
        }
        else {

            tail.next = new Node(element);
            tail = tail.next;
        }

        size++;
    }

    @Override
    public E dequeue() {

        if (size == 0){
            throw new IllegalArgumentException("队列为空");
        }

        Node tem = head;
        head = head.next;
        tem.next = null;

        size--;

        if (size == 0) {
            tail = null;
        }

        return tem.element;
    }

    @Override
    public E getFront() {

        if (size == 0){
            throw new IllegalArgumentException("队列为空");
        }

        return head.element;
    }

    @Override
    public String toString(){

        StringBuilder str = new StringBuilder();
        System.out.print("队列的元素个数为:" + getSize() + "\nhead [");

        for (Node cur = head; cur != null; cur = cur.next) {

            str.append(cur.element);

            if (cur.next != null){
                str.append(", ");
            }
        }

        str.append("] tail");

        return str.toString();
    }
}

class LoopQueue<E> implements Queue<E>{

    private E[] data;
    private int front, tail;

    public LoopQueue(int capacity){

        data = (E[]) new Object[capacity + 1];
        front = 0;
        tail = 0;
    }

    public LoopQueue(){

        this(5);
    }

    @Override
    public int getSize() {

        if (front <= tail){
            return tail - front;
        }
        else {
            return data.length - (front - tail);
        }
    }

    @Override
    public boolean isEmpty() {

        return front == tail;
    }

    @Override
    public void enqueue(E element) {

        if (front == (tail + 1) % data.length){
            resize(2 * (data.length - 1));
        }

        data[tail] = element;

        tail = (tail + 1) % data.length;
    }

    @Override
    public E dequeue() {

        if (isEmpty()){
            throw new IllegalArgumentException("队列空了");
        }

        E tem = data[front];

        data[front] = null;
        front = (front + 1) % data.length;

        if (getSize() == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }

        return tem;
    }

    public void resize(int newCapacity){

        E[] newData = (E[]) new Object[newCapacity + 1];

        for (int i = 0; i < getSize(); i++) {
            newData[i] = data[(front + i) % data.length];
        }

        data = newData;
        front = 0;
        tail = getSize();
    }

    @Override
    public E getFront() {

        if (isEmpty()){
            throw new IllegalArgumentException("队列空了");
        }

        return data[front];
    }

    @Override
    public String toString() {

        StringBuilder str = new StringBuilder();

        str.append(String.format("Queue: 队列的容量为%d,元素个数为%d\nfront [", data.length - 1, getSize()));

        for (int i = 0; i < getSize(); i++) {

            str.append(data[(front + i) % data.length]);

            if (i != getSize()- 1){
                str.append(", ");
            }
        }

        str.append("] tail");

        return str.toString();
    }
}

class ArrayQueue<E> implements Queue<E>{

    Array<E> array;

    public ArrayQueue(int capacity){

        array = new Array<>(capacity);
    }

    public ArrayQueue(){

        array = new Array<>();
    }

    @Override
    public int getSize() {

        return array.getSize();
    }

    @Override
    public boolean isEmpty() {

        return array.isEmpty();
    }

    @Override
    public void enqueue(E element) {

        array.addLast(element);
    }

    @Override
    public E dequeue() {

        return array.removeFirst();
    }

    @Override
    public E getFront() {

        return array.getFirst();
    }

    @Override
    public String toString() {

        StringBuilder str = new StringBuilder();

        str.append("Queue: front [");

        for (int i = 0; i < array.getSize(); i++) {

            str.append(array.get(i));

            if (i != array.getSize() - 1){
                str.append(", ");
            }
        }
        str.append("] tail");

        return str.toString();
    }
}

class Array<E> {

    private E[] data;
    private int size;

    public Array(int capacity){

        data = (E[]) new Object[capacity];
        size = 0;
    }

    public Array(){

        this(10);
    }

    public void add(int index, E element){

        if (index < 0 || index > size){
            throw new IllegalArgumentException("索引值非法");
        }

        if (size == data.length){
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = element;
        size++;
    }

    public void addLast(E element){

        add(size, element);
    }

    public void remove(int index){

        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("索引值非法");
        }

        for (int i = index; i < size - 1; i++) {
            data[i] = data[i + 1];
        }

        size--;
        data[size] = null;

        if (size == data.length / 4 && data.length / 2 != 0){
            resize(data.length / 2);
        }
    }

    public E removeFirst(){

        if (size == 0) {
            throw new IllegalArgumentException("索引值非法");
        }

        E tem = data[0];
        remove(0);

        return tem;
    }

    public int getSize(){

        return size;
    }

    public E get(int index){

        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("索引值非法");
        }

        return data[index];
    }

    public E getFirst(){

        return get(0);
    }

    private void resize(int newCapacity){

        E[] newData = (E[]) new Object[newCapacity];

        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }

        data = newData;
    }

    public boolean isEmpty(){

        return size == 0;
    }

    @Override
    public String toString() {

        StringBuilder str = new StringBuilder();

        str.append(String.format("数组的容量为%d,元素个数为%d\n", data.length, size));
        str.append("[");

        for (int i = 0; i < size; i++) {

            str.append(data[i]);

            if (i != size - 1){
                str.append(", ");
            }
        }

        str.append("]");

        return str.toString();
    }
}
posted @ 2021-10-16 15:05  振袖秋枫问红叶  阅读(118)  评论(0)    收藏  举报