单链表
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:数据域和指针域,数据域就是存储数据的存储单元,指针域就是连接每个结点的地址数据。
思路分析
首先我们定义一个操作类,用来实现链表的增删改查。在操作类类中再定义一个内部私有的节点类(封装性),在其中定义数据域和指针域,并重写toString()方法。为了便于链表的管理和使用,在操作类中定义一个虚拟头节点head,里面不存放数据,只存放下一个结点的地址。虚拟头节点的下一个结点才是真正的头节点。
public class LinkedList {//链表操作类,对链表进行增删改查
private class Node {// 链表的组成部分————结点
private int no;//编号
private String data;//数据
private Node next;//下一个结点的地址
public Node(int no, String data) {//构造方法,给节点赋值
this.no = no;
this.data = data;
}
public String toString() { //输出结点信息
return "No:" + no + "\tdata:" + data + "\t" + next;
}
}
Node head = new Node(0, "");//定义一个虚拟头节点
}
类定义完成后,接下来定义添加结点的方法。
public void add(int no, String data) {//传入数据域的值
Node newNode = new Node(no, data);//创建一个新节点把传进来的值赋进去
Node temp = head;//头节点是链表的开始位置,不能动,所以定义一个指针temp指向虚拟头节点
while (true) {//循环遍历链表
if (temp.next == null) { // 如果temp指向的节点的下一个节点为空,那么说明已经遍历到了链表的最后一个节点
break; //退出循环
}
temp = temp.next;//否则temp后移,指向下一个节点
}
temp.next = newNode; // 在链表的最后一个节点的后面添加新节点
}
这里肯定很多人会疑惑,为什么节点的next就是下一个节点,你是怎么保证的?请看下图分析。
将下一节点的地址值赋给当前结点的next,通过值传递来实现节点的next就是下一个节点。可以看出,是一个套娃的过程。
接下来是链表的更新。
public void update(int no, String data) { //传入节点中的数据,通过no来找到响应的结点
Node temp = head; // 定义一个temp指向虚拟头节点
while (true) {//循环遍历链表
if (temp.next == null) { // 如果temp的下一个节点为空,那么就说明没有找到该节点,跳出循环
System.out.println("没有编号为" + no + "的结点");
break;
}
if (temp.next.no == no) {//如果找到对应编号的结点,则修改数据,跳出循环
temp.next.data = data;
break;
}
temp = temp.next;//如果不满足两个if中的条件,则指针后移
}
}
接下来是链表的删除。
public void remove(int no) { // 传入no值
Node temp = head; // 和前面一样,定义一个temp指向虚拟head节点
while (true) {//循环遍历链表
if (temp.next == null) { // 如果遍历到最后,temp的下一节点为空,则没有与编号匹配的节点
System.out.println("没有编号为" + no + "的结点");
break;
}
if (temp.next.no == no) {//如果有和编号相匹配的节点
//将temp的下一个节点设为空
Node trash = temp.next;
trash = null;
//让temp的下一个节点等于下下个节点
temp.next = temp.next.next;
break;
}
temp = temp.next;//如果不满足两个if中的条件,则指针后移,继续遍历链表
}
}
接下来是链表的查询
public void search(int no) { // 传入编号
Node temp = head; //和前面一样,定义一个temp指向虚拟head节点
if (head.next == null) { // 如果虚拟头节点的后一个节点位空,则链表为空
System.out.println("链表为空!");
return;
}
while (true) {
if (temp.next == null) { //如果遍历到最后,temp的下一节点为空,则没有与编号匹配的节点
System.out.println("查询不到节点");
break;
}
if (temp.next.no == no) {//如果有,则输出该节点的数据
System.out.println(temp.next);
break;
}
temp = temp.next;//指针后移
}
}
接下来是链表的打印
public void show() {
Node temp = head; // 头节点不能改变,否则无法遍历! 定义一个temp指针指向头节点
if (temp.next == null) { // 如果虚拟头节点的后一个节点位空,则链表为空
System.out.println("链表为空");
}
while (true) {
if (temp.next != null) { // 如果temp的下一个节点为空
temp = temp.next; //temp指向后一个节点
System.out.println(temp);//输出后一个节点的值
} else {
break; //为空跳出循环
}
}
}
最后是链表的按序添加
这里要注意,如图所示:
public void addByOrder(int no, String data) { // 按顺序添加,将值传入
Node newNode = new Node(no, data); //创建新节点将值赋进去
Node temp = head;//定义一个temp指向头节点
while (true) {
if (temp.next == null) {//如果遍历到最后,即链表到了最后一个节点
temp.next = newNode;//将最后一个节点的后一位指向newNode
break;
}
if (temp.next.no >= newNode.no) {//如果链表中temp.next的编号大于待插入编号
newNode.next = temp.next;//将新节点的下一个节点指向原来temp指向的下一个节点
temp.next = newNode;//temp指向新的节点
break;//跳出循环
}
//temp = temp.next;//temp后移
}
}
这样我们就实现了单链表的CRUD操作啦!
下面附上完整代码,代码还可以更进一步优化,欢迎指正!
public class LinkedList {//链表操作类,对链表进行增删改查
private class Node {// 链表的组成部分————结点
private int no;//编号
private String data;//数据
private Node next;//下一个结点的地址
public Node(int no, String data) {//构造方法,给节点赋值
this.no = no;
this.data = data;
}
public String toString() { //输出结点信息
return "No:" + no + "\tdata:" + data;
}
}
Node head = new Node(0, "");//定义一个虚拟头节点
public void add(int no, String data) {//传入数据域的值
Node newNode = new Node(no, data);//创建一个新节点把传进来的值赋进去
Node temp = head;//头节点是链表的开始位置,不能动,所以定义一个temp节点指向虚拟头节点
while (true) {
if (temp.next == null) { // 如果temp节点的下一个节点为空,那么说明已经遍历到了链表的最后一个节点
break; //退出循环
}
temp = temp.next;//否则temp后移,指向下一个节点
}
temp.next = newNode; // 在链表的最后一个节点的后面添加新节点
/*while (true){
if (temp.next !=null){
temp = temp.next;
}else {
temp.next = newNode;// 在链表的最后一个节点的后面添加新节点
break;
}
}*/
}
public void addByOrder(int no, String data) { // 按顺序添加,将值传入
Node newNode = new Node(no, data); //创建新节点将值赋进去
Node temp = head;//定义一个temp指向头节点
while (true) {
if (temp.next == null) {//如果遍历到最后,即链表到了最后一个节点
temp.next = newNode;//将最后一个节点的后一位指向newNode
break;
}
if (temp.next.no >= newNode.no) {//如果链表中temp.next的编号大于待插入编号
newNode.next = temp.next;//将新节点的下一个节点指向原来temp指向的下一个节点
temp.next = newNode;//temp指向新的节点
break;//跳出循环
}
//temp = temp.next;//temp后移
}
}
public void update(int no, String data) { //传入节点中的数据
Node temp = head; // 定义一个temp指向虚拟头节点
while (true) {
if (temp.next == null) { // 如果temp的下一个节点为空,那么就说明没有找到该节点,跳出循环
System.out.println("没有编号为" + no + "的结点");
break;
}
if (temp.next.no == no) {//如果找到对应编号的结点,则修改数据,跳出循环
temp.next.data = data;
break;
}
temp = temp.next;//如果不满足两个if中的条件,则指针后移,继续遍历链表
}
}
public void remove(int no) { // 传入no值
Node temp = head; // 和前面一样,定义一个temp指向head节点
while (true) {//循环遍历链表
if (temp.next == null) { // 如果遍历到最后,temp的下一节点为空,则没有与编号匹配的节点
System.out.println("没有编号为" + no + "的结点");
break;
}
if (temp.next.no == no) {//如果有和编号相匹配的节点
//将temp的下一个节点设为空
Node trash = temp.next;
trash = null;
//让temp的下一个节点等于下下个节点
temp.next = temp.next.next;
break;
}
temp = temp.next;//如果不满足两个if中的条件,则指针后移,继续遍历链表
}
}
public void search(int no) { // 传入编号
Node temp = head; //和前面一样,定义一个temp指向虚拟head节点
if (head.next == null) { // 如果虚拟头节点的后一个节点位空,则链表为空
System.out.println("链表为空!");
return;
}
while (true) {
if (temp.next == null) { //如果遍历到最后,temp的下一节点为空,则没有与编号匹配的节点
System.out.println("查询不到节点");
break;
}
if (temp.next.no == no) {//如果有,则输出该节点的数据
System.out.println(temp.next);
break;
}
temp = temp.next;//指针后移
}
}
public void show() {
Node temp = head; // 头节点不能改变,否则无法遍历! 定义一个temp指针指向头节点
if (temp.next == null) { // 如果虚拟头节点的后一个节点位空,则链表为空
System.out.println("链表为空");
}
while (true) {
if (temp.next != null) { // 如果temp的下一个节点为空
temp = temp.next; //temp指向后一个节点
System.out.println(temp);//输出后一个节点的值
} else {
break; //为空跳出循环
}
}
}
}