链表:是一种非线性存储的数据结构,相较于线性存储的数据结构, 线性存储结构需要事先关注数据量的大小,而非线性结构不必。非线性链表具有增删快查询慢的特点,线性链表删除,插入慢,查询快。
单向链表
单向链表的节点需要包含存储其后一个节点的指针变量,是必须的。
单项链表增、删、插 、改时需关注的地方
1. 增加时
在链表中,头节点不要直接使用。应使用头结点的拷贝变量。
可以在链表中,加入一个,最后增加项的指针,暂且定义为 last,让这个指针始终指向最后一次添加的数据的地址,这样方便下次增加数据时直接加到链表的后方,而不需要从头再遍历寻找链表的最后一个节点,再去加入。
public class SingleLinkedList {
/**
* 链表头节点
*/
private HeroNode head = new HeroNode();
/**
* 最后增加的数据的地址指向
*/
private HeroNode last = head;
/**
* 添加节点
*
* @param node 节点
*/
public void add(HeroNode node) {
last.setNext(node);
last = last.getNext();
}
2. 删除时
在删除单项链表的其中一项元素时,不仅要拷贝头节(current = head)点的指针去遍历整个链表查找待删除节点,还要记录下要删除元素的前一个节点,以便删除节点后和下一个节点连接,可以使用一个临时的变量before,这样,我们就可以专心地关注 current 就行了。
/**
* 从链表中移除 no 元素
*
* @param no 元素编号
*/
public void remove(int no) {
HeroNode current = head;
HeroNode before = current;
// 别忘记判断空
if (isEmpty()){
System.out.println("链表为空,没法删");
return;
}
while ((current = current.getNext()) != null) {
// 找到了要删除的节点
if (current.getNo() == no) {
// 跨越当前节点连接到下一个节点
before.setNext(current.getNext());
break;
}
before = current;
}
// 没找到要删除的节点
if (current == null) {
System.out.println("未找到这个节点");
}
}
3. 插入时,倒没什么需要特别注意的点。找到要插入的位置直接连接就行了
4. 修改时:
找到要修改的节点将要修改的数据直接写入节点即可。
/**
* 根据元素编号更新元素
*
* @param heroNode 包含编号的待更新元素
*/
public void updateNode(HeroNode heroNode) {
HeroNode temp = head;
while ((temp = temp.getNext()) != null) {
if (temp.getNo() == heroNode.getNo()) {
temp.setName(heroNode.getName());
temp.setNickName(heroNode.getNickName());
break;
}
}
if (temp == null) {
System.out.println("未找到要更新的节点");
}
}
单项链表的逆序,可以构建一个新的链表,采用头插法,将旧链表的数据依次取出插入即可。
/**
* 获取反转链表
*
* @return 反转链表头
*/
public HeroNode getReverse() {
// 判断链表是否为空
if (isEmpty()) {
System.out.println("链表为空,无需反转");
return null;
}
// 遍历链表,采用头插法,插入到新的链表中,将新链表的表头,赋值给原链表
// 创建新链表
HeroNode newHead = new HeroNode();
HeroNode newTemp = null;
// 遍历链表
HeroNode temp = head;
// 如果有效节点不为空,则添加到新链表中
while ((temp = temp.getNext()) != null) {
// 头插法插入数据
// temp 取出节点的值
newTemp = new HeroNode(temp.getNo(), temp.getName(), temp.getNickName());
if (newHead.getNext() == null) {
newHead.setNext(newTemp);
} else {
HeroNode node = newHead.getNext();
newTemp.setNext(node);
newHead.setNext(newTemp);
}
}
return newHead;
}
附上完整单项链表代码,并不完美。
package pers.uxteam.data;
public class SingleLinkedList {
/**
* 链表头节点
*/
private HeroNode head = new HeroNode();
/**
* 最后增加的数据的地址指向
*/
private HeroNode last = head;
/**
* 添加节点
*
* @param node 节点
*/
public void add(HeroNode node) {
last.setNext(node);
last = last.getNext();
}
/**
* 根据元素编号更新元素
*
* @param heroNode 包含编号的待更新元素
*/
public void updateNode(HeroNode heroNode) {
HeroNode temp = head;
while ((temp = temp.getNext()) != null) {
if (temp.getNo() == heroNode.getNo()) {
temp.setName(heroNode.getName());
temp.setNickName(heroNode.getNickName());
break;
}
}
if (temp == null) {
System.out.println("未找到要更新的节点");
}
}
/**
* 从链表中移除 no 元素
*
* @param no 元素编号
*/
public void remove(int no) {
HeroNode current = head;
HeroNode before = current;
// 别忘记判断空
if (isEmpty()){
System.out.println("链表为空,没法删");
return;
}
while ((current = current.getNext()) != null) {
// 找到了要删除的节点
if (current.getNo() == no) {
// 跨越当前节点连接到下一个节点
before.setNext(current.getNext());
break;
}
before = current;
}
// 没找到要删除的节点
if (current == null) {
System.out.println("未找到这个节点");
}
}
/**
* 获取链表头节点
*
* @return 链表头节点
*/
public HeroNode getHead() {
return head;
}
/**
* v2.0 有序添加到链表中,并返回链表头
* v1.0 有序添加到链表中
*
* @param newNode 元素
*/
public void addBySort(HeroNode newNode) {
HeroNode temp = head;
HeroNode oldNode;
boolean insertFlag = false; // 是已经将新的节点否插入链表的标识 ,默认未插入
while ((oldNode = temp.getNext()) != null) {
// 要添加的数据跟当前旧的的数据比较 ,
if (oldNode.getNo() > newNode.getNo()) { //如果旧节点 > 新节点
newNode.setNext(oldNode); //那么就让新节点的 next = 旧节点
temp.setNext(newNode);
insertFlag = true;
break;
}
if (oldNode.getNo() == newNode.getNo()) { // 说明该编号已经存在,不添加
System.out.println("链表中已经存在该编号:" + newNode.getNo());
insertFlag = true;
break;
}
temp = temp.getNext();
}
if (!insertFlag) { // 如果没有插入,则放到队列的最后
temp.setNext(newNode);
}
}
/**
* 显示每个节点的详细信息
*/
public void showAll() {
HeroNode temp = head.getNext();
while (temp != null) {
System.out.println(temp.toString());
temp = temp.getNext();
}
}
// todo 判断是否为空链表 -- 完成
/**
* 判断是否为空链表
*
* @return true / false
*/
public boolean isEmpty() {
return head.getNext() == null;
}
// todo 链表中有效节点的个数 -- 完成
/**
* 链表中有效节点的个数
*
* @return
*/
public int size() {
int length = 0;
// 链表是否为空
if (isEmpty()) {
return length;
}
HeroNode temp = head;
while ((temp = temp.getNext()) != null) {
length++;
}
return length;
}
// todo 链表中倒数第 k 个节点 -- 完成
/**
* 获取链表中倒数第 k 个节点
*
* @param index 倒数第 index 节点
* @return 倒数第 index 节点信息
*/
public HeroNode getLastIndexOf(int index) {
// 首个有效的节点
HeroNode temp = head.getNext();
// 如果链表为空
if (isEmpty()) {
System.out.println("链表为空,无法获取倒数第 k 个节点");
return null;
}
// 判断 index 是否属于链表范围内
if (size() - index < 0) {
System.out.println("要查找的下标超范围!");
return null;
}
// 循环到倒数第 k 个元素 { 共 n 个元素, 下标从 0 开始,到 n-1 }
for (int i = 0; i < size() - index; i++) {
temp = temp.getNext();
}
return temp;
}
// todo 链表的反转 -- 完成
/**
* 获取反转链表
*
* @return 反转链表头
*/
public HeroNode getReverse() {
// 判断链表是否为空
if (isEmpty()) {
System.out.println("链表为空,无需反转");
return null;
}
// 遍历链表,采用头插法,插入到新的链表中,将新链表的表头,赋值给原链表
// 创建新链表
HeroNode newHead = new HeroNode();
HeroNode newTemp = null;
// 遍历链表
HeroNode temp = head;
// 如果有效节点不为空,则添加到新链表中
while ((temp = temp.getNext()) != null) {
// 头插法插入数据
// temp 取出节点的值
newTemp = new HeroNode(temp.getNo(), temp.getName(), temp.getNickName());
if (newHead.getNext() == null) {
newHead.setNext(newTemp);
} else {
HeroNode node = newHead.getNext();
newTemp.setNext(node);
newHead.setNext(newTemp);
}
}
return newHead;
}
// todo 从尾部到头部打印链表 要求: 反向遍历和栈 OR 递归 -- 完成
public void showFromRear() {
// 去除头节点,进行递归打印
// recursionOutput(head.getNext());
// 反向遍历
// 将链表反转
/*
HeroNode reverse = getReverse();
while ((reverse = reverse.getNext()) != null){
// 打印每一个有效节点
System.out.println(reverse);
}
*/
// 栈方式打印
// 创建栈空间
StackBox stackBox = new StackBox(size());
// 1. 将链表压入栈中
HeroNode temp = head;
// 如果为有效元素
while ((temp = temp.getNext()) != null) {
// 压入栈
stackBox.push(temp);
}
// 将栈中的每个元素弹出
while ((temp = stackBox.pop()) != null) {
System.out.println(temp);
}
}
// todo 合并两个有序单链表,合并之后依然有序 我记得有道 力扣题 好像需要这个算法!去做一下 -- 完成
/**
* 合并两个有序单链表,合并之后依然有序
*
* @param list 要合并的链表头
* @return 合并后的链表头
*/
public HeroNode mergeOtherSingleLinkList(SingleLinkedList list) {
// 遍历两个链表,连接到新链表,连接过程中,做插入
// 创建合并后的链表
SingleLinkedList outListHead = new SingleLinkedList();
// 首先本链表有序的加入 outListHead
HeroNode temp = this.head;
while ((temp = temp.getNext()) != null) {
// 取出链表每个有效元素,使用 addBySort 方法加入加入 outListHead
HeroNode node = new HeroNode(temp.getNo(), temp.getName(), temp.getNickName());
outListHead.addBySort(node);
}
// 将用户传进来的链表有序的加入 outListHead
temp = list.getHead();
while ((temp = temp.getNext()) != null) {
// 取出链表每个有效元素,使用 addBySort 方法加入加入 outListHead
HeroNode node = new HeroNode(temp.getNo(), temp.getName(), temp.getNickName());
outListHead.addBySort(node);
}
return outListHead.getHead();
}
/**
* 递归打印
*
* @param firstEle 第一个有效数据
*/
private void recursionOutput(HeroNode firstEle) {
// 如果还存在有效节点,继续递归
if (head.getNext() != null) {
recursionOutput(head.getNext());
}
System.out.println(head);
}
/**
* 显示链表所有有效元素
*/
public void show() {
HeroNode temp = head;
while ((temp = temp.getNext()) != null) {
System.out.println(temp);
}
}
}
package pers.uxteam.data;
/***
* 水浒英雄类
*/
public class HeroNode {
/**
* 英雄排名
*/
private int no;
/**
* 英雄姓名
*/
private String name;
/**
* 英雄称号
*/
private String nickName;
private HeroNode next;
public int getNo() {
return no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public HeroNode getNext() {
return next;
}
public void setNext(HeroNode next) {
this.next = next;
}
public HeroNode() {
}
/**
* 英雄构造方法
*
* @param no 英雄排名
* @param name 英雄姓名
* @param nickName 英雄称号
*/
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 + '\'' +
'}';
}
}