这是我参与8月更文挑战的第七天,活动详情查看:8月更文挑战
链表介绍
- 链表是一系列的存储数据元素的单元通过指针串接起来形成的,因此每个单元至少有两个域,一个域用于数据元素的存储,另一个或两个域是指向其他单元的指针。这里具有一个数据域和多个指针域的存储单元通常称为节点(node)。
- 链表的第一个节点和最后一个节点,分别称为链表的头节点和尾节点。尾节点的特征是其 next 引用为空(null)。链表中每个节点的 next 引用都相当于一个指针,指向另一个节点,借助这些 next 引用,我们可以从链表的头节点移动到尾节点。
链表是有序的列表,但是他在内存中式存储如下:
小结
1)链表是以节点的方式来存储,是链式存储
2)每个节点包含data域,next域(指向下一个节点)
3)如图所示,发现链表的各个节点不一定是连续存储
4)链表分带头结点的链表和没有头节点的链表,根据实际的需求来确定
链表数据结构中主要包含单向链表、双向链表及循环链表:
-------------------------------今天我们先学习单向链表------------------------------------
单链表介绍
单链表(带头结点)逻辑结构示意图如下:
单向链表中,每个节点的数据域都是通过一个 Object 类的对象引用来指向数据元素的,与数组类似,单向链表中的节点也具有一个线性次序,即如果节点 a1 的 next 引用指向节点 a2,则 a1 就是 a2 的直接前驱,a2 是 a1 的直接后续。只能通过前驱节点找到后续节点,而无法从后续节点找到前驱节点。
特点
- 数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素。每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。
- 逻辑上相邻的节点物理上不必相邻。
缺点
1、比顺序存储结构的存储密度小 (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多)。
2、查找结点时链式存储要比顺序存储慢(每个节点地址不连续、无规律,导致按照索引查询效率低下)。
优点
1、插入、删除灵活 (不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)。
2、有元素才会分配结点空间,不会有闲置的结点。
代码实现
//先初始化一个头结点,头结点不要动,不存放具体数据
private HeroNode head= new HeroNode(0,"","");
//添加节点到单向链表
//思路,当不考虑编号顺序时
//1.找到当前链表的最后节点
//2.将最后这个节点的next指向新的节点
public void add(HeroNode heroNode){
//因为head节点不能动,因此我们需要一个辅助遍历temp
HeroNode temp = head;
//遍历链表,找到最后
while(true){
if(temp.next == null){
break;
}
//如果没有找到最后,temp就后移
temp = temp.next;
}
// while(temp.next != null){
// temp = temp.next;
// }
//如果这样写,temp就直接成了temp的next,那退出循环时指向的就是temp的next的next
// 后面描述如果用这种方式来遍历链表,结果会不同!!!!!
//当退出while循环时,temp就指向了链表的最后
//将最后这个节点的next指向新的节点
temp.next = heroNode;
}
//显示链表【遍历】
public void list(){
//判断链表是否为空
if(head.next == null){
System.out.println("链表为空");
return ;
}
//因为头结点不能动,我们需要一个辅助变量来遍历
HeroNode temp = head.next;
// while(temp != null){
while(true){
if(temp == null){
break;
}
//输出节点信息
System.out.println(temp);
//并将temp后移,不然是死循环!!
temp = temp.next;
}
}
}
class HeroNode{
private int no;
private String name;
private String nickname;
public HeroNode next;//指向下一个节点
//构造器
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
//为了显示方法,用toString
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
测试数据
public class SingleLinkedListDemo {
public static void main(String[] args) {
HeroNode hero1 = new HeroNode(1,"宋江","及时雨");
HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");
HeroNode hero3 = new HeroNode(3,"吴用","智多星");
HeroNode hero4 = new HeroNode(4,"林冲","豹子头");
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(hero1);
singleLinkedList.add(hero2);
singleLinkedList.add(hero3);
singleLinkedList.add(hero4);
//显示链表
singleLinkedList.list();
}
}
测试结果
若当中遍历链表时用以下方法
// while(temp.next != null){
// temp = temp.next;
// }
//如果这样写,temp就直接成了temp的next,那退出循环时指向的就是temp的next的next