我们在面试中面试官一般都会让我们现场写代码,如果你面试的时候面试官让你十分钟写一个链表,你是不是懵逼了?十分钟写一个链表,怎么可能?事实上是有可能的,十分钟写出的链表也能震惊面试官。
我们学习单链表其实是为了后面学习更高级的数据结构,因为单链表一般不会用来存储数据,只是用来作为更高级数据结构的子结构,如哈希桶、图的邻接表等。单链表看似简单,其实一点也不简单,在进行增删改查操作的时候需要注意很多问题。
在实际中,我们一般使用双向带头循环链表存储数据,因为在链表结构中,双向带头循环链表可以说是链表当中最优的结构了。双向带头循环链表,名字听起来很高大上,看起来很难的样子,其实不是的,它就是一个纸老虎,看起来难,其实它的实现很容易。
双向带头循环链表有一个数据域和两个指针域,第一个指针域指向直接后继,第二个指针域指向直接前驱。表中最后一个节点的第二个指针域指向头节点,头节点的第一个指针域指向最后一个节点,整个链表形成一个环。
双向带头循环链表的结构
- typedef int DListDataType;
- typedef struct DListNode
- {
- DListDataType* data;
- struct DListNode* prev;
- struct DListNode* next;
- }DListNode;
链表节点的创建
- DListNode* BuyListNode(DListDataType x)
- {
- DListNode* node = (DListNode*)malloc(sizeof(DListNode));
- if (node == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- node->data = x;
- node->prev = NULL;
- node->next = NULL;
- return node;
- }
链表的初始化
创建一个头节点,让头节点的两个指针域都分别指向自己。
- DListNode* LTInit()
- {
- DListNode* phead = BuyListNode(-1);//头节点的data可以为任意值
- phead->next = phead;
- phead->prev = phead;
- return phead;
- }
链表的打印
- void LTPrint(DListNode* phead)
- {
- assert(phead);
-
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- printf("%d ", cur->data);
- cur = cur->next;
- }
- printf("\n");
- }
链表的尾插
- void LTPushBack(DListNode* phead, DListDataType x)
- {
- assert(phead);
- DListNode* newnode = BuyListNode(x);
- DListNode* tail = phead->prev;//保存尾节点(头节点的prev)
-
- tail->next = newnode;
- newnode->prev = tail;
-
- newnode->prev = phead;
- phead->prev = newnode;
-
- }
链表的尾删
- void LTPopBack(DListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 判断是否为空
-
- DListNode* tail = phead->prev;
-
- tail->prev->next = phead;
- phead->prev = tail;
- free(tail);
- }
链表的头插
- void LTPushFront(DListNode* phead, DListDataType x)
- {
- assert(phead);
- DListNode* newnode = BuyListNode(x);
- newnode->next = phead->next;
- phead->next->prev = newnode;
-
- phead->next = newnode;
- newnode->prev = phead;
-
- }
链表的头删
- void LTPopFront(DListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 判断是否为空
-
- DListNode* pheadnext = phead->next;
-
- phead->next = pheadnext->next;
- pheadnext->next->prev = phead;
- free(pheadnext);
- }
在链表中插入节点
- // pos位置之前插入
- void LTInsert(DListNode* pos, DListDataType x)
- {
- assert(pos);
- DListNode* prevpos = pos->prev;
- DListNode* newnode = BuyListNode(x);
- // prev newnode pos
- prevpos->next = newnode;
- newnode->prev = prevpos;
- newnode->next = pos;
- pos->prev = newnode;
- }
删除链表中的节点
- // 删除pos位置
- void LTErase(DListNode* pos)
- {
- assert(pos);
- DListNode* prevpos = pos->prev;
- DListNode* nextpos = pos->next;
-
- free(pos);
- prevpos->next = nextpos;
- nextpos->prev = prevpos;
-
- }
判断链表是否为空
- bool LTEmpty(DListNode* phead)
- {
- assert(phead);
-
- /*if (phead->next == phead)
- {
- return true;
- }
- else
- {
- return false;
- }*/
-
- return phead->next == phead;
- }
计算链表的节点
- size_t LTSize(DListNode* phead)
- {
- assert(phead);
-
- size_t size = 0;
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- ++size;
- cur = cur->next;
- }
-
- return size;
- }
销毁链表
- void LTDestroy(DListNode* phead)
- {
- assert(phead);
-
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- DListNode* next = cur->next;
- free(cur);
-
- cur = next;
- }
- free(phead);
- }
以上就是带头双向链表的基本操作,如果想在十分钟之内写出这个链表,只需要在头插,尾插复用
LTInsert(),头删,尾删复用 LTErase(),这样你只需要写出LTInsert()和 LTErase()就等于将头插,尾插、头删,尾删写出来了。
完整代码:
DList.h 接口函数
- #include
- #include
-
- typedef int DListDataType;
- typedef struct DListNode
- {
- DListDataType* data;
- struct DListNode* prev;
- struct DListNode* next;
- }DListNode;
-
- DListNode* BuyListNode(DListDataType x);
- void LTPrint(DListNode* phead);
- DListNode* LTInit();
- void LTPushBack(DListNode* phead, DListDataType x);
- void LTPopBack(DListNode* phead);
-
- void LTPushFront(DListNode* phead, DListDataType x);
- void LTPopFront(DListNode* phead);
- DListNode* LTFind(DListNode* phead, DListDataType x);
-
- // pos位置之前插入
- void LTInsert(DListNode* pos, DListDataType x);
- // pos位置之后插入
- void LTErase(DListNode* pos);
-
- bool LTEmpty(DListNode* phead);
- size_t LTSize(DListNode* phead);
- void LTDestroy(DListNode* phead);
DList.c 函数的实现
- #include "DList.h"
-
- DListNode* BuyListNode(DListDataType x)
- {
- DListNode* node = (DListNode*)malloc(sizeof(DListNode));
- if (node == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- node->data = x;
- node->prev = NULL;
- node->next = NULL;
- return node;
- }
- DListNode* LTInit()
- {
- DListNode* phead = BuyListNode(-1);//头节点的data可以为任意值
- phead->next = phead;
- phead->prev = phead;
- return phead;
- }
- void LTPrint(DListNode* phead)
- {
- assert(phead);
-
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- printf("%d ", cur->data);
- cur = cur->next;
- }
- printf("\n");
- }
- void LTPushBack(DListNode* phead, DListDataType x)
- {
- assert(phead);
- //DListNode* newnode = BuyListNode(x);
- //DListNode* tail = phead->prev;//保存尾节点(头节点的prev)
-
- //tail->next = newnode;
- //newnode->prev = tail;
-
- //newnode->prev = phead;
- //phead->prev = newnode;
-
- LTInsert(phead, x);
-
- }
- void LTPopBack(DListNode* phead)
- {
- assert(phead);
- //assert(phead->next != phead); // 判断是否为空
-
- //DListNode* tail = phead->prev;
-
- //tail->prev->next = phead;
- //phead->prev = tail;
- //free(tail);
-
- LTErase(phead->prev);
- }
-
- void LTPushFront(DListNode* phead, DListDataType x)
- {
- assert(phead);
- /*DListNode* newnode = BuyListNode(x);
- newnode->next = phead->next;
- phead->next->prev = newnode;
- phead->next = newnode;
- newnode->prev = phead;*/
-
- LTInsert(phead->next, x);
-
- }
- void LTPopFront(DListNode* phead)
- {
- assert(phead);
- //assert(phead->next != phead); // 判断是否为空
-
- //DListNode* pheadnext = phead->next;
-
- //phead->next = pheadnext->next;
- //pheadnext->next->prev = phead;
- //free(pheadnext);
- LTErase(phead->next);
- }
- DListNode* LTFind(DListNode* phead, DListDataType x)
- {
- assert(phead);
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- if (cur->data == x)
- {
- return cur;
- }
- cur = cur->next;
- }
- return NULL;
- }
-
- // pos位置之前插入
- void LTInsert(DListNode* pos, DListDataType x)
- {
- assert(pos);
- DListNode* prevpos = pos->prev;
- DListNode* newnode = BuyListNode(x);
- // prev newnode pos
- prevpos->next = newnode;
- newnode->prev = prevpos;
- newnode->next = pos;
- pos->prev = newnode;
- }
- // 删除pos位置
- void LTErase(DListNode* pos)
- {
- assert(pos);
- DListNode* prevpos = pos->prev;
- DListNode* nextpos = pos->next;
-
- free(pos);
- prevpos->next = nextpos;
- nextpos->prev = prevpos;
-
- }
-
- bool LTEmpty(DListNode* phead)
- {
- assert(phead);
-
- /*if (phead->next == phead)
- {
- return true;
- }
- else
- {
- return false;
- }*/
-
- return phead->next == phead;
- }
- size_t LTSize(DListNode* phead)
- {
- assert(phead);
-
- size_t size = 0;
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- ++size;
- cur = cur->next;
- }
-
- return size;
- }
- void LTDestroy(DListNode* phead)
- {
- assert(phead);
-
- DListNode* cur = phead->next;
- while (cur != phead)
- {
- DListNode* next = cur->next;
- free(cur);
-
- cur = next;
- }
- free(phead);
- }
Test.c 测试函数
- #include "DList.h"
-
- void TestList1()
- {
- DListNode* phead = LTInit();
- LTPushBack(phead, 1);
- LTPushBack(phead, 2);
- LTPushBack(phead, 3);
- LTPushBack(phead, 4);
- LTPushBack(phead, 5);
- LTPrint(phead);
-
- LTPopBack(phead);
- LTPrint(phead);
- LTPopBack(phead);
- LTPrint(phead);
- LTPopBack(phead);
- LTPrint(phead);
- LTPopBack(phead);
- LTPrint(phead);
- LTPopBack(phead);
- LTPrint(phead);
-
- //LTPopBack(phead);
- }
-
- void TestList2()
- {
- DListNode* phead = LTInit();
- LTPushFront(phead, 1);
- LTPushFront(phead, 2);
- LTPushFront(phead, 3);
- LTPushFront(phead, 4);
- LTPushFront(phead, 5);
- LTPrint(phead);
-
- LTPopFront(phead);
- LTPrint(phead);
- LTPopFront(phead);
- LTPrint(phead);
- LTPopFront(phead);
- LTPrint(phead);
- LTPopFront(phead);
- LTPrint(phead);
- LTPopFront(phead);
- LTPrint(phead);
- }
-
- void TestList3()
- {
- DListNode* phead = LTInit();
- LTPushFront(phead, 1);
- LTPushFront(phead, 2);
- LTPushFront(phead, 3);
- LTPushFront(phead, 4);
- LTPushFront(phead, 5);
- LTPrint(phead);
-
- DListNode* pos = LTFind(phead, 3);
- LTPrint(phead);
-
- LTDestroy(phead);
- phead = NULL;
- }
-
- int main()
- {
-
- TestList1();
- //TestList2();
- //TestList3();
- return 0;
- }