数据结构与算法之数组与链表

线性表包括数组,链表(单链表,双向链表,循环链表,双向循环链表,静态链表),栈(顺序栈,链式栈),队列(普通队列,双端队列,阻塞队列,并发队列,阻塞并发队列)。

数组

数组(Array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。相信大家对数组都不会陌生,每种编程语言中都会有这种数据类型。数组的下标都是从0开始的,那么数组的下标为什么从 0 开始而不是从 1 开始呢?这里牵扯到内存地址计算的问题,举个例子说明,我们声明一个长度为 10 的整型数组 int[] array = new int[10] ,每一个整型需要4个字节,所以这个数组需要分配40字节的连续的内存空间,我们假设是从1000 - 1039,数组的每一个元素的内存地址是怎么计算的呢,array[i]-address = base-address + i*type-size, 就是元素的地址等于基地址加上元素所占的大小,例如第一个元素的地址应该是该连续内存空间的首地址,即偏移为0,所以说从 0 开始有利于计算机快速计算数据元素的地址。
C#或Java中都有ArrayList,ArrayList封装了数组的一些操作饼支持动态扩展,而数组使用时必须提前指定大小。
数组插入、删除的时间复杂度是O(n)

链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表。

下图为单链表结构


单链表

链表中第一个结点的存储位置叫做头指针,头结点的数据域可以不存储任何信息,最后一个节点指针为空NULL

  • 头指针,是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针,头指针具有标识作用,所以常用头指针冠以链表的名字,无论链表是否为空,头指针均不为空,头指针是链表的必要元素
  • 头结点,是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(也可存放链表的长度),有了头结点,对在第一个元素结点前出入结点和删除第一个结点,其操作与其它结点的操作就统一了,头结点不一定是链表的必须要素。
    链表的插入、删除操作的时间复杂度是O(1), 链表随机访问的速度没有数组好,需要O(n) 的时间复杂度。
    下图来源于网络,用动画模式展示了单链表反转的操作步骤


    单链表反转
package dataStructures;
//单链表的一些常见操作(Java)
public class singlyLinkedList {

    private Node head = null;

    public void insertToHead(String value) {
        Node newNode = new Node(value, null);
        insertToHead(newNode);
    }

    public void insertToHead(Node newNode) {
        if (head == null) {
            head = newNode;
        } else {
            newNode.next = head;
            head = newNode;
        }
    }

    public void insertToTail(String value) {
        if (head == null) {
            head = new Node(value, null);
        } else {
            Node lastNode = head.next;
            if (lastNode == null) {
                lastNode = new Node(value, null);
                head.next = lastNode;
            } else {
                while (lastNode.next != null) {
                    lastNode = lastNode.next;
                }
                lastNode.next = new Node(value, null);
            }
        }
    }

    public void insertAfter(Node p, String value) {
        Node newNode = new Node(value, null);
        insertAfter(p, newNode);
    }

    public void insertAfter(Node p, Node newNode) {
        if (p == null) return;
        newNode.next = p.next;
        p.next = newNode;
    }

    public void insertBefore(Node p, String value) {
        Node newNode = new Node(value, null);
        insertBefore(p, newNode);
    }

    public void insertBefore(Node p, Node newNode) {
        if (p == null) return;
        if (head == p) {
            insertToHead(newNode);
            return;
        }
        Node tmpNode = head;
        while (tmpNode != null && tmpNode.next != p) {
            tmpNode = tmpNode.next;
        }
        if (tmpNode == null) return;

        tmpNode.next = newNode;
        newNode.next = p;
    }

    //链表反转
    public void reverse() {
        Node prev = null;
        Node current = head;
        Node nextNode;
        while (current != null) {
            nextNode = current.next;
            current.next = prev;
            prev = current;
            current = nextNode;
        }
        head = prev;
    }

    public Node findByValue(String value) {
        Node tmpNode = head;
        while (tmpNode != null && tmpNode.getData() != value) {
            tmpNode = tmpNode.next;
        }
        return tmpNode;
    }

    public void printAll() {
        Node tmpNode = head;
        while (tmpNode != null) {
            System.out.print(tmpNode.data + " ");
            tmpNode = tmpNode.next;
        }
        System.out.println();
    }

    static class Node {
        private String data;
        private Node next;

        public Node(String data, Node next) {
            this.data = data;
            this.next = next;
        }

        public String getData() {
            return data;
        }
    }

    public static void main(String[] args) {
        singlyLinkedList list = new singlyLinkedList();
        list.head = new Node("Start", null);
        list.insertToTail("First");
        list.insertToTail("Second");
        list.insertToTail("Third");
        list.insertToHead("BeforeHead");
        Node first = list.findByValue("First");
        list.insertBefore(first, "BeforeFirst");
        list.printAll();//输出 BeforeHead Start BeforeFirst First Second Third 
        list.reverse();
        list.printAll();//输出 Third Second First BeforeFirst Start BeforeHead 
    }
}

你可能感兴趣的:(数据结构与算法之数组与链表)