链式存储定义:
为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
线性表链式存储优点:
无需一次性定制链表的容量
插入和删除操作无需移动数据元素
线性表链式存储缺点:
数据元素必须保存后继元素的位置信息
获取指定数据的元素操作需要顺序访问之前的元素
线性表链式存储与顺序存储的比较:
线性表的顺序存储结构要求逻辑关系上相邻的元素在物理位置上也相邻,这样方便了随机存取,但是在插入和删除元素时,需要移动大量元素;
而线性表的链式存储则不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构的可随机存取的优点,不过在插入和删除元素时比较方便。
linklist.h
#ifndef __MYLINKLIST_H__
#define __MYLINKLIST_H__
//传统链表 数据和指针在一个节点
//现代链表 把指针独立出来,做成节点 在包含数据的节点中包含这个独立节点
typedef void LinkList;
typedef struct _tag_LinkListNode//被大千世界包含
{
struct _tag_LinkListNode*next;
}LinkListNode;
LinkList* LinkList_Create();
void LinkList_Destroy(LinkList* list);
void LinkList_Clear(LinkList* list);
int LinkList_Length(LinkList* list);
int LinkList_Insert(LinkList*list,LinkListNode*node,int pos);
LinkListNode* LinkList_Get(LinkList*list,int pos);
LinkListNode* LinkList_Delete(LinkList*list,int pos);
#endif
linklist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linklist.h"
//头结点 封装了一个 节点和链表长度
typedef struct _tag_LinkList
{
LinkListNode header;
int length;
}TLinkList;
LinkList* LinkList_Create()//创建
{
TLinkList *ret = NULL;
ret = (TLinkList *)malloc(sizeof(TLinkList)) ;
memset(ret,0,sizeof(TLinkList));//等价于下面两句
//tmp->length = 0;
//tmp->header.next = NULL;
return ret;//任何类型的指针都可以直接赋值给void*
}
void LinkList_Destroy(LinkList* list)
{
if (list!=NULL)
{
free(list);
list = NULL;
}
}
//让链表恢复到初始化状态
void LinkList_Clear(LinkList* list)
{
TLinkList *tlist = NULL;
if (list==NULL)
{
return ;
}
tlist = (TLinkList *)list;//void*类型的指针必须转换后赋值给其他类型的指针
tlist->length = 0;
tlist->header.next = NULL;
}
int LinkList_Length(LinkList* list)
{
TLinkList *tlist = NULL;
if (list==NULL)
{
return 0;
}
tlist = (TLinkList *)list;//void*类型的指针先转换再赋值给相应类型的指针
return tlist->length;
}
int LinkList_Insert(LinkList*list,LinkListNode*node,int pos)
{
int ret = 0,i;
LinkListNode*current=NULL;//引入辅助指针变量?
TLinkList *tList = NULL;
if (list ==NULL||node==NULL||pos<0)
{
ret = -1;
printf("func LinkList_Insert() error :%d\n ",ret);
return ret;
}
tList = (TLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
current = &tList->header;//一开始让辅助指针current指向链表头结点的LinkListNode
//假设要删除3号位置的元素(从0号位置开始),3号元素位置保存在2号位置元素的next域
//移动辅助指针变量current指向2号位置
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
//让新节点node指向后续节点
node->next = current->next;
//当前位置2号 的next域赋值为node
current->next = node;
tList->length++;
return ret;
}
LinkListNode* LinkList_Get(LinkList*list,int pos)
{
int ret = 0,i;
LinkListNode*current=NULL;
TLinkList *tList = NULL;
if (list ==NULL||pos<0)
{
ret = -1;
printf("func LinkList_Get() error :%d\n ",ret);
return NULL;
}
tList = (TLinkList*)list;
current = &tList->header;//辅助指针变量指向链表头部
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
return current->next;
}
LinkListNode* LinkList_Delete(LinkList*list,int pos)
{
int i;
LinkListNode*current=NULL;//引入辅助指针变量
LinkListNode*ret=NULL;//引入辅助指针变量 缓存删除的节点 返回
TLinkList *tList = NULL;
if (list ==NULL||pos<0)
{
printf("func LinkList_Delete() error\n");
return NULL;
}
tList = (TLinkList*)list;
current = &tList->header;//一开始让辅助指针current指向链表头结点的LinkListNode
//假设删除3号位置节点 移动current指向2号位置
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
ret = current->next;//缓存即将删除的节点(3号位置)
//连线
current->next = ret->next;
tList->length--;
return ret;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include<stdio.h>
#include <string.h>
#include "linklist.h"
//现代链表:业务节点(大千世界)包含链表节点(我)
typedef struct Teacher
{
LinkListNode listnode;
char*name;
int age;
}Teacher;
int main()
{
//创建一个线性表
LinkList* list= NULL;
int length ,ret,i;
Teacher t1,t2,t3,t4,t5,t6;
t1.age = 30;
t2.age = 32;
t3.age = 33;
t4.age = 34;
t5.age = 35;
t6.age = 36;
//创建并返回TLinkList类型的头结点
list = LinkList_Create();//任意类型的指针变量都可以直接辅助给void*
if (list == NULL)
{
return -1;
}
length = LinkList_Length(list);//TLinkList
printf("length:%d\n",length);
//插入
/*说明LinkListNode*)&t1 把结构体Teacher转为结构体LinkListNode 是可以的
因为结构体Teacher的地址和结构体Teacherz中LinkListNode地址重叠*/
//实现链表算法和具体业务节点的分离(细细体会)
//头插法
/*ret = LinkList_Insert(list,(LinkListNode*)&t1, 0);
ret = LinkList_Insert(list,(LinkListNode*)&t3, 0);
ret = LinkList_Insert(list,(LinkListNode*)&t4, 0);
ret = LinkList_Insert(list,(LinkListNode*)&t5, 0);
ret = LinkList_Insert(list,(LinkListNode*)&t6, 0);*/
//尾插法
ret = LinkList_Insert(list,(LinkListNode*)&t1, LinkList_Length(list));
ret = LinkList_Insert(list,(LinkListNode*)&t3, LinkList_Length(list));
ret = LinkList_Insert(list,(LinkListNode*)&t4, LinkList_Length(list));
ret = LinkList_Insert(list,(LinkListNode*)&t5, LinkList_Length(list));
ret = LinkList_Insert(list,(LinkListNode*)&t6, LinkList_Length(list));
length = LinkList_Length(list);
printf("length:%d\n",length);
//遍历
for (i=0;i<length;i++)
{
Teacher *tmp =(Teacher *)LinkList_Get(list,i);
if (tmp==NULL)
{
return;
}
printf("%d\n",tmp->age);
}
//删除
while (LinkList_Length(list)>0)
{
Teacher*tmp =(Teacher*) LinkList_Delete(list,0);
if (tmp==NULL)
{
return ;
}
printf("delete : %d\n",tmp->age);
}
//销毁
//LinkList_Destroy(list);
system("pause");
return 0;
}