[简述]数据结构-线性表(c语言实现)second60 20180422
1. 线性表的定义
线性表是具有相同特性的数据元素的一个有限序列。
2. 线性表抽象数据类型描述
ADT List
{
数据对象:
D = { ai | 1<= i <= n,n>=0,ai 属于ElemType类型}
数据关系:
R={ | ai, ai+1属于D, i=1...n-1}
基本运算:
initList(&L): 初始化线性表
destroyList(&L): 销毁线性表
listEmpty(L): 线性表是为空
listLength(L): 线性表的长度
dispList(L): 输出线性表
getElem(L,i,&e):求线性表某元素
locateElem(L,e):按元素查找
listInsert(&L, i, e):插入元素
listDelete(&L,i,e):删除元素
}ADT List
3. 线性表的顺序存储结构 --- 顺序表
线性表中的所有元素按照其逻辑顺序依次存储到从存储器中指定位置开始的一块连续的存储空间中。
线性表的元素类型为: ElemType
线性表的长度为: length
线性表的大小为: MaxSize
线性表的顺序存储结构
typedef struct{
ElemType data[MaxSize]; // 存放元素
int length; // 顺序表当前长度
}SqList; // 顺序表类型
代码如下,C语言版,代码已测试通过:
#include "stdio.h"
#define MAXSIZE 12
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
void CreateList(SqList **list, ElemType a[], int n)
{
int i;
if(list == NULL || *list == NULL) return ;
if(n > MAXSIZE) n = MAXSIZE;
for(i=0; i < n; i++)
{
(*list)->data[i] = a[i];
}
(*list)->length = n;
}
void InitList(SqList **list)
{
(*list) = (SqList*) malloc(sizeof(SqList));
(*list)->length = 0;
}
void DestroyList(SqList **list)
{
if( list != NULL && (*list) != NULL)
{
free(*list);
(*list) = NULL;
}
}
int ListEmpty(SqList *list)
{
if(list == NULL) return -1;
return (list->length == 0);
}
int ListLength(SqList *list)
{
if(list == NULL) return -1;
return list->length;
}
void DispList(SqList *list)
{
int i;
if(list == NULL) return -1;
if(ListEmpty(list)) return ;
for(i=0;i< list->length; i++)
printf("%d ", list->data[i]);
printf("\n");
}
int GetElem(SqList *list,int i, ElemType *e)
{
if(list == NULL) return -1;
if( i< 1 || i > list->length)
return 0;
*e = list->data[i-1];
return 1;
}
int LocateElem(SqList *list, ElemType e)
{
int i = 0;
if(list == NULL) return -1;
while(ilength && list->data[i] != e) i++;
if( i>= list->length)
return 0;
else
return i+1;
}
int ListInsert(SqList **list, int i , ElemType e)
{
int j;
if(list == NULL || *list == NULL) return -1;
if( i < 1 || i > (*list)->length +1)
return 0;
i --;
for( j = (*list)->length; j>i; j--)
{
(*list)->data[j] = (*list)->data[j-1];
}
(*list)->data[i] = e;
(*list)->length ++;
return 1;
}
int ListDelete(SqList **list, int i , ElemType *e)
{
int j;
if(list == NULL || *list == NULL) return -1;
if( i < 1 || i > (*list)->length)
return 0;
i --;
*e = (*list)->data[i];
for(j=i; j< (*list)->length -1;j++)
(*list)->data[j] = (*list)->data[j+1];
(*list)->length --;
return 1;
}
/*测试代码*/
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int value = 0;
SqList *list;
InitList(list);
CreateList(&list, a, 10);
printf("ListLength=%d\n",ListLength(list));
DispList(list);
GetElem(list, 3, &value);
printf("GetElem 3 =%d\n", value);
ListDelete(&list,3,&value);
printf("ListDelete 3=%d\n",value);
DispList(list);
ListInsert(&list,3,888);
DispList(list);
DestroyList(&list);
DispList(list);
getchar();
return 1;
}
4. 线性表的链式存储结构 --- 链表
在链式存储中,每个存储结点包括元素本身外,还包含元素间的逻辑关系(结点的后继结点)。
单链表:包含后继
双链表:包含前驱和后继
在链式储存中,为了便于插入和删除算法的实现,每个链表带有一个头结点,并通过头结点的指针唯一标识该链表。
单链表为例:
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkList;
双链表结构:
typedef struct DNode
{
ElemType data;
struct DNode *prior;
struct DNode *next;
}DLinkList;
单链表,算法实现,C语言版:
#include "stdio.h"
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkList;
void CreateListF(LinkList **list, ElemType a[], int n)
{
LinkList *s;
int i ;
*list = (LinkList*)malloc(sizeof(LinkList));
(*list)->next = NULL;
for(i = 0;i < n ; i++)
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i];
s->next = (*list)->next;
(*list)->next = s;
}
}
void CreateListR(LinkList **list, ElemType a[], int n)
{
LinkList *s,*r;
int i;
*list = (LinkList*)malloc(sizeof(LinkList));
r = *list;
for(i=0;i < n;i++)
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;
r = s;
}
r->next = NULL;
}
void InitList(LinkList **list)
{
*list = (LinkList*)malloc(sizeof(LinkList));
(*list)->next = NULL;
}
void DestroyList(LinkList **list)
{
LinkList *p = *list, *q = p->next;
while( q!= NULL)
{
free(p);
p = q;
q = p->next;
}
free(p);
}
int ListEmpty(LinkList *list)
{
return (list->next == NULL);
}
int ListLength(LinkList *list)
{
LinkList *p = list;
int i = 0;
while(p->next != NULL)
{
i++;
p = p->next;
}
return i;
}
void DispList(LinkList *list)
{
LinkList *p = list->next;
while( p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
int GetElem(LinkList *list, int i , ElemType *e)
{
int j = 0;
LinkList *p = list;
while( j
{
j++;
p = p->next;
}
if(p == NULL)
return 0;
else
{
*e = p->data;
return 1;
}
}
int LocateElem(LinkList *list,ElemType e)
{
LinkList *p = list->next;
int i = 1;
while(p!= NULL && p->data != e)
{
p = p->next;
i++;
}
if(p==NULL) return 0;
else return i;
}
int ListInsert(LinkList **list, int i, ElemType e)
{
int j = 0;
LinkList *p=*list,*s;
while( j < i -1 && p!= NULL)
{
j++;
p = p->next;
}
if(p==NULL)
return 0;
else
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = e;
s->next = p->next;
p->next = s;
return 1;
}
}
int ListDelete(LinkList **list,int i,ElemType *e)
{
int j = 0;
LinkList *p = *list, *q;
while( j< i-1 && p!= NULL)
{
j++;
p = p->next;
}
if(p== NULL) return 0;
else
{
*e = p->data;
q = p->next;
if(q == NULL) return 0;
p->next = q->next;
free(q);
return 1;
}
}
int main()
{
int value = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
LinkList *list = NULL;
/*CreateListF(&list, arr, 10); */
CreateListR(&list, arr, 10);
DispList(list);
ListInsert(&list, 4, 444);
DispList(list);
ListDelete(&list,4,&value);
DispList(list);
GetElem(list, 4 , &value);
printf("GetElem 4=%d\n",value);
printf("LocateElem 9=%d\n",LocateElem(list,8));
getchar();
return 1;
}
5 优缺点
线性表的两种存储方式:顺序表和链表,各有好处。
顺序表的优点:
1. 随机存取
2. 除数据外,不用额外空间
3. 查找效率高
顺序表的缺点:
1. 浪费空间,在开始就占用MAXSIZE内存
2. 大小固定不易扩展
3. 插入删除效率低
链表的优点:
1. 插入删除效率高
2. 空间大小根据现有数据有关,不用预分配空间
链表的缺点:
1. 查找效率低
2. 不能随机存取
3. 每个结构需额外的指针,空间大
4. 经常插入删除会造成内存碎片,影响效率和浪费内存空间
6. 用途
顺序表:用途没那么广,因为很多限制,扩展性难。
链表
在平时开发中,链表是用的最多的数据结构之一。
因为链表的插入和删除的效率高。
同时为了避免链表的缺点,造成大量内存碎片。
解决方法为:预分配空间方法,使用时找一个空的结点,删除时,并不真正删除,只是标记为未使用。
7. 总结
半夜了,总结了线性表,加深自已的记忆,同时,也编译过了代码,直接拷贝就能运行。这里都是最简单的顺序表和链表。实际运用中,结构比较复杂,会添加很多字段或标记,但原理都是一样的,特别是链表,运用非常广,基本上用到动态扩展的数组,都会用链表,这里主要是C运用中,C++中直接用STL的vector。
虽然C++中的STL提供了很多好用的容器,但本质的东西,还是要多用多练,才能知道使用的好处。建议都用C来实现一遍,后面的数据结构内容,都会用C来实现。用C的好处就是:你可以把指针练的如火纯青。