单向链表的概念
-
链表中每个数据的存储都由以下两部分组成:数据元素本身,其所在的区域称为数据域;指向直接后继元素的指针,所在的区域称为指针域,数据域和指针域结合起来就是一个节点;
-
链表的各个节点不一定是连续存储,不像顺序存储结构比如数组;
-
链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定;
上图结构在链表中称为节点。也就是说,链表实际存储的是一个一个的节点,真正的数据元素包含在这些节点中的数据域中
单向链表构成的结构如上图所示。
头节点,首元节点
其实,上图图所示的链表结构并不完整。一个完整的链表需要由以下几部分构成:
节点:链表中的节点又细分为头节点、首元节点和其他节点:
头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。
头结点中的next指针域存放的是第一个有数据的节点(首元节点)在内存中的地址
一般来说,设置一个头结点,指向首元节点,即head.next==firstNode对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
其他节点:链表中其他的节点;
因此,一个存储 {1,2,3} 的完整链表结构如下图所示:
Java代码思路分析
首先有一个节点类,用来存放数据和指针域next.
其次建立一个单向链表类,表示一个链表,链表的本质就是一个个节点串联而成形成的线性表。 单向链表类一方面存储着节点数据,另一方面对该链表中的数据节点进行增删改查等操作。
对于增删改查的操作: 基本思路都是通过一个临时节点变量,初始赋值为head头节点,然后进行循环遍历,**找到对应的节点位置,关键点是把握好要增删改的节点的前一个和后一个节点。**对前后节点的指针指向进行重新赋值,达到增删改查的操作。
代码示例
package com.ethan.linkedlist;
import org.junit.Test;
public class SingleLinkedListDemo {
//LinkedList
@Test
public void SingleLinkedListTest() {
//创建几个英雄节点
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//创建初始化单向链表
SingleLinkedList linkedList = new SingleLinkedList();
// linkedList.add(hero1);
// linkedList.add(hero4);
// linkedList.add(hero3);
// linkedList.add(hero2);
linkedList.addByOrder(hero1);
linkedList.addByOrder(hero4);
linkedList.addByOrder(hero3);
linkedList.addByOrder(hero2);
System.out.println("########按顺序添加英雄列表#########");
linkedList.showList();
HeroNode hero5 = new HeroNode(4, "李逵", "黑旋风");
linkedList.update(hero5);
System.out.println("########更新某个英雄列表#########");
linkedList.showList();
System.out.println("########删除编号为no的英雄列表#########");
linkedList.delete(4);
linkedList.showList();
}
}
class SingleLinkedList{
//初始化一个头结点,不存储数据
HeroNode head = new HeroNode(0,null,null);
//向链表末尾添加新节点
public void add(HeroNode node) {
HeroNode temp = head;
while(true) {
if(temp.next==null) {
break;
}
temp = temp.next;
}
temp.next = node;
}
//第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
public void addByOrder(HeroNode node) {
HeroNode temp = head;
boolean flag = false;//标志链表内是否有同样编号的节点
while(true) {
if(temp.next==null) {//遍历到了最后一个节点,退出
break;
}
if(temp.next.no > node.no) {
//假设插入节点为C,如果遍历找到的中间某节点A的下一个节点B的编号比我们插入的英雄节点编号大,
//那么我们就找到了要插入节点C的前一个节点为A,确定此时的temp=A
break;
}else if(temp.next.no == node.no){
//如果遍历找到的中间某节点A的下一个节点B的编号比我们插入的英雄节点编号相等
//那么无法将该节点插入到链表中,已存在该排名
flag=true;
break;
}
temp = temp.next;
}
if(flag) {
System.out.printf("该英雄编号[%d]已存在",node.no);
}else {
//最关键的步骤,先将要插入的节点C的next指针指向B,然后再将A节点的next指针指向C,顺序不能变
node.next = temp.next;
temp.next = node;
}
}
//修改英雄节点的数据(名字或者绰号,根据编号来修改
public void update(HeroNode node) {
//首先先判断链表是不是空的
if(head.next ==null) {
System.out.println("链表为空,无法修改");
return ;
}
HeroNode temp = head.next;
boolean flag = false;//标志链表内是否有同样编号的节点
while(true) {
if(temp==null) {//此时temp已经是最后一个元素的后面的null,表示已经遍历完链表了,退出
break;
}
if(temp.no == node.no) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
temp.name=node.name;
temp.nickName=node.nickName;
}else {
System.out.printf("链表内无[%d]节点!",node.no);
}
}
//删除英雄节点的数据(根据编号来删除
public void delete(int no) {
//首先先判断链表是不是空的
if(head.next ==null) {
System.out.println("链表为空,无法修改");
return ;
}
HeroNode temp = head;
boolean flag = false;//标志链表内是否有同样编号的节点
while(true) {
if(temp.next==null) {//遍历到最后一个节点
if(temp.no == no) {
flag = true;
}
break;
}
if(temp.next.no==no) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {//找到了要删除的节点的前一个节点temp
//将要删除的节点C的前一个节点A指向C的下一个节点B即可
temp.next=temp.next.next;
}else {
System.out.printf("链表内无[%d]节点!",no);
}
}
//遍历打印链表内的数据
public void showList() {
//先判断链表是否为空
if(head.next==null) {
System.out.println("链表为空~~~");
return ;
}
//头节点无数据,又无法移动,让一个中间变量进行辅助遍历
//因为链表不为空,最起码有一个节点
HeroNode temp = head.next;
while(true) {
//首先判断是否到链表尾
if(temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
class HeroNode{
public int no;//排名编号
public String name;//名字
public String nickName;//绰号
public HeroNode next;//下一个节点
public HeroNode() {
}
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickName=" + nickName + "]";
}
}