考察:逻辑思维能力、编程思路清晰度、对内存管理的应用、对指针的灵活运用。
一、链表及其原理
1、存储结构
在线性表
L
=
(
a
0
,
a
1
,
.
.
.
,
a
n
−
1
)
中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。
在线性表 L=(a_0, a_1, ..., a_{n-1}) 中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。
在线性表L=(a0,a1,...,an−1)中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。
这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。
这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。
这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。
结点的
d
a
t
a
于存放数据元素
a
i
,而
n
e
x
t
域是一个指针,指向
a
i
直接后继
a
i
+
1
所在的结点。
结点的data于存放数据元素a_i,而next域是一个指针,指向a_i直接后继a_{i+1}所在的结点。
结点的data于存放数据元素ai,而next域是一个指针,指向ai直接后继ai+1所在的结点。
2、结点类型描述
typedef struct node
{
data_t data;//终节点的数据域
struct node *next;//终节点的后继指针域
}listnode, *linklist;
设
P
指向链表中的结点
a
i
。
设P指向链表中的结点a_i。
设P指向链表中的结点ai。
获取
a
i
,写作:
p
−
>
d
a
t
a
;
获取a_i,写作:p->data;
获取ai,写作:p−>data;
而获取
a
i
+
1
,写作:
p
−
>
n
e
x
t
−
>
d
a
t
a
;
而获取a_{i+1},写作:p->next->data;
而获取ai+1,写作:p−>next−>data;
若指针
p
为
N
U
L
L
,则他不指向任何结点,此时获取
p
−
>
d
a
t
a
或
p
−
>
n
e
x
t
是错误的。
若指针p为NULL,则他不指向任何结点,此时获取p->data或p->next是错误的。
若指针p为NULL,则他不指向任何结点,此时获取p−>data或p−>next是错误的。
可调用C语言中malloc()函数向系统申请结点的存储空间
linklist p;
p = (linklist)malloc(sizeof(listnode));
则创建一个类型为linklist的结点,且该结点的地址已存入指针变量p中;
二、单链表创建
1、建立单链表
依次读入表
L
=
(
a
0
,
.
.
.
.
.
,
a
n
−
1
)
中每一元素
a
i
(
假设为整型
)
,若
a
i
≠
结束符(
−
1
),
依次读入表L=(a_0,.....,a_{n-1})中每一元素a_i(假设为整型),若a_i≠结束符(-1),
依次读入表L=(a0,.....,an−1)中每一元素ai(假设为整型),若ai=结束符(−1),
则为
a
i
创建一结点,然后插入表尾,最后返回链表的头结点指针
H
。
则为a_i创建一结点,然后插入表尾,最后返回链表的头结点指针H。
则为ai创建一结点,然后插入表尾,最后返回链表的头结点指针H。
链表的结构是在算法运行时动态形成的,也就是说,在算法开始执行之前,链表的结构是不存在的。
linklist list_create()
{
linklist H;
H = (linklist)malloc(sizeof(listnode));
if (NULL == H)
{
printf("malloc failed\n");
return H;
}
H->data = 0;
H->next = NULL;
return H;
}
三、单链表尾部插入和遍历
1、尾部插入
算法思路:
1、建立一个新结点:①malloc内存 ②赋值。
2、找链表H的尾部节点。
3、做尾部插入。
int list_tail_insert(linklist H, data_t value)
{
if (NULL == H)
{
printf("H is NULL");
return -1;
}
linklist p;
linklist q;
//1.new node p
if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
{
printf("malloc failed\n");
return -1;
}
p->data = value;
p->next = NULL;
//2.locate tail node
q = H;
while (q->next != NULL)
{
q = q->next;
}
//3.insert
q->next = p;
return 0;
}
2、链表的遍历
算法思路
1、判断H是否为有效链表。
2、若有效,则打印值p->next->data; 而后p = p->next;
int list_show(linklist H)
{
linklist p;
if (NULL == H)
{
printf("H is NULL\n");
return -1;
}
p = H;
while (p->next != NULL)
{
printf("%d ", p->next->data);
p = p->next;
}
puts("");
return 0;
}
3、链表的查找
按序号查找:实现list_get(h, i)运算。
算法思路
从链表的a0结点开始,判断当前结点是否为第i个结点。如果是,则返回该结点的指针;如果不是,则继续查找下一个结点,并重复这个过程。依次类推,直到找到第i个结点或遍历完整个链表。
linklist list_get(linklist H, int pos)
{
linklist p;
int i;
if(NULL == H)
{
printf("H is NULL\n");
return NULL;
}
if(pos == -1)
{
return H;
}
if(pos < -1)
{
printf("pos is invalid\n");
return NULL;
}
p = H;
i = -1;
while(i < pos)
{
p = p->next;
if(p == NULL)
{
printf("pos is invalid\n");
return NULL;
}
i++;
}
return p;
}
4、链表的插入
即实现list_insert(h, i, i,)。将x插入表中结点ai之前的情况。
算法思路
①调用算法list_get(h, i-1),获取结点a(i-1)的指针p(ai 之前驱)。
②然后申请一个q结点,存入x,并将其插入p指向的结点之后。
注意:插入时新结点q的next域先指向插入位置后边,然后p指向q。
int list_insert(linklist H, data_t value, int pos)
{
linklist p;
linklist q;
if (NULL == H)
{
printf("H is NULL\n");
return -1;
}
//1.locate node p (pos-1)
p = list_get(H, pos-1);
if (p == NULL)
{
return -1;
}
//2.new node q
if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
{
printf("malloc failed\n");
return -1;
}
q->data = value;
q->next = NULL;
//3.insert
q->next = p->next;
p->next = q;
return 0;
}
5、链表的删除
即实现list_delete(h, i)。
算法思路
采用插入法,首先通过调用函数list_get(h, i-1)
来获取结点ai
的前驱节点,随后将结点ai
从链表中删除。
int list_delete(linklist H, int pos)
{
linklist p;
linklist q;
//1
if(NULL == H)
{
printf("H is NULL\n");
return -1;
}
//2.locate prior
p = list_get(H, pos-1);
if(p == NULL)
{
return -1;
}
if(p->next == NULL)
{
printf("delete pos is invalid\n");
return -1;
}
//3.update
q = p->next;
p->next = q->next;//p->next = p->next->next;
//4.free
printf("free:%d\n",q->data);
free(q);
q = NULL;
return 0;
}
6、链表的释放
即实现list_free(H)。
算法思路
移动头结点H,将p指向H,释放p结点。
linklist list_free(linklist H)
{
linklist p;
if(NULL == H)
{
return NULL;
}
p = H;
printf("free:");
while(H != NULL)
{
p = H;
H = H->next;
printf("%d ",p->data);
free(p);
}
puts("");
return NULL;
}
四、链表的复杂操作
1、链表的反转
设计并实现一个算法,用于将单链表H进行倒置。
算法思路
依次取原链表中各个结点,将其作为新链表首结点插入H结点后。
int list_reverse(linklist H)
{
linklist p;
linklist q;
if(NULL == H)
{
printf("H is NULL\n");
return -1;
}
if(H->next == NULL || H->next->next == NULL)
{
return 0;
}
p = H->next->next;
H->next->next = NULL;
while(p != NULL)
{
q = p;
p = p->next;
q->next = H->next;
H->next = q;
}
return 0;
}
2、链表求相邻两结点最大值
假设链表中的每个结点包含一个整型数据域(data),我们需要找出链表中相邻两个结点数据域(data)值之和最大的那个第一结点的指针。
算法思路
在链表中,假设p和q是相邻的两个结点指针。我们需要找到这样一对结点,即p指向的结点和q指向的结点的数据之和是最大的。最后,返回指向p的指针即可。
linklist list_adjmax(linklist H, data_t *value)
{
linklist p, q, r;
data_t sum;
if(NULL == H)
{
printf("H is NULL\n");
return NULL;
}
if(H->next == NULL || H->next->next == NULL || \
H->next->next->next == NULL)
{
return H;
}
q = H->next;
p = H->next->next;
r = q;
sum = q->data + p->data;
while(p->next != NULL)
{
p = p->next;
q = q->next;
if(sum < q->data + p->data)
{
sum = q->data + p->data;
r = q;
}
}
*value = sum;
return r;
}
3、有序链表合并
设两单链表A、B按data值(设为整型)递增有序,将表A和B合并成一表A,且表A也按data值递增有序。
算法思路
设指针p、q分别指向表A和B中的结点,若p->data ≤q->data则p结点进入结果表,否则q结点进入结果表。
int list_merge(linklist H1, linklist H2)
{
linklist p, q, r;
if(NULL == H1 || NULL == H2)
{
printf(" H1 or H2 is NULL\n");
return -1;
}
p = H1->next;
q = H2->next;
r = H1;
H1->next = NULL;
H2->next = NULL;
while(p && q)//while(p != NULL && q != NULL)
{
if(p->data <= q->data)
{
r->next = p;
p = p->next;
r = r->next;
r->next = NULL;
}
else
{
r->next = q;
q = q->next;
r = r->next;
r->next = NULL;
}
}
if(p == NULL)
{
r->next = q;
}
else
{
r->next = p;
}
return 0;
}
五、程序
1、test.c
#include <stdio.h>
#include "linklist.h"
void test_get();
void test_insert();
void test_delete();
void test_reverse();
void test_adjmax();
int main(int argc, char *argv[])
{
linklist H1, H2;
int arr1[] = {1, 3, 5, 7, 9, 14, 17, 19};
int arr2[] = {2, 4, 6, 8, 10, 13, 15, 20};
int i;
H1 = list_create();
if(NULL == H1)
return -1;
H2 = list_create();
if(NULL == H2)
return -1;
for(i = 0; i < sizeof(arr1)/sizeof(int); i++)
{
list_tail_insert(H1, arr1[i]);
}
for(i = 0; i < sizeof(arr2)/sizeof(int); i++)
{
list_tail_insert(H2, arr2[i]);
}
list_show(H1);
list_show(H2);
list_merge(H1, H2);
printf("merge:\n");
list_free(H1);
list_free(H2);
return 0;
}
void test_get()
{
linklist H;
int value;
linklist p;
H = list_create();
if(NULL == H)
return;
printf("input:");
while(1)
{
scanf("%d",&value);
if(value == -1)
{
break;
}
list_tail_insert(H, value);
printf("input:");
}
list_show(H);
p = list_get(H, 4);
if(p != NULL)
{
printf("value=%d\n",p->data);
}
}
void test_insert()
{
linklist H;
int value;
H = list_create();
if(NULL == H)
return;
printf("input:");
while(1)
{
scanf("%d",&value);
if(value == -1)
{
break;
}
list_tail_insert(H, value);
printf("input:");
}
list_show(H);
list_insert(H, 100, 2);
list_show(H);
}
void test_delete()
{
linklist H;
int value;
H = list_create();
if(NULL == H)
return;
printf("input:");
while(1)
{
scanf("%d",&value);
if(value == -1)
{
break;
}
list_tail_insert(H, value);
printf("input:");
}
list_show(H);
printf("H = %p\n", H);
H = list_free(H);
printf("H = %p\n", H);
list_delete(H, -4);
list_show(H);
}
void test_reverse()
{
linklist H;
int value;
H = list_create();
if(NULL == H)
return;
printf("input:");
while(1)
{
scanf("%d",&value);
if(value == -1)
{
break;
}
list_tail_insert(H, value);
printf("input:");
}
list_show(H);
list_reverse(H);
list_show(H);
list_free(H);
}
void test_adjmax()
{
linklist H;
linklist r;
int value;
int sum;
H = list_create();
if(NULL == H)
return;
printf("input:");
while(1)
{
scanf("%d",&value);
if(value == -1)
{
break;
}
list_tail_insert(H, value);
printf("input:");
}
list_show(H);
r = list_adjmax(H, &sum);
if(r != NULL && r != H)
{
printf("data:%d, sum:%d\n", r->data, sum);
}
list_show(H);
list_free(H);
}
2、linklist.c
#include <stdio.h>
#include <stdlib.h>
#include "linklist.h"
linklist list_create()
{
linklist H;
H = (linklist)malloc(sizeof(listnode));
if (NULL == H)
{
printf("malloc failed\n");
return H;
}
H->data = 0;
H->next = NULL;
return H;
}
int list_tail_insert(linklist H, data_t value)
{
if (NULL == H)
{
printf("H is NULL");
return -1;
}
linklist p;
linklist q;
//1.new node p
if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
{
printf("malloc failed\n");
return -1;
}
p->data = value;
p->next = NULL;
//2.locate tail node
q = H;
while (q->next != NULL)
{
q = q->next;
}
//3.insert
q->next = p;
return 0;
}
int list_show(linklist H)
{
linklist p;
if (NULL == H)
{
printf("H is NULL\n");
return -1;
}
p = H;
while (p->next != NULL)
{
printf("%d ", p->next->data);
p = p->next;
}
puts("");
return 0;
}
linklist list_get(linklist H, int pos)
{
linklist p;
int i;
if(NULL == H)
{
printf("H is NULL\n");
return NULL;
}
if(pos == -1)
{
return H;
}
if(pos < -1)
{
printf("pos is invalid\n");
return NULL;
}
p = H;
i = -1;
while(i < pos)
{
p = p->next;
if(p == NULL)
{
printf("pos is invalid\n");
return NULL;
}
i++;
}
return p;
}
int list_insert(linklist H, data_t value, int pos)
{
linklist p;
linklist q;
if (NULL == H)
{
printf("H is NULL\n");
return -1;
}
//1.locate node p (pos-1)
p = list_get(H, pos-1);
if (p == NULL)
{
return -1;
}
//2.new node q
if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
{
printf("malloc failed\n");
return -1;
}
q->data = value;
q->next = NULL;
//3.insert
q->next = p->next;
p->next = q;
return 0;
}
int list_delete(linklist H, int pos)
{
linklist p;
linklist q;
//1
if(NULL == H)
{
printf("H is NULL\n");
return -1;
}
//2.locate prior
p = list_get(H, pos-1);
if(p == NULL)
{
return -1;
}
if(p->next == NULL)
{
printf("delete pos is invalid\n");
return -1;
}
//3.update
q = p->next;
p->next = q->next;//p->next = p->next->next;
//4.free
printf("free:%d\n",q->data);
free(q);
q = NULL;
return 0;
}
linklist list_free(linklist H)
{
linklist p;
if(NULL == H)
{
return NULL;
}
p = H;
printf("free:");
while(H != NULL)
{
p = H;
H = H->next;
printf("%d ",p->data);
free(p);
}
puts("");
return NULL;
}
int list_reverse(linklist H)
{
linklist p;
linklist q;
if(NULL == H)
{
printf("H is NULL\n");
return -1;
}
if(H->next == NULL || H->next->next == NULL)
{
return 0;
}
p = H->next->next;
H->next->next = NULL;
while(p != NULL)
{
q = p;
p = p->next;
q->next = H->next;
H->next = q;
}
return 0;
}
linklist list_adjmax(linklist H, data_t *value)
{
linklist p, q, r;
data_t sum;
if(NULL == H)
{
printf("H is NULL\n");
return NULL;
}
if(H->next == NULL || H->next->next == NULL || \
H->next->next->next == NULL)
{
return H;
}
q = H->next;
p = H->next->next;
r = q;
sum = q->data + p->data;
while(p->next != NULL)
{
p = p->next;
q = q->next;
if(sum < q->data + p->data)
{
sum = q->data + p->data;
r = q;
}
}
*value = sum;
return r;
}
int list_merge(linklist H1, linklist H2)
{
linklist p, q, r;
if(NULL == H1 || NULL == H2)
{
printf(" H1 or H2 is NULL\n");
return -1;
}
p = H1->next;
q = H2->next;
r = H1;
H1->next = NULL;
H2->next = NULL;
while(p && q)//while(p != NULL && q != NULL)
{
if(p->data <= q->data)
{
r->next = p;
p = p->next;
r = r->next;
r->next = NULL;
}
else
{
r->next = q;
q = q->next;
r = r->next;
r->next = NULL;
}
}
if(p == NULL)
{
r->next = q;
}
else
{
r->next = p;
}
return 0;
}
3、linklist.h
typedef int data_t;
typedef struct node
{
data_t data;
struct node *next;
}listnode, *linklist;
linklist list_create();
int list_tail_insert(linklist H, data_t value);//head
linklist list_get(linklist H, int pos);
int list_insert(linklist H, data_t value, int pos);
int list_show(linklist H);
int list_delete(linklist H, int pos);
linklist list_free(linklist H);
int list_reverse(linklist H);
linklist list_adjmax(linklist H, data_t *value);
int list_merge(linklist H1, linklist H2);