看了也不会系列-链表

数组

经过使用数组,能够存储一连串的数据java

在java中,能够存储全部的基本类型以及对象类型。node

可是若是在代码中,须要频繁对数组中的元素进行添加和删除,特别是从数组的头部进行添加和删除,就会有以下的问题:数组

  • 从头部添加时须要把全部的元素日后移,以腾出第一个位置插入新元素
  • 从头部删除时,须要把全部的元素往前移。
//初始化一个数组
int[] a=new int[10];
a[0]=0;
a[1]=1;
a[2]=2;
//为了方便,使用currentSize来表示数组实际使用了多少
index=3;

//从头部插入一个元素-1,会使用以下方法
if(currentSize<10){
	for(int i=currentSize;i>0;i--){
		a[i]=a[i-1];
	}
  a[0]=-1;
}
//以上操做就是先把原先存在的数据日后移动,而后再插入头部

//当从头部删除一个元素时,使用方法以下
if(currentSize>0){
  for(int i=0;i<currentSize;i++){
    a[i]=a[i+1];
  }
}
//以上操做就是把后面的元素往前移动
复制代码

从代码能够看出,对数组的插入和删除操做麻烦,若是数组的容量很是大的话,那么循环次数将很是多。编辑器

还有一个问题就是,当数组已存储的元素个数达到初始化的容量,若要继续添加元素,就须要对数组进行扩容。以下代码工具

int[] a=new int[2];
a[0]=0;
a[1]=1;
//此时数组已经达到最大数量,须要进行扩容。
int[] b=new int[a.length*2];
for(int i=0;i<a.length;i++){
  b[i]=a[i];
}
a=b;
//此时数组a的容量就扩大到原来的两倍
复制代码

链表

为了不使用数组带来的插入和删除的开销,须要保证元素能够不在内存地址上进行连续存储,不然添加或删除一个元素就可能须要总体移动。这是咱们就以使用链表来存储数据。this

结构

链表由一系列节点组成,这些节点在内存中不必定相连。在最简单的链表中,每个节点均包含数据元素和该数据元素的后继的指针,将其称之为next。最后一个元素的next指向null。spa

如图: debug

代码实现:

//链表节点实现
class Node {
    //使用Object类型,让节点可以保存任意类型的数据
    private Object data;
    private Node next;

    public Node(Object data) {
        this.data = data;
    }
  
    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

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

//链表使用
public class Test {
    public static void main(String[] args) {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(null);
      //这样的链表就和数组 int[] a=new int[]{1,2,3}差很少,因为node3的后继不存在,因此node3指向null
    }
}
复制代码

咱们能够经过intellij编辑器的断点进行查看: 3d

关系就是node1->node2->node3->null指针

通常的,咱们会为链表设置一个表头,它不存储数据,可是指向第一个元素

package list;

class Node {
    //使用Object类型,让节点可以保存任意类型的数据
    private Object data;
    private Node next;

    public Node() {
    }

    public Node(Object data) {
        this.data = data;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

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

public class Test {
    public static void main(String[] args) {
    		//声明表头
        Node head=new Node();
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        //直接指向node1,里面没有存储数据
        head.setNext(node1);
        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(null);
    }
}
复制代码

链表的插入和删除

插入

链表的插入很简单,好比要想n节点插入到s节点的后面,首先对链表进行遍历,找到s节点后,先让n.next=s.next,而后让s.next=n

如图,插入新节点A4,就是先让A4指向A3,再让A2指向A4

public class Test {
    public static void main(String[] args) {
        Node head = new Node();
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        head.setNext(node1);
        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(null);
        //声明一个node4
        Node node4 = new Node(4);
        //要想获得某个指定的节点,就得对链表进行遍历,若是到了某个节点,其next指向为null,说明整个链表都遍历完毕,不存在指定的节点元素,全部可使用while循环进行遍历
        Node node = head;
        while ((node = node.getNext()) != null) {
            //等于2的时候,就是找到了节点node2
            if (node.getData().equals(2)) {
                //先让新节点node4指向node2的下一节点,也就是node3
                node4.setNext(node2.getNext());
                //而后让node2指向新节点node4
                node2.setNext(node4);
                //跳出循环
                break;
            }
        }
    }
}
复制代码

使用debug工具进行查看:

删除

删除和插入相似,好比要想删除节点n,首先对链表进行遍历,找到s节点,知足s.next=n,而后让s.next=n.next ,n.ndex=null,节点n会因为虚拟机的垃圾回收机制被自动回收

public class Test {
    public static void main(String[] args) {
        Node head = new Node();
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        head.setNext(node1);
        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(null);
        //要想获得某个指定的节点,就得对链表进行遍历,若是到了某个节点,其next指向为null,说明整个链表都遍历完毕,不存在指定的节点元素,全部可使用while循环进行遍历
        Node node = head;
        while ((node = node.getNext()) != null) {
            //节点的next.data等于2的时候,就找到了须要被删除节点的前驱
            if (node.getNext().getData().equals(2)) {
                Node deleteNode = node.getNext();
                //先让前驱指向后继
                node.setNext(deleteNode.getNext());
                //而后把删除节点的后继指向null
                deleteNode.setNext(null);
                //跳出循环
                break;
            }
        }
    }
}
复制代码

debug查看

能够看到,该链表中再也不包含2

总结

总的来讲,数组和链表使用频率都大,下面总结下各自的优势和缺点

数组

优势

查询快,由于有下标

缺点
  • 插入和删除须要移动大量元素
  • 容量固定,当容量不够的时候,须要进行扩容

链表

优势
  • 容量大,其大小和内存有关,由于节点都是不连续的
  • 插入和删除方便

缺点

由于链表中的节点不是连续的,因此想要获取某个元素的时候须要对链表进行遍历和判断

使用

须要常常添加和删除元素时,用链表。元素不会常常变更,或者查询频率较高,用数组

相关文章
相关标签/搜索