链表
特点
地址可以不连续 大小不固定
删除和插入的时候,不需要移动元素,比较方便
访问不方便,只能从头结点开始访问
术语
单向
链表中任意一个结点,只有一个指针域,存放下一个结点的地址。
单向循环链表
特点:
链表 + 尾结点指向头结点
注意:
在单向不循环链表的基础上修改判断尾结点的条件。 p->pNext == NULL ==> p->pNext == pList
双向
链表中任意一个结点,有两个指针域,分别存放下一个结点的地址和前一个结点的地址。
双向循环链表
笔试题:
1. 合并两个已知尾结点的单向循环链表。
方法一
方法二
2. 合并两个有序链表,使它成为一个有序链表。
3. 链表排序
4. 链表逆序
5. 链表的删除
注意:
写一个函数API来实现上面的某个功能
在这个API中尽量不要出现malloc。
头结点
数据域为空(数据域不存放有效数据)
首结点
头结点的下一个结点
尾结点
链表中某个结点的指针域为空,就是尾结点。
循环
尾结点保存头结点的地址
空链表
头结点的指针域为空就是空链表。
带头结点的单向不循环的链表 --- 链表
定义一个数据类型来描述链表
struct student
{};
typedef struct student data_t;
struct node
{
data_t data; //数据域
struct node * pNext; //指针域
};
typedef struct node LIST;
相关操作
创建链表
//函数功能:创建线性表
7 LIST * createList( )
8 {
9 LIST * pList = NULL;
10
11 pList = ( LIST * )malloc( sizeof(LIST) );
12 if ( NULL != pList )
13 {
14 memset( pList, 0, sizeof(LIST) ); //空链表
15 }
16 return pList;
17 }
插入
//头插
23 //1.堆区新建结点
24 LIST * pNew=NULL;
25 pNew=(LIST *)malloc(sizeof(LIST));
26 if(NULL==pNew)
27 {
28 return ERROR;
29 }
30 //2.初始化新结点
31 memset(pNew,0,sizeof(LIST));
32 pNew->data=newData;//要插入的数据
33
34 //3.链接
35 if(HEAD==offset)
36 {
37 //保护头结点之后的数据
38 pNew->pNext=pList->pNext;
39 //插入
40 pList->pNext=pNew;
41 }
查询
修改
删除
LIST *p=NULL;
145 LIST *q=NULL;
146 int i;
147 //头删
148 if(NULL==pList)
149 {
150 return ERROR;
151 }
152 if(HEAD==offset)
153 {
154 //1.p指向被删除的结点(首结点)
155 p=pList->pNext;
156 //2.保护p后面的所有结点
157 pList->pNext=p->pNext;
158 //3.保存被删除的数据
159 *pData=p->data;
160 //4.释放p
161 free(p);
162 p=NULL;
163 }
//中间删
165 else
166 {
167 q=pList;
168 p=pList->pNext;
169 for(i=0;i<offset-1 && p;i++)
170 {
171 q=q->pNext;
172 p=p->pNext;
173 }
174 //判断是否为尾结点
175 if(!p)
176 {
177 return ERROR;
178
179 }
180 //判断pData是否是指针
181 if(pData)
182 *pData=p->data;
183 q->pNext=p->pNext;
184 free(p);p=NULL;
}
//尾删
188 else if(TAIL==offset)
189 {
190 //1.p指向尾结点,并且q指向尾结点的前一个结点
191 q=pList;
192 p=pList->pNext;
193 //判断p是不是尾结点
194 while(NULL!=p->pNext)
195 {
196 //p不是尾结点
197 //向后移动p和q
198 q=q->pNext;
199 p=p->pNext;
200 }
201 //13.将q作为新的尾结点
202 q->pNext=NULL;
203 //3.保存p的数据
204 *pData=p->data;
205 //4.释放p
206 free(p);
207 p=NULL;
208 }
显示
//函数功能:显示链表中的元素
83 void showList( LIST * pList )
84 {
85 LIST *p=NULL;
86 if(NULL==pList || NULL==pList->pNext)
87 {
88 return;
89 }
90 //1.p指向首结点
91 p=pList->pNext;
92 //2.判断p是否为空,如果空,就结束。如果不为空,就输出p的数据
93 while(NULL!=p)
94 {
95 printf("%d ",p->data);
96 //3.p向后移动
97 p=p->pNext;
98 }
99 printf("\n");
100 }
销毁链表
测试代码:
int main()
{
创建链表
把信息插入链表中
查询
修改
删除
显示
销毁链表
}
#include <stdio.h>
#include "list.h"
int main(int argc, const char *argv[])
{
//创建链表
LIST *pList=NULL;
pList=createList();
//插入数据到链表
int i;
for(i=15;i>8;i--)
{
//头插
insertDataToList(pList,i,HEAD);
}
//尾巴插入
insertDataToList(pList,444,TAIL);
//中间插入
insertDataToList(pList,6666,2);
//显示链表中的元素
showList(pList);
//查询
int ret1;
ret1=searchList(pList,6666);
if(0>ret1)
{
printf("search error\n");
return ERROR;
}
printf("要查询数字存在\n");
//修改
int ret2;
ret2=updateList(pList,10,999);
if(0>ret2)
{
printf("update error\n");
return ERROR;
}
//删除链表中的元素
data_t pData;
int ret3;
ret3=deleteFromList(pList,TAIL,&pData);
if(0>ret3)
{
printf("success error\n");
return ERROR;
}
//显示链表中的元素
showList(pList);
//销毁链表
destroyList(pList);
return 0;
}