数据结构——顺序表与链表
1.顺序表
我在学习完顺序表后一直对顺序表、数组、结构体数组这几个概念存在一些疑问,这里给出一些分析和看法。
顺序表:在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构。
数组:把具有相同数据类型的若干变量按有序的形式组织起来,以便于程序处理,这些数据元素的集合就是数组。
从顺序表的定义来看,它和数组确实有很大的联系。首先,它是以数组的形式来保存的,其次,它和数组一样都是存储的元素物理地址相邻,具有很大的相似性。
但,仔细研究还是会发现它们的不同之处:
typedef struct Sqlist { ElemType *slist; /*存储空间的基地址,动态分配的一维数组*/ int length; /*顺序表的当前长度*/ int listsize; /*当前分配的存储空间*/ } Sqlist;
顺序表是通过结构体来定义的,在结构体中有一个数组来保存顺序表所存储的数据,然后用定义的整形变量来确定数组中存储的有效数据的个数。而数组直接定义,简洁明了,很容易使用。这样看来数组比顺序表更加方便使用了。但是,数组也有它的缺陷它不能进行增,删等操作,而这些对顺序表来说就很简单了。
1 #include<stdio.h> 2 #include<malloc.h> 3 #define ERROR 0 4 #define OK 1 5 #define INIT_SIZE 5 /*初始分配的顺序表长度*/ 6 #define INCREM 5 /*溢出时,顺序表长度的增量*/ 7 typedef int ElemType; /*定义表元素的类型*/ 8 typedef struct Sqlist 9 { 10 ElemType *slist; /*存储空间的基地址*/ 11 int length; /*顺序表的当前长度*/ 12 int listsize; /*当前分配的存储空间*/ 13 } Sqlist; 14 15 int InitList_sq(Sqlist *L); /*线性表的初始化*/ 16 int CreateList_sq(Sqlist *L,int n); /* 构造一个长度为n顺序表 */ 17 int ListInsert_sq(Sqlist *L,int i,ElemType e);/*在表的第i个位置前插入一个元素e */ 18 int PrintList_sq(Sqlist *L); /*输出顺序表的元素*/ 19 int ListDelete_sq(Sqlist *L,int i); /*删除第i个元素*/ 20 int ListLocate(Sqlist *L,ElemType e); /*查找值为e的元素*/ 21 22 int InitList_sq(Sqlist *L) 23 { 24 L->slist=(ElemType*)malloc(INIT_SIZE*sizeof(ElemType)); 25 if(!L->slist) 26 { 27 return ERROR; 28 } 29 L->length=0;///空表的长度为0 30 L->listsize=INIT_SIZE;///初始存储容量 31 return OK; 32 }/*InitList*/ 33 34 int CreateList_sq(Sqlist *L,int n) 35 { 36 ElemType e; 37 int i; 38 for(i=0; i<n; i++) 39 { 40 printf("input data %d",i+1); 41 scanf("%d",&e); 42 if(!ListInsert_sq(L,i+1,e)) 43 { 44 return ERROR; 45 } 46 } 47 return OK; 48 }/*CreateList*/ 49 50 /*输出顺序表中的元素*/ 51 int PrintList_sq(Sqlist *L) 52 { 53 int i; 54 for(i=1; i<=L->length; i++) 55 { 56 printf("%5d",L->slist[i-1]); 57 } 58 return OK; 59 }/*PrintList*/ 60 61 int ListInsert_sq(Sqlist *L,int i,ElemType e) 62 { 63 int k; 64 if(i<1||i>L->length+1)///i值不合法 65 { 66 return ERROR; 67 } 68 if(L->length>=L->listsize)///当前存储空间已满,增加分配 69 { 70 L->slist=(ElemType*)realloc(L->slist,(INIT_SIZE+INCREM)*sizeof(ElemType)); 71 if(!L->slist) 72 { 73 return ERROR; 74 } 75 L->listsize+=INCREM; 76 } 77 for(k=L->length-1; k>=i-1; k--)///元素后移 78 { 79 L->slist[k+1]= L->slist[k]; 80 } 81 L->slist[i-1]=e;///在第i个位置上插入元素e 82 L->length++;///表长加1 83 return OK; 84 }/*ListInsert*/ 85 86 /*在顺序表中删除第i个元素*/ 87 int ListDelete_sq(Sqlist *L,int i) 88 { 89 int p; 90 if(i<1||i>L->length)///i值不合法 91 { 92 return ERROR; 93 } 94 for(p=i-1; p<L->length-1; p++)///元素后移 95 { 96 L->slist[p]=L->slist[p+1]; 97 } 98 L->length--;///表长减1 99 return OK; 100 } 101 /*在顺序表中查找指定值元素,返回其序号*/ 102 int ListLocate(Sqlist *L,ElemType e) 103 { 104 int i; 105 i=0; 106 while((i<=L->length)&&(L->slist[i]!=e)) 107 { 108 i++; 109 } 110 if(i<=L->length) 111 { 112 return i+1; 113 } 114 else 115 { 116 return -1; 117 } 118 } 119 120 int main() 121 { 122 Sqlist sl; 123 int n,m,k; 124 printf("please input n:"); /*输入顺序表的元素个数*/ 125 scanf("%d",&n); 126 if(n>0) 127 { 128 printf("\n1-Create Sqlist:\n"); 129 InitList_sq(&sl); 130 CreateList_sq(&sl,n); 131 printf("\n2-Print Sqlist:\n"); 132 PrintList_sq(&sl); 133 printf("\nplease input insert location and data:(location,data)\n"); 134 scanf("%d,%d",&m,&k); 135 ListInsert_sq(&sl,m,k); 136 printf("\n3-Print Sqlist:\n"); 137 PrintList_sq(&sl); 138 printf("\n"); 139 } 140 else 141 printf("ERROR"); 142 return 0; 143 }
2.链表
顺序表在使用的时候有以下两个主要的缺点:
(1)插入和删除运算的时候必须移动大量(几乎一半)数据元素,效率低下。
(2)必须预先分配存储空间,造成空间利用率低,而且表的容量很难扩充。
为了克服顺序表的缺点,可以使用动态存储分配来存储线性表,也就是链式存储结构。
typedef struct LNode /*线性表的单链表存储*/ { ElemType data;///数据域 struct LNode *next;///指针域 } LNode,*LinkList;
在单链表中任何两个元素的存储位置之间没有固定的联系,每个元素的存储位置都包含在其直接前驱结点的信息中。
1 #include<stdio.h> 2 #include<malloc.h> 3 #define ERROR 0 4 #define OK 1 5 typedef int ElemType; /*定义表元素的类型*/ 6 typedef struct LNode /*线性表的单链表存储*/ 7 { 8 ElemType data;///数据域 9 struct LNode *next;///指针域 10 } LNode,*LinkList; 11 12 LinkList CreateList(int n); /*构造长度为n的链表*/ 13 void PrintList(LinkList L); /*输出带头结点单链表的所有元素*/ 14 int GetElem(LinkList L,int i,ElemType *e); /* 在第i个元素存在的情况下,替换为e*/ 15 16 LinkList CreateList(int n) 17 { 18 LNode *p,*q,*head; 19 int i; 20 head=(LinkList)malloc(sizeof(LNode)); 21 head->next=NULL;///头结点 22 p=head; 23 for(i=0; i<n; i++) 24 { 25 q=(LinkList)malloc(sizeof(LNode)); 26 printf("input data %i:",i+1); 27 scanf("%d",&q->data); /*输入元素值*/ 28 q->next=NULL; /*结点指针域置空*/ 29 p->next=q; /*新结点连在表末尾*/ 30 p=q; 31 } 32 return head; 33 }/*CreateList*/ 34 35 void PrintList(LinkList L)///输出链表存储内容 36 { 37 LNode *p; 38 p=L->next; /*p指向单链表的第1个元素*/ 39 while(p!=NULL) 40 { 41 printf("%5d",p->data); 42 p=p->next; 43 } 44 }/*PrintList*/ 45 46 int GetElem(LinkList L,int i,ElemType *e)///获取链表第i位置上的元素 47 { 48 LNode *p; 49 int j=1; 50 p=L->next;///指向第一个节点 51 while(p&&j<i)///向后查找,直到p指向第i个元素或者p为空 52 { 53 p=p->next; 54 j++; 55 } 56 if(!p||j>i)///第i个元素不存在 57 { 58 return ERROR; 59 } 60 *e=p->data;///获取到第i个元素 61 return OK; 62 }/*GetElem*/ 63 64 int ListInsert_sq(LinkList L,int i,ElemType e)///向链表中第i个位置前插入元素 65 { 66 int j=0; 67 LinkList p=L,s; 68 while(p&&j<i-1)///向后查找寻找第i-1个元素 69 { 70 p=p->next; 71 j++; 72 } 73 if(!p||j>i-1) 74 { 75 return ERROR; 76 } 77 s=(LinkList)malloc(sizeof(struct LNode));///生成新结点 78 s->data=e;///插入 79 s->next=p->next; 80 p->next=s; 81 return OK; 82 } 83 int ListDelete(LinkList L,int i,ElemType *e)///删除链表第i个位置上的元素,并由e返回其值 84 { 85 int j=0; 86 LinkList p=L,q;///p初始化时指向头结点 87 while(p->next&&j<i-1)///向后寻找第i个结点,并令p指向其前驱 88 { 89 p=p->next; 90 j++; 91 } 92 if(!(p->next)||j>i-1) 93 { 94 return ERROR; 95 } 96 q=p->next;///q指向被删除结点 97 p->next=q->next; 98 *e=q->data; 99 free(q); 100 return OK; 101 } 102 int main() 103 { 104 int n,i; 105 ElemType e; 106 LinkList L=NULL; /*定义指向单链表的指针*/ 107 printf("please input n:"); /*输入单链表的元素个数*/ 108 scanf("%d",&n); 109 if(n>0) 110 { 111 printf("\n1-Create LinkList:\n"); 112 L=CreateList(n); 113 printf("\n2-Print LinkList:\n"); 114 PrintList(L); 115 printf("\n3-GetElem from LinkList:\n"); 116 printf("input i="); 117 scanf("%d",&i); 118 if(GetElem(L,i,&e)) 119 printf("No%i is %d",i,e); 120 else 121 printf("not exists"); 122 } 123 else 124 printf("ERROR"); 125 return 0; 126 }