赞
踩
List.h
#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int LTDataType; typedef struct ListNode { LTDataType data; struct ListNode* next; struct ListNode* prev; }LTNode; LTNode* LTInit(); void LTPushBack(LTNode* phead, LTDataType x); void LTPushFront(LTNode* phead, LTDataType x); void LTPrint(LTNode* phead); void LTPopBack(LTNode* phead, LTDataType x); void LTInsert(LTNode* pos, LTDataType x); void LTErase(LTNode* pos); LTNode* LTFind(LTNode* phead, LTDataType); void LTDestTroy(LTNode* phead);List.c
#include"List.h" LTNode* LTBuyNode(LTDataType x) { LTNode* node = (LTNode*)malloc(sizeof(LTNode)); if (node == NULL) { perror("malloc fail"); exit(1); } node->data = x; node->next = node->prev = node; return node; } //void LTInit(LTNode** pphead) //{ // *pphead = LTBuyNode(-1); //} LTNode* LTInit() { LTNode* phead = LTBuyNode(-1); return phead; } void LTPushBack(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = LTBuyNode(x); newnode->prev = phead->prev; newnode->next = phead; phead->prev->next = newnode; phead->prev = newnode; } void LTPushFront(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = LTBuyNode(x); newnode->next = phead->next; newnode->prev = phead; phead->next->prev = newnode; phead->next = newnode; } void LTPrint(LTNode* phead) { LTNode* pcur = phead->next; while (pcur != phead) { printf("%d->", pcur->data); pcur = pcur->next; } printf("\n"); } void LTPopBack(LTNode* phead, LTDataType x) { //链表必须有效且链表不能为空(只有一个哨兵位) assert(phead&&phead->next!=phead); LTNode* del = phead->prev; del->prev->next = phead; phead->prev = del->prev; free(del); del = NULL; } void LTPopFront(LTNode* phead, LTDataType x) { assert(phead && phead->next != phead); LTNode* del = phead->next; phead->next = del->next; del->next->prev = phead; free(del); del = NULL; } LTNode* LTFind(LTNode* phead, LTDataType x) { LTNode* pcur = phead->next; while (pcur != phead) { if (pcur->data == x) { return pcur; } pcur = pcur->next; } return NULL; } void LTInsert(LTNode* pos, LTDataType x) { assert(pos); LTNode* newnode = LTBuyNode(x); newnode->next = pos->next; newnode->prev = pos; pos->next->prev = newnode; pos->next = newnode; } void LTErase(LTNode* pos) { assert(pos); pos->next->prev = pos->prev; pos->prev->next = pos->next; free(pos); pos = NULL; } void LTDestTroy(LTNode* phead) { assert(phead); LTNode* pcur = phead->next; while (pcur != phead) { LTNode* next = pcur->next; free(pcur); pcur = next; } //此时pcur指向phead,而phead还没被销毁 free(phead); phead = NULL; }test.c
#include"List.h" void ListTest01() { //LTNode* plist = NULL; //LTInit(&plist); LTNode* plist = LTInit(); LTPushBack(plist, 1); LTPrint(plist); LTPushBack(plist, 2); LTPrint(plist); LTPushBack(plist, 3); LTPrint(plist); LTNode* find = LTFind(plist, 3); if (find == NULL) { printf("找不到\n"); } else printf("找到了\n"); LTInsert(find, 66); LTPrint(plist); LTPushFront(plist, 4); LTPrint(plist); LTErase(find); find = NULL; LTPrint(plist); LTDestTroy(plist); plist = NULL; } int main() { ListTest01(); return 0; }
双向链表是一个带头链表,带头结点又俗称哨兵结点,是用来标记指针位置的作用的。
这里我们给哨兵结点赋予一个标记值-1,当然也可以用其他值代替,这里只起到一个标记作用。实际上我们访问链表是从phead->next也就是pcur结点开始访问。
创建一个结点,我们就得申请一个结点的内存空间:
返回创建的node值,并在以后头插尾插的时候创建一个newnode结点来接受node结点的内存空间,这里我们以尾插为例:
这里插入一个新数据时,我们改变指针的指向就能实现尾插了,并注意这里的newnode与phead的指针指向的顺序不能改变,否则phead的next会被修改,导致找不到phead的next指针。
在这里虽然有些函数传入了形参,但用的是一级指针,无法改变实参,我们需要在test函数中额外来释放节点。
函数都设置为一级指针,是因为更方便我们来观察函数,不然一会一级一行二级指针,是真的很会让人抓狂的。
在这一块我们设置一个查询节点的函数,类型为LTNode*,返回一个结点,即->data==3的那个结点,并返回。
在后续的插入和消除一个值的时候,传入该函数的返回值find。
注意这里Find函数while循环中的条件,即从pcur也就是phead的笑一个结点开始扫描,由于循环的原因,当扫到带头结点即结束扫描,从而访问到了整个链表.
而这块代码当删除头或尾结点时,为什么不可以直接调用PopBack或PopFront函数呢:
是因为该函数没有传入plist指针,无法访问到plist数组。
这里的Pop函数的条件十分重要,不能只剩下一个带头结点,因为链表必须要有效.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。