当我们在解决下标问题时,我们在想些什么。

2016-06-05  本文已影响130人  三年两度

很多时候,知其然不知所以然是个很不好的东西,所以有了本文。
本文将帮助解决顺序表和链表的问题。
首先是 顺序表:

public int insert( int i ,T x ){                                          //插入方法
            i= i-1;                                                       //加上这一句。
            if( x== null){
                 throw new NullPointerException( "x==null");
           }
            if( i<0 ){ i=0; }
            if( i> this. n ){ i = this. n; }
           Object[] source = this. element;
            if( this. n == element. length){
                 this. element = new Object[ source. length*2];
                 for( int j=0; j< i; j++){
                      this. element[ j] = source[ j];
                }
           }
            for( int j = this. n-1; j>= i; j--){
                 this. element[ j+1] = source[ j];
           }
            this. element[ i] = x;
            this. n++;
            return i;
     }
public T remove(int i ){                                                  //输出方法
            i= i-1;                                                              //同样加这一句                    
            if( this. n>0&& i>=0&& i< this. n){
                T old = (T)this . element[ i];
                 for( int j= i; j< this. n-1; j++){
                      this. element[ j] = this. element[ j+1];
                }
                 this. element[ this. n-1] = null;
                 this. n--;
                 return old;
           }
            return null;
     }
      public void clear(){
            this. n = 0;
     }

这个很简单,把输入的内容减一位,变成人们理解的从一开始的顺序就好。

下面的链表有些难度。
因为顺序表的代码里,已经计算好了顺序表的长度。
所以在做容错处理的时候,可以直接把输入值和长度相比较。

而我写链表的时候,并没有链表的长度属性。
所以,在做容错处理的时候,要首先把链表的长度通过遍历的方法计算出来。
如下:

class Node<T>                             //单链表结点类,T指定结点的元素类型
{
    public T data;
    public Node<T> next;

    public Node(T data, Node<T> next)            //构造结点,data指定数据元素,next指定后继结点
    {
        this. data = data;                        //T对象引用赋值
        this. next = next;                        //Node<T>对象引用赋值
    }
    public Node()
    {
        this( null, null);
    }
    public String toString()                     //返回结点数据域的描述字符串
    {
        return this. data.toString();
    }
}
public class SinglyList<T> extends Object
{
    public Node<T> head;                                   //头指针,指向单链表的头结点
      private int n;
    //(1)构造方法
    public SinglyList()                                    //构造方法,构造空单链表
    {
        this. head = new Node<T>();                         //创建头结点,data和next值均为null

    }

    //构造单链表,由values数组提供元素,忽略其中空对象。采用尾插入,单链表元素次序与数组元素次序相同
    public SinglyList(T[] values)
    {
        this();                                            //创建空单链表,只有头结点
        Node<T> rear= this. head;                            //rear指向单链表最后一个结点
        for ( int i=0; i< values. length; i++)                //若values.length==0,构造空链表
            if ( values[ i]!= null)
            {
                rear. next= new Node<T>( values[ i], null);     //尾插入,创建结点链入rear结点之后
                rear = rear. next;                          //rear指向新的链尾结点
            }
    }

    public boolean isEmpty()                               //判断单链表是否空,O(1)
    {
        return this. head. next== null;
    }

    public T get( int i)                                    //返回第i个元素,0≤i<表长度。若i越界,返回null。O(n)
    {
        Node<T> p= this. head. next;
        for ( int j=0; p!= null && j< i; j++)                 //遍历单链表,寻找第i个结点(p指向)
            p = p. next;
        return ( i>=0 && p!= null) ? p. data : null;          //若p指向第i个结点,返回其元素值
    }

    //设置第i个元素为x,0≤i<n。若i越界,抛出序号越界异常;若x==null,抛出空对象异常。O(n)
    public void set( int i, T x)
    {
        if ( x== null)
            throw new NullPointerException( "x==null");     //抛出空对象异常
        Node<T> p= this. head. next;
        for ( int j=0; p!= null && j< i; j++)                 //遍历寻找第i个结点(p指向)
            p = p. next;
        if ( i>=0 && p!= null)
            p. data = x;                                    //p指向第i个结点
        else throw new IndexOutOfBoundsException( i+ "");    //抛出序号越界异常
    }

    //返回单链表所有元素的描述字符串,形式为“(,)”。覆盖Object类的toString()方法,O(n)
    public String toString()
    { n=0;
        String str= this.getClass().getName()+ "(";          //返回类名
//        String str="(";
        for (Node<T> p= this. head. next;  p!= null;  p= p. next) //p遍历单链表
        {   str += p. data.toString();
            n= n+1;
            if ( p. next!= null)
                str += ",";                                //不是最后一个结点时,加分隔符
        }
        return str+ ")";                                    //空表返回()
    }

    public int size()                                      //返回单链表长度,O(n)
    {
        int i=0;
        for (Node<T> p= this. head. next;  p!= null;  p= p. next) //p遍历单链表
            i++;
        return i;
    }
    //插入x作为第i个元素,x!=null,返回插入结点。
    //对序号i采取容错措施,若i<0,插入x在最前;若i>n,插入x在最后。O(n)
    public Node<T> insert( int i, T x)
    { i= i-1;
        if ( x== null)
            throw new NullPointerException( "x==null");     //抛出空对象异常
        Node<T> front= this. head;                           //front指向头结点
        for ( int j=0;  front. next!= null && j< i;  j++)      //寻找第i-1个或最后一个结点(front指向)
            front = front. next;
        front. next = new Node<T>( x, front. next);           //在front之后插入值为x结点,包括头插入、中间/尾插入
        return front. next;                                 //返回插入结点
    }
    public Node<T> insert(T x)                             //在单链表最后添加x对象,O(n)
    {
        return insert(Integer. MAX_VALUE, x);
                             //调用insert(i,x),用整数最大值指定插入在最后,遍历一次,i必须容错
//        return insert(this.count(), x);                  //需遍历单链表两次,效率较低
    }
    public T remove( int i)    //删除第i个元素,0≤i<n,返回被删除元素;若i越界,返回null。O(n)
    {
        int n=0;
        Node<T> front= this. head;
        Node<T> end= this. head;
        while( end. next!= null){
            n= n+1;
            end= end. next;
        }
        if( i> n){ i= n;}
        i= i-1;
        for ( int j=0; front. next!= null && j< i; j++){      //遍历寻找第i-1结点(front指向)
            front = front. next;
            }
        if( i<0 ){ i=0; }
        if( i> n&& front. next!= null)   {
            for ( int j=0;  end. next. next!= null && j< i;  j++)
                front. next= null;
        }
        if ( i>=0 && front. next!= null)                      //若front的后继结点存在,则删除之
        {
            T old = front. next. data;                       //获得待删除结点引用的对象
            front. next = front. next. next;                  //删除front的后继结点,包括头删除、中间/尾删除
            return old;
            }
        return null;
    }
    public void clear()                                    //删除单链表所有元素
    {
        this. head. next= null;                               //Java自动收回所有结点占用的存储空间
    }

    public static void main(String[] args){
     String[] values = { "a", "b", "c", "d", "e"};
     SinglyList<String> list = new SinglyList<String>( values);
      //list.toString();
     System. out.println( list);
      list.insert(111, "z");
     System. out.println( list);
      list.remove(22);
     System. out.println( list);
    }
}

因为insert超出链表长度会自动转化为头插入和未插入,所以同样假如一个i=i-1就好。

而删除的问题就很大了,
这时就需要根据插入的值来判断处理步骤了,

如上表。
首先定义一个链表的长度值,记为n:
int n=0;

为下面的两次遍历分别定义节点:
Node<T> front= this. head; //用于删除操作
Node<T> end= this. head; //用于计算长度操作

之后遍历得出长度:
while( end. next!= null ){
n= n+1;
end= end. next;
}

下面开始判断:
若超出长度,则认为其是尾删除,并变成从一计数。
if( i> n){ i= n;}
i= i-1;

若小于零的话,就认为是头删除,
if( i<0 ){ i=0; }

试验运行,没有错误。
于是问题就全部解决了,该去玩游戏了。。

上一篇 下一篇

猜你喜欢

热点阅读