程序内功篇三--单链表
一、线性表的链式存储结构
1、链表含义
将
线性表L=(a0,a1,……,an-1)
中各元素
分布在存储器的不同存储块
,称为结点
,通过地址或指针
建立元素之间的联系
结点
的data域
存放数据元素ai
,而next域
是一个指针,指向ai的直接后继ai+1
所在的结点。
2、结点类型描述
typedef struct node
{
data_t data; //结点的数据域//
struct node *next; //结点的后继指针域//
}listnode, *linklist;
例如:
listnode A;
linklist p = &A;
3、链表结点用法与创建
(1)设p
指向链表中结点ai
获取ai,写作:p->data;
而取ai+1,写作:p->next->data
若指针p的值为NULL,则它不指向任何结点,
此时取p->data或p->next是错误的。
(2)可调用C语言中malloc()
函数向系统申请结点的存储空间
linklist p;
p = (linklist)malloc(sizeof(listnode));
则创建一个类型为linklist
的结点,且该结点的地址已存入指针变量p
中
二、单链表的基本相关算法
1、建立单链表
①创建
头结点
,为头结点开辟空间。函数如下:
/**
* @description:单链表创建
* @param {*}
* @return {链表头指针}
*/
linklist linknode_create()
{
linklist H = (linklist)malloc(sizeof(linknode)); //开辟头指针空间
if(H == NULL)
{
return 0;
}
H->data = 0;
H->next = NULL;
return H;
}
②为链表增添元素我们使用
尾插法
,函数如下:
/**
* @description: 尾插法增添链表元素
* @param {linklist} H -头指针
* @param {data_t} value -结点的数据
* @return {0-函数失败,1-函数成功}
*/
int linknode_tail_insert(linklist H, data_t value)
{
linklist p = NULL,q = H;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
p = (linklist)malloc(sizeof(linknode)); //结点空间开辟
if(p == NULL)
{
DEBUG_CREATE();
return 0;
}
p->data = value;
p->next = NULL;
while(q->next) //遍历到最后链表最后一位结点
q = q->next;
q->next = p;
return 1;
}
③为链表加入元素,可以输入-1作为结束。例如:
while(1)
{
printf("Please input:\n");
scanf("%d",&value);
if(value == -1)
break;
linknode_tail_insert(H, value);
}
2、链表查找
按序号查找:实现
linknode_element_inqure(H, i)
运算。
算法思路:
从链表的a0
起,判断是否为第i结点
,若是则返回该结点的指针
,否则查找下一结点,依次类推。函数如下:
/**
* @description:链表元素查询
* @param {linklist} H -链表头指针
* @param {int} i -查询的位置
* @return {结点位置的地址}
*/
linklist linknode_element_inqure(linklist H, int i)
{
int n = -1;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
if(i == -1)
return H;
if(i < -1)
{
#if DEBUG
printf(" i value error !\n");
#endif
return 0;
}
while(n < i)
{
H = H->next;
if(H == NULL)
{
#if DEBUG
printf("i is too big!\n");
#endif
return 0;
}
n++;
}
return H;
}
3、链表遍历
从头结点开始打印结点数据,函数如下:
/**
* @description:链表遍历
* @param {linklist} H -链表头指针
* @return {0-函数失败,1-函数成功}
*/
int linkshow(linklist H)
{
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
while(H->next)
{
H = H->next;
printf("%d ", H->data);
}
puts("");
return 1;
}
4、链表结点的插入
即实现
InsertLinklist(h, x, i,)
。将x
插入表中结点ai
之前的情况。
算法思路:
调用算法linknode_element_inqure(h, i-1)
,获取结点ai-1
的指针p
(ai 之前驱),然后申请一个q结点
,存入x
,并将其插入p
指向的结点之后。
函数如下:
/**
* @description: 链表元素按位置插入
* @param {linklist} H -链表头结点指针
* @param {data_t} value -插入结点的值
* @param {int} i -插入的位置
* @return {0-函数失败,1-函数成功}
*/
int linknode_element_insert(linklist H, data_t value, int i)
{
linklist p,q = (linklist)malloc(sizeof(linknode));
if(q == NULL)
{
DEBUG_CREATE();
return 0;
}
if(H == NULL)
{
DEBUG_NULL();
free(q);
return 0;
}
p = linknode_element_inqure(H, i-1);
if(p == NULL)
{
free(q);
return 0;
}
q->data = value;
q->next = p->next;
p->next = q;
return 1;
}
5、链表结点的删除
即实现linknode_element_delete(h, i)。
算法思路:
同插入法,先调用函数linknode_element_inqure(h, i-1)
,找到结点ai的前驱
,然后将结点ai
删除之。
函数如下:
/**
* @description: 链表元素按位置删除
* @param {linklist} H -链表头指针
* @param {int} i -删除的位置
* @return {0-函数失败,1-函数成功}
*/
int linknode_element_delete(linklist H, int i)
{
linklist p,q;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
p = linknode_element_inqure(H, i-1);
if(p == NULL)
{
return 0;
}
if(p->next == NULL)
{
#if DEBUG
printf("The place of i is NULL!\n");
#endif
return 0;
}
q = p->next;
p->next = q->next;
free(q);
q = NULL;
return 1;
}
6、链表删除
删除整个链表
/**
* @description: 链表删除
* @param {linklist} H -链表头指针
* @return {0-函数失败,1-函数成功}
*/
int linknode_delete(linklist H)
{
linklist p = H;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
while(p)
{
p = p->next; //指针前移
free(H); //释放上一个结点
H = p;
}
return 1;
}
三、相关单链表的题
1、单链表倒置
设计算法,将
单链表H
倒置
算法思路:
依次取原链表中·各结点·,将其作为·新链表首结点·插入H结点之后
函数如下:
/**
* @description: 链表倒置
* @param {linklist} H -链表头指针
* @return {0-函数失败,1-函数成功}
*/
int linknode_invert(linklist H)
{
linklist p,q;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
if(H->next == NULL)
{
#if DEBUG
printf("empty list!\n");
#endif
return 0;
}
p = H->next;
q = p->next;
p->next = NULL;
while(q)
{
p = q;
q = q->next;
p->next = H->next;
H->next = p;
}
return 1;
}
2、相邻两结点最大值
设结点
data域
为整型,求链表中相邻两结点
data值之和为最大的第一结点的指针
。
算法思路:
设p,q
分别为链表中相邻两结点指针
,求p->data+q->data
为最大的那一组值,返回其相应的指针p
即可
/**
* @description: 链表求相邻结点数据之和最大值
* @param {linklist} H -链表头指针
* @param {data_t} *value -数据之和最大值的地址
* @return {相邻结点数据之和最大值的第一个结点地址}
*/
linklist linknode_max_adjoin(linklist H,data_t *value)
{
linklist p,q,m;
int n = 0;
if(H == NULL)
{
DEBUG_NULL();
return 0;
}
if(H->next == NULL || H->next->next == NULL)
{
#if DEBUG
printf("empty list or only one element!\n");
#endif
return 0;
}
p = H->next;
q = p->next;
m = p;
n = p->data + q->data;
while(q->next)
{
p = q;
q = q->next;
if(n < p->data + q->data)
{
m = p;
n = p->data + q->data;
}
}
*value = n;
return m;
}
3、有序排列问题
设
两单链表A、B
按data值(设为整型)递增有序
,将表A和B
合并成一表A
,且表A
也按data值递增有序
。
算法思路:
设指针p、q
分别指向表A和B
中的结点,若p->data ≤q->data
则p结点进入结果表,否则q结点
进入结果表
。
函数如下:
/**
* @description: 两个有序链表并排为一个有序列表
* @param {linklist} H1 -链表1头指针
* @param {linklist} H2 -链表2头指针
* @return {0-函数失败,1-函数成功}
*/
int linknode_order_merger(linklist H1,linklist H2)
{
linklist p;
if(H1 == NULL || H2 == NULL)
{
DEBUG_NULL();
return 0;
}
if(H2->next == NULL)
{
#if DEBUG
printf("list2 is a empty list!\n");
#endif
return 0;
}
p = H2->next;
while(p)
{
while(H1->next)
{
if(H1->next->data > p->data)
break;
H1 = H1->next;
}
H2 = p;
p = p->next;
H2->next = H1->next;
H1->next = H2;
}
return 1;
}
这里给出免费的程序实现文件下载链接;
单链表程序文件
到这里就结束啦!