前言
链表使用地方较多,多出源码中均有出现的地方,毕竟链表作为基础数据结构在申请内存时比数组唯一好处就是不用申请连续内存(数组在内存中必须是连续的),因此掌握链表很有必要
代码
下面以java代码为例 写了个简单的实现
package com.chinanums.hh.study.base;
/**
* 单链表
* node -> node -> node
*
* @author hh
* @since 2021-11-10
* Copyright (C), 2021,
*/
public class SingleLinkedList<T> {
// 头结点
private Node<T> head;
// 尾结点
private Node<T> tail;
// 链表大小
private int count;
/**
* 增加节点
*
* @param data 数据
*/
public void add(T data) {
insert(new Node<T>(data));
}
/**
* 按照下标位置增加节点
* @param data 数据
* @param index 下标
*/
public void add(T data,int index){insert(new Node<>(data),index);}
/**
* 插入节点
*
* @param tNode node
*/
private void insert(Node<T> tNode) {
// 说明是第一次插入
if (head == null) {
head = tNode;
} else {
// 追加到队列尾部
tail.next = tNode;
}
tail = tNode;
count++;
}
/**
* 按照指定位置插入节点
*
* @param tNode node
*/
private void insert(Node<T> tNode, int index) {
Node<T> p = head;
// 说明是第一次插入
if (head == null) {
head = tNode;
tail = head;
count++;
} else {
// 这个作为指针 记录查找到符合值的上一个节点
Node<T> q = null;
int i = 0;
while (p != null && i != index) {
q = p;
p = p.next;
i++;
}
// 说明找到了
if (p != null) {
// q为null 说明即将插入的tNode是头结点
if (q == null) {
tNode.next = head;
head = tNode;
// 插入到队列中间
} else {
// q 上一个节点 改变上一个节点的next指向
q.next = tNode;
// 保持连贯 新节点next指向原节点p
tNode.next = p;
}
count++;
// 插入到尾部
} else if (q == tail) {
tail.next = tNode;
tail = tNode;
count++;
}
}
}
/**
* 根据下标查找
*
* @param index 下标
* @return 值
*/
public T find(int index) {
// 获取当前队列中index的值
if (index >= count || index < 0) {
return null;
}
// 从头开始找
Node<T> p = head;
int i = 0;
// 如果链表中没有 那么while循环走完之后 p节点应该是个null
while (p != null && i != index) {
p = p.next;
i++;
}
// p!=null 说明找到了符合下标的
if (p != null) {
return p.data;
}
return null;
}
/**
* 根据值删除
* a -> b -> c
* 删除的节点可能是首尾或者中间节点
* a -> c
* b->c
* a->b
*
* @param data 数据
*/
public void remove(T data) {
// 从头开始遍历查找删除
Node<T> p = head;
// 这个作为指针 记录查找到符合值的上一个节点
Node<T> q = null;
while (p != null && p.data != data) {
q = p;
p = p.next;
}
// 说明找到了
if (p != null) {
// 找到的节点可能是首尾 或者中间节点
// q为null 说明即将删除的p是头结点
if (q == null) {
head = p.next;
} else {
// 删除节点的下一个节点引用赋值给上一个节点的next
q.next = p.next;
}
count--;
}
}
public int size() {
return count;
}
/**
* 内部节点类
*
* @param <T> 泛型
*/
private static class Node<T> {
// 数据
private T data;
private Node<T> next;
Node(T data) {
this.data = data;
}
}
}
链表作为大厂面试笔试中较高的出现频率 我觉得也有必要学