数据结构day1—— 线性表

1.什么是线性表

由有限元素组成的序列。

2.线性表的分类

1)有序线性表:元素的值与其所在位置有关;

2)无序线性表:元素的值与其所在位置并无联系。

3.线性表ADT的定义

public interface ListADT {
	public void clear();
	public void insert(Object item);//插入数据到当前指针处(前置插入)
	public void append(Object item);//在表尾添加一个数据项
	public Object remove();//移除当前指针所指的数据项
	public void setFirst();//设置当前指针指向表头
	public void next();//指针前进
	public void prev();//指针后退
	public int length();//获得线性表的长度;
	public void setPos(int pos);//使指针指向某个位置
	public void setValue(Object val);//将当前指针所指的数据value设置为指定值
	public Object currValue();//返回当前指针所指的数据值
	public boolean isEmpty();//返回表是否为空
	public boolean isInList();//当前指针是否在表内
	public void print();//打印表内数据
}

4.线性表的两种实现方法

1)顺序表实现

即用数组来实现。

好处:允许随机访问,对于某个位置的访问只需要O(1)的时间。

缺点:对于位置 i 的插入和删除来说,平均耗时达到了O(n);存在存储空间的浪费。

实现具体代码:

public class AList implements ListADT{
	private static final int defaultSize=10;//默认数组大小
	private int maxSize;//最大线性表大小
	private int numInList;//线性表中的实际数据数量
	private int curr;//当前指针位置
	private Object[] listArray;//用于实现线性表的数组本身
	
	AList(){
		init(defaultSize);
	}
	
	AList(int size){
		init(size);
	}
	
	public void init(int size){
		maxSize=size;
		numInList=curr=0;
		listArray=new Object[size];
	}
	
	@Override
	public void clear() {
		// TODO Auto-generated method stub
		numInList=curr=0;
		
	}
	@Override
	public void insert(Object item) {
		// TODO Auto-generated method stub
		if(numInList>=maxSize){
			System.out.println("List is full");
		}
		else if(curr<0||curr>numInList){
			System.out.println("bad value for curr:"+curr);
		}
		else {
			for(int i=numInList;i>curr;i--){
				listArray[i]=listArray[i-1];
			}
			listArray[curr]=item;
			numInList+=1;
		}
		
		
	}
	@Override
	public void append(Object item) {
		// TODO Auto-generated method stub
		if(numInList>=maxSize){
			System.out.println("List is full");
		}
		else{
			listArray[numInList]=item;
		}
	}
	@Override
	public Object remove() {
		// TODO Auto-generated method stub
		if(isEmpty()){
			System.out.println("List is Empty");
		}
		else if(!isInList()){
			System.out.println("No current Element");
		}
		else{
			Object item=listArray[curr];
			for(int i=curr;i<numInList-1;i++){
				listArray[i]=listArray[i+1];
			}
			numInList-=1;
			return item;
		}
		return null;
	}
	@Override
	public void setFirst() {
		// TODO Auto-generated method stub
		curr=0;
	}
	@Override
	public void next() {
		// TODO Auto-generated method stub
		curr++;
	}
	@Override
	public void prev() {
		// TODO Auto-generated method stub
		curr--;
	}
	@Override
	public int length() {
		// TODO Auto-generated method stub
		return numInList;
	}
	@Override
	public void setPos(int pos) {
		// TODO Auto-generated method stub
		curr=pos;
	}
	@Override
	public void setValue(Object val) {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else {
			listArray[curr]=val;
		}
	}
	@Override
	public Object currValue() {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else {
			return listArray[curr];
		}
		return null;
	}
	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return numInList==0;
	}
	@Override
	public boolean isInList() {
		// TODO Auto-generated method stub
		return curr>0&&curr<numInList;
	}
	@Override
	public void print() {
		// TODO Auto-generated method stub
		if(isEmpty()){
			System.out.println("()");
		}
		else {
			System.out.println("(");
			for(setFirst();isInList();next()){
				System.out.println(currValue()+" ");
			}
			System.out.println(")");
		}
	}
	
	
}

2)链表实现

a.单链表的实现:插入删除很快O(1)就能完成,但无法做到随机访问。节省存储空间。

//单链表的实现
public class Link {
	private Object element;
	private Link next;
	
	Link(Object item,Link nextval){
		element=item;
		next=nextval;
	}
	
	Link(Link nextval){
		next=nextval;
	}
	
	Link next(){
		return next;
	}
	
	Link setNext(Link nextval){
		return next=nextval;
	}
	
	Object element(){
		return element;
	}
	Object setElement(Object item){
		return element=item;
	}
}

用单链表实现线性表:为了创建链接的方便性,实现时的curr实际是指向当前节点的前驱节点,因此而需要一个特殊的头节点,这个头节点并不存放任何值。

//用单链表实现的线性表
public class LList implements ListADT{
	private Link head;
	private Link tail;
	private Link curr;
	
	LList(int size){
		init();
	}
	LList(){
		init();
	}
	
	void init(){
		tail=head=curr=new Link(null);
	}

	@Override
	public void clear() {
		// TODO Auto-generated method stub
		head.setNext(null);
		curr=tail=head;
	}

	@Override
	public void insert(Object item) {
		// TODO Auto-generated method stub
		if(curr==null){
			System.out.println("No current Element");
		}
		else{
			curr.setNext(new Link(item,curr.next()));
			if(tail==curr)tail=curr.next();
		}
	}

	@Override
	public void append(Object item) {
		// TODO Auto-generated method stub
		tail.setNext(new Link(item,null));
		tail=tail.next();
	}

	@Override
	public Object remove() {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else {
			Object item=curr.next().element();
			if(tail==curr.next())tail=curr;
			curr.setNext(curr.next().next());
			return item;
		}
		return null;
	}

	@Override
	public void setFirst() {
		// TODO Auto-generated method stub
		curr=head;
	}

	@Override
	public void next() {
		// TODO Auto-generated method stub
		if(curr==null){
			System.out.println("No current Element");
		}
		else {
			curr=curr.next();
		}
	}

	@Override
	public void prev() {
		// TODO Auto-generated method stub
		if(curr==null||curr==head){
			curr=null;
		}
		else{
			Link temp=head;
			while(temp!=null&&temp.next()!=curr){
				temp=temp.next();
			}
			curr=temp;
		}
	}

	@Override
	public int length() {
		// TODO Auto-generated method stub
		int numInList=0;
		for(Link temp=head.next();temp!=null;temp.next()){
			numInList++;
		}
		return numInList;
	}

	@Override
	public void setPos(int pos) {
		// TODO Auto-generated method stub
		curr=head;
		for(int i=0;(curr!=null)&&(i<pos);i++){
			curr=curr.next();
		}
	}

	@Override
	public void setValue(Object val) {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else{
			curr.next().setElement(val);
		}
	}

	@Override
	public Object currValue() {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else{
			return curr.next().element();
		}
		return null;
	}

	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return head.next()==null;
	}

	@Override
	public boolean isInList() {
		// TODO Auto-generated method stub
		return curr!=null&&curr.next()!=null;
	}

	@Override
	public void print() {
		// TODO Auto-generated method stub
		if(isEmpty()){
			System.out.println("()");
		}
		else {
			System.out.println("(");
			for(setFirst();isInList();next()){
				System.out.println(currValue()+" ");
			}
			System.out.println(")");
		}
	}
}

ps: 链表型线性表的优化:

在表的插入、删除操作时,每次都需要调用系统级的new操作,很费时间,而且对于被删除的结点变量,使用完之后也需要回收。

优化办法:使用freelist静态变量,称为可利用空间表,用于存储的分配和回收。当且仅当freelist空间中没有可用结点时才调用系统级的new操作。而对于删除的结点,将其放在可用空间表的首部。等待再次使用。因此,可利用空间表的长度永远不会超过链表的最大长度。

实现办法:在Link类里添加如下有关freelist的代码即可:

//下面支持freelist可用空间表的操作
		static Link freelist=null;
		
		//get操作,从freelist中取用一个结点空间
		static Link get(Object item,Link nextval){
			if(freelist==null){
				return new Link(item,nextval);
			}
			Link temp=freelist;
			freelist=freelist.next();
			temp.setElement(item);
			temp.setNext(nextval);
			return temp;
		}
		
		//release操作,对于已经被删除的结点,放在可用空间表的首端
		void release(){
			element=null;
			next=freelist;
			freelist=this;
		}

那么,线性表的插入删除等操作就可以使用get和release来完成了:

@Override
	public void insert(Object item) {
		// TODO Auto-generated method stub
		if(curr==null){
			System.out.println("No current Element");
		}
		else{
//			curr.setNext(new Link(item,curr.next()));
			curr.setNext(Link.get(item,curr.next()));
			if(tail==curr)tail=curr.next();
		}
	}

	@Override
	public void append(Object item) {
		// TODO Auto-generated method stub
//		tail.setNext(new Link(item,null));
		tail.setNext(Link.get(item, null));
		tail=tail.next();
	}

	@Override
	public Object remove() {
		// TODO Auto-generated method stub
		if(!isInList()){
			System.out.println("No current Element");
		}
		else {
			Object item=curr.next().element();
			if(tail==curr.next())tail=curr;
			Link temp=curr.next();
			curr.setNext(curr.next().next());
			//回收被删除结点
			temp.release();
			return item;
		}
		return null;
	}

双链表的应用:

单链表时只保存每个结点的后继结点,因此需要curr其实是指向其后面的一个结点,且引入了不存数据的头节点,略有些费神;双链表则更容易实现,尤其是指针前移时,不必再向单链表那样从头结点开始比较,直到找到curr;双链表中每个结点保存自己的前驱和后继指针,比单链表所占用的空间更多。

注意:双链表中仍然习惯性的让curr指向当前元素的前驱结点,这样做是为了可以将一个结点插入到表的任意位置。

循环链表的使用:

即链尾指向链首,没有明显的头部和首部。弊端是可能使链表操作陷入死循环,但可用通过head指针来标记处理操作是否已经周游整个链表。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值