1.双向链表的数据结构
typedef char DLinkType;typedef struct DLinkNode
{ DLinkType data; struct DLinkNode* next; struct DLinkNode* prev;
}DLinkNode;
双向带头结点的链表有三个成员, 一个是数据, 一个是指针 next 指向当前结点的下一个结点, 一个指针 prev 指向当前结点的 前一个结点,即每一个节点都有一个直接的前驱, 一个直接的后继, 这里只讨论带头结点的双向链表。
2.双向链表的初始化
/*** 初始化双链表**/void DLinkListInit(DLinkNode** phead)
{if(phead == NULL){return;//非法输入}*phead = (DLinkNode*)malloc(sizeof(DLinkNode));(*phead) -> data = 0;(*phead) -> next = *phead;(*phead) -> prev = *phead;
}
带头结点双向链表的初始化即就是用一个傀儡节点代表其为空,这里将头节点的数据初始化为 0, 注意,双向链表中没有空指针,所以将双向链表的头节点的 next 指向它自己, 将双向链表的 prev 指向它自己,即判断一个双向带头节点的链表是否为空的条件就是head -> next == head 或者是 head -> prev == head。
3.创建一个新节点
既然需要对链表进行插入数据, 那便需要对其创建一个指定数据的节点,然后将其插入到当前的链表中。创建一个节点就是对其先分配空间, 再将节点对应的数据改为需要的目标数据。
/*** 创建一个新节点**/
DLinkNode* DLinkNodeCreat(DLinkType value)
{DLinkNode* new_node = (DLinkNode*)malloc(sizeof(DLinkNode));if(new_node == NULL){return NULL;//内存分配失败}new_node -> data = value;new_node -> next = new_node;new_node -> prev = new_node;return new_node;
}
4.头插一个新结点
&enap; &enap;对双向链表的头插节点也就是先创建一个新结点,然后定义一个指针 prev 指向头结点的下一个节点, 即prev = head -> next, 然后修改对应的 prev 和 所创建的新结点 new_node 的指向, 即 让 prev -> next = new_node, 再让new_node -> prev = prev,就OK了
/*** 头插一个节点**/void DLinkListPushFront(DLinkNode* head, DLinkType value)
{if(head == NULL){return;//非法输入}DLinkNode* new_node = DLinkNodeCreat(value);DLinkNode* prev = head;DLinkNode* next = head -> next;prev -> next = new_node;new_node -> prev = prev;new_node -> next = next;next -> prev = new_node;
}
5.头删一个节点
既然头删一个节点就必须先定义一个指针 to_delete 指向要删除的节点, 即 head ->next, 然后在定义一个指针指向要删除的元素的下一个节点, 即定义一个指针 next = to_delete -> next, 然后修改head和 next 指针的指向即可, 最后将 to_delete 所指向的节点销毁即可。
/*** 头删一个节点**/
void DLinkListPopFront(DLinkNode* head)
{if(head == NULL){return;//非法输入}if(head -> next == head){return;//空链表}DLinkNode* to_delete = head -> next;DLinkNode* next = to_delete -> next;head -> next = next;next -> prev = head;DLinkNodeDstroy(to_delete);
}/*** 销毁一个节点**/void DLinkNodeDstroy(DLinkNode* to_delete)
{free(to_delete);to_delete = NULL;
}
6.尾插一个节点
尾插一个节点和链表头插一个元素道理基本相似, 即先创建一个节点 new_node, 然后在定义一个指针 prev = head -> prev,最后修改 head 和 new_node 指针的指向, 接下来再修改 new_node 和 next 指针的指向就可以了。即让 head -> prev = new_node, new_node -> next = head, new_node -> prev = prev, prev -> next = new_node, 此时就已经将新的元素尾插到链表中了
/*** 尾插一个节点**/DLinkNode* DLinkListPushBack(DLinkNode* head, DLinkType value)
{if(head == NULL){return NULL;//非法输入}DLinkNode* new_node = DLinkNodeCreat(value);DLinkNode* next = head -> next;new_node -> next = next;next -> prev = new_node;head -> next = new_node;new_node -> prev = head;return head;
}
7.尾删一个节点
往双链表中尾插一个元素就是先找到最后一个节点, to_delete = head -> prev, 然后记下 to_delete 节点的前一个节点, 即 prev = to_delete -> prev, 然后让 prev -> next = head, haed -> prev = prev, 再将to_delete 删除就可以了
/*** 尾删一个元素**/void DLinkListPopBack(DLinkNode* head)
{if(head == NULL){return;//非法输入}if(head -> next == head){return;//删除空链表}DLinkNode* to_delete = head -> prev;DLinkNode* prev = to_delete -> prev;head -> prev = prev;prev -> next = head;DLinkNodeDstroy(to_delete);
}
8.在双向链表中查找值为 to_find 节点,并且返回该节点所对应的位置
&esnp; 定义一个指针cur 指向 head -> next, 然后让 cur 依次遍历整个链表, 当 cur -> data == to_find 时,就将此时的 cur 返回即可
DLinkNode* DLinkListFind(DLinkNode* head, DLinkType to_find)
{if(head == NULL){return NULL;//非法输入}if(head -> next == head){return NULL;}DLinkNode* cur = head -> next;for(; cur != head; cur = cur -> next){if(cur -> data == to_find){return cur;}}return NULL;//没有找到
}
9.往对应位置 pos 处插入一个值为 value的新结点
往指定位置插入一个值为value 的节点, 需要先创建一个新结点, 然后定义一个指针 prev = pos -> prev, 再定义一个指针 next = pos -> next, 然后改变 prev 和 新节点 之间指针的指向, 以及 新结点和next指针之间的指向就可以将其插入到指定位置 pos 处了
/*** 往 pos 所对应的位置处插入 value ***/void DLinkListInsert(DLinkNode* pos, DLinkType value)
{if(pos == NULL){return;//非法输入}DLinkNode* new_node = DLinkNodeCreat(value);DLinkNode* prev = pos -> prev;//prev vs new_nodeprev -> next = new_node;new_node -> prev = prev;//new_node vs posnew_node -> next = pos;pos -> prev = new_node;
}
10.往指定位置之后插入一个值为 value 的结点
先创建一个新结点 new_node,再定义一个指针 prev = pos, 接下来定义一个指针 next = pos -> next, 然后改变 new_node 节点和 prev 节点 之间的指向, 最后改变 new_node 和 next 之间节点指针指向就 OK 了
/*** 往指定位置之后插入一个元素**/void DLinkListInsertAfter(DLinkNode* pos, DLinkType value)
{if(pos == NULL){return;//非法输入}DLinkNode* new_node = DLinkNodeCreat(value);DLinkNode* next = pos -> next;pos -> next = new_node;new_node -> prev = pos;new_node -> next = next;next -> prev = new_node;
}
11. 删除某个元素对应的结点
删除某个元素对应的节点就先找到要删除节点对应的位置, 然后再将这个位置对应的节点删掉就可以了, 如果链表中没有这个元素,或者链表是空链表则直接返回.
/*** 删除某个元素对应的结点**/void DLinkListErase(DLinkNode* head, DLinkType to_delete)
{if(head == NULL){return;//非法输入}if(head -> next == NULL){return;//删除空链表}DLinkNode* cur = DLinkListFind(head, to_delete);if(cur == NULL){return;//删除不存在的节点}DLinkNode* prev = cur -> prev;DLinkNode* next = cur -> next;prev -> next = next;next -> prev = prev;DLinkNodeDstroy(cur);
}
12.求链表长度
/*** 求链表长度**/size_t DLinkListSize(DLinkNode* head)
{DLinkNode* cur = head -> next;size_t size = 0;while(cur != head){cur = cur -> next;size++;}return size;
}
13. 链表判空
/*** 链表判空**/int DLinkListEmpty(DLinkNode* head)
{if(head == NULL){return -1;//非法输入}if(head -> next == head){return 1;}return 0;
}
14.删除链表中所有相同的结点
void DLinkListRemoveAll(DLinkNode* head, DLinkType to_delete)
{if(head == NULL){ return;//非法输入}if(head -> next == head){return;//空链表}DLinkNode* cur = head -> next;while(cur != head){if(to_delete == cur -> data){DLinkListErase(head, to_delete);}cur = cur -> next;}
}