上一篇文章给大家讲了一个很简单的单向不带头,不循环的链表,是为了让大家更好的理解链表,现在我们就来学习学习他的升级版,双向,带头,循环链表。希望多多支持哦!
首先我们要使链表带头,那么我们定义的结构体中必须有两个结构体指针变量,一个指向前一个节点一个指向后一个节点。注意我们所说的头也是一个节点,但它不储存数据是为方便我们使优化结构使速率更快,时间复杂度更优。下面是其结构
定义的结构体节点
首先还是老样子,用typedef来命名结构体和类型,定义数据和两个指针,prev为指向前一个节点的指针,back则指向后一个节点的指针。
typedef int Type; typedef struct Listnode Dlistnode; struct Listnode { struct Listnode* prev; struct Listnode* next; Type date; };
开辟结构体节点的函数
Dlistnode* Buynode(Type a) { Dlistnode* newnode = (Dlistnode*)malloc(sizeof(Dlistnode)); newnode->date = a; newnode->next = NULL; newnode->prev = NULL; }
这里就跟昨天文章一样没什么好说的。
头插函数
void Pushfront(Dlistnode* phead,Type a) { assert(phead); Dlistnode* first = phead->next; Dlistnode* new = Buynode(a); new->next = first; new->prev = phead; phead->next = new; phead->next->prev = new; }
首先我们要保证传过来的phead头节点不为空,所以assert一下。这里创建辅助变量first即指向第一个节点的 指针(这里的第一个指针是指第一个存放数据的指针即头节点往后的哪一个节点)是为了使后面的赋值不用考虑赋值的顺序,后面的四个赋值顺序随便哪个在前代码都不会出错。
尾插函数
void Pushback(Dlistnode* phead,Type a) { assert(phead); Dlistnode* tail = phead->prev; Dlistnode* new = Buynode(a); new->prev = tail; new->next = phead; phead->prev = new; phead->prev = new; }
这里需要注意的跟头插差不多。
头删函数
void Popfront(Dlistnode* phead) { assert(phead); assert(phead->next != phead); Dlistnode* first = phead->next; Dlistnode* second = phead->next->next; phead->next = second; second->prev = phead; free(first); first = NULL; }
代码解析:assert(phead->next != phead);主要是当不断头删只剩下头节点时,不会将头节点也删去。然后后面的first,second也是辅助变量。
尾删函数
void Popback(Dlistnode* phead) { assert(phead); assert(phead->prev != phead); Dlistnode* first = phead->prev; Dlistnode* second = phead->prev->prev; second->next = phead; phead->prev = second; free(first); first = NULL; }
也是和头删的差不多就不多解释了。
打印函数
void Print(Dlistnode* phead) { Dlistnode* cur = phead->next; while (cur != phead) { printf("%d->", cur->date); } printf("\n"); }
也是常规打印就也不解释了。
今天文章到此结束。