1. 链表的介绍
链表是有序的列表,但是它在内存中的存储如下:
1. 链表是以节点的方式来储存的,是链式存储。
2 . 每个节点包括 data域、 next域:指向下一个节点。
3 . 链表的每个节点不一定是连续存储的,如上图所示。
4 链表分为带头节点的链表 和不带头节点的链表。
带头节点的单链表的逻辑结构示意图如下:
2. 单链表的应用
使用带头节点的单向链表实现: 对水浒传的英雄排行榜进行增删改查。
第一 种方式在添加英雄时,直接添加到链表的尾部。
具体思路:
1. 首先添加一个head头节点,作用就是表示单链表的头。
2. 后面每添加一个节点,就直接加入到链表的后面。
3. 通过一个辅助变量(指针)遍历整个链表。
代码实现
public class Linkedlist {
public static void main(String[] args) {
//测试。先创建节点
Lnode lnode1=new Lnode(1,"宋江");
Lnode lnode2=new Lnode(2,"卢俊义");
Lnode lnode3=new Lnode(3,"吴用");
Lnode lnode4=new Lnode(4,"林冲");
//创建链表
SingleLinkedList singleLinkedList=new SingleLinkedList();
//向链表中接入元素
singleLinkedList.add(lnode1);
singleLinkedList.add(lnode2);
singleLinkedList.add(lnode3);
singleLinkedList.add(lnode4);
//显示链表
singleLinkedList.list();
}
}
//定义SingleLinkedList单链表,管理我们的英雄
class SingleLinkedList{
//初始化一个头节点,头节点不要动,不存放具体的数据
private Lnode head=new Lnode(0,"");
//添加节点到单向链表
/**
* 思路:在不要考虑节点编号顺序时,
* 1.找到当前链表的最后一个节点
* 2 将最后节点的next指向新的节点
* @param lnode
*/
public void add(Lnode lnode){
//因为链表的头节点不能动,因此需要一个辅助的变量(指针)tmp.
Lnode temp=head;
//遍历链表,找到链表的最后一个节点
while (true){
if(temp.next==null){//链表的最后
break;
}
//如果没有遍历到链表的最后
temp=temp.next;
}
//退出while循环时,已经遍历到了链表的最后,将链表最后的节点指向新的节点就是添加了数据
temp.next=lnode;
}
//遍历链表
public void list(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为链表的头节点不能动,因此需要一个辅助的变量(指针)tmp.
Lnode temp=head.next;//tmp指向链表的第一个节点
while (true){
if(temp==null){//判断链表是否到了最后一个
break;
}
//输出节点信息
System.out.println(temp);
//将tmp后移
temp=temp.next;
}
}
}
//定义节点
class Lnode{
public int no;
public String name;
public Lnode next;//指向下一个节点
public Lnode(int no, String name) {//构造器时候,不要降next参数传入
this.no = no;
this.name = name;
}
//为了显示方法,重新定义toString
@Override
public String toString() {
return "Lnode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
第二种方式添加英雄时,根据排名将英雄插入到指定的位置(如果已经有了这个排名,则添加失败,给出提示信息)
思路分析:
需要按照编号的顺序添加英雄。
1. 首先找到新添加的节点的位置,通过辅助变量指针,通过遍历的方式来寻找
2. 新的节点.next = temp.next;
3. temp.next= 新的节点 。
代码实现:
public class Linkedlist {
public static void main(String[] args) {
//测试。先创建节点
Lnode lnode1=new Lnode(1,"宋江");
Lnode lnode2=new Lnode(2,"卢俊义");
Lnode lnode3=new Lnode(3,"吴用");
Lnode lnode4=new Lnode(4,"林冲");
//创建链表
SingleLinkedList singleLinkedList=new SingleLinkedList();
//显示链表
singleLinkedList.list();
//向链表中接入元素
singleLinkedList.addByOrder(lnode1);
singleLinkedList.addByOrder(lnode4);
singleLinkedList.addByOrder(lnode3);
singleLinkedList.addByOrder(lnode2);
System.out.println("按照顺序插入后");
singleLinkedList.list();
}
}
//定义SingleLinkedList单链表,管理我们的英雄
class SingleLinkedList{
//初始化一个头节点,头节点不要动,不存放具体的数据
private Lnode head=new Lnode(0,"");
//添加节点考虑节点的顺序,根据排名的将英雄插入到指定的位置,如果已有排民,则显示添加失败
public void addByOrder(Lnode lnode){
// 因为链表的头节点不能动,因此需要一个辅助的变量(指针)tmp.
//我们遍历的位置是要遍历插入节点的前一个节点,这样才能插入
Lnode temp=head;
Boolean flag=false;//flag标志添加的编号是否存在,默认为false
while (true){
if(temp.next==null){//这是已经遍历到了链表的最后
break;
}
if(temp.no==lnode.no){//要添加的节点的编号已经存在了
flag=true;
break;
}
if(temp.next.no>lnode.no){//要添加的位置找到了
break;
}
temp=temp.next;//后移,遍历当前链表
}
if(flag){//不能添加,说明编号已经存在了
System.out.printf("要插入的节点编号 %d 已经存在了,不能加入\n",lnode.no);
}else {
//插入到链表,temp的后面
lnode.next=temp.next;//新的节点指向temp后面的节点
temp.next=lnode;//temp节点指向新的节点
}
}
//遍历链表
public void list(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为链表的头节点不能动,因此需要一个辅助的变量(指针)tmp.
Lnode temp=head.next;//tmp指向链表的第一个节点
while (true){
if(temp==null){//判断链表是否到了最后一个
break;
}
//输出节点信息
System.out.println(temp);
//将tmp后移
temp=temp.next;
}
}
}
//定义节点
class Lnode{
public int no;
public String name;
public Lnode next;//指向下一个节点
public Lnode(int no, String name) {//构造器时候,不要降next参数传入
this.no = no;
this.name = name;
}
//为了显示方法,重新定义toString
@Override
public String toString() {
return "Lnode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
第三: 修改节点的功能
具体思路:
1. 首先找到该节点,通过遍历
2. temp. name=newlnode.neme
代码实现:
//修改节点的信息,根据no的编号来修改,
public void update(Lnode newlnode){
//判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//根据编号no找到需要修改的节点
//定义一个辅助变量
Lnode temp=head.next;
boolean flag=false;//表示是否找到该节点
while (true){
if(temp==null) {//已经遍历完了链表
break;
}
if(temp.no==newlnode.no){
//找到需要修改的
flag=true;
break;
}
temp=temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag){
temp.name=newlnode.name;
}else{
System.out.printf("没有找到编号 %d 的节点,不能修改",newlnode.no);
}
}
第四: 删除节点。
思路分析 :
从单链表中删除节点。
1. 先找到需要删除节点的前一个节点temp。
2 temp.next = temp.next.next 。
3. 被删除的节点就不会指向其他节点 会被垃圾回收机制回收。
代码实现:
//删除节点
//1.head不能动,因为我们需要一个temp辅助节点找到待删除节点的前一个节点
//2 因此 我们 在寻找删除节点时,是用temp.next.no和需要删除的节点的no进行比较。
public void del(int no){
Lnode temp=head;
Boolean flag=false;//标志找到待删除的节点
while (true){
if(temp.next==null){//已经遍历到链表的最后了
break;
}
if(temp.next.no==no){
//找到了要删除的节点temp
flag=true;
break;
}
temp=temp.next;//temp后移,遍历
}
//判断flag
if(flag){
//找到了要删除的节点
temp.next=temp.next.next;//删除节点
}else {
System.out.printf("要删除的 %d 节点不存在\n",no);
}
}