双链表
双链表属于链表的一种,有两个指针域,分别指向直接后继和直接前驱。所有在遍历双向链表时,从任意一个节点开始,都可以很方便的访问它的前驱节点和后继节点。使用这种方式解决了单链表中不能使用反向遍历的问题。
![](https://img-blog.csdnimg.cn/20200214194638860.png)
节点数据结构
在双链表中,每个节点包含三个部分:
- 指向当前节点上一个节点的指针
- 当前节点存储数据的数据域
- 指向当前节点下一个节点的指针
struct Node{
int data;
struct Node *previous,*next;
} *head=NULL;
节点的运算形式
基本的运算形式:增加、删除、更新
插入节点到序列尾部
创建一个新的节点,通过遍序列表到序列末尾,让后将节点插入到末尾。
插入过程
- 创建一个新节点并赋值,将Node的next指针设置为NULL
- 检查集合的头节点是否为空(head==NULL)
- 如果头节点为空,设置newNode->previous=NULL,head=newNode;
- 如果头节点不为空,定义一个临时节点temp=head;
- 移动temp节点直到到达序列的尾部
- 设置newNode=temp->next和temp=newNode->previous;
void insertAtENd(int value){
struct Node *newNode;
newNode=(struct Node*)malloc (sizeof(struct Node));
newNode->data=value;
newNode->next=NULL;
if (head==NULL){
newNode->previous=NULL;
head=newNode;
}else{
struct Node *temp = head;
while(temp -> next != NULL)
temp = temp -> next;
temp -> next = newNode;
newNode -> previous = temp;
}
printf("插入末尾成功");
}
插入节点到头部
创建一个新的节点,将头节点替换为新的节点,并更新指针的指向。
插入过程
- 创建一个新的节点newNode,并设置newNode->previous=NULL;
- 检查是否head==NULL;
- 如果head==NULL,newNode->next=NULL,head=newNode;
- 如果head!=NULL,newNode->next=head,head=newNode;
void insertAtBeginning(int value){
struct Node *newNode;
newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=value;
newNode->previous=NULL;
if (head==NULL){
newNode->next=NULL;
head=newNode;
}else{
newNode->next=head;
head->previous=newNode;
head=newNode;
}
printf("插入成功");
}
插入节点到指定元素后
循环遍历链表,创建一个新的节点,然后将其插入到链表中的指定位置
插入过程
- 创建一个新节点并赋值
- 检查链表是否为空(head==NULL)
- 如果(head==NULL),newNode->previous=NULL,newNode->next=NULL,head=newNode
- 定义一个temp=head
- 通过循环遍历移动temp节点,指定找到我们需要插入的节点位置(for循环到position-1)
- 如果temp->next==NULL设置flag=0,表面我们要查找的目标元素未发现,终止循环
- 如果flag=1,设置newNode->next=temp->next,temp->next->previous=newNode,temp->next=newNode和newNode->previous=temp;
void insertAfter(int value, int pos)
{
int i, flag = 1;
struct Node *newNode, *temp = head;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
if(head == NULL)
{
newNode -> previous = newNode -> next = NULL;
head = newNode;
}
else
{
for (i = 0; i < pos - 1; i++) {
temp = temp -> next;
if (temp -> next == NULL) {
flag = 0;
break;
}
}
if (flag) {
newNode -> next = temp -> next;
temp -> next -> previous = newNode;
temp -> next = newNode;
newNode -> previous = temp;
printf("\nInsertion successful\n");
}
else
printf("Number of elements is less than position entered");
}
}
从链表尾部删除节点
通过循环遍历到链表尾部,删除尾部节点
从尾部删除过程
- 检查链表是否为空(head==NULL)
- 如果为空,抛出错误并终止程序
- 如果不为空,定义一个temp=head;
- 检查是否链表只存在一个节点(temp->previous=NULL和temp->next=NULL)
- 如果只存在一个节点,设置head=NULL并删除temp节点
- 如果链表存在多个节点,移动temp节点直到到达链表尾部(temp->next!=NULL)
- 设置temp->previous->next=NULL并释放temp占用内存(free(temp))
void deleteEnd()
{
if(head == NULL)
printf("链表为空");
else
{
struct Node *temp = head;
if(temp -> previous == temp -> next)
{
head = NULL;
free(temp);
}
else{
while(temp -> next != NULL)
temp = temp -> next;
temp -> previous -> next = NULL;
free(temp);
}
printf("\n删除成功");
}
}
从链表头部删除节点
从链表头部删除节点
从头部删除过程
- 检查链表是否为空(head==NULL)
- 如果为空,抛出异常并终止
- 如果不为空,定义一个temp节点,且temp=head
- 检查链表是否只有一个节点(temp->previous=temp->next)
- 如果链表只有一个节点,设置head=NULL并释放free(temp)
- 否则,设置temp->next=head,head->previous=NULL并释放free(temp)
void deleteBeginning()
{
if(head == NULL)
printf("链表为空");
else
{
struct Node *temp = head;
if(temp -> previous == temp -> next)
{
head = NULL;
free(temp);
}
else{
head = temp -> next;
head -> previous = NULL;
free(temp);
}
printf("\n删除成功");
}
}
从链表指定位置删除节点
遍历链表到找到指定节点
从指定位置删除过程
- 检查链表是否为空(head==NULL)
- 如果为空,抛出错误并终止
- 如果不为空,定义一个temp,且temp=head
- 移动temp节点,直到找到目标节点或者到达链表末尾
- 如果到达节点末尾,抛出未发现目标节点异常,并终止
- 如果到达目标节点,检查是否只有一个节点
- 如果仅有一个节点,设置head=NULL并释放free(temp)
- 如果链表有多个节点,检查是否为头节点(temp==head)
- 如果为头节点(temp=head),将头节点移动到下一个节点(head==head->next),设置head->previous=NULL并释放内存free(temp)
- 如果temp!=head,检查temp是否为最后一个节点(temp->next==NULL)
- 如果为最后一个节点,设置temp->previous->next=NULL并释放free(temp)
- 如果temp不是第一个节点也不是最后一个节点,设置temp->previous-next=temp->next,temp->next->previous=temp->previous并释放free(temp)
void deleteSpecific(int delValue)
{
if(head == NULL)
printf("List is Empty");
else
{
struct Node *temp = head;
while(temp -> data != delValue)
{
if(temp -> next == NULL)
{
printf("\nGiven node is not found in the list");
}
else
{
temp = temp -> next;
}
}
if(temp == head)
{
head = NULL;
free(temp);
}
else
{
temp -> previous -> next = temp -> next;
free(temp);
}
printf("\nDeletion successful");
}
}
双连边的应用常见
- 浏览器的历史纪录 大多数主流浏览器都支持针对浏览历史记录的前进、后退,就是基于双链表设计的
- 大多数算法
- 游戏:卡牌游戏,记录牌的顺序
更多内容,欢迎关注:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200215113246664.jpg)