引言:以下内容均只是个人看法,并无严格经过核对,如有异议,欢迎交流,发现错误,欢迎您的及时指正,感谢
1.链表的定义
单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,链表中包含多个结点,结点之间相互连接,形成类似于铁链的结构。
(单向链表判断注意事项:单向链表中结点只能有一个后继,可以有多个前驱结点指向它)
1.1.头节点
单链表的遍历一般从第一个结点依次遍历整个链表,所以第一个结点十分重要,在本文中头节点指的是第一个存放数据的结点,与其他相关文章中关于它的定义可能不同,无需深究,理解相关操作的逻辑原理即可,本质上无较大差别。
2.java如何构建出链表
public class listNode {
public int data;
public listNode next;
public listNode(int data) {
this.data = data;
}
public int getdata() {
return data;
}
public void setdata(int data) {
this.data = data;
}
public listNode getNext() {
return next;
}
public void setNext(listNode next) {
this.next = next;
}
}
上述代码中定义了listNode这个对象作为链表中的结点对象,有data:存储的值 和next:下一个结点的地址 两个属性
下面的代码创建了分别存储1,2,3,4的四个实例结点
public static void main(String[] args) {
listNode node1=new listNode(1);
listNode node2=new listNode(2);
listNode node3=new listNode(3);
listNode node4=new listNode(4);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
}
通过栈的引用(指向堆中实例对象的地址),可以找到node1,之后通过node1的next指针可以依次访问剩下的结点
3.链表操作
3.1.链表遍历
对于单链表的遍历一定从头开始,所以知道头的位置,也就是第一个结点的地址很重要,下面代码中当传入一个head结点后,我们新建一个结点node赋值为head,让node代替head来帮助我们遍历链表,以保持head的不变,不引起head的改变而导致下次遍历找不到第一个结点的位置。
public static int getLength(listNode head){
int length=0;
listNode node=head;
while(head!=null){
length++;
node=node.next;
}
return length;
}
3.2.链表插入
3.2.1表头插入
在链表的表头插入,即让要插入结点的next指针指向当前head结点,因为插入新节点后,此时head不是第一个结点,所以需要更新head的指向,head重新指向新插入的结点,以保证head是第一个结点
3.2.2中间插入
链表的中间插入结点分为两步,一 先找到要插入的位置,二 修改相关结点的next指针的指向
例如下图,假设我们要将存放5的结点插入到下面链表的第三个结点处,即把5插到1和2中间,(请注意链表插入时不能造成无法找个某个结点的现象),这里我们先让node(5).next=node(1).next;再让node(1).next=node(5);这是正确的插入元素的指针修改顺序。
试问这个两步能否调换位置?
答案是不能,我们分析一下,先node(1).next=node(5),然后下一步应该让node(5).next指向node(2)才对,但是现在我们根本无法找到node(2),之前我们通过node(1).next找到node(2),现在node(1).next已经变成node(2),也就造成永远无法找到node(2)
3.2.3表尾插入
表尾插入的关键在于找到当前链表的最后一个结点,然后让最后一个结点的next指向要插入的结点,这里不再画图表示。
/**
*
* @param insertNode 待插入的结点
* @param position 待插入的位置
* @param head 原链表的头节点
* @return
*/
public static listNode insert(listNode insertNode , int position,listNode head){
if(head==null){
return insertNode;
}
//判断插入位置是否合法
//可以插入的最大位置
int max=getLength(head)+1;
if(position<1||position>max){
System.out.println("插入位置不正确");
return head;
}
//如果在头部插入
else if(position==1){
insertNode.next=head;
head=insertNode;
return head;
}
else{
int i=1;
listNode node=head;
//循环使node指向待插入位置的前一个结点
while(i<position-1){
node=node.next;
i++;
}
insertNode.next=node.next;
node.next=insertNode;
return head;
}
}
3.3链表删除
在进行链表结点删除操作时,在Java中,内存管理是由垃圾回收器来完成的,程序员不需要手动释放内存,这里为了完整实现删除操作,补充释放内存的操作讲述,即在进行修改相关结点的指针之前,先新建一个node指向需要删除的结点,再修改指针之后,free(node)。在下面的三种删除情况中不再出现相关实现。
3.3.1表头删除
表头删除的操作直接将head指针移位,head=head.next;
3.3.2中间删除
中间删除需要找到待删除结点的前一个结点,因为在链表中通过删除结点的前一个结点
3.3.3表尾删除
找到表尾结点的前一个结点,修改该结点的next指针
/**
*
* @param position 删除结点的位置
* @param head 头结点
* @return
*/
public static listNode deleteNode(int position,listNode head){
//用来记录当前node指向第几个结点
if(head==null){
return null;
}
//可以删除的最大位置
int max=listNode.getLength(head);
//删除位置只能使1-max,不能是max+1,因为max+1位置没有结点
if(position<1||position>max){
System.out.println("删除位置不合法");
return null;
}
//删除头节点
else if(position==1){
listNode temp=head;
head=head.next;
return temp;
}
else{
int i=1;
listNode node=head;
while(i<position){
node=node.next;
i++;
}
listNode temp =node.next;
node.next=node.next.next;
return temp;
}
}