1.链表的概述
1.链表是以节点的方式来存储的,是链式存储
2.每个节点包含data域,next域(指向下一个节点)
3.链表的各个节点不一定是连续的存储
4.带头节点链表和不带头结点链表
5.在内存中的存储结构如下图:
注意:链表的各个节点不一 定是连续存


2.单链表
例子-1:不排序添加遍历
使用带头结点的单向链表,实现-英雄的排行榜管理!

思路:
head节点:表示单链表的头,不存放具体数据
next:指向下一个节点,最后不在指向,默认为null
1.添加:①先创建一个head节点,作用表示单链表的头②每增加一个节点,直接加到链表的后面
2.遍历:通过一个辅助变量,帮助遍历整个链表
public class SingLinkedListDemo {
public static void main(String[] args) {
//测试
//创建节点
HeroNode h1 = new HeroNode(1, "张飞", "射手");
HeroNode h2 = new HeroNode(2, "赵云", "辅助");
HeroNode h3 = new HeroNode(3, "关羽", "中单");
//创建一个链表
SingLinkedList singLinkedList = new SingLinkedList();
//加入
singLinkedList.add(h2);
singLinkedList.add(h1);
singLinkedList.add(h3);
//显示
singLinkedList.list();
}
}
/**
* 定义链表,管理英雄
*/
class SingLinkedList {
//设置头节点,头节点不能动(代表整个链表),不存放具体数据
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.next;
}
//当退出循环时,temp就指向最后
//将next指向新的节点
temp.next = heroNode;
}
//显示链表【遍历】
public void list() {
//判断链表是否为空
if (head.next == null) {
//为空退出
System.out.println("链表为空");
return;
}
//不为空
//因为头节点不能动,需要辅助变量来遍历
HeroNode temp = head.next;
while (true) {
//判断是否到链表最后
if (temp == null) {
//为空退出
break;
}
//不为空
//输出节点信息
System.out.println(temp);
//将next后移,不后移是死循环
temp = temp.next;
}
}
}
/**
* 定义一个节点,每个HeroNode 对象就是一个节点
*/
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; //指向下一个节点
public HeroNode() {
}
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 + '\''+"}";
}
}
显示的英雄排序和自己添加的先后顺序有关,没有考虑排序~
例子-2:有序添加
需要按照编号添加
1.首先找到新添加节点的位置,通过辅助变量(遍历)
2.新的节点.next=temp.next
3.将temp.next=新的节点
public void add2(HeroNode heroNode) {
//头节点不能动,通过辅助变量找到添加的位置
//我们的辅助变量temp,找的是添加位置的前一个节点,否则查入不了
HeroNode temp = head;
Boolean flag = false;//标识添加的编号是否存在,默认false
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;//后移遍历
}
if (flag == true) {//说明存在,不再添加
System.out.println("该英雄已经存在,不能加入!编号:"+heroNode.no);
}else {
//加入链表
heroNode.next=temp.next;
temp.next=heroNode;
}
}
例子-3:修改
1.定义一个辅助变量
2.根据编号进行查找
public void update(HeroNode heroNode) {
//判断列表是否为空
if (head.next == null) {
System.out.println("列表为空~");
return;
}
//找到需要修改的节点,根据编号(no)
//定义一个辅助变量
HeroNode temp = head;
boolean flag = false;//标识是否找到
while (true) {
if (temp == null) {//链表的最后
break;
}
if (temp.no == heroNode.no) {
flag = true;
break;
}
//指针后移
temp = temp.next;
}
//根据flag判断是否找到修改的节点
if (flag == true) {
temp.name = heroNode.name;
temp.nickname = heroNode.nickname;
} else {
System.out.println("链表中没有编号为:" + heroNode.no + "的英雄");
}
}
例子-4:删除
1.先找到需要删除节点的前一个节点temp
2.temp.next=temp.next.next (temp指向节点的下下一个节点)
3.被删除的节点,不会有其他引用指向,被垃圾回收机制回收
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 = temp.next;
}
if (flag == true) {
temp.next = temp.next.next;
} else {
System.out.println("删除的英雄不存在~");
}
}
例题
1.求单链表中有效节点的个数?
public static int getLength(HeroNode head){
if (head.next==null){//链表为空
return 0;
}
int length=0;
HeroNode temp=head.next;
while (temp!=null){
length++;
temp=temp.next;
}
return length;
}
要是测试的话,记得 给 head 声明 get方法哦~
2.查找单链表中倒数第n个节点?
public static HeroNode findLastIndex(HeroNode head,int index){//倒数第index节点
if (head.next==null){//链表为空
return null;//没有找到
}
//第一次遍历
int size=getLength(head);//获取链表节点的个数
//第二次遍历,(size-index)就是倒数的第n个节点---->下表从零开始
//判断index是否有效
if (index<=0||index>size){
return null;
}
//定义辅助变量
HeroNode temp=head.next;//指向第一个有效数据的节点
for (int i=0;i<size-index;i++){
temp=temp.next;
}
return temp;
}
3.单链表如何反转?
注意理解:temp.next = reverseHead.next
public static void reverseList(HeroNode head) {
if (head.next == null || head.next.next == null) {//为空或者只有一个节点
return;
}
//定义辅助指针,帮助遍历原来的链表
HeroNode temp = head.next;
HeroNode next = null;//指向当前节点的下一个节点
HeroNode reverseHead = new HeroNode(0, "", "");
//从头遍历原先的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
while (temp != null) {
next = temp.next;//先保留当前节点的下一个节点,不然会断掉
temp.next = reverseHead.next;//将temp的下一个节点指向新的链表的最前端
reverseHead.next=temp;//将将temp连接到新的链表上
temp = next;//让temp指向下一个节点
}
//将reverseHead在换成head
head.next = reverseHead.next;
}
3.双向链表

1.双链表可以向前或者向后查找
2.双向链表可以自我删除
3.遍历、添加、删除、修改的操作思路
遍历:和单链表一样,只是可以向前、也可以向后查找
添加(默认添加到最后):1.先找到双向链表的最后这个节点。 2.temp.next=newHeroNode。
3.newHeroNode.pre=temp;
修改:和单向链表一样
删除:1.因为是双向链表,可以自我删除某个节点
2.直接找到要删除的节点,比如temp
3.temp.pre.next=temp.next
4.temp.next.pre=temp.pre
代码实现:
package linklist.doublelinklist;
public class DoubleLinkDemo1 {
public static void main(String[] args) {
//创建测试节点
HeroNode h1=new HeroNode(1,"张飞","辅助");
HeroNode h2=new HeroNode(2,"马超","射手");
HeroNode h3=new HeroNode(3,"赵云","打野");
HeroNode h4=new HeroNode(4,"刘备","中单");
HeroNode h5=new HeroNode(5,"关羽","上单");
HeroNode h6=new HeroNode(5,"曹操","打野");
//创建一个双链表
DoubleLink doubleLink=new DoubleLink();
doubleLink.add(h1);
doubleLink.add(h2);
doubleLink.add(h3);
doubleLink.add(h4);
doubleLink.add(h5);
doubleLink.list();
System.out.println("修改后=======");
doubleLink.update(h6);
doubleLink.list();
System.out.println("删除后=======");
doubleLink.delete(2);
doubleLink.list();
}
}
/**
* 创建一个双链表的类
*/
class DoubleLink {
//初始化一个头节点,头节点不要动,不存放具体数据
private HeroNode head = new HeroNode(0, "", "");
/**
* 删除-----直接找到要删除的节点,找到后自我删除即可
*/
public void delete(int no) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空~");
return;
}
//head头节点不能动,借用辅助接点temp
HeroNode temp = head.next;
boolean flag = false;//标志是否找到待删除的节点
while (true) {
if (temp == null) {//已经到链表最后节点的next
break;
}
if (temp.no == no) {
flag = true;//找到待删除的节点
break;
}
//未找到,temp后移
temp = temp.next;
}
if (flag) {//表示找到待删除的节点
temp.pre.next = temp.next;
//如果是最后一个节点,就不需要执行,否则出席那空指针
if (temp.next != null) {
temp.next.pre = temp.pre;
}
} else {
System.out.println("要删除的节点【" + no + "】不存在~");
}
}
/**
* 修改 --- 和单向链表一样
*/
public void update(HeroNode heroNode) {
//判断链表是否为空
if (head.next == null) {
return;
}
//head不能动,需要一个辅助节点temp
HeroNode temp = head;
boolean flag = false;//表示是否找到该节点
while (true) {
//到链表最后
if (temp == null) {
break;
}
if (temp.no == heroNode.no) {
flag = true; //找到修改节点
break;
}
//未找到,temp后移
temp = temp.next;
;
}
//根据flag判断是否找到要修改的节点
if (flag) {
temp.name = heroNode.name;
temp.nickname = heroNode.nickname;
} else {
System.out.println("未找到要修改的节点~");
}
}
/**
* 添加:默认添加到链表尾部
*/
public void add(HeroNode heroNode) {
//因为head节点不能动,因此我们需要一个辅助节点temp
HeroNode temp = head;
while (true) {
//找到链表尾部
if (temp.next == null) {
break;
}
//如果没有找到将temp后移
temp = temp.next;
}
//退出循环,temp指向链表最后
//形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
/**
* 遍历双向链表
*/
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空~");
return;
}
//因为头节点不动,需要一个辅助变量来遍历
HeroNode temp = head.next;
while (true) {
//判断是否到链表最后
if (temp == null) {
break;
}
//输出节点信息
System.out.println(temp);
//temp后移
temp = temp.next;
}
}
/**
* 返回头节点
*/
public HeroNode getHead() {
return head;
}
}
/**
* 定义HeroNode,每个HeroNode对象就是一个节点
*/
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next;//指向下一个节点,默认为null
public HeroNode pre;//指向前一个结点,默认为null
public HeroNode() {
}
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 + '\'' + "}";
}
}
4.环形链表

Josephu(与瑟夫)问题:

构建环形链表的思路:
1.先创建第一个节点,让first指向该节点,并形成环形
2.后面没创建一个新的节点,就把该节点加入到环形链表中
遍历环形链表:
1.让一个辅助指针cur,指向first节点
2.然后while遍历该环形链表 ,cur.next==first结束
小孩儿出圈的思路:
1.创建一个辅助指针helper,刚开始指向环形链表的最后这个节点,
2.当小孩报数前,先让first和helper移动k-1次,报数时,让first和helper指针同时移动m-1次
3.这时就可以将first指向的小孩儿节点出圈
3.1first=first.next;
3.2helper.next=first;
出圈的顺序:2-4-1-5-3
代码实现:
public class JosepFu {
public static void main(String[] args) {
CircleSingleLinked circleSingleLinked = new CircleSingleLinked();
circleSingleLinked.addBoy(125);
circleSingleLinked.count(10,20,125);
}
}
//创建一个环形单向链表
class CircleSingleLinked {
//创建一个first节点,当前无编号
private Boy first = null;
/**
* 添加节点,构建成环形链表
*/
public void addBoy(int nums) {
//判断nums
if (nums < 1) {
System.out.println("nums的值不正确!!!");
return;
}
Boy cur = null;//辅助指针,帮助构建环形链表
//使用for循环创建环形链表
for (int i = 1; i <= nums; i++) {
//根据编号创建小孩儿节点
Boy boy = new Boy(i);
//第一个节点
if (i == 1) {
first = boy;
first.setNext(first);//构成环,只有一个节点
cur = first;//让cur指向第一个节点
} else {
cur.setNext(boy);//指向新的节点
boy.setNext(first);//新的节点指向第一个节点
cur = boy;//辅助节点后移
}
}
}
/**
* 遍历环状链表
*/
public void list() {
if (first == null) {
System.out.println("链表为空");
return;
}
//first头节点不动,借用辅助指针
Boy temp = first;
while (true) {
System.out.println("当前节点:" + temp.getNo());
if (temp.getNext() == first) {
break;
} else {
temp = temp.getNext();//指针后移
}
}
}
/**
* 根据 用户的输入,计算出小孩出圈的顺序
* strNo:从第几个节点开始数数
* countNo:表示数几次
* nums:表示有几个节点
*/
public void count(int strNo, int countNo, int nums) {
//对数据进行校验
if (first == null || strNo < 1 || strNo > nums) {
System.out.println("参数输入有误~~");
return;
}
//创建一个辅助指针
Boy helper = first;
while (true) {
if (helper.getNext() == first) {//说明helper时最后有个节点
break;
} else {
helper = helper.getNext();
}
}//结束循环找到helper指向最后这个节点
//报数时,让first和helper指针同时移动k-1次
for (int i=0;i<strNo-1;i++){
first=first.getNext();
helper=helper.getNext();
}
//让小孩报数,让first和helper指针同时移动m-1次,然后出圈,知道圈中只有一个节点
while (true){
if (helper==first){//说明圈中只有一个节点
break;
}
//让first和helper同时移动countNo-1次
for (int i=0;i<countNo-1;i++){
first=first.getNext();
helper=helper.getNext();
}//for循环结束后,这时的first这个节点就是要移除的节点
System.out.println("小孩儿:"+first.getNo()+"出圈");
//这时让first出圈
first=first.getNext();
helper.setNext(first);//helper指向新的first
}//退出while循环时,只有一个节点
System.out.println("最后的节点为:"+first.getNo());
}
}
//创建一个Boy类,表示一个节点
class Boy {
private int no;//编号
private Boy next;//指向下一个节点
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
本文围绕链表数据结构展开,介绍了链表以节点存储、链式存储等特点。详细阐述单链表的不排序添加遍历、有序添加、修改、删除操作及相关例题,还讲解了双向链表和环形链表的操作思路,如双向链表的遍历、添加、删除、修改,环形链表的构建、遍历及小孩出圈问题。
7039

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



