前面我们已经使用顺序表实现了一个动态数组,当然动态数组还有另一种实现形式——链表。这里就涉及到另一个问题,JAVA中有没有指针?严格来说JAVA是没有真正意义上的指针的,它只有引用。具体的分析我们这里暂不做讨论。接下来我们就利用JAVA的引用来实现一个动态数组,也就是链表。
一、链表的物理结构
链表是由一个个独立的节点组织起来的,它具有真正意义上的动态。顺序表在计算机内部是顺序储存的,它每次在创建时都要求开辟一个连续的内存空间。而链表不同,由于链表的每一个节点都可以通过引用找到它的下一个相邻节点,因此它在计算机内部是离散储存的。每一个节点都是独立的单位。因此每当你需要增加一个节点时,我们就创建一个新节点保存要增加的数据,并且通过引用,把最后一个节点的引用指向这个新节点。同样的,如果要删除一个节点,我们就把该节点删除掉,再改变其前后节点引用的指向即可。我们可以看一下下图
根据上面的分析我们可以知道链表的节点需要有两个信息,一个是数据对象——储存待保存对象,另一个是节点引用——指向该节点的下一相邻节点。
二、链表的方法实现
1.添加元素的方法——append()
基本步骤:A.新建一个节点(data储存新数据,next为空);B..找到最后一个节点;C.修改最后一个节点的next引用,使其指向新建的节点
2.获取任意位置数据的方法——get()
基本步骤:判断index是不是0,如果是0直接返回根节点的数据;否则通过循环找到相应的节点,然后返回相应节点的数据
3.返回当前链表大小的方法——size()
定义一个int型数据len,每次调用append()或者insert()等会改变当前数组数据个数的操作时都相应地增加或者减少len的大小
4.在指定位置插入数据的方法——insert()
A.新建一个保存待插入数据的新节点
B.判断要插入的位置是不是根节点的位置,如果是,则将该节点的next指向当前的根节点,再把根节点指向这个新建的节点;如果不是,就需要改变该位置的前后两个节点的指针
5.删除指定位置数据的方法——delete()
A.保存要删除的节点
B.判断是不是根节点,如果是,则将根节点的引用head指向它的下一节点(即移动根节点);如果不是,则将该节点前一个节点的next指向该节点的next
三、具体代码实现
//创建一个用链表实现的动态数组类DynamicArray
public class DynamicArray {
Node head;//定义一个头节点,head不存数据
Node curr;//定义一个当前节点
int size;//定义一个储存数组长度的数据
//需要一个节点类
public class Node {
Object data;//这个节点的数据
Node next;//指向下一个节点
public Node(Object data) {
this.data=data;
}
}
//需要一个添加方法
public void append(Object o) {
Node newNode=new Node(o);
if(head==null) {
head=newNode;
head.next=null;
curr=head;//移动当前节点的位置
}
//从第一个位置开始找
else {
//移动当前节点的位置
curr.next=newNode;
curr=curr.next;
}
size++;//数组大小加1
}
//需要一个获取节点信息的方法
public Object get(int index) {
int position = 0;
Node temp=head;
while(position<index) {
temp=temp.next;
position++;
}
return temp.data;
}
//获取当前动态数组的长度
public int size() {
return size;
}
//在指定位置插入一个数据
public void insert(Object o,int index) {
Node newNode=new Node(o);
if(index==0) {
newNode.next=head;
head=newNode;//重新设置头节点
}
else {
int position = 0;
Node temp=head;
//获取待插入位置的前一个位置
while(position<index-1) {
temp=temp.next;
position++;
}
newNode.next=temp.next;
temp.next=newNode;
}
size++;
}
//在指定位置删除数据
public Object delete(int index) {
Object retuNode;
if((size==0)||(index>size-1)) {
System.out.println("操作有误");
}
//如果删除的是头结点
if(index==0) {
retuNode=head.data;
head=head.next;//直接移动头节点的位置即可
}
else {
int position = 0;
Node temp=head;
//获取待插入位置的前一个位置
while(position<index-1) {
temp=temp.next;
position++;
}
retuNode=temp.next.data;
temp.next=temp.next.next;
}
size--;
return retuNode;
}
}
//定义一个测试类
public class Test {
public int number;
public String name;
public Test(int number,String name) {
this.number=number;
this.name=name;
}
public static void main(String[] args) {
Test t1=new Test(1,"a");
Test t2=new Test(2,"b");
Test t3=new Test(3,"c");
Test t4=new Test(4,"d");
DynamicArray Array1=new DynamicArray();
Array1.append(t1);
Array1.append(t2);
Array1.append(t3);
int len1=Array1.size();
System.out.println(len1);
Test testT1=(Test)Array1.get(0);
System.out.println(testT1.number+":"+testT1.name);
Array1.insert(t4, 0);
int len2=Array1.size();
System.out.println(len2);
Test testT2=(Test)Array1.get(0);
System.out.println(testT2.number+":"+testT2.name);
Array1.delete(0);
int len3=Array1.size();
System.out.println(len3);
Test testT3=(Test)Array1.get(0);
System.out.println(testT3.number+":"+testT3.name);
}
}
四、总结——顺序表和链表
1.储存方式不同:顺序表在计算机中是连续储存的,而链表是离散储存的。
2.适用场景:顺序表适用于索引操作比较多的应用场景,而链表适用于插入删除操作比较多的适用场景。