数据结构(java)之线性表

1.        线性表的抽象数据类型

a)       数据元素:任意同属一类对象的数据对象

b)       数据关系:线性关系,除第一个元素外,每个元素都有唯一的直接前驱元素,除最后一个元素外,每个元素都有唯一后继元素

c)        数据操作:定义在IList结构中,代码如下

publicinterface IList<E> {

  boolean add(E e);

  boolean insert(E e,intindex);

  E remove(intindex);

  int indexof(E e);

  E get(intindex);

  int size();

  void clear();

  boolean isEmpty();

  boolean isFull();

}

2.        线性表实现之顺序表:使用MyArrayList类实现IList接口

a)       顺序表的存储结构:由一个数组存储,下标为0的元素为顺序表第一个元素

b)       顺序标基本操作

                     i.            初始化顺序表:创建一个空顺序表,申请一个存储大小为maxsize的数组空间,并将数组已存储元素个数size设置为0

                    ii.            向顺序表末尾添加元素:找到顺序表最后一个元素下标size,在第size+1个位置添加元素,并将size自增

                  iii.            删除元素:首先找到顺序表中数组下标为i的元素并将其存放在临时变量中,然后从第i个元素开始到最后一个元素,使每个元素值等于其下一个元素的指,然后将size自减,并返回临时变量

c)        代码实现

publicclass MyArrayList<E> implements IList<E> {

  private E data[];

  privateintmaxsize;

  privateintsize;

 

  //  构造方法初始化线性表

  public MyArrayList(intmaxsize) {

     super();

     this.maxsize=maxsize;

     this.data=(E[])new Object[maxsize]; //不能创建泛型数组,通过Object类来强转为泛型

     this.size=0;

  }

 

  @Override

  publicboolean add(E e) {

     while(!this.isFull()) {

         this.size++;

         this.data[this.size]=e;

         returntrue;

     }

     returnfalse;

  }

 

  @Override

  //在下标为index的位置插入e

  publicboolean insert(E e, intindex) {

     this.check(index); //检查下表是否合法

     while(!this.isFull()) {

         for(inti=this.size;i>=index;i--) {

            this.data[i+1]=data[i];

         }

         this.data[index]=e;

         this.size++;

         returntrue;

     }

     returnfalse;

  }

 

  @Override

  //删除下标为index的元素

  public E remove(intindex) {

     this.check(index);    //检查下标

     E temp;

     temp=this.data[index];

     for(inti=index;i<this.size;i++) {

         this.data[i]=this.data[i+1];

     }

     this.size--;

     returntemp;

  }

 

  @Override

  publicint indexof(E e) {

     for(inti=1;i<=this.size;i++) {

         if(this.data[i]==e)

            returni;

     }

     return -1;

  }

 

  @Override

  public E get(intindex) {

     this.check(index);  //检查下表

     returnthis.data[index];

  }

 

  @Override

  publicint size() {

     returnthis.size;

  }

 

  @Override

  publicvoid clear() {

     this.size=0;

    

  }

 

  @Override

  publicboolean isEmpty() {

     if(this.size==0)

         returntrue;

     returnfalse;

  }

 

  @Override

  publicboolean isFull() {

     if(this.size>=this.maxsize-1)

         returntrue;

     returnfalse;

  }

 

  //判断输入的下标是否越界

  publicvoid check(intindex) {

     if(index<=0||index>this.size)

         thrownew IndexOutOfBoundsException("index is "+index+",last element's index is "+(this.size-1));

  }

}

3.        线性表实现之单链表:使用MyLinkList类实现IList接口

a)       单链表的存储结构:由节点组成,每个节点存储一个元素并指向下一个节点,每个单链表都有一个头节点,头节点中不存储数据元素,只存储指向下一个节点

b)       单链表的基本操作

                     i.            初始化单链表:为头节点申请空间,将头节点数据元素和下一节点元素都设为null,并将单链表元素个数size设为0

                    ii.            在末尾添加节点:首先通过遍历得到最后一个节点对象,然后申请一个新的节点,节点数据元素设位给定的数据,指向下一节点值设为null,然后将最后一个节点对象的next设为新申请的节点,并将单链表元素个数size自增

                  iii.            删除指定节点元素:通过遍历得到指定节点对象的上一个节点对象和下一个节点对象,然后将上一个节点的next设置为指定节点的下一个节点,将指定节点对象的next设为null,并返回指定节点的数据,然后将单链表节点个数size自减

c)        代码实现

class Node<E>{

  private E e;

  private Node<E> next;

  public Node(E e, Node<E> next) {

     super();

     this.e = e;

     this.next = next;

  }

  public E getE() {

     returnthis.e;

  }

  publicvoid setE(E e) {

     this.e = e;

  }

  public Node<E> getNext() {

     returnthis.next;

  }

  publicvoid setNext(Node<E> next) {

     this.next = next;

  }

 

}

 

publicclass MyLinkList<E> implements IList<E> {

 

  private Node<E> start;

  privateintsize;

 

  public MyLinkList() {

     super();

     this.start = new Node<E>(null,null);

     this.size =0;

  }

 

  @Override

  publicboolean add(E e) {

     Node<E> temp=this.start;

     while(temp.getNext()!=null){

         temp=temp.getNext();

     }

     if(temp.getNext()==null) {

         Node<E> node=new Node<E>(e,null);

         temp.setNext(node);

         this.size++;

         returntrue;

     }

     else

         returnfalse;

  }

 

  @Override

  publicboolean insert(E e, intindex) {

     if(index>this.size||index<0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     else {

         Node<E> temp=this.start;

         Node<E> node=new Node(e, null);

         for(inti=0;i<index;i++) {

            temp=temp.getNext();

         }

         Node<E> curr=temp.getNext();

         temp.setNext(node);

         node.setNext(curr);

         this.size++;

         returntrue;

     }

  }

 

  @Override

  public E remove(intindex) {

     if(index>this.size||index<=0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     else {

         Node<E> temp=this.start;

         for(inti=1;i<index;i++) {

            temp=temp.getNext();

         }

         Node<E> node=temp.getNext();

         Node<E> curr=temp.getNext().getNext();

         temp.setNext(curr);

         node.setNext(null);

         this.size--;

         returnnode.getE();

     }

    

  }

 

  @Override

  publicint indexof(E e) {

     Node<E> temp=this.start;

     intindex=0;

     while(temp.getNext()!=null) {

         temp=temp.getNext();

         index++;

         if(temp.getE()==e) {

            returnindex;

         }

     }

    

     return -1;

  }

 

  @Override

  public E get(intindex) {

     if(index>this.size||index<=0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     Node<E> temp=this.start;

     for(inti=1;i<=index;i++) {

         temp=temp.getNext();

     }

     returntemp.getE();

  }

 

  @Override

  publicint size() {

    

     returnthis.size;

  }

 

  @Override

  publicvoid clear() {

     this.start.setNext(null);

     this.size=0;

  }

 

  @Override

  publicboolean isEmpty() {

     if(this.size==0)

         returntrue;

     returnfalse;

  }

 

  @Override

  publicboolean isFull() {

     // TODO Auto-generated method stub

     returnfalse;

  }

}

4.   线性表实现之双向链表

a)   双向链表的存储结构:由节点组成,每个节点由数据元素以及指向前一个节点的pre和指向后一个节点的next组成,每个双向链表都有一个头节点,头节点的数据元素和pre都是nullnext指向下一个元素

b)   双向链表的基本操作

        i.     初始化双向链表:为头节点申请空间,并将头节点的数据元素、prenext都设为null,链表节点个数size设为0

      ii.     向末尾添加元素:为新节点申请空间,并将数据设为指定数据,将链表最后一个元素的next指向新节点,将新节点的pre设为链表最后一个元素,新节点的next设为null

5.   线性表实现之循环链表:将最后一个元素的next指向头节点的单向链表

6.   应用:约瑟夫环

a)   约瑟夫环:假设编号从123…nn个人顺时针围坐一圈,每人持有随机生成的一个密码m1-5),从指定编号为1的位置开始报数,到1号的密码m位置,m出列,并将他的密码作为m,他的下一个位置为1重新开始报数,知道所有人全部出列,设计一个程序求出出列顺序

b)   代码实现

//约瑟夫节点,包括号码和密码

class JosephusNode {

  privateintno;

  privateintpwd;

  public JosephusNode(intno, intpwd) {

     super();

     this.no = no;

     this.pwd = pwd;

  }

  publicint getNo() {

     return no;

  }

  publicvoid setNo(intno) {

     this.no = no;

  }

  publicint getPwd() {

     returnpwd;

  }

  publicvoid setPwd(intpwd) {

     this.pwd = pwd;

  }

  @Override

  public String toString() {

     return"("+this.no+","+this.pwd+")";

  }

 

}

 

publicclass Josephus {

  private IList<JosephusNode> list;

  privateintnum;

 

  //初始化约瑟夫环,为环中每个节点分配no1-5的密码 

  public Josephus(IList<JosephusNode> list,intnum) {

     super();

     this.list = list;

     this.num=num;

     for(inti=1;i<=num;i++) {

         intno=i;

         intpwd=(int)(Math.random()*5)+1;

         list.add(new JosephusNode(no,pwd));

     }

  }

 

  publicint getNum() {

     returnnum;

  }

 

  publicvoid setNum(intnum) {

     this.num = num;

  }

 

  //返回环中序号为index的节点

  public JosephusNode get(intindex) {

     returnlist.get(index);

  }

 

  //出环游戏

  publicvoid game() {

     intstart=1;   //1号开始

     intcnt;

     System.out.println("游戏开始");

     for(inti=1;i<=this.num;i++) {

         //出列号码是start开始计数start对应的密码位

         cnt=(start+this.list.get(start).getPwd()-2)%this.list.size()+1;

         System.out.println(this.list.remove(cnt));

         if(this.list.size()==0)

            break;

         start=(cnt-1)%this.list.size()+1;

     }

  }

}

7.   不同结构的线性表复杂度分析

a)   顺序表

        i.     插入:boolean insert(E e,int index);顺序表的插入时间主要耗费在移动数据元素上,时间复杂度为O(n)

      ii.     删除:E remove(int index);顺序表的删除时间主要耗费在移动数据元素上,时间复杂度为O(n)

     iii.     取表元素:E get(int index);顺序表的取表元素只需要按照给定序号查找对应数组元素,时间复杂度为O(1)

      iv.     定位元素:int indexOf(E e);顺序表的定位时间主要耗费在元素比较上,时间复杂度为O(n)

b)   单链表

        i.     插入:boolean insert(E e,int index);单链表的插入时间主要耗费在查找插入位置上,时间复杂度为O(n)

      ii.     删除:E remove(int index);单链表的删除时间主要耗费在查找删除位置上,时间复杂度为O(n)

     iii.     取表元素:E get(int index);单链表的取表元素时间主要耗费在查找取表元素位置上,时间复杂度为O(n)

      iv.     定位元素:int indexOf(E e);单链表的定位时间主要耗费在元素比较上,时间复杂度为O(n)

 

c)   总结:由于顺序表的元素存储是连续的,所以查找很方便,效率很高,但是插入和删除需要移动大量的元素,效率很低;单链表中由于数据存储是不连续的,所以插入和删除的效率很高,但是查找需要从头开始遍历,所以效率较低。因此,如果需要进行大量的查找等操作而不经常插入或删除,采用顺序表,反之,采用链表。

1.        线性表的抽象数据类型

a)       数据元素:任意同属一类对象的数据对象

b)       数据关系:线性关系,除第一个元素外,每个元素都有唯一的直接前驱元素,除最后一个元素外,每个元素都有唯一后继元素

c)        数据操作:定义在IList结构中,代码如下

publicinterface IList<E> {

  boolean add(E e);

  boolean insert(E e,intindex);

  E remove(intindex);

  int indexof(E e);

  E get(intindex);

  int size();

  void clear();

  boolean isEmpty();

  boolean isFull();

}

2.        线性表实现之顺序表:使用MyArrayList类实现IList接口

a)       顺序表的存储结构:由一个数组存储,下标为0的元素为顺序表第一个元素

b)       顺序标基本操作

                     i.            初始化顺序表:创建一个空顺序表,申请一个存储大小为maxsize的数组空间,并将数组已存储元素个数size设置为0

                    ii.            向顺序表末尾添加元素:找到顺序表最后一个元素下标size,在第size+1个位置添加元素,并将size自增

                  iii.            删除元素:首先找到顺序表中数组下标为i的元素并将其存放在临时变量中,然后从第i个元素开始到最后一个元素,使每个元素值等于其下一个元素的指,然后将size自减,并返回临时变量

c)        代码实现

publicclass MyArrayList<E> implements IList<E> {

  private E data[];

  privateintmaxsize;

  privateintsize;

 

  //  构造方法初始化线性表

  public MyArrayList(intmaxsize) {

     super();

     this.maxsize=maxsize;

     this.data=(E[])new Object[maxsize]; //不能创建泛型数组,通过Object类来强转为泛型

     this.size=0;

  }

 

  @Override

  publicboolean add(E e) {

     while(!this.isFull()) {

         this.size++;

         this.data[this.size]=e;

         returntrue;

     }

     returnfalse;

  }

 

  @Override

  //在下标为index的位置插入e

  publicboolean insert(E e, intindex) {

     this.check(index); //检查下表是否合法

     while(!this.isFull()) {

         for(inti=this.size;i>=index;i--) {

            this.data[i+1]=data[i];

         }

         this.data[index]=e;

         this.size++;

         returntrue;

     }

     returnfalse;

  }

 

  @Override

  //删除下标为index的元素

  public E remove(intindex) {

     this.check(index);    //检查下标

     E temp;

     temp=this.data[index];

     for(inti=index;i<this.size;i++) {

         this.data[i]=this.data[i+1];

     }

     this.size--;

     returntemp;

  }

 

  @Override

  publicint indexof(E e) {

     for(inti=1;i<=this.size;i++) {

         if(this.data[i]==e)

            returni;

     }

     return -1;

  }

 

  @Override

  public E get(intindex) {

     this.check(index);  //检查下表

     returnthis.data[index];

  }

 

  @Override

  publicint size() {

     returnthis.size;

  }

 

  @Override

  publicvoid clear() {

     this.size=0;

    

  }

 

  @Override

  publicboolean isEmpty() {

     if(this.size==0)

         returntrue;

     returnfalse;

  }

 

  @Override

  publicboolean isFull() {

     if(this.size>=this.maxsize-1)

         returntrue;

     returnfalse;

  }

 

  //判断输入的下标是否越界

  publicvoid check(intindex) {

     if(index<=0||index>this.size)

         thrownew IndexOutOfBoundsException("index is "+index+",last element's index is "+(this.size-1));

  }

}

3.        线性表实现之单链表:使用MyLinkList类实现IList接口

a)       单链表的存储结构:由节点组成,每个节点存储一个元素并指向下一个节点,每个单链表都有一个头节点,头节点中不存储数据元素,只存储指向下一个节点

b)       单链表的基本操作

                     i.            初始化单链表:为头节点申请空间,将头节点数据元素和下一节点元素都设为null,并将单链表元素个数size设为0

                    ii.            在末尾添加节点:首先通过遍历得到最后一个节点对象,然后申请一个新的节点,节点数据元素设位给定的数据,指向下一节点值设为null,然后将最后一个节点对象的next设为新申请的节点,并将单链表元素个数size自增

                  iii.            删除指定节点元素:通过遍历得到指定节点对象的上一个节点对象和下一个节点对象,然后将上一个节点的next设置为指定节点的下一个节点,将指定节点对象的next设为null,并返回指定节点的数据,然后将单链表节点个数size自减

c)        代码实现

class Node<E>{

  private E e;

  private Node<E> next;

  public Node(E e, Node<E> next) {

     super();

     this.e = e;

     this.next = next;

  }

  public E getE() {

     returnthis.e;

  }

  publicvoid setE(E e) {

     this.e = e;

  }

  public Node<E> getNext() {

     returnthis.next;

  }

  publicvoid setNext(Node<E> next) {

     this.next = next;

  }

 

}

 

publicclass MyLinkList<E> implements IList<E> {

 

  private Node<E> start;

  privateintsize;

 

  public MyLinkList() {

     super();

     this.start = new Node<E>(null,null);

     this.size =0;

  }

 

  @Override

  publicboolean add(E e) {

     Node<E> temp=this.start;

     while(temp.getNext()!=null){

         temp=temp.getNext();

     }

     if(temp.getNext()==null) {

         Node<E> node=new Node<E>(e,null);

         temp.setNext(node);

         this.size++;

         returntrue;

     }

     else

         returnfalse;

  }

 

  @Override

  publicboolean insert(E e, intindex) {

     if(index>this.size||index<0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     else {

         Node<E> temp=this.start;

         Node<E> node=new Node(e, null);

         for(inti=0;i<index;i++) {

            temp=temp.getNext();

         }

         Node<E> curr=temp.getNext();

         temp.setNext(node);

         node.setNext(curr);

         this.size++;

         returntrue;

     }

  }

 

  @Override

  public E remove(intindex) {

     if(index>this.size||index<=0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     else {

         Node<E> temp=this.start;

         for(inti=1;i<index;i++) {

            temp=temp.getNext();

         }

         Node<E> node=temp.getNext();

         Node<E> curr=temp.getNext().getNext();

         temp.setNext(curr);

         node.setNext(null);

         this.size--;

         returnnode.getE();

     }

    

  }

 

  @Override

  publicint indexof(E e) {

     Node<E> temp=this.start;

     intindex=0;

     while(temp.getNext()!=null) {

         temp=temp.getNext();

         index++;

         if(temp.getE()==e) {

            returnindex;

         }

     }

    

     return -1;

  }

 

  @Override

  public E get(intindex) {

     if(index>this.size||index<=0) {

         thrownew IndexOutOfBoundsException("下标错误");

     }

     Node<E> temp=this.start;

     for(inti=1;i<=index;i++) {

         temp=temp.getNext();

     }

     returntemp.getE();

  }

 

  @Override

  publicint size() {

    

     returnthis.size;

  }

 

  @Override

  publicvoid clear() {

     this.start.setNext(null);

     this.size=0;

  }

 

  @Override

  publicboolean isEmpty() {

     if(this.size==0)

         returntrue;

     returnfalse;

  }

 

  @Override

  publicboolean isFull() {

     // TODO Auto-generated method stub

     returnfalse;

  }

}

4.   线性表实现之双向链表

a)   双向链表的存储结构:由节点组成,每个节点由数据元素以及指向前一个节点的pre和指向后一个节点的next组成,每个双向链表都有一个头节点,头节点的数据元素和pre都是nullnext指向下一个元素

b)   双向链表的基本操作

        i.     初始化双向链表:为头节点申请空间,并将头节点的数据元素、prenext都设为null,链表节点个数size设为0

      ii.     向末尾添加元素:为新节点申请空间,并将数据设为指定数据,将链表最后一个元素的next指向新节点,将新节点的pre设为链表最后一个元素,新节点的next设为null

5.   线性表实现之循环链表:将最后一个元素的next指向头节点的单向链表

6.   应用:约瑟夫环

a)   约瑟夫环:假设编号从123…nn个人顺时针围坐一圈,每人持有随机生成的一个密码m1-5),从指定编号为1的位置开始报数,到1号的密码m位置,m出列,并将他的密码作为m,他的下一个位置为1重新开始报数,知道所有人全部出列,设计一个程序求出出列顺序

b)   代码实现

//约瑟夫节点,包括号码和密码

class JosephusNode {

  privateintno;

  privateintpwd;

  public JosephusNode(intno, intpwd) {

     super();

     this.no = no;

     this.pwd = pwd;

  }

  publicint getNo() {

     return no;

  }

  publicvoid setNo(intno) {

     this.no = no;

  }

  publicint getPwd() {

     returnpwd;

  }

  publicvoid setPwd(intpwd) {

     this.pwd = pwd;

  }

  @Override

  public String toString() {

     return"("+this.no+","+this.pwd+")";

  }

 

}

 

publicclass Josephus {

  private IList<JosephusNode> list;

  privateintnum;

 

  //初始化约瑟夫环,为环中每个节点分配no1-5的密码 

  public Josephus(IList<JosephusNode> list,intnum) {

     super();

     this.list = list;

     this.num=num;

     for(inti=1;i<=num;i++) {

         intno=i;

         intpwd=(int)(Math.random()*5)+1;

         list.add(new JosephusNode(no,pwd));

     }

  }

 

  publicint getNum() {

     returnnum;

  }

 

  publicvoid setNum(intnum) {

     this.num = num;

  }

 

  //返回环中序号为index的节点

  public JosephusNode get(intindex) {

     returnlist.get(index);

  }

 

  //出环游戏

  publicvoid game() {

     intstart=1;   //1号开始

     intcnt;

     System.out.println("游戏开始");

     for(inti=1;i<=this.num;i++) {

         //出列号码是start开始计数start对应的密码位

         cnt=(start+this.list.get(start).getPwd()-2)%this.list.size()+1;

         System.out.println(this.list.remove(cnt));

         if(this.list.size()==0)

            break;

         start=(cnt-1)%this.list.size()+1;

     }

  }

}

7.   不同结构的线性表复杂度分析

a)   顺序表

        i.     插入:boolean insert(E e,int index);顺序表的插入时间主要耗费在移动数据元素上,时间复杂度为O(n)

      ii.     删除:E remove(int index);顺序表的删除时间主要耗费在移动数据元素上,时间复杂度为O(n)

     iii.     取表元素:E get(int index);顺序表的取表元素只需要按照给定序号查找对应数组元素,时间复杂度为O(1)

      iv.     定位元素:int indexOf(E e);顺序表的定位时间主要耗费在元素比较上,时间复杂度为O(n)

b)   单链表

        i.     插入:boolean insert(E e,int index);单链表的插入时间主要耗费在查找插入位置上,时间复杂度为O(n)

      ii.     删除:E remove(int index);单链表的删除时间主要耗费在查找删除位置上,时间复杂度为O(n)

     iii.     取表元素:E get(int index);单链表的取表元素时间主要耗费在查找取表元素位置上,时间复杂度为O(n)

      iv.     定位元素:int indexOf(E e);单链表的定位时间主要耗费在元素比较上,时间复杂度为O(n)

c)   总结:由于顺序表的元素存储是连续的,所以查找很方便,效率很高,但是插入和删除需要移动大量的元素,效率很低;单链表中由于数据存储是不连续的,所以插入和删除的效率很高,但是查找需要从头开始遍历,所以效率较低。因此,如果需要进行大量的查找等操作而不经常插入或删除,采用顺序表,反之,采用链表。

 

posted @ 2018-07-24 16:57  带带大璞璞  阅读(498)  评论(0编辑  收藏  举报