线性表的链式存储结构C语言实现
首先是结构体:
typedef struct Lnode
{
int info;
Lnode* next;
}node,*pnode;
我们选择建立一个带头结点的链表,有了头结点方便插入。头结点的信息部分还可以存放链表的长度,但是信息部分不一定有int类型的存储结构,这些还是看具体情况吧。
当然我们还可以用这样的结构体,记录一些关于链表的信息,不过之后的代码都会使用之前提到的“Lnode”结构体。
typedef struct node
{
int info;
node *next;
}node,*pnode;
typedef struct Line
{
pnode firstnode;//指向头节点
int length;//记录表长
pnode lastnode;//指向尾结点
}Line,*pLine;
1.创建线性表
int initlist(pnode &list)
{
if (list != NULL)
{
printf_s("指针不为空,很有可能丢失原有的链表\n");
return 1;
}
else
{
list = (pnode)malloc(sizeof(node));
if (list != NULL)
{
list->next = NULL;
//list->info = 0;
//头节点中可以存放相关数据,比如这里我可以放单链表的长度。
//但这只是一种投机取巧的办法,并不能适用于所有情况,所以之后的代码中没有关于这一点的利用。
}
return 0;
}
}
2.销毁线性表
int destroylist(pnode& list)
{
if (list == NULL)
{
printf_s("指针为空,链表已经不存在。");
//我看过很多人的代码,他们似乎不在乎指针原本是否为空,只要销毁链表就达到目的
//但是我觉得传入一个空的指针给销毁函数本身就是一种异常。
return 1;
}
while (list)
{
pnode keep = list;
list = list->next;
free(keep);
}
return 0;
}
3.清空线性表
int clearlist(pnode& list)
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
pnode p;
p = list->next;
list->next = NULL;
if(p==NULL)
return 0;
else
{
while (p)
{
pnode keep = p;
p = p->next;
free(keep);
}
}
return 0;
}
4.检查线性表是否为空
int listempty(pnode list)
{
if(list==NULL)
{
printf_s("线性表不存在,返回-1\n");
return -1;
}
if (list->next == NULL)
return 1;
else
return 0;
}
5.返回线性表数据元素的个数
int listlength(pnode list)
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return -1;
}
int lenth = 0;
pnode p=list;
if (p->next == NULL)
return 0;
else
{
p = p->next;
while (p!=NULL)
{
p = p->next;
lenth++;
}
}
return lenth;
}
6.返回线性表中指定位置的元素的值
int getinfolist(pnode list,int i,int &e)
{
int listlength(pnode);
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int lenth = listlength(list);
if (lenth == 0)
{
printf_s("链表为空,无法找到数据\n");
return 3;
}
if (i > lenth)
{
printf_s("该位置没有存储数据\n");
return 2;
}
pnode p = list;
for (int j = 1; j <= i; j++)
{
p = p->next;
}
return p->info;
}
7.找到符合条件的元素,返回其位置
bool compare(int a, int b)
{
return a < b;
}
bool (*comparing)(int a, int b) = compare;
int locateelemlist(pnode list, int e, bool (*compare)(int a, int b))
{
int listlength(pnode);
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int lenth = listlength(list);
if (lenth == 0)
{
printf_s("链表为空,无法找到数据\n");
return 3;
}
pnode p = list->next;
for (int i = 1; i <= lenth; i++)
{
if (compare(e, p->info))
{
return i;//compare 表示如果a>b则为true;结果返回的就是线性表中第一个比e小的数字的位置
}
p = p->next;
}
return -1;//没有比e小的数字则返回-1;
}
8.返回线性表中指定元素的前一个元素的位置
int get_prior(pnode list, int numth, pnode& prior)
{
prior = NULL;
int listlength(pnode);
if (list == NULL)
{
//printf_s("线性表不存在\n");
return 1;
}
int lenth = listlength(list);
if (numth > lenth + 1)
{
//printf_s("超出链表范围\n");
return 2;
}
pnode p = list;
for (int i = 1; i <= numth; i++)
{
if (i == numth)
{
prior = p;
break;
}
else
p = p->next;
}
return 0;
}
9.返回线性表中指定元素的后一个元素的位置
int get_next(pnode list, int numth, pnode& next)
{
next = NULL;
//提前置为空,之后的三种特殊情况都会返回NULL
int listlength(pnode);
if (list == NULL)
{
//printf_s("线性表不存在\n");
return 1;
}
int lenth = listlength(list);
if (lenth == 0)
{
//printf_s("链表为空\n");
return 2;
}
if (numth > lenth - 1)
{
//printf_s("位置超出存在后继范围,不存在后继\n");
return 3;
}
pnode p = list->next;
for (int i = 0; i <= numth; i++)
{
if (i == numth)
{
next = p;
break;
}
else
p = p->next;
}
return 0;
}
10.在线性表指定位置插入一个元素
链表中插入元素需要找到该元素的前驱和该元素的位置。当然,找到了该元素的前驱就自然而然的找到了该元素的位置,毕竟两者是相邻的。需要注意的是,我的代码中,链表的下标是从1开始的,而上一篇的顺序表下标是从0开始的。
int listinsert(pnode list, int numth, int e)
{
int get_prior(pnode, int, pnode&);
int listlength(pnode);
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int lenth = listlength(list);
if (numth > lenth+1)
{
printf_s("位置无效,无法插入\n");
return 3;
}
pnode newnode = (pnode)malloc(sizeof(node));
if (newnode != NULL)
{
newnode->info = e;
newnode->next = NULL;
pnode pre=NULL;
get_prior(list, numth, pre);
pnode mid = pre->next;
newnode->next = mid;
pre->next = newnode;
return 0;
}
else return 4;
}
11.删除线性表中指定位置的元素
int ListDelete(pnode& list, int numth, int& e)
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int listlength(pnode);
int lenth = listlength(list);
if (lenth == 0)
{
printf_s("链表为空\n");
return 2;
}
if (numth > lenth)
{
printf_s("位置无效,无法删除\n");
return 3;
}
int get_prior(pnode, int, pnode&);
int get_next(pnode, int, pnode&);
pnode mid, pre, next;
get_prior(list, numth, pre);
get_next(list, numth, next);
mid = pre->next;
e = mid->info;
pre->next = next;
free(mid);
return 0;
}
12.遍历线性表
void visit(int e)
{
printf_s("%d ", e);
return;
}
void (*q)(int a) = visit;
int List1Traverse(pnode list, void (*visit1)(int a))
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int listlength(pnode);
int lenth = listlength(list);
if (lenth == 0)
{
printf_s("链表为空\n");
return 2;
}
pnode p = list->next;
for (; p != NULL; p = p->next)
{
visit1(p->info);
}
printf_s("\n");
return 0;
}
为了方便的建立链表,我这次设计了两个函数,分别代表头插法和尾插法。
1.头插法
int List1push_pre(pnode& list, int e)
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int listinsert(pnode, int, int);
listinsert(list, 1, e);
return 0;
}
2.尾插法
int List1push_back(pnode& list, int e)
{
if (list == NULL)
{
printf_s("线性表不存在\n");
return 1;
}
int listinsert(pnode, int, int);
int listlength(pnode);
listinsert(list, listlength(list)+1, e);
return 0;
}
其实只是限制了函数‘listinsert’的插入位置,并没有太大的区别。
我就不写总体的实现了,写过一遍之后就知道那并没有什么意思,=_=
顺序表的C++的实现,只是申请动态空间的方式不一样而已。其实并没有太大的区别。
所以下一篇我会介绍一下 <vector>
的使用方法,了解了这个,你甚至会开始怀疑我们学习C语言的线性表到底是为了什么。@_@