链表(linked list)是一种在物理上非连续的、非顺序的存储结构,由若干节点(Node)组成.
单向链表的每一个节点又包含两部分,一部分是该节点的数据变量data,另一部分是指向下一节点的指针next.
一、节点的实现
public class Node {
private int data;
private Node next;
public Node(int data) {//构造方法用于给节点赋值
this.data=data;
}
//set,get方法省略....
}
二、单链表的定义
链表的第一个节点被称为头节点,最后一个被称为尾节点,尾节点的next指针指向空。
与数组的下标寻址方式不同,在单链表中,我们要查找某一节点只能从头节点开始,根据next指针一个一个地往下找。
在jvm中有栈区和堆区,栈区主要存引用,也就是一个指向对象的实际地址,而堆区存的才是创建的对象
单链表在堆,栈中的存在形式我们可以表示为:
在栈中其实只存在头节点,通过next指针去堆区依次找到各个节点。
三、节点的插入
单链表中节点的插入分为3种情况:
(1)头部插入
1.先让新节点等于头节点
2.再让头节点等于新的节点
(2)尾部插入
比较简单,直接让尾节点的next指向新节点即可
(3)中间插入
我们假设要插入的位置为insertNode 首先要找到insertNode的上一个节点,我们命名为preNode
先让newNode的next指向preNode的next (即insertNode),
再让preNode的next指向newNode
注意 图中第2,3步 不能颠覆 (要是先执行第3步即 先让preNode的next指向newNode,那么从头节点开始就只能找到newNode了 ,insertNode开始的后面的节点将会从链表中脱离)
四、节点的删除
节点的删除同样分为三种情况
(1)头部删除
令头节点head=head.next即可,原来的节点不可达会被jvm回收掉
(2)尾部删除
假设要删除的节点为removeNode,我们先找到removeNode的上一个节点命名为preNode
只要令preNode的next为 null ,没有外部引用指向的节点会被jvm自动回收。
(3)中间删除
假设要删除的节点为removeNode,我们先找到removeNode的上一个节点命名为preNode
首先令preNode的next 等于removeNode的next
再让removeNode的next为空
注意 ,同中间插入节点同理,图中2和3顺序不能颠覆。
具体代码实现如下:
public class ListNode {
private Node head; // 头节点
private Node last; // 尾节点
private int size; // 链表长度
// 获取链表长度
public int getSize() {
return size;
}
// 根据索引获取链表中相应的节点
public Node getIndex(int index) {
if (index < 0 || index >= size) { // 判断查询范围是否正常,注意加上index=size,当前链表为空时也能抛出异常
throw new IndexOutOfBoundsException("超出链表节点范围!");
}
Node temp = head; // 定义临时变量,从头节点开始依次往下直到找到index位置的节点
for (int i = 0; i < index; i++) {
temp = temp.getNext();
}
return temp;
}
// 插入节点
public void insert(int index, int data) {
if (index < 0 || index > size) { // 判断插入范围是否正常
throw new IndexOutOfBoundsException("超出链表节点范围!");
}
Node insertNode = new Node(data); // 定义插入节点(新节点)
if (size == 0) {// 当前链表为空
head = insertNode; // 则头节点,尾节点都是该节点
last = insertNode;
}
else if (index == 0) { // 头部插入
insertNode.setNext(head);
head = insertNode;
}
else if (index == size) {// 尾部插入
last.setNext(insertNode);
last = insertNode;
}
else {
// 其他情况均属于中间插入,注意2和3的顺序不能颠覆
Node preNode = getIndex(index - 1); // 1.获取要插入位置的上一个节点
insertNode.setNext(preNode.getNext());// 2.令新节点的下一个节点=preNode的下一个节点(该索引处的节点)
preNode.setNext(insertNode);//3.preNode的下一个节点=新节点
}
size++; // 插入节点成功后,链表长度+1
}
//删除节点
public Node remove(int index) {
if (index < 0 || index >= size) { // 判断删除范围是否正常,注意加上index=size,当前链表为空时也能抛出异常
throw new IndexOutOfBoundsException("超出链表节点范围!");
}
Node removeNode=null;//要删除的节点
if(index==0) {//头部删除
removeNode=head;//要删除的节点=head
head=head.getNext();//head=head的下一个节点
removeNode.setNext(null);
}else if(index==size-1) {//尾部删除
Node preNode=getIndex(index-1);//获取尾部节点的上一个节点
removeNode=last;
preNode.setNext(null);
last=preNode;
}else {
//中间删除,注意3和4的顺序不能颠覆
Node preNode = getIndex(index - 1); // 1.获取要删除位置的上一个节点
removeNode=getIndex(index);//2.获取要删除位置的节点
preNode.setNext(removeNode.getNext());//3.preNode的下一个节点=要删除节点的下一个节点
removeNode.setNext(null);//4.要删除节点的下一个节点=null
}
size--;// 删除节点成功后,链表长度-1
return removeNode; //返回删除的节点
}
//输出链表
public void output() {
Node temp=head;
while(temp!=null) {
System.out.print(temp.getData()+"-->");
temp=temp.getNext();
}
}