链表是有序的列表,以节点的方式进行存储,但在内存中的各节点地址并不连续。其中单链表的特点是每个节点包含data域(存储数据)和next域(指向下一个节点)。单链表分带头结点链表和不带头结点的链表,根据实际需求来确定。
这里用代码实现带头结点的单链表:
1.先自定义一个节点:
class Node {
int data;//节点存储的数据
Node next;//指向下一个节点
public Node(int data) {
this.data = data;
}
//打印节点
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
}
2.实现自定义单链表
并给出了一些单链表算法题的实现
/**
* 自定义单向链表
*/
public class MyLinkedList {
//定义一个头节点,该节点不存储任何数据,只用来指向第一个节点
private Node head = new Node(0);
/**
* 添加节点
* @param node
*/
public void add(Node node) {
//定义一个辅助节点,用来遍历链表
Node tmp = head;
//先循环遍历到链表的最后一个位置
while (tmp.next != null){
tmp = tmp.next;
}
//经过循环之后,tmp指向最后一个节点,再将最后一个节点的next指向要添加的节点
tmp.next = node;
}
/**
* 遍历节点
*/
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//定义一个辅助节点,用来遍历链表
Node tmp = head;
while (tmp.next != null) {
tmp = tmp.next;
System.out.println(tmp);
}
}
/**
* 插入节点
*
* @param srcNode 表示要在哪一个节点后面插入新节点
* @param afterNode 要插入的新节点
*/
public void insert(Node srcNode, Node afterNode) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
//找到需要修改的编号
Node tmp = head.next;
boolean flag = false;
while (true) {
if (tmp == null) {
break;//已遍历完链表
}
if (tmp.data == srcNode.data) {
flag = true;//找到了要修改的节点
break;
}
tmp = tmp.next;
}
if (flag) {
//此时tmp就是指向要插入的位置前一个节点,于是在tmp节点后面插入节点
//此处是一个断链再合链的常规操作
afterNode.next = tmp.next;
tmp.next = afterNode;
} else {
System.out.printf("没有找到数据为%d的节点", srcNode.data);
}
}
/**
* 删除节点
*
* @param node
*/
public void delete(Node node) {
/*
因为这里是单向链表,在删除一个节点时,因为无法知道一个节点的前一个节点是哪个,所有只能在遍历到该节点的前一个节点时,
进行删除操作
*/
//所以这里开始指向头结点,不能开始就指向第一个节点
Node tmp = head;
while (tmp.next != null) {
if (tmp.next == node) {//找到了要删除的节点
tmp.next = tmp.next.next;
return;//结束方法
}
tmp = tmp.next;
}
//循环退出,说明没有找到要删除的节点
System.out.println("要删除的节点不存在");
}
/**
* 求单链表中节点的个数
*
* @return
*/
public int getLength() {
if (head.next == null) {
return 0;
}
Node tmp = head.next;
int length = 0;
while (tmp != null) {
length++;
tmp = tmp.next;
}
return length;
}
/**
* 求单链表中倒数第index个节点
*
* @param index
* @return
*/
public Node getLastIndexNode(int index) {
//如果链表为空,则返回null
if (head.next == null) {
return null;
}
//对index进行校验
if (index <= 0 || index > this.getLength()) {
return null;
}
Node tmp = head.next;
for (int i = 0; i < (this.getLength() - index); i++) {
tmp = tmp.next;
}
return tmp;
}
/**
* 反转链表
*/
public void reverse() {
/*
思路:从第二个节点开始,分别依次将节点放到第一个节点前面
因此,1.先定义一个tmp指针用于遍历节点
2.将每个节点放到第一个节点之前,要也就是要插入到head和第一个节点之间,
所以将第一个节点赋给待插入节点的next,再将带插入节点赋给head的next即可
*/
//链表为空或者链表只有一个节点
if (head.next == null || head.next.next == null) {
return;
}
Node tmp = head.next.next;//用于遍历节点,从第二个节点开始
head.next.next = null;//由于第一个节点反转之后会成为最后一个节点,所以将第一个节点的next置为null
Node next = null;//用于记录所遍历节点的next指向的下一个节点
while (tmp != null){
next = tmp.next;
tmp.next = head.next;//将tmp.next指向第一个节点
head.next = tmp;
tmp = next;
}
}
/**
* 逆序打印,不改变链表原有结构
*/
public void reversePrint(){
if(head.next == null){
return;//空链表,不打印
}
//创建一个栈,用于保存链表中的节点
Stack<Node> stack = new Stack<>();
//遍历节点,依次将节点压入栈中
Node tmp = head.next;
while (tmp != null){
stack.push(tmp);
tmp = tmp.next;
}
//再将栈中的元素依次弹栈
while (stack.size() > 0){
Node node = stack.pop();
System.out.println(node);
}
}
/**
* 递归实现链表倒序输出
*/
public void reversePrint2(){
print(head.next);
}
private void print(Node node){
if(node == null){
return;
}
print(node.next);
System.out.println(node);
}
}