数组结构之----- 链表
1. 链表基本概念
1.1 什么是链表
- 链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。
- 数据域用来存储数据,指针域用于建立与下一个结点的联系。
- 建立链表时无需预先知道数据总量的,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据。
- 链表的开销,主要是访问顺序性和组织链的空间损失

1.2 数组和链表的区别
数组:
- 一次性分配一块连续的存储区域。
- 随机访问元素效率高
- 需要分配一块连续的存储区域(很大区域,有可能分配失败)
- 删除和插入某个元素效率低
链表:
- 无需一次性分配一块连续的存储区域,只需分配n块节点存储区域,通过指针建立关系;
- 不需要一块连续的存储区域
- 删除和插入某个元素效率高
- 随机访问元素效率低
1.3 链表节点
- 链表的节点类型实际上是结构体变量,此结构体包含数据域和指针域
- 数据域用来存储数据;
- 指针域用于建立与下一个结点的联系,当此节点为尾节点时,指针域的值为NULL;

2. 链表的分类
2.1 静态链表
所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”
示例
struct linkNode {
int No;
char name[64];
struct linkNode *next;
};
void initNodes() {
struct linkNode linkNode1 = {1, "node1", NULL};
struct linkNode linkNode2 = {2, "node2", NULL};
struct linkNode linkNode3 = {3, "node3", NULL};
struct linkNode linkNode4 = {4, "node4", NULL};
linkNode1.next = &linkNode2;
linkNode2.next = &linkNode3;
linkNode3.next = &linkNode4;
//遍历链表
struct linkNode *p = &linkNode1;
while (p != NULL) {
printf("N0=%d name=%s\n", p->No, p->name);
p = p->next;
}
//N0=1 name=node1
//N0=2 name=node2
//N0=3 name=node3
}
void initNodesInHeap() {
//堆区创建节点
struct linkNode *node1 = malloc(sizeof(struct linkNode));
struct linkNode *node2 = malloc(sizeof(struct linkNode));
struct linkNode *node3 = malloc(sizeof(struct linkNode));
struct linkNode *node4 = malloc(sizeof(struct linkNode));
node1->No = 1;
strcpy(node1->name,"node1");
node1->next = node2;
node2->No = 2;
strcpy(node2->name,"node2");
node2->next = node3;
node3->No = 3;
strcpy(node3->name,"node3");
node3->next = node4;
node4->No = 4;
strcpy(node4->name,"node4");
node4->next = NULL;
//遍历
struct linkNode *p = node1;
while (p != NULL) {
printf("N0=%d name=%s\n", p->No, p->name);
p = p->next;
}
//释放内存
free(node4);
free(node3);
free(node2);
free(node1);
}
int main() {
//initNodes();
initNodesInHeap();
return 0;
}
2.2 动态链表
动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系
示例
typedef struct Stu{
int id; //数据域
char name[100];
struct Stu *next; //指针域
}Stu;
void test(){
//动态分配3个节点
Stu *s1 = (Stu *)malloc(sizeof(Stu));
s1->id = 1;
strcpy(s1->name, "yuri");
Stu *s2 = (Stu *)malloc(sizeof(Stu));
s2->id = 2;
strcpy(s2->name, "lily");
Stu *s3 = (Stu *)malloc(sizeof(Stu));
s3->id = 3;
strcpy(s3->name, "lilei");
//建立节点的关系
s1->next = s2; //s1的next指针指向s2
s2->next = s3;
s3->next = NULL; //尾结点
//遍历节点
Stu *p = s1;
while (p != NULL)
{
printf("id = %d, name = %s\n", p->id, p->name);
//结点往后移动一位
p = p->next;
}
//释放节点空间
p = s1;
Stu *tmp = NULL;
while (p != NULL)
{
tmp = p;
p = p->next;
free(tmp);
tmp = NULL;
}
}
2.3 带头链表
带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变
2.4 不带头链表
不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新节点,然后,新节点重新作为链表的头结点)。
2.5 单向链表、双向链表、循环链表
单向链表

双向链表

循环链表

3. 链表基本操作(重点)
3.1 创建链表
struct linkNode {
int No;
char name[64];
struct linkNode *next;
};
struct linkNode *headNode = NULL;
/**
* @method 初始化头结点
*/
void initHeadNode() {
headNode = malloc(sizeof(struct linkNode));
headNode->No = 1;
strcpy(headNode->name, "headNode");
headNode->next = NULL;
//printMsg();
}
/**
* @method 创建新节点
* @param no
* @param name
* @return
*/
struct linkNode *createNode(int no, char name[64]) {
struct linkNode *newNode = malloc(sizeof(struct linkNode));
newNode->No = no;
strcpy(newNode->name, name);
newNode->next = NULL;
return newNode;
}
/**
* @method 向链表的尾部添加节点
* @param newNode
*/
void addNodeAtEnd(struct linkNode *newNode) {
//找到末尾节点
struct linkNode *curr = headNode;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = newNode;
}
3.2 遍历链表
void printMsg() {
struct linkNode *p = headNode;
while (p != NULL) {
printf("NO=%d name=%s\n", p->No, p->name);
p = p->next;
}
}
3.3 链表插入
/**
* @method 往链表中插入节点
* @param newNode
*/
void insertNodeByNo(struct linkNode *newNode) {
struct linkNode *curr = headNode;
while (curr != NULL) {
if (newNode->No > curr->No && newNode->No < curr->next->No) {
break;
}
curr = curr->next;
}
newNode->next = curr->next;
curr->next = newNode;
}
3.3 删除链表中的节点
/**
* @method 删除节点数据
* @param No
*/
void deleteNode(int No) {
struct linkNode *curr = headNode;
while (curr != NULL) {
if (curr->No == No) {
break;
}
curr = curr->next;
}
if (curr == NULL) {
printf("no such node\n");
return;
} else {
struct linkNode *p = curr;
curr->No = curr->next->No;
strcpy(curr->name, curr->next->name);
p = curr->next;
curr->next = curr->next->next;
free(p);
}
}
3.4 清空链表
/**
* @method :清空链表
*/
void clearLinkedList() {
if (headNode == NULL) {
perror("no node in list");
return;
}
struct linkNode *tmp = headNode->next;
while (tmp != NULL) {
struct linkNode *nextNode = tmp->next;
free(tmp);
tmp = nextNode;
}
headNode->next = NULL;
}
3.5 销毁链表
/**
* @method 清空链表
*/
void destroyLinkedList() {
if (headNode == NULL) {
perror("no node in list");
return;
}
//链表中有元素
clearLinkedList();
//清空头结点
free(headNode);
headNode = NULL;
}

168

被折叠的 条评论
为什么被折叠?



