单链表中结点类型的描述如下:
class LNode {
Integer data;
LNode next;
}
1.头插法建立单链表。
从一个空表开始,生成新节点,并将读取到的数据存放到新节点的数据域中,然后将新节点插入到当前链表的表头,即头结点之后,如图所示:
算法如下:
public LNode createList1() {
LNode l = new LNode();
l.next = null;
for(int i=0; i<10; i++) {
LNode tmp = new LNode();
tmp.data = i;
tmp.next = l.next;
l.next = tmp;
}
return l;
}
2.尾插法建立单链表
将新节点插入到当前链表的表尾,为此增加一个尾指针r,使其指向当前链表的表尾,如图:
算法如下:
public LNode createList2() {
LNode l = new LNode();
LNode r = l;
for(int i=0; i<10; i++) {
LNode tmp = new LNode();
tmp.data = i;
r.next = tmp;
r = tmp;
}
r.next = null;
return l;
}
3.按序号查找节点值。
在单链表中从第一个节点出发,顺指针next域住个往下搜索,直到找到第i个节点为止,否则返回最后一个节点指针域NULL。
public LNode getElem(LNode l, int i) {
if(i < 1) { //如果要查找的元素的位置比1还小,返回NUll
return null;
}
int j = 1;
LNode p = l.next;
while(null != p && j < i) { //从第一个节点开始查找,查找第i个节点
p = p.next;
j++;
}
return p; //返回第i个节点的指针,如果i大于表长,p=NULL
}
4.按值查找表节点
从单链表第一个节点开始,由前往后依次比较表中各节点数据域的值,若某节点数据域的值等于给定值e,则返回该节点指针,若整个单链表中没有这样的节点,则返回NULL。
public LNode locateElem(LNode l, int e) {
LNode p = l.next;
while(null != p && e != p.data) {
p = p.next;
}
return p;
}
5.插入节点操作
插入操作是将值为x的新节点插入到单链表的第i个位置。首先检查插入位置的合理性,然后找到待插入位置的前驱节点,即第i-1个元素,再在其后面插入新的节点。
算法首先调用getElem(LNode l)
方法查找第i-1个节点,假设返回的第i-1个节点为p, 然后令新节点s的指针指向p的后继节点,再令节点p的指针域指向新插入的节点s,如图所示:
(1) p = getElem(l, i-1);
(2) s.next = p.next;
(3) p.next = s;
本算法主要开销在查找第i-1个元素,时间复杂度为O(n)。
此外,可以采用另一种方式将其转换为后插操作来实现,设带插入节点为s,将s插入到p的前面。我们采用将s插入到p的后面的方式,然后将p.data与s.data交换即可,这样既能满足逻辑关系,又能使得时间复杂度为O(1),思想如下:
s.next = p.next;
p.next = s;
temp = p.data;
p.data = s.data;
s.data = temp;
6.删除节点元素
删除操作是将单链表的第i个节点删除,先检查其删除位置的合法性,然后查找表中第i-1个节点,即被删除节点的前驱节点,再将其删除,如下图所示:
假设节点p为找到的被删除节点的前驱节点,为了实现这一操作后的逻辑关系的变化,仅需要修改p的指针域,即将p的指针域next指向q的下一个节点。
代码片段如下:
p = getElem(l, i-1);
q = p.next;
p.next = q.next;
q.next = null;
和插入算法一样,时间主要消耗在查找操作上,时间复杂度为O(n)。
其实,删除节点p的操作可以用删除p的后继节点操作来实现,实质就是将其后继节点的值赋予其自身,然后删除后继节点,使得时间复杂度为O(1)。
q = p.next;
p.data = q.data; //将后继元素的值赋予该元素
p.next = q.next;
q.next = null;