当我们在解决下标问题时,我们在想些什么。
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; }
试验运行,没有错误。
于是问题就全部解决了,该去玩游戏了。。