1、基础知识
关于链表,相对于数组来说,有着更加灵活的存储分配方式,链表可以不连续存储,从另一方面来讲,也算充分利用空间;插入以及删除操作也相对容易,不会像数组会整体数据的移动,但是对于查找来说,会比数组要费时的多,数组查找只需要O(1),而链表的查找则需要O(N);一般都会采用动态分配内存来生成链表,灵活性也有一定的保证;
2、链表描述
2.1 单链表
链表为一个一个结点组成,每个结点可以用一个结构体描述,由于链表不是连续的,如果要找到下一个结点所在的地址就必须使用一个指针来保存下个结点的地址,这样结点一般就由两部分构成:数据和指向下个结点的指针;最末尾的结点的指针指向NULL,防止访问或操作到非法的内存地址;
结构体描述:
struct Node
{
int element;
struct Node *pnext;
};
代码示例:
#include
#include
struct Node
{
int element;
struct Node *pnext;
};
int add_node(struct Node *phead, int element)
{
struct Node *new_node = NULL;
struct Node *ptmp = phead;
new_node = (struct Node *)malloc(sizeof(struct Node));
if(new_node == NULL)
{
printf("malloc Node failed !\n");
return -1;
}
while(ptmp->pnext != NULL)
{
ptmp = ptmp->pnext;
}
ptmp->pnext = new_node;
new_node->pnext = NULL;
new_node->element = element;
return 0;
}
struct Node *find_node(struct Node *head_list, int element)
{
struct Node *ptmp = head_list;
while(ptmp != NULL && ptmp->element != element)
{
ptmp = ptmp->pnext;
}
if(ptmp == NULL)
{
printf("can't find element %d\n", element);
return NULL;
}
else
{
printf("find element %d success\n", element);
return ptmp;
}
}
struct Node *find_previous_node(struct Node *head_list, int element)
{
struct Node *ptmp = head_list;
while(ptmp->pnext != NULL && ptmp->pnext->element != element)
{
ptmp = ptmp->pnext;
}
if(ptmp->pnext == NULL)
{
printf("can't find element %d\n", element);
return NULL;
}
else
{
printf("find element %d success\n", ptmp->pnext->element);
return ptmp;
}
}
int del_node(struct Node *phead, int element)
{
struct Node *ptmp, *pdeltmp;
ptmp = find_previous_node(phead, element);
if(ptmp == NULL)
{
printf("delete element %d failed!\n", element);
return -1;
}
pdeltmp = ptmp->pnext;
ptmp->pnext = pdeltmp->pnext;
free(pdeltmp);
printf("delete success\n");
return 0;
}
int insert_node(struct Node *node, int element)
{
struct Node *new_node = NULL;
struct Node *ptmp = node->pnext;
new_node = (struct Node *)malloc(sizeof(struct Node));
if(new_node == NULL || node == NULL)
{
printf("malloc Node failed !\n");
return -1;
}
node->pnext = new_node;
new_node->pnext = ptmp;
new_node->element = element;
return 0;
}
int print_list(struct Node *List)
{
struct Node *ptmp = List;
int list_len = 0;
while(ptmp != NULL)
{
printf("%d -> ", ptmp->element);
list_len++;
ptmp = ptmp->pnext;
}
printf("end, list len : %d\n", list_len);
return 0;
}
int del_list(struct Node *head_list)
{
struct Node *ptmp, *pfreetmp;
ptmp = head_list->pnext;
head_list->pnext = NULL;
while(ptmp != NULL)
{
pfreetmp = ptmp->pnext;
free(pfreetmp);
ptmp = pfreetmp;
}
return 0;
}
int main(void)
{
struct Node head_list;
int i;
head_list.element = 0;
head_list.pnext = NULL;
for (i = 1; i < 100000; i++)
add_node(&head_list, i);
print_list(&head_list);
del_node(&head_list, 9);
print_list(&head_list);
insert_node(find_node(&head_list, 3), 12);
print_list(&head_list);
return 0;
}
2.2 单向循环链表
顾名思义,即将原本末尾节点指向NULL的指针,指向头结点,这样就构成了一个循环链表,这可以叫做单向循环链表;可以通过单链表示例代码中add_node()中的new_node->pnext指向phead即可实现;
2.3 循环双向链表
首先理解双向链表指的是,可以通过链表中的某一结点,找到此结点的上一个结点和下一个结点;即一个结点中需要保存两个指针,分别指向本结点的上一个结点和下一个结点;循环双向链表,即将末尾结点的pnext指向phead,头结点的ppre指向末尾结点,有种连接起来的感觉;
结构体描述:
struct Node
{
struct Node *ppre;
int element;
struct Node *pnext;
};
示例代码:
#include
#include
//应该尽量避免直接操作传下来的指针,而是应该先用变量获取这个指针,多并发的情况下参数指针的值可能会改变
struct Node
{
struct Node *ppre;
int element;
struct Node *pnext;
};
int add_node(struct Node *phead, int element)
{
struct Node *new_node = NULL;
struct Node *ptmp = phead;
new_node = (struct Node *)malloc(sizeof(struct Node));
if(new_node == NULL)
{
printf("malloc Node failed !\n");
return -1;
}
while(ptmp->pnext != phead)
{
ptmp = ptmp->pnext;
}
ptmp->pnext = new_node;
new_node->pnext = phead;
new_node->ppre = ptmp;
new_node->element = element;
return 0;
}
struct Node *find_node(struct Node *head_list, int element)
{
struct Node *ptmp = head_list;
while(ptmp->pnext != head_list && ptmp->element != element)
{
ptmp = ptmp->pnext;
}
if(ptmp == NULL)
{
printf("can't find element %d\n", element);
return NULL;
}
else
{
printf("find element %d success\n", element);
return ptmp;
}
}
struct Node *find_previous_node(struct Node *head_list, int element)
{
struct Node *ptmp = head_list;
while(ptmp->pnext != head_list && ptmp->pnext->element != element)
{
ptmp = ptmp->pnext;
}
if(ptmp->pnext == NULL)
{
printf("can't find element %d\n", element);
return NULL;
}
else
{
printf("find element %d success\n", ptmp->pnext->element);
return ptmp;
}
}
int del_node(struct Node *phead, int element)
{
struct Node *ptmp, *pdeltmp;
ptmp = find_previous_node(phead, element);
if(ptmp == NULL)
{
printf("delete element %d failed!\n", element);
return -1;
}
pdeltmp = ptmp->pnext;
ptmp->pnext = pdeltmp->pnext;
pdeltmp->pnext->ppre = ptmp;
free(pdeltmp);
printf("delete success\n");
return 0;
}
int insert_node(struct Node *node, int element)
{
struct Node *new_node = NULL;
struct Node *pnode = node, *ptmp = node->pnext;
new_node = (struct Node *)malloc(sizeof(struct Node));
if(new_node == NULL || node == NULL)
{
printf("malloc Node failed !\n");
return -1;
}
pnode->pnext = new_node;
new_node->pnext = ptmp;
ptmp->ppre = new_node;
new_node->ppre = pnode;
new_node->element = element;
return 0;
}
int print_list(struct Node *List)
{
struct Node *ptmp = List;
int list_len = 0;
int count = 20;
//正序
do
{
printf("%d -> ", ptmp->element);
list_len++;
ptmp = ptmp->pnext;
}while(ptmp != List);
printf("end, list len : %d\n", list_len);
//逆序
list_len = 0;
ptmp = List;
do
{
printf("%d -> ", ptmp->element);
list_len++;
ptmp = ptmp->ppre;
}while(ptmp != List);
printf("end, list len : %d\n", list_len);
return 0;
}
int del_list(struct Node *head_list)
{
struct Node *ptmp, *pfreetmp;
ptmp = head_list->pnext;
head_list->pnext = NULL;
head_list->ppre = NULL;
while(ptmp != NULL)
{
pfreetmp = ptmp->pnext;
free(pfreetmp);
ptmp = pfreetmp;
}
return 0;
}
int main(void)
{
struct Node head_list;
int i;
head_list.element = 0;
head_list.pnext = &head_list;
head_list.ppre = &head_list;
for (i = 1; i < 10; i++)
add_node(&head_list, i);
print_list(&head_list);
del_node(&head_list, 9);
print_list(&head_list);
insert_node(find_node(&head_list, 3), 22);
print_list(&head_list);
return 0;
}
2.4 多重表
一般是两个或多个链表之间的交叉集合,可以应用于学生的选课统计,每一门课所包含的学生可以为一条链表,每个学生所选择的课程为另一条链表;对于课程链表来说,本结点需要记录当前学生信息以及指向下一个学生的指针,对于学生链表来说,本结点又需要记录当前学生选择的课程以及指向下一个课程的指针;实际使用较少;