Link.h这个头文件中包含了顺序表的定义和对顺序表进行操作的所有函数,所有的函数只传递结构体指针以加快程序运行速度。
- #include
- #include
- #include
- #include
//需要的头文件 -
-
- #define TYPE int
- typedef struct DoubleList
- {
- TYPE data;//数据
- struct DoubleList* prev;//指向前面节点的指针,头节点指向尾节点
- struct DoubleList* next;//指向后面节点的指针,尾节点指向头节点
- }DList;
-
-
- //初始化双向链表
- DList* InitDList(DList* p);
- //初始化节点
- DList* Buylistnode(TYPE x);
- //头插
- void DListfrontpush(DList* p, TYPE x);
- //尾插
- void DListbackpush(DList* p, TYPE x);
- //头删
- void DListfrontpop(DList* p);
- //尾删
- void DListbackpop(DList* p);
- //判读链表是否为空
- bool ListEmpty(DList* p);
- //计算大小
- size_t ListSize(DList* p);
- //寻找元素
- DList* ListFind(DList* p, TYPE x);
- // 在pos之前插入
- void ListInsert(DList* pos, TYPE x);
- // 删除pos位置
- void ListErase(DList* pos);
- //考虑用一级指针,让调用ListDestory的人置空(保持接口一致性)
- void ListDestory(DList* p);
- //打印链表
- void print(DList* p);
这些函数储存在一个function.c 文件中
DList* InitDList(DList* p);
先申请一个哨兵卫,让哨兵卫的prev和next都指向自己
- //初始化双向链表
- DList* InitDList(DList* p)
- {
- DList* guard = (DList*)malloc(sizeof(DList));
- if (guard == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- guard->next = guard;
- guard->prev = guard;
- }
DList* InitDList(DList* p);
从堆区申请一个节点的空间,让哨兵卫的prev和next都置空,等待后面的操作
- DList* Buylistnode(TYPE x)
- {
- DList* newcode = (DList*)malloc(sizeof(DList));
- if (newcode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- newcode->data = x;
- newcode->next = NULL;
- newcode->prev = NULL;
- return newcode;
- }
void DListfrontpush(DList* p, TYPE x);
定义newcode指针指向新开辟的节点,然后因为p解引用后拿到的是哨兵卫的地址,我们新建一个first指针储存哨兵卫的next同时也是有效数据储存的头一个节点。
由于双向链表可以前后寻找,所以只需要改变哨兵卫的next,新节点的prev和next,还有原来的有效数据的头的prev三个节点的数据就能将新的节点连接到里面
- //头插
- void DListfrontpush(DList* p, TYPE x)
- {
- DList* newcode = Buylistnode(x);
- DList* first = p->next;
- first->prev = newcode;
- p->next = newcode;
- newcode->next = first;
- newcode->prev = p;
- }
void DListbackpush(DList* p, TYPE x);
定义newcode指针指向新开辟的节点,然后因为p解引用后拿到的是哨兵卫的地址同时我们新建一个prevcode指针。由于链表是循环的,所以哨兵卫的prev就是尾节点,prevcode指针赋值为这个尾节点
我们首先将新的尾节点的next指向哨兵卫,然后哨兵卫的prev改成新节点的地址,我们此时的prevcode指针指向的节点就可以与newcode链接形成新链表
- //尾插
- void DListbackpush(DList* p, TYPE x)
- {
- DList* newcode = Buylistnode(x);
- DList* prevcode = p->prev;
- newcode->next = p;
- p->prev = newcode;
- prevcode->next = newcode;
- newcode->prev = prevcode;
- }
bool ListEmpty(DList* p);
很简单,看看哨兵卫next是否指向它自己
- bool ListEmpty(DList* p)
- {
- assert(p);
- return p->next == p;
- }
void DListfrontpop(DList* p);
定义两个指针,first指向有效数据头节点,second指向第二个节点,将哨兵卫的next改为指向second,second节点的prev指向哨兵卫。这里不需要担心只有一个有效节点的情况,如果只有一个节点,second就指向哨兵卫,代码跑完后还是哨兵卫自己指向自己
- //头删
- void DListfrontpop(DList* p)
- {
- assert(!ListEmpty(p));//保证链表不为空
- DList* first = p->next;
- DList* second = first->next;
- p->next = second;
- second->prev = p;
- free(first);
- first = NULL;
- }
void DListfrontpop(DList* p);
定义两个指针,tail指向有效数据尾节点,near指向倒数第二个节点,将near的next指向哨兵卫的prev改为指向near,释放空间即可。
- //尾删
- void DListbackpop(DList* p)
- {
- assert(!ListEmpty(p));
- DList* tail = p->prev;
- DList* near = tail->prev;
- near->next = p;
- p->prev = near;
- free(tail);
- tail = NULL;
- }
void print(DList* p);
以数据有效的头节点尾起点,直到遇到哨兵卫停止打印。
- //打印链表
- void print(DList* p)
- {
- assert(p);
- DList* cur = p->next;
- printf("head<=>");
- for (cur = p->next; cur != p; cur = cur->next)
- {
- printf("%d<=>",cur->data);
- }
- printf("head\n");
- }
size_t ListSize(DList* p);
以数据有效的头节点尾起点,直到遇到哨兵卫停止计数。
- size_t ListSize(DList* p)
- {
- assert(p);
- size_t n = 0;
- DList* cur = p->next;
- for (cur = p->next; cur != p;cur = cur->next)
- {
- n++;
- }
- return n;
- }
DList* ListFind(DList* p, TYPE x);
以数据有效的头节点尾起点,直到遇到对应节点停止,又循环回到了哨兵卫就返回NULL
- DList* ListFind(DList* p, TYPE x)
- {
- assert(p);
- size_t n = 0;
- DList* cur = p->next;
- while (cur != p)
- {
- if (cur->data == x)
- {
- return cur;
- }
- cur = cur->next;
- }
- return NULL;
- }
void ListInsert(DList* pos, TYPE x);
首先,不破坏原链表的链接情况,将newcode的prev和next连接前后。
然后,pos的prev依旧是原链表的前一个,此时需要改原链表前一个节点的next为新节点的地址,最后pos的prev链接newcode就完成了
- // 在pos之前插入
- void ListInsert(DList* pos, TYPE x)
- {
- assert(pos);
- DList* newcode = Buylistnode(x);
- newcode->prev = pos->prev;
- newcode->next = pos;
- pos->prev->next = newcode;
- pos->prev = newcode;
- }
void ListErase(DList* pos);
建立两个指针,prev指向pos前的节点,first指向后节点,直接将prev和first连接起来并释放pos
- // 删除pos位置
- void ListErase(DList* pos)
- {
- assert(pos);
- DList* prev = pos->prev;
- DList* next = pos->next;
- prev->next = next;
- next->prev = prev;
- free(pos);
- pos = NULL;
- }
void ListDestory(DList* p);
从数据的有效数据头节点逐个向后释放直到回到哨兵卫,然后释放哨兵卫。
然而此时虽然所有的堆区空间已经释放,但是用于传参的原指针p依旧指向那块空间,这时我们就需要手动在测试函数中置空
- //考虑用一级指针,让调用ListDestory的人置空(保持接口一致性)
- void ListDestory(DList* p)
- {
- assert(p);
- DList* cur = p->next;
- while (cur != p)
- {
- DList* next = cur->next;
- free(cur);
- cur = next;
- }
- free(p);
- p = NULL;
- }
-
-
-
- //配合使用
- int main()
- {
- DList s;
- DList* plist = &s;
- plist = InitDList(plist);
- DListfrontpush(plist, 4);
- DListfrontpush(plist, 3);
- DListfrontpush(plist, 2);
- DListfrontpush(plist, 1);
- ListDestory(plist);
- plist = NULL;//注意这里
- return 0;
- }
通过test.c来进行操作
- void test1()
- {
- DList s;
- DList* plist = &s;
- plist = InitDList(plist);
- DListfrontpush(plist, 4);
- DListfrontpush(plist, 3);
- DListfrontpush(plist, 2);
- DListfrontpush(plist, 1);
- print(plist);
-
- DListbackpush(plist, 10);
- print(plist);
-
- DListfrontpop(plist);
- print(plist);
-
- DListbackpop(plist);
- print(plist);
-
- ListInsert(ListFind(plist, 3) , 12);
- print(plist);
-
- ListErase(ListFind(plist, 2));
- print(plist);
-
- printf("%d", ListSize(plist));
-
- ListDestory(plist);
- plist = NULL;
- }
-
-
-
- int main()
- {
- test1();
- return 0;
- }