1.什么是链表
链表与栈和队列有些不同,栈和队列都是申请一段连续的空间,然后按照顺序存储数据,链表是一种物理上非连续,非顺序的存储结构,数据元素之间的顺序是通过每个元素的指针关联的。
链表由一系列的节点组成,每个节点一般至少会包含两部分信息,一部分是元素数据本身,一部分是只想下一个元素地址的指针。它这种存储结构让链表相比于其他的数据结构来说,操作会复杂一些。
与数组相比,链表更具有优势,链表不需要数组这中存储之前需要提前设定长度的特点,在运行的时候可以根据你的需要随意添加元素,并且我们知道,计算机的存储空间并不是总是连续可用的,那么链表可以灵活地使用存储空间,还能更好地对计算机的内存进行动态管理。
链表分为两种类型:单向链表和双向链表、
2.链表的存储结构
对于链表来说,我们只需关心链表之间的关系,不需要关心聊表实际的存储位置,所以我们一般使用箭头来表示关联两个连续的元素节点。
从链表的存储结构可知,链表的每个节点包含两个部分,分别是数据(data)和指向下个节点地址的指针(next)。那么在存储了一个链表后如何找到它?这里需要一个头结点,这个头结点是一个链表的第一个节点,它的指针指向下一个节点的地址,以此类推,知道指针指向为空,便表示没有下一个元素了。
下面用代码来表示节点
package cn.csu;
public class Node {
private int data;
private Node next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
3.链表的操作
链表的操作有:创建、插入、删除、查找(输出)
我们知道插入和删除操作并不只是在表头或者表尾进行的,下面我们分别来说说插入和删除的集中情况:
插入操作
插入操作分为三种情况:分别是头插入、尾插入和中间插入
头插入的操作,实际上是增加一个新的节点,然后把新增加的节点的指针指向原来头指针指向的元素,再把头指针指向新增的节点即可
尾插入的操作,是新增一个指针为空的节点,然后把原尾指针指向节点的的指针指向新增的节点,最后把原尾指针指向新增节点
中间插入可能会稍微复杂一点,首先新增一个节点,然后把新增加的节点的指针指向插入位置的后一个位置的节点,把插入位置的钱一个节点的指针指向新增节点即可。
删除操作
删除操作和差插入操作类似,也有三种情况,分别是头删除、尾删除、中间删除
删除头元素时,先把头指针指向下一节点,然后把原头结点的指针置空
删除尾元素时,首先找到链表倒数第二个元素,然后把尾指针指向这个元素,接着把原倒数第二个元素的指针置空
删除中间元素时,首先把要删除的节点的前一个及诶单的指针指向要删除及诶单的下一节点,接着要把要删除的节点的指针置空
代码实现
上面把链表的增加和删除操作思路说完了,下面来用代码具体实现这些操作
package cn.csu;
public class Link {
private int size = 0;
private Node first;
private Node last;
/**
* 初始化
*/
public Link(){
}
/**
* 表尾部插入元素
* @param data
*/
public void addLast(int data){
if(size ==0){
//如果链表为空,初始化前后元素
fillStart(data);
}else{
Node node = new Node();
node.setData(data);
last.setNext(node);
last = node;
}
size++;
}
/**
* 在链表插入第一个元素时,头、尾元素都是同一个元素
* @param data
*/
public void fillStart(int data){
first = new Node();
first.setData(data);
last = first;
}
/**
* 链表头部插入元素
* @param data
*/
public void addFirst(int data){
if(size == 0){
fillStart(data);
}else{
Node node = new Node();
node.setData(data);
node.setNext(first);
first = node;
}
size++;
}
/**
* 在链表的指定位置后面插入
* @param data 需要插入的数据
* @param index 指定位置的下标(从0开始)
*/
public void add(int data,int index){
if(size > index){
if(size == 0){
fillStart(data);
}else if(index == 0){//在表头插入元素
addFirst(data);
}else if(size == index +1){//在表尾插入元素
addLast(data);
}else{
//根据指定的index获取元素
Node temp = get(index);
Node node = new Node();
node.setData(data);
node.setNext(temp.getNext());
temp.setNext(node);
size++;
}
}else{
throw new IndexOutOfBoundsException("给定index超过了链表的长度");
}
}
/**
* 删除表头元素
*/
public void removeFirst(){
if(size == 0){
throw new IndexOutOfBoundsException("链表没有元素");
}else if(size ==1){
//链表只剩下一个元素,清除first和last
clear();
}else{
Node temp = first;
first = temp.getNext();
temp = null;//回收temp
size--;
}
}
/**
* 删除链表尾部元素
*/
public void removeLast(){
if(size == 0){
throw new IndexOutOfBoundsException("链表没有元素");
}else if(size ==1){
clear();
}else{
//获取倒数第二个元素
Node temp = get(size-2);
//直接把倒数第二个元素的下一个节点置空,直接删除尾元素
temp.setNext(null);
size--;
/**
* 这里说明以下,也可以用以下代码
Node temp = get(size-2);//获取倒数第二个元素
last =temp;//把尾指针指向这个元素
temp = null;//回收temp
size--;
*/
}
}
/**
* 删除指定下标的元素
* @param index
*/
public void remove(int index){
if(size ==0){
throw new IndexOutOfBoundsException("链表中没有元素");
}else if(size == 1){
clear();
}else{
if(index ==0){
removeFirst();
}else if(size == index+1){
removeLast();
}else{
//获取要删除元素的前一个元素
Node temp = get(index-1);
Node next = temp.getNext();//获取要被删除的元素
temp.setNext(next.getNext());
next = null;//回收被删除的元素
size--;
}
}
}
/**
* 获取指定下标元素
* @param index
* @return
*/
public Node get(int index){
Node temp = first;
for(int i=0;i<index;i++){
temp = temp.getNext();
}
return temp;
}
/**
* 打印所有元素
*/
public void printAll(){
Node temp = first;
System.out.println(temp.getData());
for(int i =0;i<size -1;i++){
temp = temp.getNext();
System.out.println(temp.getData());
}
}
public int size(){
return size;
}
/**
* 在元素只有一个时清除first和last元素
*/
public void clear(){
first = null;
last = null;
size =0;
}
}