双向链表,顾名思义,一条可双向进行的就叫双向链表。那怎么搞?也就是有前驱跟后继,有些时候有人不明白什么是前驱跟后继啊,浅显的讲讲,前驱就是当前结点指向上一个结点的箭头,那么上一个结点就是当前结点的前驱,后继就是当前结点的前一个结点,从上一个结点指向当前结点就叫后继,也就是说上个结点作为头,那当前结点作为尾,头的后继不就是尾嘛。
然后有头尾指针,双向嘛。头指针运动,尾指针也运动,所以这里要多存个尾指针。那说了这么多,先来构造给双向链表吧。
定义结构体老掉牙的东西了
思路!:
1.刚前面说了有前驱跟后继,那么结构体就一定存有前驱跟后继,单链表只有一个后继指针,所以!可以看到,双链表在这里占据了一点点内存!
2.当然怎么能少了数据域呢!,就是单链表多写个前驱而已
#include<iostream>
using namespace std;
typedef struct CDLNode
{
CDLNode* pre;//前驱
CDLNode* next;//后继
int data;
}CirNode;
好接下来就是老规矩了 我们先来初始化一下
思路!:
1.可能会好多人问为什么是类呀不是普通函数了呢?不能总是写那些吧该变着写啦!
2.首先!我们要构造函数,什么是构造函数,就是跟类同名的那个函数名就是构造函数我这里使用了带参数的构造函数,当然你也可以无参构造!
3.然后!我把我想实现的功能写进public:这个区域下!让他的对象能够访问到我的行为!也就是访问到我的函数!
4.并且我前面说过有头结点的,所以我把头结点这个不会变的量做私有变量!好!头结点还记得吗? 头结点是不存有数据只存有一个next指针的结点!一定要记得!
class CirLinkList
{
public:
CirLinkList(int a[], int sum);//建立sum个元素的双向链表
bool Empty();//判断是否为空
int Length();//获取长度
void PrintList();//打印链表
int Symmetry();//判断是否为对称
void InsertList_value();//插入数据
void Findlist_value();//查找数据
private:
CirNode* first;//头结点
};
下面才是真正的初始化!来!好好看!
思路:
1.有人会问哎呀你怎么是类外定义呢?你也可以类内定义的没差别的 但是!为了简洁看起来不那么拖拉我们就使用类外定义!类外定义就是加上这个类的作用域!作用域也就是(::)!
2.首先我们对所有指针行初始化!然后使辅助指针指向我们的头指针让它能进行移动!
3.创建新的结点,因为!你要录入数据就必须创建新的结点!除了头结点每个结点都存有前驱跟后继,并且还有数据域!
4.看不懂咋们就看图!
CirLinkList::CirLinkList(int a[],int sum)
{
first = new CirNode();
CirNode* r = first;//辅助指针!
cirNode* s = nullptr;// s为新的结点
for (int i = 0; i < sum; i++)
{
s = new CirNode();
s->data = a[i];//为新结点录入数据
r->next = s; //头结点后继就是创建的新结点
s->pre = r;//创建新的结点的前驱就是头点
r = s;//一直交替跟换!
}
r->next = first;//这里就是保证表尾连接的是头!!
first->pre = r;//头也能连接尾
}
那么接下来有了数据了 我们就可以进行一些奇怪的操作了
那么我们先来判断是否为空吧~
判空操作!:
思路!:
1.如果表头的后继还是指向表头那就为空
对 思路就这么简单!有没有茅塞顿开?没有拉出去打一顿通气了就有了。
bool CirLinkList::Empty()
{
if (first->next == first)
{
return true;
}
return false;
}
好了,接下来我们获取一下长度!
获取长度!:
思路!:
其实这个也挺简单的,就跟单链表差不多,我们就一直next,只要我的让我的下一个指针不指回表头,我们就一直next 老快乐了,然后用一个count来接收next的次数,有next的多少次就有多少个数据!
int CirLinkList::Length()
{
CirNode* p = first->next;
int count = 0;
while (p != first)
{
p = p->next;//我们一直next!
count++;
}
return count;
}
玩了这么多,我们肯定想看一下我们录入的数据能不能正常打印出来啦
那么我们来个打印操作吧~
显示操作!:
思路!:
1.首先一个辅助指针指向表头然后我们一直next
2.next一次我们就输出数据
3.有人问 为什么你有个continue啊?.那就讲讲continue语句的作用吧 它是跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为仅结束本次循环。
注意:continue语句并没有使整个循环终止。
3.其实这个跟单链表没啥差别!
void CirLinkList::PrintList()
{
CirNode* p = first->next;
while (p != first)
{
if (p == first)
{
p = p->next;
continue;
}
cout << p->data<<" ";
p = p->next;
}
}
好接下来我们再来玩一个操作
插入数据!
比较多人对插入删除比较头疼,在这里也讲解一下~
插入操作!:
思路!:
1.首先还是辅助指针!
2.首先判定要插入的位置是否非法范围!
3.当位置知道要插入的位置时我们就开始插入这个值!
4.创建一个要插入的结点并且传入值!
5.这时候我们开始把结点都连接起来!首先新结点的前驱
6.这是个插空法 但是!不能在头跟尾!
7.头插跟尾插要做另外的操作!
插空法图解:
void CirLinkList::InsertList_value(int pos,int value)
{
CirNode* p = first->next;
CirNode* r=nullptr;
if (pos < 0||pos>Length()+1)
{
cout << "这个位置不能进行插入!非法范围,请在合法范围内插入数据!" << endl;
return;
}
int count=0;//找位置用
while (count < pos-1)
{
p = p->next;
count++;
}
r = new CirNode;
r->data = value;//传入值
r->next = p->next; //这步骤不能乱要先确定下来 后面的步骤可以乱没关系
r->pre = p;//看图
p->next = r;
p->next->pre = r;
}
既然有插空法那一定有头插还有尾插法,但是,在这里不详细讲方法差不多
手动画个图就出来了
我们讲删除操作!
删除操作!:
思路!:
1.这给操作其实也挺简单的,对! 又挺简单的,来! 看思路,首先还是辅助指针,然后找位置!老掉牙的东西了就不说了
2.找到了位置 我们看图!用图给你们分析下!
void CirLinkList::Deletelist_value(int pos)
{
CirNode* p = first->next;
int count=0;
while(count<pos-1&&p)
{
count++;
p = p->next;
}
p->pre->next = p->next;
p->next->pre = p->pre;
free(p);
}
删除简单吧!我觉得挺简单的那么接下来我们就去找值啦!
值是否存在的操作!:
思路!:
1.首先!辅助指针跑不掉的!
2.只要我的头结点不指回头结点我就一直遍历!然后直到那个值我就停下,遍历到头结点了!还没找到就退出!
bool CirLinkList::Findlist_value(int value)
{
CirNode* p = first->next;
int count=0;
while (p!=first)
{
if (p->data == value)
{
return true;
}
p = p->next;
}
return false;
}
再来一个有意思的!判断是否对称!
对称操作:
1.这个其实比较有意思,就是设立两个辅助指针一个指向头一个指向尾巴,然后一起移动就是头向尾移动,尾向头移动,当移动的时候双方得到的元素都相同那我们的链表就是对称双链表!
2.这里还有一个要点!不要越过双方的底线!也就是头到尾停止,尾到头也停止!就行了!
int CirLinkList::Symmetry()
{
CirNode* p = first->next;
CirNode* q = first->pre;
while ((p != q && q != p->next))
{
if (p->data == q->data)
{
p = p->next;
q = q->pre;
}
else
return false*puts("这个链表不是对称双链表");
}
return true*puts("这个链表为对称双向链表");
}
好 让我们开始上主函数测试一下我们的成果!!!
#include<iostream>
using namespace std;
typedef struct CDLNode
{
CDLNode* pre;//前驱
CDLNode* next;//后继
int data;
}CirNode;
class CirLinkList
{
public:
CirLinkList(int a[], int sum);//建立sum个元素的双向链表
bool Empty();//判断是否为空
int Length();//获取长度
void PrintList();//输出显示
int Symmetry();//判断是否为对称
void InsertList_value(int pos,int value);//按位插入元素
void Deletelist_value(int pos);//按位删除结点
bool Findlist_value(int value);//按值查找
private:
CirNode* first;//头结点
};
bool CirLinkList::Empty()
{
if (first->next == first)
{
return true;
}
else
return false;
}
int CirLinkList::Length()
{
CirNode* p = first->next;
int count = 0;
while (p != first)
{
p = p->next;
count++;
}
return count;
}
CirLinkList::CirLinkList(int a[],int sum)
{
first = new CirNode();
CirNode* r = first, * s = nullptr;//头尾指针初始化 s为新的结点
for (int i = 0; i < sum; i++)
{
s = new CirNode();
s->data = a[i];
r->next = s;
s->pre = r;
r = s;
}
r->next = first;
first->pre = r;
}
void CirLinkList::PrintList()
{
CirNode* p = first->next;
while (p != first)
{
if (p == first)
{
p = p->next;
continue;
}
cout << p->data<<" ";
p = p->next;
}
}
void CirLinkList::InsertList_value(int pos,int value)//插空法
{
CirNode* p = first->next;
CirNode* r=nullptr;
if (pos < 0||pos>Length()+1)
{
cout << "这个位置不能进行插入!非法范围,请在合法范围内插入数据!" << endl;
return;
}
int count=0;
while (count < pos-1)
{
p = p->next;
count++;
}
r = new CirNode;
r->data = value;
r->next = p->next;
r->pre = p;
p->next = r;
p->next->pre = r;
}
int CirLinkList::Symmetry()
{
CirNode* p = first->next;
CirNode* q = first->pre;
while ((p != q && q != p->next))
{
if (p->data == q->data)
{
p = p->next;
q = q->pre;
}
else
return false*puts("这个链表不是对称双链表");
}
return true*puts("这个链表为对称双向链表");
}
void CirLinkList::Deletelist_value(int pos)
{
CirNode* p = first->next;
int count=0;
while(count<pos-1&&p)
{
count++;
p = p->next;
}
p->pre->next = p->next;
p->next->pre = p->pre;
free(p);
}
bool CirLinkList::Findlist_value(int value)
{
CirNode* p = first->next;
int count=0;
while (p!=first)
{
if (p->data == value)
{
return true;
}
p = p->next;
}
return false;
}
void Menu()
{
cout << "=================" << endl;
cout << "1.显示链表内容" << endl;
cout << "2.判断是否为空" << endl;
cout << "3.获取链表长度" << endl;
cout << "4.按位置插入结点" << endl;
cout << "5.按位置删除链表结点" << endl;
cout << "6.查找链表中的数据" << endl;
cout << "7.链表是否对称" << endl;
cout << "8.退出程序" << endl;
cout << "=================" << endl;
}
int main()
{
Menu();
cout << "初始化完成!" << endl;
int sum;
cout << "要输入多少个元素:";
cin >> sum;
int* arry = new int[sum];
cout << "请输入" << sum << "个元素:";
for (int i = 0; i < sum; i++)
{
cin >> arry[i];
}
CirLinkList L{ arry, sum };
cout << endl;
int select;
while (1)
{
cout << "请输入您要选择的操作:";
cin >> select;
switch (select)
{
case 1:
L.PrintList();
cout << endl;
break;
case 2:
if (L.Empty())
{
cout << "这个双向链表为空" << endl;
}
else
cout << "这个双向链表存有数据所以不为空!" << endl;
break;
case 3:
L.Length();
cout << endl;
break;
case 4:
int pos, value;
cout << "请输入您要插入的位置以及数据:";
cin >> pos >> value;
L.InsertList_value(pos, value);
cout << endl;
break;
case 5:
cout << "请输入要删除的结点位置:";
cin >> pos;
L.Deletelist_value(pos);
break;
case 6:
cout << "请输入要查找的值:";
cin >> value;
if (L.Findlist_value(value))
{
cout << "找到了" << value << "这个值" << endl;
}
else
{
cout << "这个双向链表里没有这个值,请重新操作!" << endl;
}
break;
case 7:
L.Symmetry();
cout << endl;
break;
case 8:
exit(-1);
}
}
return 0;
}