线性表是最简单的数据结构,其主要特征有:
1、 每个线性表只有一个头元素,一个尾元素;
2、 除第一个数据元素外,每个元素都有一个直接前驱;
3、 除最后一个数据元素外,每个元素都有一个直接后继。
一、 顺序表
顺序表是用数据元素在物理上的相邻表示逻辑上的相邻。顺序表具有随机存取的特性。
用动态分配的一位数组表示顺序表如下:
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 50
typedef struct{
ElemType *elem;
int length;
int listsize;
}SqList;
初始化顺序表:
Status InitList_Seq(SqList &L)
{
L.elem = (ElemType*)malloc(LIST_NINT_SIZE*sizeof(ElemType));
if(!L.elem)
exit(OVERFLOW);
L.length = 0;
L.lisesize = LIST_INIT_SIZE;
return OK;
}
顺序表的插入:
Status ListInsert_Seq(SqList &L, int i, ElemType e)
{
if(i < 1 || i > L.length + 1)
return ERROR;
if(L.length >= L.listsize)
{
newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(ElemType));
if(!newbase)
exit(OVERFLOW);
L.elem = newbase;
L.listsize += LISTINCREMENT;
}
q = &(L.elem(i-1));
for(p = &(L.elem[L.length-1]); p >= q;p--)
*(p+1) = *p;
*q = e;
L.length++;
return OK;
}
顺序表的删除:
Status ListDelete_Seq(SqList &L, int i, ElemType &e)
{
if(i < 1 || i > L.length)
return ERROR;
p = &L.elem[i-1];
e = *p;
for(q = L.elem+L.length-1; p < q; p++)
*p = *(p+1);
L.length--;
return OK;
}
从以上伪码可知,在顺序表中插入或删除一个元素,需要移动多个元素。对于插入,Ei = n/2; 对于删除, Ed = (n-1)/2。
二、单链表
和顺序表不同,单链表是用指针来表示数据元素之间的关系。单链表的每个结点包含两个域:一个数据域,用来存储数据元素的信息;一个指针域,用来存储后续节点的位置信息。数据元素之间的逻辑关系是用指针来表示的。逻辑上相邻的数据元素,在物理上不一定是相邻的。这样的存储结构便失去了随机存取的特性。但他的优点是插入和删除结点的时候不需要移动数据元素了。
线性表的单链表存储结构
typedef struct LNode{
ElemType data;
struct Lnode *next;
}LNode, *LinkList;
单链表中插入一个结点:
Status ListInsert_L(LinkList &L, int i, ElemType e)
{
p = L;
j = 0;
while(p && j < i-1)
{
p = p->next;
j++;
}
if(!p || j > i-1)
return ERROR;
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
单链表中删除一个元素结点:
Status ListDelete_L(LinkList &L, int i, ElemType &e)
{
p = L;
j = 0;
while(p && j < i-1)
{
p = p->next;
j++;
}
if(!p || j > i-1)
return ERROR;
q = p->next;
e = q->data;
p->next = q->next;
free(q);
return OK;
}
虽然单链表中插入和删除元素不需要移动元素了,但是获取第i个元素,却需要从头开始找起:
Status GetElem_L(LinkList L, int i, ElemType &e)
{
p = L->next;
j = 1;
while(p && j < i)
{
p = p ->next;
j++;
}
if(!p || j > i)
return ERROR;
e = p->data;
return OK;
}
在没有指针的编程语言中,我们可以用一维数组来表示线性链表——静态链表:
#define MAXSIZE 100
typedef struct{
ElemType data;
int cur;
}component, SLinkList[MAXSIZE];
三、双向链表
由于单链表中某些操作较为复杂,例如,PriorElem的执行时间为O(n),为了克服这种单向性的缺点,可以为每个结点设置两个指针域,一个指向后续结点,一个指向前驱结点,即双向链表。
typedef struct DuLNOde{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode, *DuLinkList;
双向链表的特性: d->next->prior = d->prior->next = d;
四、循环链表
循环链表是另外一种链式存储结构。其特点是表中最后一个结点的指针域又指向了头结点,使整个链表形成了一个环形。从任何一个结点出发都可以遍历表中的所有结点。有的时候,在循环链表中设立尾指针比设立头指针更方便。例如,合并两个链表的时候,如果有尾指针的话,合并操作将会非常简单。