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;
        }
}
  1. 第一种循环 -- 条件为 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位置插入结点为例子

循环输出结果如下(头节点不包括在输出中)




java 带下标的集合 java下标怎么打_数据结构


即给每个结点赋的值就代表每个结点的索引

现在向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++;
    }

运行结果如下


java 带下标的集合 java下标怎么打_链表_02


3.第二种循环 -- 条件为 i <= N

//第二种
for(int i=0;i<=N;i++)//注意,条件为小于等于N

有了第一种循环的经验,这种循环条件在结束时会停在所指定的位置上

在双向链表中可以使用,单向链表不建议使用,反而会让代码变得繁琐

  1. 第三种循环 -- 条件为 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.编写方法的建议

  1. 对于带表头的单双向链表的插入方法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++;
    }
  1. 对于链表的删除方法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;
    }