线性表的定义和特点
线性表的定义
线性表是n个类型相同元素的有限序列,通常记作($a_1$,$a_2$,$a_3$,…,$a_n$)
线性表的特点
1.集合中必存在唯一的一个第一元素
2.集合中必存在唯一的一个最后元素
3.除最后元素之外,均有唯一的后继
4.除第一元素之外,均有唯一的前驱
线性表的抽象数据类型描述
ADT List {
数据对象:D={$a_i$|$a_j$ $\in$ElemSet,i=1,2…,n,n$\geqslant$0}
{称n为线性表的表长;
称n=0时的线性表为空表}
数据关系:R={<$a_{i-1}$,$a_i$>,$a_i$ $\in$ D,i=2,3,…,n}
基本操作:
InitList(&L) //构造空线性表
DestoryList(&L) //销毁线性表L
ListEmpty(L) // 判断L是否空
ListLength(L) //求L长度
PriorElem(L,cur_e,&pre_e) //求前驱
NextElem(L,cur_e,&next_e) //求后继
GetElem(L,i,&e) //取i位置的值
PutElem(&L,i,e) //线性表赋值
LocateElem(L,e,compare()) //在线性表中查找e
ListTraverse(L,visit()) //遍历线性表
ClearList(&L) //清空线性表
ListInsert(&L,i,e) //在i位置插入e
ListDelete(&L,i,&e) //删除i位置的元素
}ADT List
线性表的顺序表示和实现
存储结构实现
用一组地址连续的存储单元依次存放线性表中的数据元素
#define LIST_INIT_SIZE 80 #define LISTINCREMENT 10 typedef int ElemType; typedef struct{ ElemType *elem; int length; int listsize; }SqList;
|
基本操作实现
#include <stdio.h> #include<stdlib.h> #define LIST_INIT_SIZE 80 #define LISTINCREMENT 10 #define OVERFLOW 0 #define OK 1 #define TRUE 1 #define FALSE 0 #define ERROR 0 typedef int Elemtype; typedef struct{ Elemtype *elem; int length; int listsize; }SqList; void InitList_Sq(SqList &L){ L.elem=(Elemtype*)malloc(LIST_INIT_SIZE*sizeof(Elemtype)); if(!L.elem) { exit(OVERFLOW); } L.length=0; L.listsize=LIST_INIT_SIZE; printf("InitList SqList successfully.\n"); } void ShowList_Sq(SqList L){ for(int i=0;i<=L.length;i++) { printf("%d\n",L.elem[i]); } } int DestoryList(SqList &L){ free(L.elem); return OK; } void ListEmpty(SqList L){ if (L.length==0) { printf("SqList is empty.\n"); } else { printf("SqList is not empty.\n"); } }
int ListLength(SqList L){ return L.length; } void PriorElem(SqList L,int cur_e,Elemtype &pre_e ){ if(cur_e<=0||L.length==0||cur_e>L.length) { printf("ERROR!"); exit(-1); } else pre_e=L.elem[cur_e-1]; } void ListInsert_Sq(SqList &L,int i,Elemtype e){ for (int j=L.length-1;j>=i-1;--j) { L.elem[j+1]=L.elem[j]; } L.elem[i-1]=e; ++L.length; printf("Insert operation succeed."); if(i<1||i>L.length+1) printf("ERROR!"); if (L.length>=L.listsize){ Elemtype *newbase; newbase=(Elemtype*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(Elemtype)); if(!newbase) printf("failed!"); L.elem=newbase; L.listsize+=LISTINCREMENT; } } void ListDelete_Sq(SqList &L,int i,Elemtype &e){ if((i<1)||(i>L.length)) { printf("ERROR!"); exit(0); } e=L.elem[i-1]; for(int j=i-1;j<L.length-1;j++) { L.elem[j]=L.elem[j+1]; } L.length--; printf("delete succeed."); } int main(){ while(1){ printf("0---exit\n1--InitList_Sq\n2--DestoryList\n3--ShowList_Sq\n4--ListEmpty\n5--ListLength\n6--PriorElem\n7--ListInsert_Sq\n8--ListDelete_Sq\n"); printf("================================\n"); int temp;SqList L; printf("input button:"); scanf("%d",&temp); if(temp==0) { printf("process exit."); exit(0); } if(temp==1) InitList_Sq(L); if(temp==2) DestoryList(L); if(temp==3) ShowList_Sq(L); if(temp==4) ListEmpty(L); if(temp==5) printf("List length:%d\n",ListLength(L)); if(temp==6){ Elemtype pre_e; int cur_e; printf("input cur_e:"); scanf("%d",&cur_e); PriorElem(L, cur_e, pre_e); printf("PriorElem is:%d",pre_e); } if(temp==7){ Elemtype e; int i; printf("insert before i:"); scanf("%d",&i); printf("input data(int):"); scanf("%d",&e); ListInsert_Sq(L, i, e); } if(temp==8){ Elemtype e; int i; printf("input number to delete:"); scanf("%d",&i); ListDelete_Sq(L, i, e); } } }
|
顺序表的优缺点
优点:节省存储空间;对线性表中的第i个结点的操作易于实现;容易查找一个结点的前驱和后继
缺点:插入和删除需要移动数据;建立空表时较难确定所需的存储空间
线性表的链式表示和实现
存储结构
typedef int ElemType; typedef struct LNode{ ElemType data; struct LNode *next; }LNode,*LinkList;
|
基本操作实现
#include <iostream> #define TRUE 1; #define FALSE 0; #define OK 1; using namespace std; typedef int ElemType; typedef struct LNode{ ElemType data; struct LNode *next; }LNode,*LinkList; void InitList(LinkList &L){ L=(LNode *)malloc(sizeof(LNode)); L->next=NULL; } int ListEmpty(LinkList L){ if(L==NULL) {return TRUE;} else {return FALSE;} } int GetElem_L(LinkList L,int i,ElemType &e){ LNode* p; p=(LNode *)malloc(sizeof(LNode)); p=L->next; int j=1; while(p&&j<i) { p=p->next; ++j; } if(!p||j>i) { cout<<"ERROR!"<<endl; exit(0); } e = p->data; return OK; } void ListInsert_L(LinkList &L,int i,ElemType e){ LNode* p=L; int j=0; while(p&&j<i-1) { p=p->next; ++j; } if(!p||j>i-1) { cout<<"ERROR!"<<endl; exit(0); } LNode *s; s=(LinkList)malloc(sizeof(LNode)); s->data=e; s->next=p->next; p->next=s; cout<<"Insert succeed."<<endl; } void ListDelete_L(LinkList &L,int i,ElemType &e){ LNode *p=L; int j=0; while(p->next&&j<i-1) { p=p->next; ++j; } if(!(p->next)||j>i-1) { cout<<"ERROR!"<<endl; exit(0); } LNode *q=p->next; p->next=q->next; e=q->data; free(q); cout<<"Delete succeed."<<endl; } void ClearList(LinkList &L){ while(L->next){ LNode *p; p=L->next; L->next=p->next; free(p); } } int main(){ LinkList L; InitList(L); ElemType e; e=1; ListInsert_L(L, 1, e); L->next->next=NULL; cout<<L->next->next<<endl; }
|
循环链表
单链表的最后一个结点的指针指向头指针
优点是从表中任意结点出发都可以找到其他结点
循环链表采用头尾指针,因为这样查找第一个结点和最后一个结点都容易
双向链表
存储实现
typedef int ElemType; typedef struct DuLNode{ ElemType data; struct DuLNode* prior; struct DuLNode* next; }DuLNode,*DuLinkList;
|