本篇中的引用是为了引起注意,不是引用
链表是一种线性表,不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针。
优点:使用链表结构可以不必要预先知道表的长度(大小),可以无限添加,可以实现灵活的内存动态管理,修改操作很简单。
缺点:查找困难,每一次查找都需要从头节点开始(或尾节点,这里由于是单向链表,所以只能从头节点开始遍历),且由于每一个节点都占用一块内存,所以内存开销较大。
单向链表采用下图方法进行存储:
也就是说,next是一个Node型数据,他就是下一个节点(指向下一个节点)
而我们要遍历,就从第一个开始,一直next,next,知道next为null时就遍历到头了。
若是我们要插入节点:insert(Node node, int index),首先要考虑是在哪里添加节点,如果我们要往头部之前添加,那么我们重新定义一个Head为要添加的节点,然后将新的Head的next赋值为之前的Head就可以了。如果我们要往中间添加节点,那么直接改变添加的位置之前的节点的next的赋值即可。上图:
这里可能有人会问,如果往尾部插入节点不用特殊考虑吗,答案是不用,因为当前尾节点指向null,我们把他当成普通节点处理,那么他会指向要插入的节点,而由于它的next为null,所以会导致新插入的节点的next指向null,没有特殊情况。
而移除结点:remove(Node node),也是相同的道理,找到相应的前后节点,改变指针指向即可
添加节点:add(Node node),则直接遍历链表找到尾节点,然后将尾节点的next指向要添加的节点即可
根据下标查找结点:get(Int index),用来直接找到对应下标位置的节点,这个方法可以直接用在:插入,移除,添加以及自己扩展的方法中,只要需要获取相关的下表,都可以使用调用这个方法
打印链表和获取长度很简单,直接代码给出
下面是代码干货,注释很详细,可以直接阅读:
可以自己扩充方法。
Node.java:
public class Node {
// 存放数据
private Object data;
// 存放下一个节点
private Node nextNode;
// 构造方法
public Node(Object data) {
this.data = data;
}
// setter getter
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNextNode() {
return nextNode;
}
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
// 打印节点信息
public String toString() {
return "Node [data=" + data + "]";
}
}
LinkedList.java
public class LinkedList {
// 头节点
private Node head;
// 节点数
private int count = 0;
// 添加节点
public void addNode(Node node) {
if (this.head == null) { // 如果没有头节点head,则添加的节点为头节点
this.head = node;
count++;
}else {
Node temp = head;
for(int i=0; i<count-1; i++) { // 如果有头节点head,则遍历到最后一个Node的位置,追加传入的Node
temp = temp.getNextNode();
}
temp.setNextNode(node); //追加节点
count++;
}
}
// 根据下标查找结点
public Node get(int index) {
if (index < 0 || index > count-1) {
throw new RuntimeException("输入非法");
}
/*
* 这段代码去掉不会影响内容,因为下面new的Node已经将head赋值给它
// 如果index是0,返回头节点
if (index == 0) {
return this.head;
}
*/
Node n = this.head;
for (int i=0; i<index; i++) { //顺着头节点,根据传进来的位置,一直摸到要找的节点
n = n.getNextNode();
}
return n;
}
// 查找节点是否在链里
public boolean contains(Node node) {
// 链表长度为0
if (count == 0) {
return false;
}
Node n = head;
// 对比data,不相同就查找下一个节点
/*
* 写法1,通过count判定
for (int i=0; i<count; i++) {
if (n.getData().equals(node.getData())) {
return true;
}
n = n.getNextNode();
}
*/
// 写法2,通过next是否为空判定
do {
if (n.getData().equals(node.getData())) {
return true;
}
}while((n = n.getNextNode()) != null); // 移动到下个节点的位置,若该节点(下个节点)为空则跳出循环
return false;
}
// 从链中移除节点
// 关注头节点
public void remove(int index) {
if (index < 0 || index > count-1) {
throw new RuntimeException("输入非法");
}
if (index == 0) {
this.head = this.head.getNextNode();
count--;
}else {
//找到要删除节点的前一个节点
Node n = this.get(index-1);
//跳过要删除的节点指向要删除节点的下一个节点
n.setNextNode(n.getNextNode().getNextNode());
count--;
}
}
// 插入节点
public void insert(Node node, int index) {
if (index < 0 || index > count) {
throw new RuntimeException("位置有误,无法插入节点");
}
// 插入头节点
if (index == 0) {
node.setNextNode(head); // 将插入节点的next指向当前的head头节点
this.head = node;
count++;
}else { // 插入到中间位置
Node n = this.get(index-1); // 找到要插入节点位置的前节点
n.setNextNode(node); // 将前节点的next指向插入的节点
node.setNextNode(n.getNextNode()); //将插入的节点的next指向后节点
count++;
}
}
// 打印所有节点
public void printAllNode() {
System.out.print("节点——");
Node n = head;
for (int i=0; i<count; i++) {
System.out.print( " data:" + n.getData() + "->");
n = n.getNextNode();
}
System.out.println();
}
// 链表长度
public int length() {
return count;
}
}
Test.java
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(new Node("一"));
list.add(new Node("二"));
list.add(new Node("三"));
System.out.println(list.get(0).toString());
System.out.println(list.get(1).toString());
System.out.println(list.get(2).toString());
list.printAllNode();
System.out.println(list.contains(new Node("一")));
System.out.println(list.contains(new Node("二")));
System.out.println(list.contains(new Node("三")));
System.out.println(list.contains(new Node("四")));
list.remove(0);
list.printAllNode();
list.remove(1);
list.printAllNode();
list.insert(new Node("一"), 0);
list.printAllNode();
//list.insert(new Node("三"), 3); // 这里会报错:位置有误,无法插入节点
list.insert(new Node("三"), 2);
list.printAllNode();
}
}
测试结果:
Node [data=一]
Node [data=二]
Node [data=三]
节点—— data:一-> data:二-> data:三->
true
true
true
false
节点—— data:二-> data:三->
节点—— data:二->
节点—— data:一-> data:二->
节点—— data:一-> data:二-> data:三->
如有不正确之处还请大佬指正,谢谢。