您的位置:首页 > 其它

循环双链表的手动构建总结

2016-09-24 16:48 246 查看
我们常用的是单链表的算法。虽然双链表和循环双链表的算法常常被拿来作为一种设计的思路,也常假想有一个循环双链表,对它进行操作,实际上我从没动手写过循环双链表。

在手动实现单链表的习题多道以后,有了一种对简单代码的熟悉度,以及轻微的掌控感。我觉得自己知道自己写的是什么,并能在大脑中视觉化执行路径了。作为一个开始时,在自己大脑中跑代码会有轻微恐慌的人来说,这是一个进步了。

也让我明白,行动是打开枷锁的钥匙。不敢说唯一,因为或许还要其他的途径。

通过写算法的过程,我感受到的不仅仅是作为一个coder写作的乐趣,更能慢慢实践那些听到的道理。很长时间,我会在一天中不专注的时间里,有轻度的抑郁症患者才有的症状。比如,怀疑一个明显的道理,逼着自己去证明,证明不了就会引起下一次的雪崩式的恐慌。担心自己就此分崩离析。然后任由它去后才发现,不过是柳暗花明的又一番景象,什么都没改变。

开始深深赞同,颠倒妄想或许指的就是这些念头。善护念的重要性就隐含其中。我不清楚颠倒妄想究竟能不能带来物质上变化,曾经我担心会让自己变得不能思考,变得更笨或者怎样,发现并没有。只是当你的念头沉浸在痛苦的妄想中时,内心好痛苦。

我说的话都是自己经历过,痛苦过。我很多次挣扎时,希望有人过来和我讲一讲其中的道理,告诉我不要怕。没有任何人来。但是我很感激,它逼着你去思考,去从其他途径去了解我们是什么。最感激的是遇到了书中的老师,遇到了佛经中那些让我升起信心的佛陀的话。

好像跑偏了。但是,我想说的仍然是:写代码是一种思考。思考本身是无为法,那么对其他事物的思考,和写代码都是殊途同归。

我们总是在自己的脑海中构建一个交织的世界。每天吸收新的知识,新的知识将添加进原有的知识网路。每天的思考,会让头脑中的知识互相之间再次加强连接或者形成新的连接。

这样的过程便会让你认知像指数于洋爆炸。想一想,人真的是一个神奇物种。可以不断去套索认知的边界。

希望以上的文字能给你带来的是信心而不是困惑。

回到循环双链表的构建问题。

由一道题目开始:

判断一个带头的循环双链表是否对称

思路:首先得构建一个循环双链表,主要是明晰结点的特征是有两个指针域。单纯的双链表会有两个结点的指针域为空:头结点的prior指针域和尾结点的next指针域。循环就是让头结点的prior指针域指向尾结点,尾结点的next指针指向头结点。判断是否对称只需要两个方向遍历,如果有奇数个结点,两个方向游走的指针相同时表示对称。偶数个结点,判断的是正向指针的next结点与反向指针的prior结点相同并且数值相同。正反向遍历时,结点值相同时才相向移动。否则,途中有不同的就退出,判断为不对称。

代码会更清晰:

#include <iostream>
#include <ctime>
#include <vector>
#include <algorithm>

using namespace std;
typedef int ElemType;
#define MAX 100

typedef struct Node
{
ElemType data;
struct Node *prior; // 前向指针
struct Node *next; // 后向指针
}SNode, *SList;

// 生成一个循环双链表,数值随机生成
// 返回指向生成链表的头结点指针

SList generateSymList(int n) // n是结点个数
{
srand((unsigned)time(NULL));
// 定义头结点
SList sl = (SList)malloc(sizeof(SNode));
sl->prior = NULL;
sl->next = NULL;

SNode *r = sl;

//尾插法建立链表:元素递增有序
for(int i = 0; i < n; i++)
{
SNode *s = (SNode*)malloc(sizeof(SNode));
s->data = rand() % MAX; // 随机值
s->prior = r;
s->next = sl; //

r->next = s;
sl->prior = s; //需要更新的是sl的prior,r跟踪是工作结点

r = s;
}
return sl;
}

bool IsSymmetrical(SList sl) // sl是循环双链表的头结点
{
SNode *p = sl->next; // 第一个结点
SNode *q = sl->prior; // 最后一个结点

while(p && q)
{
if(p == q || (p->next == q && q->prior == p && p->data == q->data))// 如果比较到p和q相等或者对称
{
return true;
}
else
{
if(p->data == q->data) // 只有数据相同时,才有机会进行下一次的比较
{
p = p->next;
q = q->prior;
}
else // 否则直接判定不对称
{
return false;
}
}
}
return false;
}

int main()
{
int n;
cout << "Input the number of nodes: ";
cin >> n ;
SList sl = generateSymList(n);

// 正向输出
SNode *p = sl->next; //指向第一个结点
cout << "正向 : ";
while(p != sl->prior) //p指向最后一个结点时结束
{
cout << p->data << " ";
p = p->next;
}
cout << p->data; //最后一个结点值需要特别输出

cout << endl;

// 反向输出
p = sl->prior; //指向最后一个结点
cout << "反向 : ";
while(p != sl->next) //p指向第一个结点时结束
{
cout << p->data << " ";
p = p->prior;
}
cout << p->data; //第一个结点也需要特别输出
cout << endl;

// 题目的主要逻辑

bool b = IsSymmetrical(sl);
if(b)
{
cout << "Yes!" << endl;
}
else
{
cout << "No!" << endl;
}
return 0;
}


如果要代码解析的话,先看结点特征:

typedef struct Node
{
ElemType data;
struct Node *prior; // 前向指针
struct Node *next; // 后向指针
}SNode, *SList;


生成过程:

SList generateSymList(int n) // n是结点个数
{
srand((unsigned)time(NULL));
// 定义头结点
SList sl = (SList)malloc(sizeof(SNode));
sl->prior = NULL;
sl->next = NULL;

SNode *r = sl;

//尾插法建立链表
for(int i = 0; i < n; i++)
{
SNode *s = (SNode*)malloc(sizeof(SNode));
s->data = rand() % MAX; // 随机值
s->prior = r;
s->next = sl; //新的结点next域指向头结点,因为用r在跟踪当前工作结点,因此,这个结点的next域会更新为指向下一个结点

r->next = s; // 这句就是在更新新结点的前驱的next域指向新结点
sl->prior = s; //需要更新的是sl的prior,r跟踪是工作结点

r = s;
}
return sl;
}


判断过程:

bool IsSymmetrical(SList sl) // sl是循环双链表的头结点
{
SNode *p = sl->next; // 第一个结点
SNode *q = sl->prior; // 最后一个结点

while(p && q)
{
if(p == q || (p->next == q && q->prior == p && p->data == q->data))// 如果比较到p和q相等或者对称
{
return true;
}
else
{
if(p->data == q->data) // 只有数据相同时,才有机会进行下一次的比较
{
p = p->next;
q = q->prior;
}
else // 否则直接判定不对称
{
return false;
}
}
}
return false;
}


这种是直接反应思路的一种代码结构,看着有些丑,至于怎样重构,我没有思路。

以上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息