1.问题描述
在用java学习链表时,遇到如下问题:
在使用尾插法和循环遍历寻找i位置的结点时,会出现以下三种循环结束的条件
//在insert和delete方法中会出现的循环
//第一种
for(int i=0;i<N;i++)//注意这里的条件为小于N
//第二种
for(int i=0;i<=N;i++)//注意,条件为小于等于N
//第三种
for(int i=0;i<N-1;i++)//注意条件,这里小于N-1
这三种经常会搞混,导致链表API中的尾插法,insert方法和delete方法经常出错,下面是个人的一些见解
2.详细分析
以下所使用的结点类在此给出
class Node {
private Node next;
private int value;
public Node(Node next, int value) {
this.next = next;
this.value = value;
}
}
- 第一种循环 -- 条件为 i < N
//第一种
for(int i=0;i<N;i++)//注意这里的条件为小于N
//这里的N为index,即要插入结点的位置
public void insert(int index,int value) {
Node n = head;
for(int i=0;i<index;i++) {//第一种循环
n = n.next;
}
Node newNode = new Node(n.next, value);
n.next = newNode;
N++;
}
分析: 首先这种循环针对于带头结点的单双向链表,对于循环条件为i=0; i < index(即目标结点位置),在循环结束后n即为目标位置的前一个位置。i从0开始的原因是有头节点。
我们向链表中添加0-9的元素
for(int i=0;i<10;i++) {
linkList.add(i);//add方法是尾插法插入元素
// linkList是链表名称
}
调用insert方法插入10000(选择该数值的原因是便于观察插入位置)
linkList.insert(3,10000);//以向3位置插入结点为例子
循环输出结果如下(头节点不包括在输出中)
即给每个结点赋的值就代表每个结点的索引
现在向insert方法内的循环结束位置添加一条输出语句,如下
public void insert(int index,int value) {
Node n = head;
for(int i=0;i<index;i++) {
n = n.next;
}
//添加输出语句,判断结束时n的位置
System.out.println("循环条件为i < N,在循环结束时的位置为:"+n.value);
Node newNode = new Node(n.next, value);
n.next = newNode;
N++;
}
运行结果如下
3.第二种循环 -- 条件为 i <= N
//第二种
for(int i=0;i<=N;i++)//注意,条件为小于等于N
有了第一种循环的经验,这种循环条件在结束时会停在所指定的位置上
在双向链表中可以使用,单向链表不建议使用,反而会让代码变得繁琐
- 第三种循环 -- 条件为 i < N-1
//第三种
for(int i=0;i<N-1;i++)//注意条件,这里小于N-1
public void insert(int index,int value) {
Node n = head.next;//注意初始化的变化
for(int i=0;i<index-1;i++) {
n = n.next;
}
Node newNode = new Node(n.next, value);
n.next = newNode;
N++;
}
分析:对于第三种循环,初始化不再是n = head,而是n = head.next,忽略了头节点,相当于无头结点的链表使用的循环条件,本质等价于第一种循环条件
3.编写方法的建议
- 对于带表头的单双向链表的插入方法insert(int index , int value)和用下标删除方法delete(int index),在内部循环时建议使用如下
for(int i=0;i<index;i++)//代表头
for(int i=0;i<index-1;i++)//不带表头,所以循环次数-1
给出代码如下(以双向链表为例)
public void insert(int index,int value) {
Node n = head;
if(index == N) add(value);
for(int i=0;i<index;i++) {
n = n.next;
}
Node newNode = new Node(value, n.next, n);
n.next = newNode;
n.next.prev = newNode;
N++;
}
- 对于链表的删除方法delete(int value),建议使用如下循环
while(n.next!=null)
给出代码如下
public int delete(int value) {
if(isEmpty()) return -1;//先判断链表是否为空,防止空指针引用
Node n = head.next;
int i=0;
if(last.value==value) {
last.prev.next = null;
return N--;
}
while(n.next!=null) {
if(n.value==value) {
n.prev.next = n.next;
n.next.prev = n.prev;
return i;
}
i++;
n = n.next;
}
return -1;
}