赞
踩
在C++里,存在多种类型的表,其中有一种线性表,链表则是一种线性表,正如它的名字一样,链表的样子就像是用一条链子串起来的表(这里,我主要讲的是双循环链表)
而用来连接链子的每个环节的是指针,在最基本的单向不循环链表中,有一根单向指针next,这根指针的作用显而易见,就是串联上下两个节点。
说到串联节点,那么必不可少的自然是节点,节点又如何创建呢。首先,我们要创建一个节点类(class Node),在双循环链表中,想要实现双循环,那么就要求要有双向效果,也就是用来指向的指针应该要有两根,一根指向本节点的下一个节点(next),一根指向本节点的上一个节点(prior),以此达到双向的效果
这里,我以一个记录学生信息的双循环链表作为例子
- class Node
- {
- public:
- int ID;
- string name;
- Node * next;
- Node * prior;
- Node() :next(nullptr), prior(nullptr) {}
- Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}
- Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}
- void display()
- {
- cout << "ID:" << ID << endl << "Name:" << name << endl;
- }
- };
在这个类里面,要有三种构造函数,无参构造主要是为了在链表类里面创建头结点使用,第一类有参构造是为了主函数内创建节点,而第二类构造则是拷贝构造,目的在于拷贝节点信息。
创建完节点,需要做的便是将节点串联起来。串联节点,首先需要有一个起点,这个起点就是一个标志性作用,在循环链表里面,头结点既是开头也是结尾,起到关键作用
所以,在创建链表的时候,首先应该先有一个Node型的头结点
- Link::Link()
- {
- head = new Node();
- head->name = "头结点";
- head->next = head;
- head->prior = head;
- }
在没有插入任何其他节点的时候,头结点既是头节点的上一节点也是头结点的下一节点,也就是所有指针全部指向自己。当然,这个头结点是private的,不可被外界访问到。有了头节点之后,我们可以开始对这个链表进行各种操作了。
- class Link
- {
- private:
- Node * head;
- public:
- Link();
- ~Link();
- void traverse1();
- void traverse2();
- void insert(Node &n);
- void deleteByID(int id);
- void modifyByID(int id, string name);
- };
这里,我只写了顺序遍历、倒序遍历、插入、按ID关键字删除、 按关键字修改信息五个函数。首先,我们先讲插入,没有插入就没有其他节点的存在。双循环链表的插入比较复杂一点,最好可以用纸笔进行模拟,可以促进理解。
插入,有两种情况,第一种是在除了头结点外为空的链上插入,此时我们要做的是将头节点的next指向要插入的节点(下面称为本节点),将头结点的prior指向本节点,将本节点的prior指向头结点,将本节点的next指向头结点。这样就可以实现头结点与本节点之间的双向循环。至于第二种,就是在链表已经足够长的情况下插入节点,我们这里的插入只是单纯插在尾部,至于其他类型的插入,大致不会差多少,所以谨以此例子作为示范。要插在尾部,我们要先找到尾部在哪里,于是有了一根指针(tail),tail=head,当tail的下一个节点不是头结点的时候,我们就继续找下一个,一直到他指向头结点,此时tail所代表的节点必然就是最后一个节点,new 出一个新的实体,这个实体的所有信息与传入的节点的信息完全一致,也就是进行拷贝,然后使newNode得next指向头结点,将头结点的prior指向newNode,将newNode的prior指向tail(最后一个节点),将最后一个节点的next指向newNode,这样,newNode就成为新的最后一个节点,并且是支持双向循环的。
- void Link::insert(Node &n)
- {
- Node *tail = head;
- if (head->next == head)
- {
- Node *newNode = new Node(n);
- head->next = newNode;
- head->prior = newNode;
- newNode->prior = head;
- newNode->next = head;
- }
- else
- {
- while (tail->next != head)
- {
- tail = tail->next;
- }
- Node *newNode = new Node(n);
- newNode->next = head;
- head->prior = newNode;
- newNode->prior = tail;
- tail->next = newNode;
- }
- }
然后我们讲遍历,两种遍历的方法基本一致,只是顺序不一样而已,我只讲一种顺序遍历。首先创建一根副指针p,p指向头节点的下一个节点,然后将其输出出来,然后使p=p->next,实现指向再下一个节点,以循环不断做,直到p指向的是头结点才结束,这样就能够全部打印一遍。
- void Link::traverse1()
- {
- Node *p = head->next;
- while (p != head)
- {
- p->display();
- p = p->next;
- }
- cout << endl;
- }
然后是删除,按关键字删除,那么首先要做的就是找到关键字所在地方,建造两根副指针,p,q;p指向头结点的下一个节点,q指向本节点,以p查看每个节点信息,q作为他后面的节点,方便q的修改,当找到关键字后,先修改q的next,使其变成p的next,然后删除p节点,这样p所在位置的节点就被删除了,然后还需要做的是将原来p的next的节点的prior指向q,所以我们让p指向该节点,然后使他的prior指向q.这样就完成删除。(查找过程依然采用遍历查找)
- void Link::deleteByID(int id)
- {
- Node *p = head->next;
- Node *q = head;
- while (p != head)
- {
- if (p->ID == id)
- {
- q->next = p->next;
- delete(p);
- p = q->next;
- p->prior = q;
- }
- else
- {
- p = p->next;
- q = q->next;
- }
- }
- }
最后讲的是按关键字修改。当然了,按关键字修改最重要的依然是找到关键字,所以重复同样的方法,创建副指针,循环查找,当找到关键字的时候,将信息修改成传入的信息即可。
- void Link::modifyByID(int id, string name)
- {
- Node *p = head->next;
- Node *q = head->prior;
- while (p != head)
- {
- if (p->ID == id)
- {
- p->name = name;
- }
- p = p->next;
- q = q->prior;
- }
- }
最后补充说一下,查找的过程中可能遍历了整个链都找不到关键字,此时应该要报错或者有其他处理方法,我这里写的都是认为一定可以找到的,所以没有进行处理,有需要的还是要做一下处理。
那么双循环链表就讲到这里,如果有不同意见或者见解,又或者觉得我有哪里写错了,欢迎私聊我,大家一起学习一起进步,谢谢啦(QQ:2245440426)
完整代码如下
- #include<iostream>
- #include<string>
- using namespace std;
- /*######################################*/
- /* 学生类双链表 */
- /*######################################*/
- class Node
- {
- public:
- int ID;
- string name;
- Node * next;
- Node * prior;
- Node() :next(nullptr), prior(nullptr) {}
- Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}
- Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}
- void display()
- {
- cout << "ID:" << ID << endl << "Name:" << name << endl;
- }
- };
- class Link
- {
- private:
- Node * head;
- public:
- Link();
- ~Link();
- void traverse1();
- void traverse2();
- void insert(Node &n);
- void deleteByID(int id);
- void modifyByID(int id, string name);
- };
- Link::Link()
- {
- head = new Node();
- head->name = "头结点";
- head->next = head;
- head->prior = head;
- }
- Link::~Link()
- {
- while (head != nullptr)
- {
- Node *p = head->next;
- delete(head);
- head = p;
- }
- }
- void Link::traverse1()
- {
- Node *p = head->next;
- while (p != head)
- {
- p->display();
- p = p->next;
- }
- cout << endl;
- }
- void Link::traverse2()
- {
- Node *p = head->prior;
- while (p != head)
- {
- p->display();
- p = p->prior;
- }
- cout << endl;
- }
- void Link::insert(Node &n)
- {
- Node *tail = head;
- if (head->next == head)
- {
- Node *newNode = new Node(n);
- head->next = newNode;
- head->prior = newNode;
- newNode->prior = head;
- newNode->next = head;
- }
- else
- {
- while (tail->next != head)
- {
- tail = tail->next;
- }
- Node *newNode = new Node(n);
- newNode->next = head;
- head->prior = newNode;
- newNode->prior = tail;
- tail->next = newNode;
- }
- }
- void Link::deleteByID(int id)
- {
- Node *p = head->next;
- Node *q = head;
- while (p != head)
- {
- if (p->ID == id)
- {
- q->next = p->next;
- delete(p);
- p = q->next;
- p->prior = q;
- }
- else
- {
- p = p->next;
- q = q->next;
- }
- }
- }
- void Link::modifyByID(int id, string name)
- {
- Node *p = head->next;
- Node *q = head->prior;
- while (p != head)
- {
- if (p->ID == id)
- {
- p->name = name;
- }
- p = p->next;
- q = q->prior;
- }
- }
- int main()
- {
- Link link;
- Node s1(1001, "张三");
- Node s2(1002, "李四");
- Node s3(2001, "王五");
- link.insert(s1);
- link.insert(s2);
- link.insert(s3);
- link.traverse1();
- link.traverse2();
- link.deleteByID(s2.ID);
- link.traverse1();
- link.traverse2();
- link.modifyByID(s3.ID, "赵六");
- link.traverse1();
- link.traverse2();
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。