java语言实现双链表其实很简单,双链表的基本操作有:增、删、改、差、取得指定节点的内容、判空、节点个数、清除、输出。
相对比较难一点的是链表的删除,这里说明一下具体的实现删除可以在外部写一个删除的方法,此处用的方法非常容易理解,不
用像以前那样考虑是否为头结点、是否为空等等,只需要两步操作即可。具体操作在代码中展示:
代码实现如下:
interface ILink {
void add(Object obj); // 增
boolean remove(Object obj); // 删
Object set(int index, Object newData); // 替换
Object get(int index); // 取得指定节点的内容
int contains(Object data); // 判断节点内容是否存在
int size(); // 节点个数
void clear(); // 清除
Object[] toArray(); // 将链表转化为数组
void printLink(); // 输出
}
class LinkImpl implements ILink {
private Node head; // 设置头结点
private Node last; // 设置尾节点
private int size; // 设置节点个数
private class Node { // 通过内部类设置节点
private Node pre; // 前指针
private Node next; // 后指针
private Object data; // 节点数据
public Node(Node pre, Node next, Object data) {
this.pre = pre;
this.next = next;
this.data = data;
}
}
@Override
public void add(Object obj) { // 采用尾插的方式
Node temp = this.last; // 保存当前链表的尾节点
Node newNode = new Node(temp, null, obj); // 设置新节点
this.last = newNode; // 因为是尾插的方式,先让链表的尾节点指向新节点
if (this.head == null) { // 如果链表为空,直接插入
this.head = newNode;
} else {
temp.next = newNode; // 如果链表不为空,让当前链表的尾节点指向新节点
}
this.size++; // 插入节点完成,节点个数++
}
@Override
public boolean remove(Object obj) {
if (obj == null) { // 若要删除的节点为空节点
for (Node temp = head; temp != null; temp = temp.next) {
if (temp.data == null) { // 找到空节点
unLink(temp); // 删除
return true;
}
}
} else { // 要删除节点不为空
for (Node temp = head; temp != null; temp = temp.next) {
if (obj.equals(temp.data)) { // 找到该节点
unLink(temp); // 删除该节点
return true;
}
}
}
return false;
}
@Override
public Object set(int index, Object newData) {
if (!isLinkIndex(index)) { // 判断该节点是否存在
return null;
}
Object result = node(index).data; // 保存该节点的data
node(index).data = newData; // 设置新的data
return result; // 返回
}
@Override
public Object get(int index) {
if (!isLinkIndex(index)) { // 判断该节点是否存在
return null;
}
return node(index).data; // 返回该下标的节点
}
@Override
public int contains(Object data) {
int i = 0;
if (data == null) { // 若要查找节点为空节点
for (Node temp = head; temp != null; temp = temp.next) {
if (temp.data == null) {
return i;
}
i++;
}
} else { // 若要查找节点不为空节点
for (Node temp = head; temp != null; temp = temp.next) {
if (data.equals(temp.data)) {
return i;
}
i++;
}
}
return -1; // 找不到返回-1
}
@Override
public int size() {
return this.size;
}
@Override
public void clear() {
for (Node temp = head; temp != null;) {
Node flag = temp.next;
temp.pre = temp = temp.next = null;
temp = flag;
this.size--;
}
}
@Override
public Object[] toArray() {
Object[] result = new Object[this.size];
int i = 0;
for (Node temp = head; temp != null; temp = temp.next) {
result[i++] = temp.data;
}
return result;
}
@Override
public void printLink() {
Object[] data = this.toArray();
for (Object temp : data) {
System.out.println(temp);
}
}
// ==========================
// 根据下标返回节点
private Node node(int index) {
if (index < (this.size >> 1)) { // 小于中间节点下标,从前往后找
Node temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp;
} else {
Node temp = this.last;
for (int i = this.size - 1; i > index; i--) {
temp = temp.pre;
}
return temp;
}
}
// 判断传入结点的下标是否存在
private boolean isLinkIndex(int index) {
return index >= 0 && index < this.size;
}
// 删除节点
private void unLink(Node node) {
Node nodePre = node.pre; // 保存要删除节点的前一个节点
Node nodeNext = node.next; // 保存要删除节点的下一个节点
// 先判断前指针(pre指针域)
if (node.pre == null) { // 如果要删除节点为头结点
this.head = nodeNext; // 当前头结点指向该节点下一个节点
} else {
nodePre.next = nodeNext; // 前一个节点的next域指向该节点的next
node.pre = null; // 释放该节点的前指针域(pre)
}
// 再判断后指针(next指针域)
if (node.next == null) { // 如果要删除的节点为尾节点
this.last = nodePre; // 当前的尾节点指向该节点的前一个节点
} else {
nodeNext.pre = nodePre; // 下一个节点的pre域指向该节点的pre
node.next = null; // 释放该节点的后指针域(next)
}
node.data = null; // 释放该节点的值域(data)
this.size--; // 节点个数减1
}
}
代码检测:
1、插入的检测(采用尾插):
代码:
public class Test{
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
link.printLink();
}
}
运行结果:
2、删除操作的检测:(如果删除成功返回true,失败返回false)
代码:
public class Test{
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
link.remove("节点2"); // 删除节点2
System.out.println(link.remove("节点6")); // 删除一个不存在的节点-->会返回false
link.printLink();
}
}
运行结果:
3、替换(根据指定位置替换)
代码:
public class Test{
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
link.set(2,"Hello World!!!"); // 把下标为2的节点改为Hello World
link.printLink();
}
}
运行结果:
4、 查找(根据下标查找该节点的值)
代码:
public class Test{
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
System.out.println(link.get(2)); // 查找下标为2的节点的值
}
}
运行结果:
5、判断链表中该节点是否存在(存在,返回该节点的值,不存在,返回null)
public class Test{
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
System.out.println(link.get(2)); // 查找下标为2的节点的值
System.out.println(link.get(5)); // 查找一个不存在的节点
}
}
运行结果:
6、输出节点的个数(清除节点后输节点的个数)
代码:
public class Test {
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("头结点");
link.add("节点1");
link.add("节点2");
link.add("节点3");
link.add("尾节点");
link.printLink();
System.out.println("==========================");
System.out.println("未清除之前节点个数为:" + link.size());
System.out.println("==========================");
link.clear();
System.out.println("清除后节点个数为:" + link.size());
}
}
运行结果: