前言:相信大家都是学过单链表的,有头无头学过一个就行,那么这篇文章对于你就是最合适不过的了
如果没学过单链表不要慌,传送门在此
在我的单链表里面提到过,链表其实有八种结构
但是最常用的,也就是做题常出的就是单链表,其次就是我们今天学的,因为他的结构足够复杂所以以后题目要用到循环,或者带头,或者双向 你都不会怕。
有了之前的铺垫这个实现简直so easy
目录
1.和单链表不一样的地方
1.1 初始化
因为这个链表是带头的,所以我们要对其初始化
那么现在我们来思考一下结构应该是什么样的
就是这样的结构,如果我们还是和单链表一样,只是放一个存放数据的data,和指向下一个存储单元的指针 无法实现 “向前指”
那简单啊,数据前面再加一个指向前面单元的指针不就行了
我们把指向前一个节点的指针叫做prev,指向下一个节点的指针叫做next
真是太天才的想法了!!!!!
但是我们需要一个头啊~~~
所以我们把第一个节点当做头,注意这个头不能被头删或者尾删删除掉,也不能改变地址,初始化是谁就是谁,其次,头的数据可以任意放,比如在需要头往后遍历的时候,只需要从头下一个节点开始遍历就好啦~~
而且头的好处多多,不用和单链表一样每次头删之后找到新的头返回去,有这个哨兵位方便极了
1.2不一样的细节
比如尾删,不用每次都从头开始找到尾节点之前的节点,然后想办法保存再删除,只需要把尾巴的节点看成是哨兵位之前的节点就好了,哨兵位prev指向的节点就是尾节点
这就是双向链表的好处,不需要快慢指针,尾删不需要从头遍历
然后就是头删或者任意位置的删除,因为我们节点里保存了前一个节点的地址,所以不需要快慢指针找到要删除之前或者之后的节点再保存地址来回赋值之类的,只需要直接传一个位置(指针形式)在链表里锁定位置之后把他前面节点对next直接指向他下一个节点,同理 他下一个节点的prev直接指向他前一个节点!太容易了
但是一个问题大家肯定发现了 就是怎么找到传过来对指针到底是指向哪个节点的地址?用一个find函数,传一下目标节点里面的数据就可以了,用find函数从头节点时候的节点开始遍历,只要找到目标位置(pos)返回他的地址就好啦
尾删的时候其实每一个尾巴也可以看成哨兵位之前的头!!所以无需遍历只要找一下哨兵位prev指向对节点就是要删除的尾巴啦~具体的操作如下(自己先思考一下哦)
2.具体的实现
头文件的实现
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int type;
typedef struct SeqList
{
struct SeqList* prev;
struct SeqList* next;
type data;
}SL;
SL* Buy(type x);
SL* Init(void);// 初始化形成一个头
void PushBack(SL*phead,type x);//尾插
void Print(SL* phead);
void PopBack(SL* phead);//尾删
void PushFront(SL* phead, type x);//头插
void PopFront(SL* phead);//头删
void Destory(SL* phead); //销毁
void InsertSL(SL*phead,SL* pos, type x,type val); //在pos之前插入x
size_t Find(SL*phead ,type val);//查找位置是否有效
void EraseSL(SL* phead,SL* pos, type val);//删除pos
bool Empty(SL* phead);//判断phead是否为空
List.c实现
#define _CRT_SECURE_NO_WARNINGS
#include "List.h"
//双向带头循环
SL* Buy(type x)
{
SL* newnode = (SL*)malloc(sizeof(SL));
if (newnode == NULL)
{
perror(Buy);
exit(0);
}
newnode->next = NULL;
newnode->data = x;
newnode->prev = NULL;
}
SL* Init(void)
{
SL* newnode = Buy(0);
newnode->next = newnode;
newnode->prev = newnode;
return newnode;
}
void PushBack(SL* phead,type x) //尾插
{
SL* newnode = Buy(x);
SL* tail = phead->prev;
tail->next = newnode;
newnode->next = phead;
newnode->prev = phead;
phead->prev = newnode;
}
void Print(SL* phead) //打印
{
assert(phead);
SL* cur = phead->next;
while (cur!=phead)
{
printf("%d", cur->data);
cur = cur->next;
}
printf("\n");
}
void PopBack(SL* phead)//尾删
{
assert(phead);
assert(phead->prev);
SL* tail = phead->prev;
SL* tailPrev = tail->prev;
tailPrev->next = phead;
phead->prev = tailPrev;
free(tail);
tail = NULL;
}
void PushFront(SL* phead, type x) //头插
{
assert(phead);
if (phead->next == NULL)
{
//相当于在头之后尾插
SL* newnode = Buy(x);
SL* tail = phead->prev;
tail->next = newnode;
newnode->next = phead;
newnode->prev = phead;
phead->prev = newnode;
}
else
{
SL* newnode = Buy(x);
SL* pheadNext = phead->next;
phead->next = newnode;
newnode->next = pheadNext;
pheadNext->prev = newnode;
newnode->prev = phead;
}
}
void PopFront(SL* phead)//头删
{
assert(phead);
phead->next = phead->next->next;
phead->next->next->prev = phead;
}
void Destory(SL* phead) //销毁
{
assert(phead);
SL* cur = phead->next;
while (cur != phead)
{
SL*next=cur->next;
free(cur);
cur = next;
}
}
size_t Find(SL* phead, type val)//查找位置是否有效
{
SL* cur = phead->next;
while (cur != phead)
{
if (cur->data == val)
return;
else
cur=cur->next;
}
return 0;
}
void InsertSL(SL* phead, SL* pos, type x,type val) //在pos之前插入x
{
assert(pos);
if (Find(phead,val) == 0)
{
perror("Not Find");
exit(0);
}
else
{
SL* newnode = Buy(x);
pos->prev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
newnode->prev = pos->prev;
}
}
void EraseSL(SL* phead,SL* pos,type val)//删除pos
{
assert(pos);
if (Find(phead, val) == 0)
{
perror("Not Find");
exit(0);
}
else
{
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
}
bool Empty(SL* phead)//判断phead是否为空
{
assert(phead);
return phead->next == phead;
}
测试函数参考如下
#define _CRT_SECURE_NO_WARNINGS
#include "List.h"
void test1(void)
{
SL* phead = Init();// 初始化形成一个头
PushBack(phead, 1);
PushBack(phead, 2);
PushBack(phead, 3);
PushBack(phead, 4);
Print(phead);
PopBack(phead);
Print(phead);
}
void test2(void)
{
SL* phead = Init();// 初始化形成一个头
PushFront(phead, 1);
PushFront(phead, 2);
PushFront(phead, 3);
PushFront(phead, 4);
Print(phead);
PopFront(phead);
Print(phead);
}
void test3(void)
{
SL* phead = Init();// 初始化形成一个头
PushFront(phead, 1);
PushFront(phead, 2);
PushFront(phead, 3);
PushFront(phead, 4);
SL* pos = Find(phead,3);
InsertSL(phead, pos, 6, 3);
Print(phead);
}
void test4(void)
{
SL* phead = Init();// 初始化形成一个头
PushFront(phead, 1);
PushFront(phead, 2);
PushFront(phead, 3);
PushFront(phead, 4);
SL* pos = Find(phead, 3);
EraseSL(phead, pos, 3);
Print(phead);
}
int main()
{
SL* phead=Init();// 初始化形成一个头
//test1();
//test2();
//test3();
test4();
return 0;
}
3.小试牛刀
这个题目的意思就是
其实我们不需要每次挪动一个节点就把指向关系保存一下,因为只是简单的移动没有添加,所以远原来的指向不会变也就无需保存
你会了吗?