实战写了一个自定义单链表
存在问题:
- 由于还没到泛型,暂时先用 Object 代替
- 没到泛型,链表中指定内容删除结点的功能目前还是残缺的,待完善,不过通过下标删除结点这个还是没问题的
Node 结点类
/**
* @Author IceClean
* @Date 2021-2-24
* 单链表的结点
*/
public class Node
{
private Object data;
private Node next;
/**
* 创建空结点
*/
public Node() { }
/**
* 创建有数据和指向的结点
* @param data 该节点的数据
* @param next 该节点的指向
*/
public Node(Object data, Node next)
{
this.data = data;
this.next = next;
}
/**
* 使链表指向新的结点
* @param next 下一个结点
*/
public void setNext(Node next)
{
this.next = next;
}
/**
* 修改该结点的数据
* @param data 该结点的新数据
*/
public void setData(Object data)
{
this.data = data;
}
/**
* 获取下一个结点
* @return 下一个结点
*/
public Node getNext() {
return next;
}
/**
* 获取该节点的数据
* @return 该节点的数据
*/
public Object getData() {
return data;
}
}
Link 链表类
/**
* @Author IceClean
* @Date 2021-2-24
* 单链表对象
*/
public class Link
{
private Node head; // 头节点
private int length; // 链表长度
/**
* 创建空链表
*/
public Link()
{
head = new Node(null, null);
}
/**
* 创建有头节点的链表
* @param data
*/
public Link(Object data)
{
head = new Node(data, null);
length = 1;
}
/**
* 默认向尾部添加数据
* @param data 新节点的数据
*/
public void add(Object data)
{
if(length == 0) head = new Node(data, null); // 长度为 0 的话链表为空,应初始化头节点
else
{
Node temp = head; // 创建一个临时的结点对象
Node newNode = new Node(data, null); //设置新结点的数据,并且将其指向空变成新的尾节点
while(temp.getNext()!=null) temp = temp.getNext(); // 遍历到尾节点处
temp.setNext(newNode); // 将其指向新的尾结点
}
length++; // 长度自增 1
}
/**
* 指定位置添加数据
* @param index
* @param data
*/
public void add(int index, Object data)
{
if(index == 0 && length == 0) head = new Node(data, null); // 从头节点处加入且长度为 0 的话,应初始化头节点
else if(index<0 || index>length) throw new IndexOutOfBoundsException("下标 "+index+" 越界啦!!!"); // 抛数组越界异常
else
{
Node newNode = new Node(data, null); // 创建新节点
if(index == 0) // 等于 0 的话表示在头部插入,应该把头节点换了
{
newNode.setNext(head);
head = newNode;
}
else // 不等于 0 表示从中间插入,头节点不用换
{
Node temp = head; // 使用临时结点
int nowIndex = 0;
while(nowIndex < index-1) // 遍历到指定结点的前一个位置
{
temp = temp.getNext();
nowIndex++;
}
newNode.setNext(temp.getNext()); // 将新结点指向当前结点的下一个结点
temp.setNext(newNode); // 将当前结点指向新节点
}
}
length++; // 成功到这里的话说明参数没问题,长度自增 1
}
/**
* 采用头插法插入结点
* @param data 插入结点的数据
*/
public void addHead(Object data)
{
add(0, data);
}
/**
* 采用尾插法插入数据
* @param data 插入结点的数据
*/
public void addLast(Object data)
{
add(length, data);
}
/**
* 按内容删除结点
* @param data 要删除结点的内容
* @return 删除成功返回 true ,否则 false
*/
public boolean remove(Object data)
{
int index = 0; // 存放指定内容对应结点的下标
Node temp = head; // 存放临时结点
while(temp!=null)
{
// 找出匹配的结点,根据需求可重写 equals 方法
if(temp.getData().equals(data))
return remove(index); // 删除结点,并返回
temp = temp.getNext();
index++;
}
return false; // 到这里说明删除不成功
}
/**
* 通过下标删除结点
* @param index 要删除结点的下标
* @return 删除成功返回 true ,否则 false
*/
public boolean remove(int index)
{
if(index<0 || index>length-1) throw new IndexOutOfBoundsException("下标 "+index+" 越界啦!!!"); // 抛数组越界异常
else
{
if(index == 0) head = head.getNext(); //如果删除的是头结点的话,头结点应换掉
else
{
Node temp = head; // 使用临时结点
int nowIndex = 0;
while(nowIndex < index-1) // 遍历到指定结点的前一个位置
{
temp = temp.getNext();
nowIndex++;
}
temp.setNext(temp.getNext().getNext()); // 当前结点指向要删除结点的下一个结点,完成删除
}
length--; // 长度减 1
return true;
}
}
/**
* 获取链表的长度
* @return 链表的长度
*/
public int length() {
return length;
}
/**
* 返回整条链表的数据
* @return 整条链表的数据
*/
@Override
public String toString() {
StringBuilder allData = new StringBuilder();
Node temp = head; // 使用临时结点
while(temp!=null)
{
allData.append(temp.getData()).append(" -> ");
temp = temp.getNext();
}
allData.delete(allData.length()-4, allData.length()); // " -> " 占 4 个字符
return allData.toString();
}
}
测试:
Link link = new Link();
System.out.println(link); // 空链表
link.add("第一结点");
link.add("第二结点"); // 以默认形式加入两个结点
System.out.println(link);
System.out.println(link.length()); // 获取链表长度
link.add(0, "插入的第三结点");
System.out.println(link);
link.add(1, "插入的第四结点");
System.out.println(link);
link.add(link.length(), "插入的第五结点");
System.out.println(link); // 测试两个端点以及从中间插入
link.addHead("插入的头结点");
System.out.println(link);
link.addLast(1200);
System.out.println(link); // 测试头尾插入两个方法
System.out.println(link.remove(2)); // 测试删除方法
System.out.println(link);
运行结果:
第一结点 -> 第二结点
2
插入的第三结点 -> 第一结点 -> 第二结点
插入的第三结点 -> 插入的第四结点 -> 第一结点 -> 第二结点
插入的第三结点 -> 插入的第四结点 -> 第一结点 -> 第二结点 -> 插入的第五结点
插入的头结点 -> 插入的第三结点 -> 插入的第四结点 -> 第一结点 -> 第二结点 -> 插入的第五结点
插入的头结点 -> 插入的第三结点 -> 插入的第四结点 -> 第一结点 -> 第二结点 -> 插入的第五结点 -> 1200
true
插入的头结点 -> 插入的第三结点 -> 第一结点 -> 第二结点 -> 插入的第五结点 -> 1200
以上纯个人思路,如果存在漏洞或者有改善方法的话,还请多多指出~
不断积累实战经验 —— Level Up ⬆⬆⬆ (寒冰小澈)