单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
带头结点的单链表和不带头结点的单链表
一、两者区别:
1、不带头结点的单链表对于第一个节点的操作与其他节点不一样,需要特殊处理,这增加了程序的复杂性和出现bug的机会,因此,通常在单链表的开始结点之前附设一个头结点。
2、带头结点的单链表,初始时一定返回的是指向头结点的地址,所以一定要用二维指针,否则将导致内存访问失败或异常。
3、带头结点与不带头结点初始化、插入、删除、输出操作都不样,在遍历输出链表数据时,带头结点的判断条件是while(head->next!=NULL),而不带头结点是while(head!=NULL),虽然头指针可以在初始时设定,但是如1所述,对于特殊情况如只有一个节点会出现问题。
下面就是手写带头结点的单链表代码
定义存储的数据类型
这里定义一个英雄,相当于节点,节点中指向下一个节点,英雄中存在下一个英雄。
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next;
// 构造器
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 + "]";
}
}
然后手写链表的实现
首先是添加方法 尾部添加和有序添加
// 定义一个SingleLinkedList管理英雄人物
class SingleLinkedList {
// 先初始化一个头结点 头结点不要动
private HeroNode head = new HeroNode(0, "", "");
// 添加方法 添加到链表的最后
// 不考虑编号的顺序时,找到当前链表中下一个next为null的英雄则为最后一个英雄
// 将最后英雄的next指向新节点
public void add(HeroNode heroNode) {
// head节点不能动,因此我们需要一个辅助变量
// temp相当于指针,遍历每个变量
HeroNode temp = head;
while (true) {
// 什么时候说明链表到最后了就结束
if (temp.next == null) {
break;
}
// 如果不是最后一个就后移
temp = temp.next;
} // 只有temp指向最后一个的时候 才会退出循环
// 让最后一个元素的下一个指向新添加的元素结束
temp.next = heroNode;
}
// 第二种方式添加 根据序号添加
// 如果排名存在则显示添加失败,并给出提示
public void addByOrder(HeroNode heroNode) {
// head节点不能动,因此我们需要一个辅助变量
// 因为单链表 因此我们找的temp是添加位置的前一个节点 否则插入不了
HeroNode temp = head;
boolean flag = false;
// 标志添加的节点编号是否存在,默认为false
// break会跳出循环
while (true) {
if (temp.next == null) {
// 说明temp已经在链表的最后
break;
}
// 下一个节点的序号是否比需要添加的节点大
if (temp.next.no > heroNode.no) {
break;
} else if (temp.next.no == heroNode.no) {
flag = true;// 说明编号存在
break;
}
// 往后找出当前节点的下一个节点比需要添加的节点大的节点
temp = temp.next;
}
// 判断flag的值 如果为true 证明编号存在了
if (flag) {
System.out.println("编号存在,不能加入");
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
循环遍历链表的方法
// 显示链表[遍历]
public void list() {
// 通过辅助变量 遍历链表
HeroNode temp = head;
while (true) {
// 头结点不能动 辅助变量来遍历
// 判断链表到最后结束
if (temp.next == null) {
break;
}
System.out.println(temp.next);
temp = temp.next;
}
}
修改链表中数据的方法
// 根据heroNode的no节点来修改
public void update(HeroNode heroNode) {
// 判断链表是否是空的
if (head.next == null) {
System.out.println("链表是空的");
return;
}
// 找到需要修改的节点,根据no找
// 定义辅助变量查找
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.no == heroNode.no) {
//找到了
flag = true;
break;
}
if (temp.next == null) {
// 链表遍历结束
break;
}
temp = temp.next;
}
//找到了就修改
if(flag) {
temp.name=heroNode.name;
temp.nickname=heroNode.nickname;
}else {
//flag仍然等于false 表示没有找到这个节点
System.out.println("没有找到编号等于这个的节点"+heroNode.no);
}
}
删除链表中数据的方法
//删除节点 找到需要删除的节点的前一个节点
//被删除的节点没有任何引用 会被垃圾回收机制回收
public void delete(int no) {
// 通过辅助变量 遍历链表
HeroNode temp = head;
//是否找到需要删除的
boolean flag = false;
while(true){
if (temp.next==null) {
break;
}
if(temp.next.no==no) {
//找到了待删除节点的前一个节点
flag = true;
break;
}
temp=temp.next;
}
if(flag) {
//找到了可以删除
temp.next=temp.next.next;
}else {
System.out.println("没有找到待删除的节点");
}
}