数据结构之线性表——顺序表

<1>线性表

线性表,顾名思义,其组成元素间具有线性关系的一种结构。

(Ps:两个变量之间存在一次函数关系,就称它们之间存在线性关系。。。更通俗一点讲,如果把这两个变量分别作为点的横坐标与纵坐标,其图象是平面上的一条直线,则这两个变量之间的关系就是线性关系。)

线性表(Linear List)是由n个类型相同的数据元素组成的有限序列,即:

LinearList=(a0,a1,···,an-1)

其接口LList声明如下,描述线性表的取值、置值、插入、删除等操作。

public interface LList<E>{     //线性表接口
	boolean isEmpty();         //判断线性表是否为空
	int length();              //返回线性表长度
	E get(int index);          //返回序号为index的对象,index的初值为0
	E set(int index,E element);//设置序号为index的对象为element,返回原对象
	boolean add(E element);    //插入element对象,未指定位置
	E remove(int index);       //移除序号为index的对象,返回被移除的对象
	void clear();              //清空线性表
}

线性表有顺序存储和链式存储两种结构:

public class SeqList<E> implements LList<E>           //顺序表类
public class SinglyLinkedList<E> implements LList<E>  //单链表类

 LList接口中的方法在顺序表类和链表类中表现出多态性

<2>线性表的顺序实现

顺序存储意味着物理存储次序(一组连续内存中的位置)与逻辑次序(直接前驱与直接后继)相同。

设每个元素占用c个字节,a0的存储位置为Loc(a0),则ai的存储位置为

Loc(ai)=Loc(a0)+i*c

计算一个元素地址所需时间是一个常量,因此存取任何一个元素的时间复杂度是O(1),故顺序表示一种随机存取结构

(数组是顺序存储的随机存取结构,在程序设计语言中,数组已被实现为一种构造数据类型。数组一旦占用一片存储空间,这片存储空间的地址和长度就是确定的,不能更改。数组只能进行赋值、取值两种随机存储操作,不能进行插入、删除操作。)

顺序表类:

public class SeqList<E> implements LList<E>{
	
	private Object[] table;                   //对象数组,私有成员
	private int n;                            //顺序表长度
	
	public SeqList(int capacity){             //构造方法,创建指定容量的空表
		this.table=new Object[Math.abs(capacity)];
		this.n=0;
	}
	
	public SeqList(){
		this(16);                             //构造方法的重载,指定空表的默认容量
	}
	@Override
	public boolean isEmpty() {                //判断顺序表是否为空,若空返回true
		return this.n==0;
	}

	@Override
	public int length() {
		return this.n;                         //返回顺序表的长度
	}

	//返回index(初值为0)位置的对象,若序号无效,然会null
	@Override
	public E get(int index) {
		if(index>=0&&index<this.n)
			return (E)this.table[index];
		return null;
	}

	//设置index位置的对象为element,若操作成功,返回原对象,否则返回null
	@Override
	public E set(int index, E element) {
		if(index>=0&&index<this.n&&element!=null){
			E old=(E)this.table[index];          //改变引用即可
			this.table[index]=element;
			return old;
		}
		return null;
	}

	//在index位置插入element对象,若操作成功返回true,不能插入null
	public boolean add(int index,E element) {
		if(element==null)                        //不能插入null
			return false;                       
		if(this.n==this.table.length){           //若数组满,则需要扩充顺序表容量
			Object[] temp=this.table;           
			this.table=new Object[temp.length*2];//重新申请容量更大的一个数组
			for(int i=0;i<temp.length;i++){      //复制数组元素,O(n)
				this.table[i]=temp[i];           
			}
		}
		if(index<0)                              //下标容错
			index=0;               
		if(index>this.n)
			index=this.n;
		for(int j=this.n-1;j>=index;j--)         //元素后移,平均移动n/2
			this.table[j+1]=this.table[j];
		this.table[index]=element;
		this.n++;
		return true;
	}
	
	//在顺序表最后插入element对象
	@Override
	public boolean add(E element){
		return add(this.n,element);
	}
	
	//移除index位置的对象,若操作成功,则返回被移除去对象,否则返回null
	@Override
	public E remove(int index) {
		if(this.n!=0&&index>=0&&index<this.n){
			E old=(E)this.table[index];
			for(int j=index;j<this.n-1;j++)      //元素前移,平均移动n/2
				this.table[j]=this.table[j+1];
			this.table[this.n-1]=null;
			this.n--;
			return old;			                 //操作成功,返回被移动对象
		}
		return null;
	}

	@Override
	public void clear() {                        //清空顺序表
		if(this.n!=0){
			for(int i=0;i<n;i++)
				this.table[i]=null;
			this.n=0;
		}
	}
	

}

顺序表操作效率分析

随机存取,因此存取任何一个元素的get()、set()方法的时间复杂度都是O(1)

对于插入、删除来说,所花费的时间主要用在移动元素上

 设在第i个位置插入元素的概率为pi,则插入一个元素的平均移动次数为                                    

                                                                            

 如果在各位置插入元素的概率相同,即p0=p1=····=pn=1/(n+1),则有



 

换言之,在等概率情况下,插入一个元素平均需要移动一半的元素,时间复杂度为O(n),同理,删除一个元素的时间复杂度亦为O(n)。

猜你喜欢

转载自liang-hong.iteye.com/blog/2195241