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指针来标记处理操作是否已经周游整个链表。