赞
踩
单项链表由两个结构体组成。
首先是链表的结构体。链表的结构体有两部分构成:数据域和指针域。
数据域:之前我们会写具体类型,但是在数据结构中为了让类型泛指,所以我们也是void*类型。节点上只存地址。类型由使用者指向来确定。
指针域:指向下一个结点。
- struct LinkNode {
- void* data;
- struct LinkNode* next;
- };
这个链表数据结构如上。
之前写的链表初始化函数,有个不好的地方,只返回链表头结点。
拿到链表头结点,就相当于拿到了整个链表。
这样有个问题:如果链表一千多个结点,那么每次取值都要把前面一千多个取出来,才轮到要取的值。每次统计个数,也要数这么多。
所以我们加第二个结构体。
小结:数据域更新为void*类型。
这个结构体的成员,我们首先写上链表的头结点,相当于拿到了整个链表。
一般链表常见的操作是尾插,为了便于我们尾插,我们可以在链表结构体中,再定义一个尾结点。这张插入值时,只要在尾结点后面加上一个值即可,然后更新尾结点。不用再从头跑过来。
所以这第二个结构体,是需要结合我们使用情况来自定义的。如果我们的场景需要频繁尾插,就加上某项信息(属性)。
第二个结构体,更像是一个对链表的信息统计和管理。
- // 链表数据类型
- struct LinkList {
- struct LinkNode header; // 如果写指针,需要手动开辟内存,拿到它。
- int size;
- //struct LinkNode Tail;
- };
- #pragma once
-
- // 宏定义,保证C程序在C++中也可以运行。
- #ifdef __cplusplus
- extern "C" {
- #endif
- // 链表结点数据类型
- struct LinkNode {
- void* data;
- struct LinkNode *next;
-
- };
- // 链表数据类型
- struct LinkList {
- struct LinkNode header; // 如果写指针,需要手动开辟内存,拿到它。
- int size;
- //struct LinkNode Tail;
- };
-
- // 初始化链表:以前返回链表,现在不再返回链表,不希望用户修改链表信息,做个隐藏。
- // 我们定义一个新类型:
- typedef void* LinkListll;
- LinkListll Init_LinkList();
-
- // 插入链表
- LinkListll Insert_LinkList(LinkListll list, int pos, void* data);
-
- // 遍历链表
- typedef void(*FOREACH)(void*);// 定义一个回调函数。
- void Foreach_LinkList(ListListll list, FOREACH myforeach);
-
-
-
- #ifdef __cplusplus
- }
- #endif
当然了,如果为了进一步隐藏结构体,我们可以把结构体放在.c文件中,这样就可以实现对数据的隐藏。
注意我们初始化定义了输出了一个void*类型。所以后面所有调用这个list时,函数内部,必须强制类型转换。
- #include"LinkList.h"
-
- //链表节点数据类型
- struct LinkNode
- {
- void *data;
- struct LinkNode *next;
- };
-
- //链表数据类型
- struct LList
- {
- struct LinkNode header;
- int size;
- };
-
-
-
- //初始化链表
- LinkList Init_LinkList()
- {
- struct LList *list = malloc(sizeof(struct LList));
- if (NULL == list)
- {
- return NULL;
- }
-
- list->header.data = NULL;
- list->header.next = NULL;
- list->size = 0;
-
- return list;
- }
- //插入节点
- void Insert_LinkList(LinkList list, int pos, void *data)
- {
-
- if (NULL == list)
- {
- return;
- }
-
- if (NULL == data)
- {
- return;
- }
-
- struct LList * mylist = (struct LList *)list;
-
- if (pos < 0 || pos > mylist->size)
- {
- pos = mylist->size;
- }
-
- //查找插入位置
- struct LinkNode *pCurrent = &(mylist->header);
- for (int i = 0; i < pos; ++i)
- {
- pCurrent = pCurrent->next;
- }
-
- //创建新节点
- struct LinkNode *newnode = malloc(sizeof(struct LinkNode));
- newnode->data = data;
- newnode->next = NULL;
-
- //新节点插入到链表中
- newnode->next = pCurrent->next;
- pCurrent->next = newnode;
-
- mylist->size++;
- }
-
-
- //遍历链表
- void Foreach_LinkList(LinkList list, FOREACH myforeach) /*回调函数*/
- {
-
- if (NULL == list)
- {
- return;
- }
-
- if (NULL == myforeach)
- {
- return;
- }
-
- struct LList * mylist = (struct LList *)list;
-
- struct LinkNode *pCurrent = mylist->header.next;
-
- while (pCurrent != NULL)
- {
- myforeach(pCurrent->data);
- pCurrent = pCurrent->next;
- }
-
- }
-
-
- //按位置删除
- void RemoveByPos_LinkList(LinkList list, int pos)
- {
-
- if (NULL == list)
- {
- return;
- }
-
- struct LList *mylist = (struct LList *)list;
-
- if (pos < 0 || pos > mylist->size - 1)
- {
- return;
- }
-
-
- //找位置
- struct LinkNode *pCurrent = &(mylist->header);
- for (int i = 0; i < pos; ++i)
- {
- pCurrent = pCurrent->next;
- }
-
-
- //先保存待删除结点
- struct LinkNode *pDel = pCurrent->next;
- //重新建立待删除结点的前驱和后继结点关系
- pCurrent->next = pDel->next;
- //释放删除节点内存
- free(pDel);
- pDel = NULL;
-
- mylist->size--;
-
- }
- //按值删除
- void RemoveByVal_LinkList(LinkList list, void *data, COMPARE compare)
- {
-
- if (NULL == list)
- {
- return;
- }
-
- if (NULL == data)
- {
- return;
- }
-
- if (NULL == compare)
- {
- return;
- }
-
- struct LList *mylist = (struct LList *)list;
-
- //辅助指针变量
- struct LinkNode *pPrev = &(mylist->header);
- struct LinkNode *pCurrent = pPrev->next;
-
- while (pCurrent != NULL)
- {
- if (compare(pCurrent->data, data))
- {
- //找到了
- pPrev->next = pCurrent->next;
- //释放删除节点内存
- free(pCurrent);
- pCurrent = NULL;
- mylist->size--;
- break;
- }
-
- pPrev = pCurrent;
- pCurrent = pCurrent->next;
- }
-
- }
- //清空链表
- void Clear_LinkList(LinkList list)
- {
- if (NULL == list)
- {
- return;
- }
-
- struct LList *mylist = (struct LList *)list;
-
- //辅助指针变量
- struct LinkNode *pCurrent = mylist->header.next;
-
- while (pCurrent != NULL)
- {
- //先缓存下一个节点的地址
- struct LinkNode *pNext = pCurrent->next;
- //释放当前结点内存
- free(pCurrent);
-
- pCurrent = pNext;
-
- }
-
- mylist->header.next = NULL;
- mylist->size = 0;
-
- }
- //大小
- int Size_LinkList(LinkList list)
- {
- if (NULL == list)
- {
- return -1;
- }
-
- struct LList *mylist = (struct LList *)list;
-
- return mylist->size;
- }
- //销毁链表
- void Destroy_LinkList(LinkList list)
- {
-
- if (NULL == list)
- {
- return;
- }
-
- //清空链表
- Clear_LinkList(list);
- free(list);
- list = NULL;
- }
注意遍历时需要用户输入回调函数;
注意按值删除时,需要用户输入值对比函数myCompare。因为到底如何对比,我们在设计时是不知道的,但是我们知道这边必须输入一个比较的函数。
- #define _CRT_SECURE_NO_WARNINGS
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
-
- struct LinkNode
- {
- struct LinkNode *next;
- };
-
-
- struct Person
- {
- struct LinkNode node;
- char name[64];
- int age;
- };
类似于类的继承关系。
单向链表如上,如果是双向链表,那么在LinkNode中再加一个自身结构体指针就可以。
解释一句:这个LinkNode最好放在结构体的前面,这样的结构便于查找地址,如果放在后面,就增加了计算偏移量的操作。也就是说:node属性在name和age之前。
最重要的是:注意类型的转换。
- void test()
- {
-
- struct Person p1 = { NULL, "aaa", 10 };
- struct Person p2 = { NULL, "bbb", 20 };
- struct Person p3 = { NULL, "ccc", 30 };
- struct Person p4 = { NULL, "ddd", 40 };
- struct Person p5 = { NULL, "eee", 50 };
-
- //这种指针类型转换一定要看懂。
- struct LinkNode *pp1 = (struct LinkNode *)&p1;
- struct LinkNode *pp2 = (struct LinkNode *)&p2;
- struct LinkNode *pp3 = (struct LinkNode *)&p3;
- struct LinkNode *pp4 = (struct LinkNode *)&p4;
- struct LinkNode *pp5 = (struct LinkNode *)&p5;
-
- // 这种转成指针域指针之后,无法访问到数据域的内容。
- pp1->next = pp2;
- pp2->next = pp3;
- pp3->next = pp4;
- pp4->next = pp5;
- pp5->next = NULL;
-
-
- struct LinkNode *pCurrent = pp1;
- while (pCurrent != NULL)
- {
- // 再将类型转换回来。变成数据域指针。
- struct Person *person = (struct Person *)pCurrent;
- printf("Name:%s Age:%d\n",person->name,person->age);
-
- pCurrent = pCurrent->next;
- }
- }
-
- int main(){
-
- test();
-
- system("pause");
- return EXIT_SUCCESS;
- }
将一种结构体数据域指针类型的指针,转换为另一种结构体指针域类型的指针。这样就可以保证转换之后,使用者访问不到数据域内容,进而只操作指针域内容。完成链接。
如果需要访问数据域内容,就需要再次转换回来。
指针类型可以实现互相转换呢。
- #define _CRT_SECURE_NO_WARNINGS
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
-
-
- //链表节点数据结构
- struct LinkNode
- {
- struct LinkNode *next;
- };
-
-
- //链表结构体
- struct LList
- {
- struct LinkNode header; //头结点
- int size;
- };
-
- typedef void * LinkList;
-
-
- //初始化链表
- LinkList Init_LinkList()
- {
-
- struct LList *list = malloc(sizeof(struct LList));
- if (NULL == list)
- {
- return NULL;
- }
-
- list->header.next = NULL;
- list->size = 0;
-
- return list;
- }
-
-
- void Insert_LinkList(LinkList list, int position, void *data)
- {
- if (NULL == list)
- {
- return;
- }
-
- if (NULL == data)
- {
- return;
- }
-
- struct LList * mylist = (struct LList *)list;
- struct LinkNode *mynode = (struct LinkNode *)data;
-
-
- if (position < 0 || position > mylist->size)
- {
- position = mylist->size;
- }
-
-
- //找位置(找到position位置的前一个位置)
- struct LinkNode *pCurrent = &(mylist->header);
- for (int i = 0; i < position; ++i)
- {
- pCurrent = pCurrent->next;
- }
-
-
- //数据入链表
- mynode->next = pCurrent->next;
- pCurrent->next = mynode;
-
-
- mylist->size++;
- }
-
-
- void Foreach_LinkList(LinkList list, void(*myforeach)(void *) )
- {
-
- if (NULL == list)
- {
- return;
- }
-
- if (NULL == myforeach)
- {
- return;
- }
-
- struct LList * mylist = (struct LList *)list;
-
- struct LinkNode *pCurrent = mylist->header.next;
-
- while (pCurrent != NULL)
- {
- struct LinkNode *pNext = pCurrent->next;
- myforeach(pCurrent);
- pCurrent = pNext;
- }
-
- }
-
- //删除节点
- void RemoveByPos_LinkList(LinkList list, int position)
- {
- if (NULL == list)
- {
- return;
- }
-
- struct LList * mylist = (struct LList *)list;
-
- if (position < 0 || position > mylist->size - 1)
- {
- return;
- }
-
- //辅助指针
- struct LinkNode *pCurrent = &(mylist->header);
- for (int i = 0; i < position; ++i)
- {
- pCurrent = pCurrent->next;
- }
-
-
- //缓存下待删除节点
- struct LinkNode *pDel = pCurrent->next;
-
- //重新建立待删除节点的前驱和后继结点关系
- pCurrent->next = pDel->next;
-
- mylist->size--;
- }
-
-
- void Destroy_LinkList(LinkList list)
- {
- if (NULL == list)
- {
- return;
- }
-
- free(list);
- list = NULL;
- }
-
- struct Person
- {
- struct LinkNode node; //结构体应该指针?
- char name[64];
- int age;
- };
-
- void myPrint(void *data)
- {
- struct Person *person = (struct Person *)data;
- printf("Name:%s Age:%d\n", person->name,person->age);
- }
-
- void test()
- {
-
- //初始化链表
- LinkList list = Init_LinkList();
-
-
- //创建数据
- struct Person p1 = { NULL, "aaa", 10 };
- struct Person p2 = { NULL, "bbb", 20 };
- struct Person p3 = { NULL, "ccc", 30 };
- struct Person p4 = { NULL, "ddd", 40 };
- struct Person p5 = { NULL, "eee", 50 };
- struct Person p6 = { NULL, "fff", 60 };
-
-
- //插入数据
- Insert_LinkList(list, 0, &p1);
- Insert_LinkList(list, 0, &p2);
- Insert_LinkList(list, 0, &p3);
- Insert_LinkList(list, 0, &p4);
- Insert_LinkList(list, 0, &p5);
- Insert_LinkList(list, 0, &p6);
-
-
- //遍历
- Foreach_LinkList(list, myPrint);
-
- //删除
- RemoveByPos_LinkList(list, 3);
-
- printf("------------------\n");
-
- //遍历
- Foreach_LinkList(list, myPrint);
-
- //销毁
- Destroy_LinkList(list);
- }
-
- int main(){
-
- test();
-
- system("pause");
- return EXIT_SUCCESS;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。