算法和数据结构是程序员逃不过的一个坎,因此趁着闲余时间,开始学习基础的算法和数据结构。这里记录下本身实现简单的单项链表的过程,若有错误,敬请指正。node
在Java中,经常使用的数据容器里面,跟链表关系紧密的当属LinkedList了,它的底层实现为双向链表,这里就以它为参照物,实现本身的简单的单向链表。另外,还须要支持增删改查、获取大小等功能。 以下所示,先定义了增删改查方法支持的范围。git
/**
* 增删改查方法
* add(index, E) index >= 0 && index <= size
* remove(index) index >= 0 && index < size
* set(index, E) index >= 0 && index < size
* get(index) index >= 0 && index < size
*/
复制代码
结点的定义很简单,这里为了简单起见,不支持泛型,结点存储的数据类型固定为int。程序员
private static class Node {
private int item;
private Node next;
public Node(int item, Node next) {
this.item = item;
this.next = next;
}
public void setItem(int item) {
this.item = item;
}
@Override
public String toString() {
return String.valueOf(this.item);
}
}
复制代码
既然是单向链表,容器中只须要持有first引用便可,不须要last引用。再须要定义一个size用来表示链表大小;另外,为了方便查看测试结果,咱们重写toString方法。代码以下:github
public class SingleLinkedList {
private int size = 0;
private Node first = null;
...
}
复制代码
/**
* 返回当前容器大小。
* @return
*/
public int getSize() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (first != null) {
Node curr = first;
while (curr != null) {
sb.append(String.valueOf(curr));
if (curr.next != null) {
sb.append(",").append(" ");
}
curr = curr.next;
}
}
sb.append("]");
return sb.toString();
}
复制代码
在已有数据中获取数据,这个最为简单,因为单向链表容器中只存储首个结点first引用,因此不能直接角标index获取,须要从first中根据角标依次遍历,代码以下:算法
/**
* 获取角标上的数据。
*
* @param index
* @return
*/
public Node get(int index) {
checkElementIndex(index);
Node x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
}
复制代码
与另外三组方法(获取、修改、删除)均不一样,增长方法不单单要支持链表现有的数据范围,并且还须要支持角标为size的位置。 在当前位置插入结点,须要获取到前一个结点,让前一个结点的next指向新增的结点,再让新增结点的next指向该位置原来的结点。须要注意一些特殊状况的处理,还有不要忘了size的数值增长。代码以下:bash
/**
* 在角标为index的位置插入数据
*
* @param index
* @param value
* @return
*/
public boolean add(int index, int value) {
checkPositionIndex(index);
if (index == 0) {
if (first == null) {
first = new Node(value, null);
} else {
Node next = get(0);
Node node = new Node(value, next);
first = node;
}
} else {
Node prev = get(index - 1);
Node node = new Node(value, prev.next);
prev.next = node;
}
size++;
return true;
}
复制代码
修改方法也很简单,只需根据角标获取已经存在结点,而后将结点中存储的数据修改便可,代码以下:数据结构
public boolean set(int index, int value) {
checkElementIndex(index);
Node node = get(index);
node.setItem(value);
return true;
}
复制代码
删除当前位置的结点,核心是让上一个结点的next指向当前位置的下一个结点便可,与添加方法相似,须要作好极端状况的处理,代码以下:app
public boolean remove(int index) {
checkElementIndex(index);
// size > 0
if (getSize() == 1) {//size == 1
first = null;
} else {// size > 1
if (index == 0) {// 删除第一个数据
first = first.next;
} else if (getSize() - 1 == index) {// 删除最后一个数据
Node prev = get(index - 1);
prev.next = null;
} else {// 删除中间的数据
get(index - 1).next = get(index).next;
}
}
size--;
return true;
}
复制代码
为了实现单向链表的反转,在遍历的过程当中,让每一个节点的next指向上一个结点。代码以下:ide
/**
* 反转链表
*
* @return
*/
public Node reverseList() {
Node prev = null;
Node curr = first;
while (curr != null) {
Node temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
logFromHead("reverseList", prev);
return prev;
}
复制代码
项目中搜索SingleLinkedList便可。 github传送门 github.com/tinyvampire…学习
gitee传送门 gitee.com/tinytongton…