其实我不想写但是还是要写的的前景知识:
什么是链表
链式存储结构是一种基于指针实现的存储结构。我们把一个数据结构和一个指针称为结点。链式存储结构用指针把相互关联的点连接起来。链式存储结构的线性表就称为链表
什么是单链表
链表的每个结点中只包含一个指针域,叫做单链表(即构成链表的每个结点只有一个指向直接后继结点的指针)。结点结构如下:
关于头节点和头指针
头结点:1.头结点是为了操作方便和统一设置的,放在第一个有数据元素的结点之前,其数据域一般无意义(有些情况下也可以用来存放链表长度,用作监视哨等)
2.首元结点也就是第一个有数据元素的结点,是头结点后面第一个结点
3.头结点不是链表必须的,头结点不算在链表长度中。
头指针:1.在线性表的链式存储结构中,头指针是指向第一个结点的指针,假如链表有头结点,则头指针就是指向链表头节点的指针
2.头指针具有标识作用,常用头指针冠以链表的名字
3.头指针是指针,不是结点,没有数据域。
4.无论链表是否为空,头指针均不为空。头指针是链表必需的元素。可以理解为头指针是存储在栈中的地址,用来访问链表。
5.因为链表在物理存储结构上不连续,只是逻辑是连续。而头指针很大一部分意义在于,我们访问链表时需要知道链表由何处开始访问,从而由头指针指向头结点(如果没有则指向第一个有数据元素的结点),每个结点的指针依次指向下一个结点,这样可以通过指针和给定下标找到需要的结点。所以说头指针是必须的元素,没有头指针无法访问链表。
不带头结点的链表:
带头结点的链表:
(头指针和头节点的区分可以参考:https://blog.csdn.net/hitwhylz/article/details/12305021)
ok前面的说完了直接开始自定义链表
1.首先自定义结点类。单链表是有一个个节点组成的,因此设计单链表必须要先设计结点类。结点类的变量有:指针元素,数据元素
1.构造方法
2.设置指针域和数据域的方法
3.得到指针域和数据域的方法
简单易懂,贴个代码
public class Node {
Object data;//数据域
Node next;//指针域
public Node() {
}
public Node(Object obj) {
super();
this.data = obj;
}
//获取当前节点数据域的值
public Object getData() {
return data;
}
//设置当前节点数据域的值
public void setData(Object data) {
this.data = data;
}
//获取当前节点的指针域
public Node getNext() {
return next;
}
//设置当前节点的指针域
public void setNext(Node next) {
this.next = next;
}
}
2.单链表类
定义头节点root(最好定义有头结点的链表,方便操作),尾节点last(也可以理解为当前结点),指针元素next(为设计方便),节点个数size
方法根据自己的需求设计,如:
1.末尾添加结点
2.取出数据
3.删除数据
4.反转数据
......
设计方法时并没有难度,主要是在指针域的置空,删除和赋值中弄来弄去,容易混淆。
1.首先为了节省代码量,写一个方法判断下标index是否符合要求
//单向链表
public class LinkList {
static Node root = new Node();
Node next;
Node last;
int size;
//返回size长度的方法
public int size() {
return size;
}
// 判断index是否合格
public void checkindex(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("参数不符合要求");
}
}
2.在末尾添加,假如没有结点即size=0,直接让最先创建的root头结点指向加入的新结点,置新节点为last结点。
// 末尾添加
public void add(Object obj) {
next = new Node(obj);
if (size == 0) {
root.setNext(next);
last = next;
} else {
last.setNext(next);
last = next;
}
size++;
}
3.根据index得到数据,以及更新数据。根据index在for循环中遍历得到下标为index的结点,返回数据或者设置数据
先声明的node = root.getnext()为index=0 。当i=0时得到index=1的node。当 i=index-1时得到index的node。
// 得到数据
public Object get(int index) {
checkindex(index);
Node node = root.getNext();
// 找到下标为index
for (int i = 0; i < index; i++)
node = node.getNext();
return node.getData();
}
// 设置数据
public void update(int index, Object obj) {
checkindex(index);
Node node = root.getNext();
// 找到下标为index
for (int i = 0; i < index; i++)
node = node.getNext();
node.setData(obj);
}
4.移除数据。分情况判断。
public void remove(int index) {
checkindex(index);
Node node = root.getNext();
for (int i = 0; i < index; i++)// 找到下标为index
node = node.getNext();
Node node1 = root.getNext();
for (int i = 0; i < index - 1; i++)// 找到下标为index-1
node1 = node1.getNext();
if (index != 0) {//假如index不等于0
if (index != size - 1) {// 假如不是最后一个
Node node2 = node.getNext();// index的下一个节点
node1.setNext(null);
node1.setNext(node2);
} else {// 最后一个节点
node1.setNext(null);
}
node.setData(null);
node.setNext(null);
} else {//假如index等于0
root.setNext(null);
root.setNext(node.next);
node.setNext(null);
node.setData(null);
}
size--;
}
5.插入。创建一个要插入的结点。找到要插入结点位置index和前一结点位置index-1 。若要插入点是最后一个,直接将原最后结点指向新加入结点。若不是,置空index-1的结点指针,再设置指向要加入结点,设置要加入结点指针指向原index结点。
public void insert(int index, Object obj) {
checkindex(index);
Node node = root.getNext();
for (int i = 0; i < index; i++) // 找到下标为index的点
node = node.getNext();
Node node1 = root.getNext();
for (int i = 0; i < index - 1; i++)// 找到下标为index-1
node1 = node1.getNext();
Node node2 = new Node(obj);// 将要插入的节点
if (index == size - 1) {
node.setNext(new Node(obj));
} else {
node1.setNext(null);
node1.setNext(node2);
node2.setNext(node);
}
size++;
}
6.反转(递归方法反转)
1.如果是空链或者尾节点,返回结点。当然大多数情况是返回尾节点。
2.设置当前结点root.next指向前一结点,并将前一结点root的指针置空,不再指向root.next,这就完成了尾节点的反转。
3.递归。得到新的尾节点,反转。依次类推。直到前一结点为null,表示已经反转到了头指针,反转结束。
4.反转结束时,将头指针指向新的首元结点。
public Node reverse(Node root) {
// root看作是前一个节点,root.getNext()为当前节点,reHead是反转后新链表的头结点
if (root == null || root.next == null) {
// 若为空链或者当前节点在尾节点,则直接返回
return root;
} else {
Node reroot = reverse(root.next);// 先反转后续节点head.next
root.next.next = root;// 将当前节点的指针域指向前一节点
root.next = null;// 将当前节点的指针域置空
return reroot;// 反转后新链表的头结点
}
}