• 第三章节 表,栈,队列


    抽象数据类型(abstract data type ADT)是带有一组操作的对象的集合。

    一、 表ADT

    • 表的数组实现 

    数组实现时,插入和删除的花费可能会比较大,这样要看操作发生在什么地方。最坏情况如,在0处插入,和删除第一个元素。此时为O(N)。

    如果表是通过在高端插入,其后只发生对数组的查找访问(findKth),则数组很适合。否则,不是。

    • 简单链表

    为了避免插入和删除的线性开销,应该使表可以不连续存储。否则表的每个部分都可能要整体移动。这样就是链表。

    findKth(i)要花费O(i)的时间,但是在实践中这是比较保守的情况 ,因为findKth常常以排序后的方式进行,如findkth(1),

    findkth(2),...都是一次性的完成的。

    二、Collections API中的表

    Collections API中有一些普通的的数据结构的实现,表ADT中其中的一部分。

    • Collections 接口
    public interface Collection<AnyType> extends Iterable <Anytype>{
        int size();
        boolean isEmpty();
        void clear ();
        boolean contains(Anytype x );
        boolean add(Anytype x);
        boolean remove (Anytype x );
        Iterator <Anytype> interator ();
    }
    

    collection接口扩展了Iterable 接口,实现了Iterable接口的类有增强的for循环。

    • Iterator接口

    实现了Iterable 接口的集合一定要有一个iterator的方法,这个方法返回一个Iterator类型的对象 。定义如下

    public interface Iterator <Anytype> {
        boolean hasNext ();
        Anytype next ();
        void remove ();//删除next最新返回的项
    }
    

    思路是,通过iterator 方法,每个集合可以创建并返回客户一个实现了Iterator接口的对象,并将当前位置的概念在对象内部存储下来。

     当编译器遇到用于Iterator对象的增强的for循环时,它用对iterator方法的那些 调用 代替增强的for循环以得到一个 Iterator对象 ,然后调用 next/ hasNext .

    也就是说,下面的程序将会被编译器重写 。

    for (Anytype item : collection )
        system.out.println(item);
    }
    
    //重写为
    Iterator <Anytype> itr = collection.iterator();
    while (itr.hasNext()){
        Anytype item = itr.next();
        System.out.println(item);
    }
    

    Iterator的remove()方法优点 :

    Collection的remove()一定要先找到被删除的项。如我们要每隔一项删除一个,用迭代器更好。

    当直接使用Iterator(而不是通过一个增强for循环间接使用时)一个重要的是:

    如果正在被迭代的集合结构正在发生变化 (如add, remove , clear 方法)那么迭代器就不再合法,会有ConcurrentModificationException。

    然而如果迭代器调用 的是自己的remove方法,则是没有问题的,这个也是我们更愿意用迭代 器的remove方法的第二个原因。

    • List接口、ArrayList接口、LinkedList接口

    List继承了Collection接口。

    public interface List<Anytype> extends Collection<Anytype>{
        Anytype get(int idx );
        Anytype set(int idx ,Anytype x );
        void add (int idx ,Antype x );
        void remove (int idx );
        
        ListIterator<Anytype> listIterator(int pos );
    }
    

     List ADT 有两种实现方式 :

    ArrayList:可增长的数组的实现

      优点:get set 是常数时间

      缺点:新插入的项和删除代价高,除非在ArrayList的末尾进行。

    LikedList:双链表的实现  

      优点:insert /remove开销小。

      缺点:不容易 做索引,因此get 开销大,除非 get 操作是对接口两个端点的。(可以考虑使用 iterator )

    对搜索来说,ArrayList/ LinkedList都是低效的,对 Collections 的contains / remove两个方法,它们都要花费线性时间。

    • remove方法对LinkedList的使用

    对ArrayList,肯定是不行,因为remove代价很大。

    分析 LinkedList。

    方案1 :

    public static void removeEven( List <Integer> lst){
        int i =0;
        while (i<lst.size()){
            if (lst.get(i)%2==0){
                lst.remove(i);
            }else {
                i++;
            }
        }
    }
    

    上面有两个问题:

    1. get效率不高,因此上面的要花费二次时间。

    2. remove 调用 同样开销大,因为找到位置 i 代价很大。

    方案2:

    for (Integer x : lst)
        if (x%2==0)
            lst.remove(x);  //这里是collection的remove 
    

    这里我们不使用get  而是使用迭代器遍历,这是高效的。但是却是Collection的remove,这个一定要重新搜索,所以不是高效的,要

    线性时间。

    最重要的是,程序 会的异常,因为当一个项被删除(collection的 remove)的时候,迭代器的使用是非法的。

    方案3:

    Iterator<Integer> itr = lst.iterator();
    
    while (itr.hasNext ()){
        if (itr.next()%2==0)
            itr.remove() ;
    }
    } 

    我们使用迭代 器来删除 。整个程序只要线性时间,而不是二次时间。

    • ArrayList的实现 
    • LinkedList的实现 

    三、栈ADT 

     stack是限制 插入 和 删除 只能在一个位置上进行的表,这个位置中表的末端。 也叫top. 

    栈的实现 : 任何实现表的方法.

    因为栈的操作是常数时间,所以要改进 很难.我们给出两个流行的实现方法,对ArrayList 和 LinkedList进行了一定的简化 。

    • 链表实现 

    单链表

    • 数组实现 

    避免了链而且可能是最流行的方法。

    它们的操作不仅以常数时间进行,而且是非常快的常数时间进行。栈很可能是在数组后最基本的一种数据结构。

    package c3;
    
    import java.lang.reflect.Array;
    
    public class MyArrayStack <AnyType> {
    	private static final int DEFAULT_SIZE=12;
    	private AnyType [] mArray ;
    	private int count ;
    	
    	public MyArrayStack(Class <AnyType> type, int size){
    		//这里用到了反射
    		mArray = (AnyType [])Array.newInstance(type, size) ;
    		count =0;
    	}
    	public MyArrayStack(Class<AnyType> type){
    		this(type, DEFAULT_SIZE) ;
    	}
    	public void push( AnyType val ){
    		mArray[count++]= val ;
    	}
    	//只返回顶部元素
    	public AnyType peek(){
    		return mArray[count-1] ;
    	}
    	//返回并删除 
    	public AnyType pop(){
    		AnyType ret = mArray[count-1] ;
    		count --;
    		return ret ;
    	}
    	public int size(){
    		return count ;
    	}
    
    }

    四、队列ADT 

    和栈一样,队列也是表,但插入在一端,删除 在另一端。

    • 队列模型

    基本操作:

    enqueue:在末端插入一个。

    dequeue: 在表头,也就是队头删除一个。

    • 队列的数组实现 

    任何表的实现都是合法的,链表和数组实现都可给出O(1)的运行时间。队列的链表实现很简单,不再说。下面讨论数组。

    我们要保存两个位置, front 和 back 。同时有些问题要解决,方法是使用循环数组。不再细说。

    package c3;
    
    import java.lang.reflect.Array;
    
    public class MyArrayQueue<AnyType> {
    	private int count;
    	private AnyType []  mArray ;
    	
    	public MyArrayQueue( Class <AnyType> type, int size){
    		mArray = (AnyType[]) Array.newInstance(type, size) ;
    		count =0;
    	}
    	public void add(AnyType val){
    		mArray [count++] = val ;
    	}
    	//返回队头元素
    	public AnyType front(){
    		return mArray [0] ;
    	}
    	//返回并删除队头元素
    	public AnyType pop(){
    		AnyType ret = mArray[0] ;
    		count --;
    		for (int i=0 ;i<count ;i++){
    			mArray[i]= mArray[i+1] ; 
    		}
    		return ret ;
    	}
    	public int size (){
    		return count ;
    	}
    	public boolean isEmpty(){
    		return size()==0;
    	}
    }
    

    第三章节到此结束 

  • 相关阅读:
    用Azure VM + Azure Database for MySQL搭建Web服务
    Azure镜像的跨区域复制—Shared Image Gallery(共享映像库)初探
    Azure上几种常见的VM复制操作
    Exchange 2016与国内版O365混合部署(6):混合后的操作和验证
    Exchange 2016与国内版O365混合部署(5):运行AAD Connect及混合部署向导
    Exchange 2016与国内版O365混合部署(4):配置Exchange 公网证书
    Exchange 2016与国内版O365混合部署(3):安装Exchange2016并配置邮件的外网收发
    Exchange 2016与国内版O365混合部署(2):搭建域环境
    Exchange 2016与国内版O365混合部署(1):过程总览
    Cross-Tanant 步骤
  • 原文地址:https://www.cnblogs.com/chuiyuan/p/4492957.html
Copyright © 2020-2023  润新知