链表
1、概念
链表是一种在物理上非顺序、非连续的物理结构,由若干的节点所组成。(1)单向链表
单向链表的节点包括两部分,一部分是存放数据的data,另一部分是指向下一个节点的指针next,定义一个链表的节点:private static class Node{
int data;
Node next;
}
链表的第一个节点称之为头节点,最后一个节点称为尾节点 ,尾节点的next指针指向空。数组访问元素是利用下标来进行访问,链表访问元素得通过指针一个一个慢慢找下去,直到找到元素。
(2)双向链表
双向链表与单向链表相比,多了一个指向前一个节点指针prev。2、链表的存储方式
数组是在内存中占有了连续完成的存储空间,为顺序存储,链表和数组就不一样了,是随机存储的方式,就是哪有位置就去哪,在内存中分布在不同的位置,依靠指针next进行联系,所以看看链表相比数组是不是很自由,自由有自由的好处,但是也避免不了任何事物都存在两面性。3、链表的基本操作
(1)查找节点
链表查找节点起来那真是有一点点小麻烦,就像你在游乐园玩寻宝游戏一样,找到一个地方才有下一个宝藏地点的提示,不像数组那么灵活,链表需要一个指针一个指针去查,查找节点最坏的时间复杂度为O(n),因为只能按照顺序进行访问。(2)更新节点
不考虑查找节点的过程,其实更新节点是异常简单的,就是把旧数据用新数据替换就好了,所以时间复杂度是O(1)。(3)插入节点
链表插入节点就不像数组那么复杂还要将元素移动,链表中的元素因为实在内存空间中随机分布的,就是哪有位置就去哪,所以插入元素是很简单的。和数组一样,分为3种插入方式。
1、尾部插入
就是把链表最后一个节点的指针指向新插入的节点即可。
2、中间插入
将新插入节点的指针指向插入位置节点的指针,插入位置前置节点的next指针,指向新节点。
3、头部插入
将新插入节点的指针指向之前头部节点的指针,把新节点变为链表的头节点。。
(4)删除节点
链表删除节点就不像数组那么复杂还要将元素移动,分为3种删除方式。
1、尾部删除
就是把链表倒数第二个节点的指针指向新插入的节点即可。
2、中间删除
将要删除的节点的前置指针指向要删除节点的下一个节点即可。
3、头部删除
把链表的头节点设为原先头节点的next指针就可以。
插入和删除节点的时间复杂度为O(1);java中有垃圾自动回收的机制,只要没有外部的节点指向被删除的节点,那么,被删除的节点就会被自动回收。
下面是是实现链表的完整代码:
public class LinkList {
//头节点指针
private Node head;
//尾节点指针
private Node last;
//链表实际长度
private int size;
/*
* 链表节点
*/
private static class Node{
int data;
Node next;
Node(int data)
{
this.data = data;
}
}
/**
* 链表插入元素
* @param data 插入元素
* @param 插入位置
*/
public void insert(int data,int index) throws Exception{
if(index < 0 || index > size)
{
throw new IndexOutOfBoundsException("超出链表节点范围");
}
Node insertedNode = new Node(data);
//空链表
if(size == 0)
{
head = insertedNode;
last = insertedNode;
}
else if(index == 0)
{
//插入头部
insertedNode.next = head;
head = insertedNode;
}
else if(index == size)
{
//插入尾部
last.next = insertedNode;
last = insertedNode;
}
else
{
//插入中间
Node prevNode = get(index - 1);
insertedNode.next = prevNode.next;
prevNode.next = insertedNode;
}
size++;
}
/**
* 链表删除元素
* @param index 删除的位置
*/
public Node remove(int index) throws Exception{
if(index < 0 || index >= size)
{
throw new IndexOutOfBoundsException("超出链表结点范围");
}
Node removeNode = null;
if(index == 0)
{
removeNode = head;
head = head.next;
}
else if(index == size -1)
{
//删除尾结点
Node prevNode = get(index - 1);
removeNode = prevNode.next;
prevNode.next = null;
last = prevNode;
}
else {
//删除中间节点
Node prevNode = get(index - 1);
Node nextNode = prevNode.next.next;
removeNode = prevNode.next;
prevNode.next = nextNode;
}
size--;
return removeNode;
}
/**
* 链表查找元素
* @param index //查找的位置
*/
public Node get(int index) throws Exception
{
if(index < 0 || index >=size)
{
throw new IndexOutOfBoundsException("超出链表结点范围");
}
Node temp = head;
for(int i = 0; i < index; i++)
{
temp = temp.next;
}
return temp;
}
/**
* 输出链表
*/
public void output()
{
Node temp = head;
while(temp != null)
{
System.out.println(temp.data);
temp = temp.next;
}
}
public static void main(String[] args) throws Exception{
LinkList linkList = new LinkList();
linkList.insert(3, 0);
linkList.insert(7, 1);
linkList.insert(9, 2);
linkList.insert(5, 3);
linkList.insert(6, 1);
linkList.remove(0);
linkList.output();
}
}
4、数组和链表
数组和链表哪个好呢?当然是各有千秋。数组:查找O(1),更新O(1),删除和插入O(n)。
链表:查找O(n),更新O(1),删除和插入O(1)。
所以:数组适合的是访问较多,更新和删除少的情况,而链表呢,适合的是访问较少,删除和更新较多的情况。