链表是有序的列表,它在内存中的存储方式如下图所示。
头指针为head,指向的地址是150,而150地址指向的节点data域是a1,next域是110;110指向的节点的data域是a2,next域是130;…;170指向的节点的data域是a5,next域为null。
地址 | data域 | next域 | |
---|---|---|---|
110 | a2 | 130 | |
头指针 150 | 120 | ||
130 | a3 | 180 | |
140 | |||
150 | a1 | 110 | |
160 | |||
170 | a5 | null | |
180 | a4 | 170 | |
190 |
1、链表是以节点的方式进行存储的
2、每一个节点都包含data域:用来存放数据、next域:用来指向下一个节点
3、链表的各个节点之间不一定是连续存储的
4、链表分为带头结点的链表和不带头节点的链表,根据实际需求进行使用。
public class SingleLinkedListDemo {
public static void main(String[] args) {
// 测试
// 创建链表
SingleLinkedList linkedList = new SingleLinkedList() ;
// 创建节点对象
HeroNode node1 = new HeroNode(1,"宋江","及时雨");
HeroNode node2 = new HeroNode(2,"卢俊义","玉麒麟");
HeroNode node3 = new HeroNode(3,"吴用","智多星");
HeroNode node4 = new HeroNode(4,"林冲","豹子头");
// 向链表中添加节点
linkedList.addNode(node1);
linkedList.addNode(node2);
linkedList.addNode(node3);
linkedList.addNode(node4);
// 遍历链表
linkedList.showList();
}
}
// 定义heroNode表示节点对象
class HeroNode {
int no ; // 编号
String name ; // 名字
String nickName ; // 昵称
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 {
// 初始化头节点,头节点不存放任何数据
HeroNode head = new HeroNode(0,"","") ;
// 添加节点到单向链表
public void addNode(HeroNode heroNode) {
// 创建一个辅助节点遍历链表
HeroNode temp = head ;
// 遍历单链表,找到单链表的最后一个节点
while (temp.next != null) { // 当没找到最后一个节点时
// 将temp指向下一个节点的next域
temp = temp.next ;
}
// 将最后一个节点的next域改为传入的节点
temp.next = heroNode ;
}
// 遍历并输出链表中的节点数据
public void showList() {
// 判断链表是否为空
// 为空
if(head.next == null) {
System.out.println("链表为空");
return;
}
// 不为空
// 创建辅助节点
HeroNode temp = head ;
// 判断当前节点后是否存在其他节点
// 存在
while (temp.next != null) {
// 指向下一个节点
temp = temp.next ;
// 输出该节点
System.out.println(temp);
}
}
}
代码
// 添加节点到单向链表(头插法)
public void addNode(HeroNode heroNode) {
// 创建一个辅助节点遍历链表
HeroNode temp = head ;
/*
要想将节点添加到头节点后,
首先要将该节点的next域指向head的next域
然后需要head节点的next域指向该节点
*/
// 将插入的节点的next域指向head的next域
heroNode.next = temp.next ;
temp.next = heroNode ;
}
代码
public class SingleLinkedListDemo {
public static void main(String[] args) {
// 测试
// 创建链表
SingleLinkedList linkedList = new SingleLinkedList() ;
// 创建节点对象
HeroNode node2 = new HeroNode(2,"卢俊义","玉麒麟");
HeroNode node1 = new HeroNode(1,"宋江","及时雨");
HeroNode node4 = new HeroNode(4,"林冲","豹子头");
HeroNode node3 = new HeroNode(3,"吴用","智多星");
// 向链表中添加节点
// linkedList.addNode(node1);
// linkedList.addNode(node2);
// linkedList.addNode(node3);
// linkedList.addNode(node4);
linkedList.addByNo(node1);
linkedList.addByNo(node2);
linkedList.addByNo(node3);
linkedList.addByNo(node4);
// 遍历链表
linkedList.showList();
}
}
// 根据英雄的no大小进行排序添加
public void addByNo(HeroNode heroNode) {
// 创建辅助节点
HeroNode temp = head ;
// 根据no进行插入节点时需要先找出其应该插入的位置,所以我们要先对链表进行遍历并比较no
// 不为空
while (temp.next != null) {
if(temp.next.no > heroNode.no) { // 当当前节点的no大于要插入节点的no时,直接在其后插入
heroNode.next = temp.next ;
temp.next = heroNode ;
}else if(temp.next.no == heroNode.no) { // 当no相同时
System.out.println("该英雄已存在");
return;
}
// 节点后移
temp = temp.next ;
}
// 为空直接插到head后
temp.next = heroNode ;
}
/**
* 传入一个节点,根据no找链表中是否存在该节点,如果存在就进行修改,不存在就提示用户不存在
* @param heroNode 传入的节点对象
*/
public void updateNode(HeroNode heroNode) {
// 创建辅助节点
HeroNode temp = head ;
// 判断链表是否为空或到达链表的尾部
while (temp.next != null) {
if(temp.next.no == heroNode.no) { // 当找到该节点时
temp.next.name = heroNode.name ;
temp.next.nickName = heroNode.nickName ;
return;
}
// 节点后移
temp = temp.next ;
}
System.out.println("该节点不存在");
}
/**
* 传入一个int类型的参数,到链表中进行查找,若该链表存在则将其删除
* @param no 英雄编号
*/
public void deleteNode(int no) {
// 创建辅助节点
HeroNode temp = head ;
// 判断链表是否为空或到达链表的尾部
while (temp.next != null) {
if(temp.next.no == no) {
temp.next = temp.next.next ;
return;
}
// 节点后移
temp = temp.next ;
}
System.out.println("该节点不存在");
}
单向链表和双向链表的对比:
1、查找的方向只能是一个方向,而双向链表可以向前或向后查找
2、单向链表不能自我删除,需要靠辅助结点,而双向链表可以实现自我删除
双向链表的遍历、添加、修改、删除的操作思路
1、遍历双向链表时,可以从前遍历,也可以从后遍历,其遍历方法和单向链表一样。
2、添加节点(尾插)
(1)先找到链表的最后一个节点temp
(2)temp.next = newNode
,将最后一个节点的next指向要插入的节点newNode
(3)newNode.pre = temp
,将新节点的pre指向temp
3、修改节点时,只需要查找到这个节点进行data的改变即可
4、删除节点
(1)在双向链表中,节点可以实现自我删除
(2)找到要删除的节点temp
(3)temp.pre.next = temp.next
,将要删除的节点的前一个节点的next指向要删除节点的后一个节点
(4)temp.next.pre = temp.pre
,将要删除的节点的后一个节点的pre指向要删除节点的前一个节点
public class DoubleLinkedListDemo {
public static void main(String[] args) {
// 创建一个双向链表
DoubleLinkedList doubleLinkedList = new DoubleLinkedList() ;
// 创建节点
LinkedNode node1 = new LinkedNode(1) ;
LinkedNode node2 = new LinkedNode(2) ;
LinkedNode node3 = new LinkedNode(3) ;
LinkedNode node4 = new LinkedNode(4) ;
// 将节点添加到双向链表
doubleLinkedList.addNode(node1);
doubleLinkedList.addNode(node2);
doubleLinkedList.addNode(node3);
doubleLinkedList.addNode(node4);
// 遍历双向链表
doubleLinkedList.listNode();
System.out.println("----------------------");
// 修改data=2的节点的data为5
doubleLinkedList.updateNode(2 , 5);
doubleLinkedList.listNode();
System.out.println("----------------------");
// 删除data=3的节点
doubleLinkedList.deleteNode(3);
doubleLinkedList.listNode();
}
}
// 创建一个节点类
class LinkedNode {
int no ; // data属性
LinkedNode pre ; // 指向前一个节点
LinkedNode next ; // 指向后一个节点
public LinkedNode(int no) {
this.no = no;
}
@Override
public String toString() {
return "LinkedNode{" +
"no=" + no +
'}';
}
}
// 创建一个双向链表的类
class DoubleLinkedList {
// 初始化一个头节点,data为-1
LinkedNode head = new LinkedNode(-1) ;
// 返回头节点
public LinkedNode getHead() {
return head ;
}
// 遍历双向链表
public void listNode() {
// 当链表为空时
if(head.next == null) {
System.out.println("链表为空");
return;
}
// 链表不为空时
// 创建辅助节点
LinkedNode temp = head ;
while (temp.next != null) {
temp = temp.next ; // 节点后移
System.out.println(temp); // 输出
}
}
/**
* 添加节点,尾插法
* @param linkedNode 要添加的节点
*/
public void addNode(LinkedNode linkedNode) {
// 创建一个辅助节点
LinkedNode temp = head ;
// 判断链表是否到最后
// 没有到最后一个节点时
while (temp.next != null) {
temp = temp.next ; // 节点后移
}
// 到最后一个节点时
temp.next = linkedNode ;
linkedNode.pre = temp ;
}
// 修改节点
public void updateNode(int no , int n) {
// 当链表为空时
if(head.next == null) {
System.out.println("链表为空");
return;
}
// 创建一个辅助节点
LinkedNode temp = head ;
// 遍历查找链表中是否存在no=n的节点
while (temp.next != null) {
temp = temp.next ;
if(temp.no == no) {
temp.no = n;
return;
}
}
System.out.println("该节点不存在");
}
// 删除节点
public void deleteNode(int n) {
// 当链表为空时
if(head.next == null) {
System.out.println("链表为空");
return;
}
// 创建一个辅助节点
LinkedNode temp = head ;
// 遍历查找链表中是否存在no=n的节点
while (temp.next != null) {
temp = temp.next;
if(temp.no == n) {
temp.pre.next = temp.next ;
temp.next.pre = temp.pre ;
return;
}
}
System.out.println("该节点不存在");
}
}