(一)定义

通俗定义

   零个或多个元素的有序序列

(二)存储结构

I.顺序存储结构

理解:通过连续地址的空间来进行存储元素,其实质就是一个数组

Linear List_线性表

三大属性:

(1)数组的存储位置:数组data,它的存储位置就是存储空间的存储位置

(2)线性表的最大存储容量:数组长度MaxSize

  (3)  线性表的当前长度:length

优点:

(1)地址连续(预先分配的地址连续的空间

(2)依次存放(根据地址的先后进行存储元素

(3)随机存取(根据下标来进行查找元素

(4)类型相同(存储的元素类型均是相同的

缺点:

(1)存储分配需要事先进行

(2)浪费空间

(3)在进行插入和删除操作时,需要最大量移动元素,时间效率低下

(1)基本结构

Linear List_单链表_02


#define MAXSIZE 100;      

typedef struct{

EleType *elem;      //存储空间的基地址

int length;            //当前长度,用来记录所存储元素的个数

}SqList

SqList L;        //方式一:在调用成员时,使用L.elem[i]的方法

SqList*L;     //方式二:在调用成员时,使用L->elem[i]的方法

(2)基本操作

      总体包含: 初始化、增加(插入)、删除、查找、修改、排序

   初始化

Linear List_线性表_03

    Status InitLIst(Sqlist &L)

     {//构造一个空的顺序表

      L.elem = new ElemType[MAXSIZE];           //为顺序表分配一个大小为MAXSIZE的空间

if(!L.elem)exit(OVERFLOW);           //存储空间分配失败

L.length=0;          //空表长度为0

return OK;

}

 查找

方式一:下标法

描述:查询第i个位置(如果存储在,下标为<i-1>)的元素,如果存在,将值赋值给e,反之就结束

Status GetElem(SqList L,int i,ElemType &e){

if(i<1||i>L.length) return ERROR;//检查位置,如果不合理,直接返回错误

e=L.elem[i-1];          //将L.elem[i-1]的值赋值给e

return OK;     

}

方式二:关键字法

描述:查找关键字key,如果查找成功,就返回下标,反之,就返回错误

int  GetElem(SqList L,int key){

for(int i=0;i<L.length;i++){

if(key==L.elem[i]){

return i+1;     //数组的下标是从0开始的,所以要进行加1

}

}

return Error;

}

时间复杂度分析:

Linear List_线性表_04

插入(添加)

Linear List_顺序表_05


Status ListInsert(SqList  &L,int i,ElemType e){

//顺序表L中第i个位置插入新元素e,i值得合理范围是1 =<i<=L.length+1

if((i<1)||(i>L.length+1))  

return ERROR;     //i值不合法

if(L.length==MAXSIZE)  //存储空间已满

return ERROR;

for(j=L.length-1;j>=i-1;j--){

L.elem[j+1]=L.elem[j];   //插入位置及之后的元素后移

L.lem[i-1]=e;                //将新元素放入第i个位置

++L.length;                  //表长加1

return OK;

}

复杂度分析:

Linear List_单链表_06

删除(覆盖)

Linear List_单链表_07

Status listDelete(SqList &L,int i){

/*在顺序表L中删除第i个元素,i值的合理范围是1=<i<=L.length*/

if((i<1)||(i>L.length)//i值不合理

return ERROR;

for(j=i;j<L.length-1;j++){

L.elem[j-1]=L.elem[j];

--L.length;  //被删除元素之后的元素前移

return OK;  //表长减1

}

时间复杂度分析:

Linear List_线性表_08

排序(此处以冒泡排序为例,升序排列)

Linear List_线性表_09

Status ListSorted(SqList &L){

for(int  i=0;i<L.length;i++){  //控制比较的趟数

for(int j=0;j<L.length-i-1;j++){//控制每趟比较的次数

if(L.elem[j]>L.elem[j-1]){

int temp=L.elem[j];

L.elem[j]=L.elem[j-1];

L.elem[j-1]=temp;

}

}

 }

}

总结:由于顺序表在进行插入和删除操作时会移动大量元素,时间复杂度比较高。

II.链式存储结构

链式存储结构,元素的存储位置并不是连续的,这样就很好的避免了在删除节点时所所需要大量移动元素所造成的时间复杂度较高的问题。

Linear List_线性表_10

(1)基本结构

Linear List_顺序表_11

缺点

在进行查找操作时,需要从前往后进行查找,无法使用“下标法”

存储结构:

typedef struct _LNode

{
       Student data;            //数据域
       struct _LNode* next; //指针域(存储下一个结点的地址或NULL)
}LNode,*LinkList;      //别名

解释:typedef将struct LNode名字改为LNode

LinkList L;//L指的是指向某结点的指针变量

LNode *L;// *L为对应的结点变量,表示该结点的名称

头结点:为了方便进行操作而引入的一个结点,数据域一般不存储数据,指针域指向下一个结点或NULL

好处:

(1)便于首元结点的处理

(2)便于空表和非空白的统一处理

首元结点:指的是在头结点后的第一个结点

(2)基本操作
初始化

Linear List_线性表_12

Status InitList(LinkList L){

//构造一个空表

L=new LNode;    //生成新结点作为头结点,头指针L指向头结点

L->next=NULL;  //指针域置空

return OK;

}

插入

方式一:头插法

Linear List_单链表_13

void CreateList_H(LinkList &head,int n){

L=new LNode;      //生成头结点

L->next=NULL;    //指针域置空

LinkList head=L;   //将head也指向头结点

LinkList rear=L;    //rear用来一直指向尾结点

for(int i=0;i<n;i++){

p=new LNode;  //生成新结点

p->data=data;  //初始化节点数据域

p->next=head->next;//前插法插入新结点

head->next=p;

rear=p;

}

}

说明:在进行插入新结点的时候,一定要保持原单链表不断

Linear List_顺序表_14

方式二:尾插法

Linear List_顺序表_15



void CreateList_H(LinkList &head,int n){

L=new LNode;      //生成头结点

L->next=NULL;    //指针域置空

LinkList head=L;   //将head也指向头结点

LinkList rear=L;    //rear用来一直指向尾结点

for(int i=0;i<n;i++){

p=new LNode;  //生成新结点

p->data=data;  //初始化节点数据域

p->next=NULL;//初始化新结点的指针域

rear->next=p;  //后插法插入新结点

rear=p;

}

}

查找(修改)

Status Getelem(LinkList L,int i,ElemType &e){

//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值

p = L->next;j=1;  //初始化,p指向首元结点,计数器j初始赋值为1

while(p&&j<i){   //顺链表向后扫描,直到p为空或p指向第i个元素 

p=p->next;          //p指向下一个结点

++j;                     //计数器j相应加1

}

if(!p||j>i)return ERROR;  //i值不合法i>n或i<=0

e=p->data;                  //取第i个结点的数据域

return OK;

}

删除

Linear List_顺序表_16

Status ListDelete (LinkList &L,int i){

//在头结点的单链表L中,删除第i个元素

p=L;j=0;

while((p->next)&&(j<i-1)){   //查找第(i-1)个结点,p指向该结点

p=p->next;++j;

}

if(!(p->next)||(j>i-1))   return ERROR;  //删除位置不合理

q=p->next;            //临时保存待删除的结点的地址以备释放

p->next=q->next; //改变删除结点的前驱结点的指针域

delete q;              //释放待删除结点的空间

return OK;

}

排序

说明:使用冒泡排序进行排序,与数组排序不同的是,数组可以通过下标来进行移动和进行基本操作,但是对于单链表而言,需要使用双指针来进行移动,进行操作。

Status SortedList(LNode *L)

{

   LNode *p,*q;

   int t;

   int length=LengthList(L)-1;  

   while(length)

   {

           p=L->next;

           q=p->next;

       while(q!=NULL)

       {

           if(p->data>q->data){

               t=p->data;

               p->data=q->data;   //交换指针所指向的元素值

               q->data=t;

               p=q;               //指针后移

               q=q->next;

           }

           else{

               p=q;

               q=q->next;

           }

       }

       length--;

   }

   if(length==0){

       printf("排序成功!\n");

   }

}

正是由于单链表的这种在查找过程中只能从前往后进行查找的缺点,所以对其提出了改进,引入了“循环链表“和“双向链表

循环链表

Linear List_顺序表_17

说明:通过将原单链表中的指针域指向头结点,形成闭合环路,在进行查找操作时,就会方便了很多,既可以向后查找,也可以向前查找。

插入:

Linear List_顺序表_18

和单链表的插入操作基本是类似的

双向链表Linear List_线性表_19

存储结构:

typedef struct DuLNode{

ElemType data;   //数据域

struct DuLNode *prior;  //指向直接前驱(前一个结点)

struct DuLNode *next;   //指向直接后继(后一个结点)

}DuLNode,*DuLinkList;

DuLinklist L;

初始化:

Linear List_单链表_20

Status InitList( DuLinkList  L){

//构造一个空表

L=new LNode;    //生成新结点作为头结点,头指针L指向头结点

L->prior=NULL;  //前指针域置空

L->next=NULL;  //后指针域置空

return OK;

}

插入

Linear List_顺序表_21

删除

Linear List_顺序表_22