一、基本概念
解决顺序存储的缺点,插入和删除,动态存储(在编写代码阶段,不需要预估存储的大小,内存使用率较高)问题。
特点:
线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。
所以前面的顺序表只需要存储数据元素信息就可以了。在链式结构中还需要一个元素存储下一个元素的地址。
为了表示每个数据元素,ai与其直接后继数据元素ai+1之间的逻辑关系,对ai来说,除了存储其本身的信息外,还需要存一个指示器直接后续的信息。把存储元素信息的域叫数据域,把存储直接后继位置的域叫指针域。这两部分信息组成数据元素ai的存储映像,叫结点(Node);
二、基本函数
单链表中,c语言的描述
typedef struct person {
char name[32];
char sex;
int age;
int score;
}DATATYPE;
typedef struct node {
DATATYPE data;
struct node *next,*prev;
}LinkNode;
typedef struct list {
LinkNode *head;
int tlen;//元素总结点个数
int clen;//当前链表里结点个数
}LinkList;
1.DouLinkList* CreateDouLinkList();
2.int InsertHeadLinkList(DouLinkList *list, DATATYPE *data);
3.int ShowDouLinkList(DouLinkList *list,DIRECT direct);
4.int GetSizeDouLinkList(DouLinkList *list);
5.int RevertDouLinkList(DouLinkList *list);
6.DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun,void* arg);
7.int DeleteLinkList(DouLinkList *list, PFUN fun,void* arg);
8.int IsEmptyDouLinkList(DouLinkList *list);
9.int ModifyDouLinkList(DouLinkList *list,PFUN fun,void* arg,DATATYPE *data);
10.int DestroyDouLinkList(DouLinkList **list);
11.int InserPosDouLinkList(DouLinkList *list,DATATYPE *data,int pos);
1. CreateDouLinkList 创建双向链表
DouLinkList *CreateDouLinkList() { DouLinkList* dl = (DouLinkList*)malloc(sizeof(DouLinkList)); if(NULL == dl) { perror("CreateDouLinkList malloc"); return NULL; } dl->head =NULL; dl->clen = 0 ; return dl; }
2. InsertHeadDouLinkList 头插
int InsertHeadLinkList(DouLinkList *list, DATATYPE *data) { DouLinkNode*newnode = malloc(sizeof(DouLinkNode)); if(NULL == newnode) { perror("InsertHeadLinkList malloc"); return 1; } memcpy(&newnode->data,data,sizeof(DATATYPE)); newnode->next = NULL; newnode->prev= NULL; if(0==list->clen)//empty { list->head = newnode; } else { newnode->next = list->head; list->head->prev = newnode; list->head = newnode; } list->clen++; return 0; }
3. ShowDouLinkList 打印
xxx.h
typedef enum{DIR_FORWARD,DIR_BACKWARD}DIRECT;
xxx.c
int ShowDouLinkList(DouLinkList *list, DIRECT direct) { int i = 0 ; DouLinkNode* tmp = list->head; if(direct==DIR_FORWARD) { for(i=0;i<GetSizeDouLinkList(list);i++) { printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score); tmp=tmp->next; } } else { while(tmp->next) { tmp=tmp->next; } for(i=0;i<GetSizeDouLinkList(list);i++) { printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score); tmp=tmp->prev; } } return 0; }
(注意:如果是头插的方式,选择顺序打印,打印的效果是相反的)
4. GetSizeDouLinkList 获取当前元素个数
int GetSizeDouLinkList(DouLinkList *list) { return list->clen; }
5.RevertDouLinkList 逆序
逆序前准备:
逆序具体操作:
int RevertDouLinkList(DouLinkList *list) { int size = GetSizeDouLinkList(list); if(size<2) { return 0; } DouLinkNode* prev= NULL; DouLinkNode* tmp = list->head; DouLinkNode*next= tmp->next; while(1) { tmp->next = prev; tmp->prev = next; prev= tmp; tmp = next; if(NULL == tmp) { break; } next =next->next; } list->head = prev; return 0; }
6. FindDouLinkList 查找
xxx.h
typedef int (*PFUN)(DATATYPE*data,void* arg);
xxx.c
DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun, void *arg) { DouLinkNode* tmp = list->head; int size = GetSizeDouLinkList(list); int i = 0; for(i = 0 ;i<size;i++) { //if(0==strcmp(tmp->data.name)) if(fun(&tmp->data,arg)) { return tmp; } tmp= tmp->next; } return NULL; }
main.c
int findbyname(DATATYPE*data,void* arg) { return (0 == strcmp(data->name,(char*)arg)); } int findbyage(DATATYPE*data,void* arg) { return data->age == *(int*)arg; } int main() { DATATYPE data[5]={ {"zhangsan",'m',20,70}, {"lisi",'f',21,60}, {"wangmazi",'m',25,80}, {"liubei",'f',30,85}, {"caocao",'f',40,90}, }; char want_name[]="lisi"; DouLinkNode* tmp = FindLinkList(dl,findbyname,want_name); int want_age = 25; DouLinkNode* tmp = FindLinkList(dl,findbyage,&want_age); if(NULL == tmp) { printf("can't find person ,name:%s\n",want_name); } else { printf("%s:%d\n",tmp->data.name,tmp->data.score); } return 0; }
7. DeleteDouLinkList 删除链表中某个结点
xxx.h
int DeleteLinkList(DouLinkList *list, PFUN fun,void* arg);
xxx.c
int DeleteLinkList(DouLinkList *list, PFUN fun, void *arg) { if(NULL == list) { fprintf(stderr,"DouLinkList is null"); return 1; } if(IsEmptyDouLinkList(list)) { fprintf(stderr,"DouLinkList is empty"); return 1; } DouLinkNode* ret = FindLinkList(list,fun,arg); if(NULL==ret) { fprintf(stderr,"DeleteLinkList error,cant find\n"); return 1; } if(ret == list->head) { list->head = ret->next; list->head->prev = NULL; } else { if(ret->next) ret->next->prev = ret->prev; ret->prev->next = ret->next; } free(ret); list->clen--; return 0; }
main.c
int findbyname(DATATYPE*data,void* arg) { return (0 == strcmp(data->name,(char*)arg)); } int findbyage(DATATYPE*data,void* arg) { return data->age == *(int*)arg; } int main() { DATATYPE data[5]={ {"zhangsan",'m',20,70}, {"lisi",'f',21,60}, {"wangmazi",'m',25,80}, {"liubei",'f',30,85}, {"caocao",'f',40,90}, }; DeleteLinkList(dl,findbyname,"lisi"); return 0; }
8. IsEmptyDouLinkList 判断链表元素是否为空
int IsEmptyDouLinkList(DouLinkList *list) { return 0 == list->clen; }
9. ModifyDouLinkList 修改结点内容
xxx.h
typedef int (*PFUN)(DATATYPE*data,void* arg); int ModifyDouLinkList(DouLinkList *list,PFUN fun,void* arg,DATATYPE *data);
xxx.c
int ModifyDouLinkList(DouLinkList *list, PFUN fun, void *arg, DATATYPE *data) { DouLinkNode* ret = FindLinkList(list,fun,arg); if(NULL == ret) { fprintf(stderr,"ModifyDouLinkList error,cant find\n"); return 1; } memcpy(&ret->data,data,sizeof(DATATYPE)); return 0; }
main.c
int findbyname(DATATYPE*data,void* arg) { return (0 == strcmp(data->name,(char*)arg)); } int findbyage(DATATYPE*data,void* arg) { return data->age == *(int*)arg; } int main() { DATATYPE data[5]={ {"zhangsan",'m',20,70}, {"lisi",'f',21,60}, {"wangmazi",'m',25,80}, {"liubei",'f',30,85}, {"caocao",'f',40,90}, }; ModifyDouLinkList(dl,findbyname,"zhangsan",&data[3]); return 0; }
10. DestroyDouLinkList 销毁链表
int DestroyDouLinkList(DouLinkList **list) { DouLinkNode* tmp=(*list)->head; while(tmp) { (*list)->head=(*list)->head->next; free(tmp); tmp = (*list)->head; } free(*list); (*list)= NULL; return 0; }
11. InserPosDouLinkList 随机位置插入结点
xxx.h
int InserPosDouLinkList(DouLinkList *list,DATATYPE *data,int pos);
xxx.c
int InserPosDouLinkList(DouLinkList *list, DATATYPE *data,int pos) { if(pos<0 ||pos>GetSizeDouLinkList(list)) { fprintf(stderr,"InserPosDouLinkList error,index error\n"); return 1; } if(IsEmptyDouLinkList(list) || 0 == pos) { return InsertHeadLinkList(list,data); } else { DouLinkNode* tmp = list->head; tmp= list->head; DouLinkNode* newnode = (DouLinkNode*)malloc(sizeof(DouLinkNode)); if(NULL == newnode) { perror("InserPosDouLinkList malloc"); return 1; } memcpy(&newnode->data,data,sizeof(DATATYPE)); newnode->prev = NULL; newnode->next = NULL; int i = pos-1; while(i--) { tmp=tmp->next; } newnode ->prev = tmp; newnode->next = tmp->next; if(tmp->next) { tmp->next->prev = newnode; } tmp->next = newnode; } list->clen++; return 0; }
main.c
int main() { DATATYPE data[5]={ {"zhangsan",'m',20,70}, {"lisi",'f',21,60}, {"wangmazi",'m',25,80}, {"liubei",'f',30,85}, {"caocao",'f',40,90}, }; InserPosDouLinkList(dl,&data[3],3); return 0; }
三、顺序表和链表 优缺点
存储方式:
顺序表是一段连续的存储单元
链表是逻辑结构连续,物理结构(在内存中的表现形式)不连续
时间性能:
查找 顺序表O(1)
链表 O(n)
插入和删除
顺序表 O(n)
链表 O(1)
空间性能
顺序表,需要预先分配空间,大小固定
链表,不需要预先分配,大小可变,动态分配
循环链表
简单的来说,就是将原来单链表中最有一个元素的next指针指向第一个元素或头结点,链表就成了一个环,头尾相连,就成了循环链表。circultlar linker list
注意非空表,和空表。多数会加入头结点。
原来结束的条件是
p->next != NULL ------->>>>> 此时遍历结束条件:p->next != Head
双向链表
double link list。
typedef struct DulNode
{
ElemType date;
struct DulNode *pri;
sturct DulNode *next;
}DulNode,*DuLinkList;