链表介绍
链表是有序的列表,但是它在内存中是存储如下

小结上图:
- 链表是以节点的方式来存储,是链式存储
- 每个节点包含data域,next域:指向下一个节点.
- 如图:发现链表的各个节点不一定是连续存储.
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
单向链表
介绍
单链表(带头结点)逻辑结构示意图如下

案例:实现链表 增 删 改 查 操作
遍历:
1. 通过一个辅助变量遍历,帮助遍历整个链表 temp
增:
1、直接添加到链表尾部

说明:
- 先创建一个head 头节点,不存放具体的数据,作用就是表示单链表的表头
- 后面每添加一个节点,就直接添加到链表的最后
最后一个.next = 新结点
2、添加到链表中间(如排序)

说明:
- 首先找到新添加的节点的位置, 是通过辅助变量(temp), 遍历来搞定
- 新的节点.next = temp.next
- temp.next = 新的节点
删:

说明:
- 先找到需要删除的这个节点的前一个节点 temp
- temp.next = temp.next.next
- 被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收
改:
1、先找到该节点,通过遍历
2、修改节点中的数据
查:
通过遍历找到该结点
代码
【节点数据】
/* 链表数据 */
class Student{
int id;
String name;
Student next; // 指向下一个节点
public Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student[ 学号 = " + id + ", 姓名 = " + name + ']';
}
}
【单链表】
/* 单链表 */
class MyLinkedList{
//初始化头结点,不存放具体数据
private Student head = new Student(0,"");
//遍历链表
public void info(){
//判断是否为空
if (head.next == null){
System.out.println("单向链表为空。");
return;
}
//遍历
Student temp = head.next; //辅助变量
while (true){
if (temp == null){//判断是否是最后一个
break;
}
//输出
System.out.println(temp);
//temp 后移
temp = temp.next;
}
}
//添加结点,不考虑编号(id)顺序
public void add(Student student){
Student temp = head;//head 头结点不能动,temp 辅助遍历
while (true){
//找到最后一个
if (temp.next == null){ //链表的最后一个为空
break;
}
//没找到, temp后移
temp = temp.next;
}
//将最后节点的 next 指向新的节点
temp.next = student;
}
//添加结点,考虑编号(id)顺序
public void addById(Student student){
Student temp = head; //辅助变量
boolean isFalse = false;//添加的编号(id)是否存在
while (true){
if (temp.next == null){ //链表最后
break;
}
if (temp.next.id > student.id){ //编号比较,位置找到
break;
}
if (temp.next.id == student.id){//要新添加的id值已经存在
isFalse = true;//表示编号存在
break;
}
//temp 后移
temp = temp.next;
}
//判断id值是否存在
if (isFalse){//存在
System.out.println("准备添加的数据已存在,学号为 = " + student.id);
}else{//不存在
//插入 - 两个指向的顺序不要换
student.next = temp.next; //①
temp.next = student; //②
}
}
//删 - 根据id值来删除节点
public void delete(int id){
Student temp = head; //辅助变量
boolean flag = false; //是否找到要删除的节点
while (true){
if (temp.next == null){ //链表最后
break;
}
if (temp.next.id == id) {//找到
flag = true;
break;
}
//temp 后移
temp = temp.next;
}
//判断 是否找到
if (flag){
//找到 - 删除
temp.next = temp.next.next;
}else {
//没找到
System.out.println("要删除的节点学号("+ id +")不存在");
}
}
//改 - 根据id值来修改节点数据
public void upadte(Student student,String name){
//是否为空
if (head.next == null){
System.out.println("链表为空");
return;
}
Student temp = head.next;
boolean flag = false; //是否找到修改的节点
while (true){
if (temp == null) {//链表最后
break;
}
if (temp.id == student.id){ //找到
flag = true;
break;
}
temp = temp.next;//后移
}
if (flag){
temp.name = name;
}else {
System.out.println("没有找到要修改的节点,即不存在学号为:" + student.id + "的学生");
}
}
//查 - 根据id值来查找节点
public void select(int id){
//判断是否为空
if (head == null){
System.out.println("链表为空");
return;
}
Student temp = head.next;
while (true){
if (temp == null){
break;
}
if (temp.id == id){
System.out.println("找到了");
System.out.println("学号 = " + temp.id + ";姓名 = " + temp.name);
return;
}
temp = temp.next;
}
System.out.println("没有找到学号为" + id + "的学生");
}
}
【测试】
/* 测试*/
public class SingleLinkedList {
public static void main(String[] args) {
//创建链表
MyLinkedList list = new MyLinkedList();
//创建节点
Student st1 = new Student(1,"小小");
Student st2 = new Student(2,"大大");
Student st3 = new Student(3,"芊芊");
Student st4 = new Student(4,"后后");
Student st5 = new Student(5,"佐佐");
//添加
list.addById(st5);
list.addById(st1);
list.addById(st2);
list.addById(st4);
list.addById(st3);
//删除
list.delete(1);
list.delete(6);
//修改
list.upadte(st2,"星星");
//查
list.select(2);
//遍历
list.info();
}
}
双向链表
介绍
管理单向链表的缺点分析:
1)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
2)单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除。

实现双向链表 增 删 改 查 操作
【增】
方式一: 默认添加到双向链表的最后

temp.next = newStudent; newStudent.pre = temp;
方式二:按编号顺序添加

添加在链表最后:
temp.next = newStudent; newStudent.pre = temp;
添加在中间:
1、newStudent.next = temp.next; temp.next.pre = newStudnet;
2、temp.next = newStudent; newStudent.pre = temp;
【删】

1、删除的是中间的节点
temp.pre.next = temp.next; temp.next.pre = temp.pre;
2、删除的是最后一个节点
temp.pre.next = temp.next;
【改】
1、先找到该节点,通过遍历
2、更改节点中的数据
【查】
通过遍历找到该结点
代码
【节点数据】
class MyStudent{
int id;
String name;
MyStudent next; // 指向下一个节点
MyStudent pre;//指向前一个节点
public MyStudent(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student[ 学号 = " + id + ", 姓名 = " + name + ']';
}
}
【双向链表】
class DoubleList{
MyStudent head = new MyStudent(0,"");//头结点
//遍历
public void show(){
if (head.next == null){
System.out.println("链表为空");
return;
}
MyStudent temp = head.next;
while (temp != null){
System.out.println(temp);//输出
temp = temp.next;//后移
}
}
//添加:不考虑编号顺序
public void add(MyStudent student){
MyStudent temp = head;
//找到最后一个节点
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
//添加到最后
temp.next = student;
student.pre = temp;
}
//添加:考虑编号顺序
public void addById(MyStudent student){
MyStudent temp = head;
while (true){
//添加在链表最后
if (temp.next == null){
temp.next = student;
student.pre = temp;
break;
}
//添加在中间
if (student.id < temp.next.id){
student.next = temp.next;
temp.next.pre = student;
temp.next = student;
student.pre = temp;
break;
}
//要添加的编号已存在
if (student.id == temp.id){
System.out.println("添加编号" + student.id + "已存在,添加失败");
break;
}
temp = temp.next;
}
}
//修改
public void update(int index,String name){
if (head.next == null){
System.out.println("链表为空");
return;
}
MyStudent temp = head.next;
while (true){
//遍历完整个链表
if (temp == null){
System.out.println("不存在这个节点编号");
break;
}
// 修改
if (index == temp.id){
temp.name = name;
break;
}
temp = temp.next;
}
}
//删除
public void delete(int index){
if (head.next == null){
System.out.println("链表为空");
return;
}
MyStudent temp = head.next;
while (true){
if (temp == null){
System.out.println("不存在这个节点");
return;
}
if (temp.id == index){
break;
}
temp = temp.next;
}
//删除
//1、删除中间节点 2、删除最后节点
if (temp.next == null){ //最后
temp.pre.next = temp.next;
}else {//中间
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
}
}
//查找
public void select(int index){
if (head.next == null){
System.out.println("链表为空");
return;
}
MyStudent temp = head.next;
while (true){
if (temp == null){
System.out.println("没找到编号为" + index + "的学生信息");
return;
}
if (temp.id == index){
break;
}
temp = temp.next;
}
System.out.println("找到编号为" + temp.id +"的信息;" + temp);
}
}
【测试】
public class DoubleLinkedList {
public static void main(String[] args) {
DoubleList list = new DoubleList();
MyStudent st1 = new MyStudent(1,"小小");
MyStudent st2 = new MyStudent(2,"大大");
MyStudent st3 = new MyStudent(3,"芊芊");
MyStudent st4 = new MyStudent(4,"后后");
MyStudent st5 = new MyStudent(5,"佐佐");
//添加
list.addById(st5);
list.addById(st1);
list.addById(st2);
list.addById(st4);
list.addById(st3);
//删除
list.delete(1);
list.delete(5);
//修改
list.update(1,"星星");
//查
list.select(1);
//遍历
list.show();
}
}
单向环形链表
单向环形链表 - 约瑟夫问题
介绍
Josephu问题为
设编号为1,2,…n的n个人围坐一圈,
约定编号为k(1<=k<=n)的人从1开始报数,
数到m的那个人出列,它的下一位又从1开始报数,
数到m的那个人又出列,依次类推,
直到所有人出列为止,由此产生一个出队编号的序列。
提示
用一个不带头结点的循环链表来处理Josephu问题:
先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,
计到m时,对应结点从链表中删除,
然后再从被删除结点的下一个结点又从1开始计数,
直到最后一个结点从链表中删除算法结束。
代码
【节点】
class Boy{
public int no;//编号
public Boy next;//指向下一个节点
public Boy(int no) {
this.no = no;
}
}
【链表】
class CircleLinkedList{
//创建first节点
private Boy first = null; //第一个节点
//添加小孩形参环
public void add(int num){
//校验
if (num < 1){
System.out.println("小孩人数要大于1");
return;
}
Boy temp = null;//辅助指针
for (int i = 1; i <= num; i++) {
Boy boy = new Boy(i);//根据编号创建小孩节点
//第一个小孩
if (i == 1){
first = boy;
first.next = first; //形成环
temp = first;
}else {
temp.next = boy;
boy.next = first;
temp = boy;
}
}
}
//遍历
public void show(){
//判断是否为空
if (first == null){
System.out.println("链表为空");
return;
}
Boy temp = first;//first不能动
while (true){
System.out.println("小孩编号为:" + temp.no);
if (temp.next == first){//遍历完成
return;
}
temp = temp.next;//temp后移
}
}
/**
* 根据用户的输入,计算出小孩出圈的顺序
* @param no 表示从第几个小孩开始数数
* @param count 表示数几下
* @param num 表示最初有多少小孩在圈中
*/
public void counts(int no,int count,int num){
//验证
if (no < 1 || first == null || no > num || count < 0){
System.out.println("参数输入有误,重新输入");
return;
}
Boy helper = first;//辅助指针,指向最后节点
while (true){
if (helper.next == first){
break;
}
helper = helper.next;
}
//指向传入的第几个小孩节点(no)
for (int i = 0; i < no - 1; i++) {
first = first.next;
helper = helper.next;
}
//出圈
while (true){
if (helper == first){//当前链表中只有一个节点
break;
}
//指针移动count - 1 下
for (int i = 0; i < count - 1; i++) {
first = first.next;
helper = helper.next;
}
//first 指向要出圈的节点
System.out.println("小孩" + first.no + "出圈");
first = first.next;
helper.next = first;
}
System.out.println("最后留在圈中的小孩编号为" + first.no);
}
}
【测试】
public class Josepfu {
public static void main(String[] args) {
//测试
CircleLinkedList list = new CircleLinkedList();
list.add(5);
list.show();
System.out.println("\n -------出圈------");
//出圈
list.counts(1,2,7);
System.out.println("\n -------遍历------");
list.show();
}
}
学习记录- - 千
本文详细介绍了链表的概念,包括单向链表、双向链表和单向环形链表的介绍、操作(增、删、改、查)以及相关代码实现。着重讲解了链表的存储方式、节点结构,并通过实例展示了链表操作的逻辑。

被折叠的 条评论
为什么被折叠?



