链表
目录
链表是有序的列表, 但是它在内存中是存储如下
- 链表是以节点的方式来储存.链式储存
- 每个节点包含data 域, next 域: 指向下一个节点
- 如图: 发现链表的各个节点不一定是连续存储.
- 链表分带头节点的链表和没有头节点的链表, 根据实际的需求来确定
单链表(带头结点) 逻辑结构示意图如下
单链表的应用实例
第一种方法在添加英雄时, 直接添加到链表的尾部
不考虑顺序编号
- 找的当前链表的最后节点
- 将最后这个节点的next指向新的节点
- 将最后这个节点的next指向新的节点 temp.next = heroNode;
public void add(HeroNode heroNode) {
//头节点不能动,需要一个辅助遍历temp
HeroNode temp = head;
//遍历链表到最后
while (true) {
if (temp.next == null) {
break;
}
//如果没有找的,将temp后移
temp = temp.next;
}
//当退出while循环,temp就指向链表的最后
//将最后这个节点的next指向新的节点
temp.next = heroNode;
}
第二种方式在添加英雄时,根据排名将英雄插入到指定位置
- 因为头节点不能动,因此我们仍然通过一个辅助指针(变量)
- 因为单链表,我们找的temp是位于添加位置的前一个节点,否则插入失败
- temp = temp.next;
- 插入到链表中, temp的后面
- heroNode.next = temp.next;
- temp.next = heroNode;
HeroNode temp = head;
boolean flag = false; // flag标志添加的编号是否存在,默认为false
while (true) {
//说明已经在链表最后了
if (temp.next == null) {
break;
}
位置找到,就在temp的后面插入
if (temp.next.no > heroNode.no) {
break;
//说明希望添加的heroNode的编号已然存在
} else if (temp.next.no == heroNode.no) {
flag = true; //说明编号存在
break;
}
temp = temp.next;
}
if (false) {
System.out.println(heroNode.no + "该编号存在");
} else {
//插入到链表中, temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
修改节点的信息
- 根据 newHeroNode 的 no 来修改即可
- 找到需要修改的节点, 根据no编号定义一个辅助变量
- 找到该节点
- 根据flag 判断是否找到要修改的节点
- temp.name = newHeroNode.name;
//修改节点
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) {
temp.name = heroNode.name;
} else {
System.out.println("没有找到要修改的编号" + heroNode.no);
}
}
删除节点
- head 不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
- 说明我们在比较时,是temp.next.no 和 需要删除的节点的no比较
- 遍历链表,找到对应的链表
- temp.next = temp.next.next;
1.head不能动,需要一个辅助变量temp 辅助节点找到要删除的节点
1.我们在比较的时候只需要找到temp.next.no 和要需要的no作比较
*/
public void del(int no) {
//找到要修改的节点,根据no修改
//定义一个辅助变量
HeroNode temp = head;
boolean flag = false;// 标志是否找到待删除节点的
while (true) {
if (temp.next == null) { //已经到链表的最后
break;//遍历完毕
}
if (temp.next.no == no) {
//找到的待删除节点的前一个节点temp
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.println("要删除的节点不在" + no);
}
}
获取到单链表的节点个数(不统计头节点)
- 定义一个辅助变量
- cur = cur.next;//遍历
public static int getLength(HeroNode head) {
if (head.next == null) {
return 0;
}
int length = 0;
//定义一个辅助变量
HeroNode cur = head.next;
while (cur != null) {
length++;
cur = cur.next;//遍历
}
return length;
}
查找单链表中的倒数第k个结点
- 编写一个方法接受head节点,同时接收一个index
- index 表示说倒数index个系欸但
- 得到链表的总的长度 getLength
- 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
- 如果找到了,则返回该节点,否则返回nulll
/*
查找单链表中的倒数第k个结点
思路:
1.编写一个方法接受head节点,同时接收一个index
2.index 表示说倒数index个系欸但
3.得到链表的总的长度 getLength
4. 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
5.如果找到了,则返回该节点,否则返回nulll
*/
public static HeroNode findLastIndexNode(HeroNode head, int index) {
//判断是否为空 返回null
if (head.next == null) {
return null;
}
//遍历得到其长度
int size = getLength(head);
if (index <= 0 || index > size) {
return null;
}
//定义辅助遍历 cur, 通过 size-index 确定其位置
HeroNode cur = head.next;
for (int i = 0; i < size - index; i++) {
cur = cur.next;
}
return cur;
}
/*
获取到单链表的节点个数(不统计头节点)
*/
public static int getLength(HeroNode head) {
if (head.next == null) {
return 0;
}
int length = 0;
//定义一个辅助变量
HeroNode cur = head.next;
while (cur != null) {
length++;
cur = cur.next;//遍历
}
return length;
}
}
反转链表
-
//设置一个辅助遍历 cur 用来遍历 HeroNode cur = head.next;
-
//用来存放cur的下一个结点,辅助遍历 HeroNode next = null; 特别注意
-
设置一个新链表每取出一个链表的头结点,每次放到新链表reverseHead的头部
-
暂时保存cur下一个节点
//必须保存 保存的是被反转的下一个的节点 /** 这种必然失败 cur.next=reverseHead.next; reverseHead.next=cur; cur= cur.next; */
-
暂时保存cur下一个节点next=cur.next;//
-
将cur的下一个节点指向新的链表的最前端cur.next=reverseHead.next;//
-
将cur 连接到新的链表上reverseHead.next=cur;//
-
将暂时保存cur下一个的节点归还给curcur= next; //
-
将head.next 指向 reverseHead.next , 实现单链表的反转 head.next = reverseHead.next;
//反转链表
public static void reverList(HeroNode head) {
//当前链表为空
if (head.next == null || head.next.next == null) {
return;
}
//设置一个辅助遍历 cur 用来遍历
HeroNode cur = head.next;
//用来存放cur的下一个结点,辅助遍历
HeroNode next = null;
//设置一个新链表
HeroNode reverseHead = new HeroNode(0, "");
//每取出一个链表的头结点,每次放到新链表reverseHead的头部
while (cur != null){
//必须保存 保存的是被反转的下一个的节点
/** 这种必然失败
cur.next=reverseHead.next;
reverseHead.next=cur;
cur= cur.next;
*/
next=cur.next;//暂时保存cur下一个节点
cur.next=reverseHead.next;//将cur的下一个节点指向新的链表的最前端
reverseHead.next=cur;//将cur 连接到新的链表上
cur= next; //将暂时保存cur下一个的节点归还给cur
}
//将head.next 指向 reverseHead.next , 实现单链表的反转
head.next = reverseHead.next;
}
逆序打印
- 思路一: 将其反转,然后打印,这样会破环链表的本身结构,不利后续的操作
- 思路二: 利用栈的这个数据结构的特殊性,先进后出,将各个节点的数据压入到栈中,然后将其输出就可以
public static void reversePrint(HeroNode head){
if (head.next==null){
return;
}
//创建一个栈,将各各节点的数据压入栈中
Stack<HeroNode> stack = new Stack<>();
//辅助遍历进行操作
HeroNode cur =head.next;
while (cur!=null){
stack.push(cur);
cur=cur.next;//cur后移,这样就可以压入下一个节点
}
//将栈中的节点进行打印,pop 出栈
while ((stack.size()>0)){
System.out.println(stack.pop());//stack的特点是先进后出
}
}
完整代码
package com.nie.linkedlist;
import java.util.Stack;
public class SingleLinkListDemo {
public static void main(String[] args) {
HeroNode hero1 = new HeroNode(1, "黎明");
HeroNode hero2 = new HeroNode(2, "郭富城");
HeroNode hero3 = new HeroNode(5, "刘德华");
HeroNode hero4 = new HeroNode(6, "张学友");
//创建链表
SingleLinkList singleLinkList = new SingleLinkList();
//增加结点
//根据插入的顺序进行插入
singleLinkList.add(hero1);
singleLinkList.add(hero2);
singleLinkList.add(hero3);
singleLinkList.add(hero4);
//增加结点
//根据节点的编号进行插入
// singleLinkList.addByOrder(hero1);
// singleLinkList.addByOrder(hero2);
// singleLinkList.addByOrder(hero4);
// singleLinkList.addByOrder(hero3);
singleLinkList.list();
System.out.println("+++++++++++");
System.out.println("-------逆序----------");
System.out.println("逆序打印单链表, 没有改变链表的结构~~");
reversePrint(singleLinkList.getHead());
System.out.println("-------反转----------");
reverList(singleLinkList.getHead());
singleLinkList.list();
System.out.println("-------修改-----------");
HeroNode newHeroNode = new HeroNode(2, "舞王郭富城");
singleLinkList.update(newHeroNode);
singleLinkList.list();
System.out.println("-------删除-----------");
singleLinkList.del(1);
singleLinkList.list();
System.out.println("有效结点的个数" + getLength(singleLinkList.getHead()));
//测试第倒数几的结点的
int indexLast = 1;
HeroNode res = findLastIndexNode(singleLinkList.getHead(), indexLast);
System.out.println("倒数第" + indexLast + "的结点为" + res);
}
//逆序打印
/*
思路一: 将其反转,然后打印,这样会破环链表的本身结构,不利后续的操作
思路二: 利用栈的这个数据结构,将各个节点的数据压入到栈中,然后将其输出就可以
*/
public static void reversePrint(HeroNode head) {
if (head.next == null) {
return;
}
//创建一个栈,将各各节点的数据压入栈中
Stack<HeroNode> stack = new Stack<>();
//辅助遍历进行操作
HeroNode cur = head.next;
while (cur != null) {
stack.push(cur);
cur = cur.next;//cur后移,这样就可以压入下一个节点
}
//将栈中的节点进行打印,pop 出栈
while ((stack.size() > 0)) {
System.out.println(stack.pop());//stack的特点是先进后出
}
}
//反转链表
public static void reverList(HeroNode head) {
//当前链表为空
if (head.next == null || head.next.next == null) {
return;
}
//设置一个辅助遍历 cur 用来遍历
HeroNode cur = head.next;
//用来存放cur的下一个结点,辅助遍历
HeroNode next = null;
//设置一个新链表
HeroNode reverseHead = new HeroNode(0, "");
//每取出一个链表的头结点,每次放到新链表reverseHead的头部
while (cur != null) {
//必须保存 保存的是被反转的下一个的节点
/** 这种必然失败
cur.next=reverseHead.next;
reverseHead.next=cur;
cur= cur.next;
*/
next = cur.next;//暂时保存cur下一个节点
cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端
reverseHead.next = cur;//将cur 连接到新的链表上
cur = next; //将暂时保存cur下一个的节点归还给cur
}
//将head.next 指向 reverseHead.next , 实现单链表的反转
head.next = reverseHead.next;
}
/*
查找单链表中的倒数第k个结点
思路:
1.编写一个方法接受head节点,同时接收一个index
2.index 表示说倒数index个系欸但
3.得到链表的总的长度 getLength
4. 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
5.如果找到了,则返回该节点,否则返回nulll
*/
public static HeroNode findLastIndexNode(HeroNode head, int index) {
//判断是否为空 返回null
if (head.next == null) {
return null;
}
//遍历得到其长度
int size = getLength(head);
if (index <= 0 || index > size) {
return null;
}
//定义辅助遍历 cur, 通过 size-index 确定其位置
HeroNode cur = head.next;
for (int i = 0; i < size - index; i++) {
cur = cur.next;
}
return cur;
}
/*
获取到单链表的节点个数(不统计头节点)
*/
public static int getLength(HeroNode head) {
if (head.next == null) {
return 0;
}
int length = 0;
//定义一个辅助变量
HeroNode cur = head.next;
while (cur != null) {
length++;
cur = cur.next;//遍历
}
return length;
}
}
class SingleLinkList {
//初始化一个头节点
private HeroNode head = new HeroNode(0, "");
//返回头节点
public HeroNode getHead() {
return head;
}
添加节点到单向链表
/*
不考虑顺序编号
1.找的当前链表的最后节点
2.将最后这个节点的next指向新的节点
*/
public void add(HeroNode heroNode) {
//头节点不能动,需要一个辅助遍历temp
HeroNode temp = head;
//遍历链表到最后
while (true) {
if (temp.next == null) {
break;
}
//如果没有找的,将temp后移
temp = temp.next;
}
//当退出while循环,temp就指向链表的最后
//将最后这个节点的next指向新的节点
temp.next = heroNode;
}
/*
第二种方式在添加英雄时,根据排名将英雄插入到指定位置
(如果有这个排名,则添加失败,并给出提示)
*/
public void addByOrder(HeroNode heroNode) {
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)
//因为单链表,我们找的temp是位于添加位置的前一个节点,否则插入失败
HeroNode temp = head;
boolean flag = false; // flag标志添加的编号是否存在,默认为false
while (true) {
//说明已经在链表最后了
if (temp.next == null) {
break;
}
位置找到,就在temp的后面插入
if (temp.next.no > heroNode.no) {
break;
//说明希望添加的heroNode的编号已然存在
} else if (temp.next.no == heroNode.no) {
flag = true; //说明编号存在
break;
}
temp = temp.next;
}
if (false) {
System.out.println(heroNode.no + "该编号存在");
} else {
//插入到链表中, temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//修改节点
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) {
temp.name = heroNode.name;
} else {
System.out.println("没有找到要修改的编号" + heroNode.no);
}
}
//修改节点
/*
思路
1.head不能动,需要一个辅助变量temp 辅助节点找到要删除的节点
1.我们在比较的时候只需要找到temp.next.no 和要需要的no作比较
*/
public void del(int no) {
//找到要修改的节点,根据no修改
//定义一个辅助变量
HeroNode temp = head;
boolean flag = false;// 标志是否找到待删除节点的
while (true) {
if (temp.next == null) { //已经到链表的最后
break;//遍历完毕
}
if (temp.next.no == no) {
//找到的待删除节点的前一个节点temp
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.println("要删除的节点不在" + no);
}
}
//显示链表
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.next;
}
}
}
class HeroNode {
public int no;
public String name;
public HeroNode next;//指向下一个结点
//构造器
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}