数据结构与算法笔记(四) 循环链表和双向链表
可以将线性表描述成一个单项循环链表,使链表的应用代码更加简洁和高效循环链表的结构如下图所示。
1,无头节点的循环链表:
2.有头节点的循环链表:
3.空列表:
将单向链表的头节点和尾节点连接起来,就成为了循环链表;
有头节点的循环链表和没有头节点的循环链表:
头节点是链表的一个附加节点,有了这个节点,空表就不用作为特殊情况来先处理了,使程序简化,有了头节点,每个链表至少包含一个节点。
使用头节点的循环链表可以使程序更加简洁,效率更高:
循环链表的实现如下:
#ifndef CIRCULAR_LIST_H #define CIRCULAR_LIST_H #include <iostream> #include "E:\back_up\code\c_plus_code\digui\external_file\linearlist.h" // ABC文件 // #include "E:\back_up\code\c_plus_code\digui\external_file\chain.h" /* template<typename T> struct chainNode // 链表的节点定义 { // 数据成员 T element; chainNode<T>* next; // 定义结构体构造方法 chainNode() {}; chainNode(T theElement) { this->element = theElement; } chainNode(T theElement, chainNode<T>* next) { this->element = theElement; this->next = next; } } */ template<typename T> class circularList : public linearList<T> // 有头的循环链表 { private: chainNode<T>* headerNode; int listSize; public: circularList(int capacity=10); // 构造函数 circularList(const circularList& c_list); // 拷贝构造函数 ~circularList(); // 析构函数 //抽象数据类型ADT bool empty() const; int size() const; T& get(int index) const; int indexOf(T x) const; void erase(int index); void clear(); void insert(int index, T x); void output() const; }; template<typename T> circularList<T>::circularList(int capacity) { if(capacity<1) { cout << "List size invalid "; return; // 函数结束 } headerNode = new chainNode<T>(); // 用到了chainNode的无参构造函数 headerNode->next = headerNode; listSize = 0; } template<typename T> circularList<T>::circularList(const circularList<T>& c_list) // 拷贝构造函数 { listSize = c_list.listSize; if(listSize == 0) // 源链表为空 { headerNode = new chainNode<T>(); headerNode->next = headerNode; return; } chainNode<T>* sourceNode = c_list.headerNode; sourceNode = sourceNode->next; // 指向源循环链表的第一个元素 headerNode = new chainNode<T>(); chainNode<T>* targetNode = headerNode; targetNode->next = new chainNode<T>(sourceNode->element); // target while(sourceNode != c_list.headerNode) { sourceNode = sourceNode->next; targetNode->next = new chainNode<T>(sourceNode->element); targetNode = targetNode->next; } targetNode->next = headerNode; } /* 析构函数没有写对,会遇到程序运行完后即使输出 结果正确,但是会陷入死循环,而且不会出现press any key to continue */ template<typename T> // 测试完璧 circularList<T>::~circularList() // 析构函数 { chainNode<T>* sourceNode = headerNode->next; // sourceNode指向链表的第一个元素 //sourceNode = sourceNode->next; while(sourceNode->next!=headerNode) { headerNode->next = sourceNode->next; delete sourceNode; sourceNode = headerNode->next; // sourceNode指向新的元素 //listSize--; } delete headerNode; // 删除头节点 listSize = 0; } template<typename T> // 测试完毕 bool circularList<T>::empty() const { return listSize==0; } template<typename T> // 测试完毕 int circularList<T>::size() const { return listSize; } template<typename T> // 测试完毕 T& circularList<T>::get(int index) const { /* if(index>listSize-1) // 这里抛出异常最好 { cout << "The index is invalid" << endl; return; } */ chainNode<T>* sourceNode = headerNode; // 指向链表第一个元素 for(int i=0; i<=index; i++) { sourceNode = sourceNode->next; } return sourceNode->element; } template<typename T> // 测试完毕 int circularList<T>::indexOf(T x) const // 查找对应元素的下表 { chainNode<T>* sourceNode = headerNode->next; // 指向链表第一个元素 int cnt = 0; // bool found_flag = false; // 标志位 while(sourceNode!=headerNode) { if(sourceNode->element == x) { return cnt; } sourceNode = sourceNode->next; cnt++; } return -1; } template<typename T> // 测试完毕 void circularList<T>::insert(int index, T x) { /* if(index<0 || index>listSize) // 这里可以写return,但是应该统一为抛出异常; { return; } */ // 插入这里应该判断一些情况 if(index == 0) // 在链表首个位置插入元素 { headerNode->next = new chainNode<T>(x, headerNode); } else if(index == listSize) // 在链表末尾插入元素 { chainNode<T>* sourceNode = headerNode->next; while(sourceNode->next != headerNode) { sourceNode = sourceNode->next; } // 此时sourceNOde指向最后一个元素 sourceNode->next = new chainNode<T>(x, headerNode); /* for(int i=0; i<index; i++) { sourceNode = sourceNode->next; } // sourceNode sourceNode = new chainNode<T>(x, headerNode); */ } else { chainNode<T>* sourceNode = headerNode; for(int i=0; i<index; i++) { sourceNode = sourceNode->next; } // sourceNode指向第index-1个元素 chainNode<T>* temp = sourceNode->next; sourceNode->next = new chainNode<T>(x, temp); // 插入对应的元素 } listSize++; } template<typename T> // 测试完毕 void circularList<T>::erase(int index) { /* if(index<0 || index>listSize) { cout << "The index is invalid" << endl; return 0; } */ if(index == 0) // 删除链表的头节点 { chainNode<T>* currentNode = headerNode->next; headerNode->next = currentNode->next; delete currentNode; } else if(index == listSize-1) // 删除链表的尾节点 { chainNode<T>* currentNode = headerNode; for(int i=0; i<index; i++) { currentNode = currentNode->next; } //delete currentNode->next; chainNode<T>* deleteNode = currentNode->next; currentNode->next = headerNode; delete deleteNode; } else { chainNode<T>* sourceNode = headerNode; for(int i=0; i<index; i++) { sourceNode = sourceNode->next; } // sourceNode指向链表的第index-1个元素 chainNode<T>* deleteNode = sourceNode->next; // 指向需要删除的元素 sourceNode->next = deleteNode->next; delete deleteNode; } listSize--; } template<typename T> // 测试完毕 void circularList<T>::clear() // 修改 { chainNode<T>* sourceNode = headerNode->next; while(sourceNode!=headerNode) { headerNode->next = sourceNode->next; delete sourceNode; sourceNode = headerNode->next; } listSize = 0; } template<typename T> // 测试完毕 void circularList<T>::output() const { chainNode<T>* currentNode = headerNode; /* int cnt = 0; while(currentNode!=headerNode) { cout << currentNode->element << " "; if((cnt+1)%10 == 0) { cout << endl; } currentNode = currentNode->next; } cout << endl; */ for(int i=0; i<listSize; i++) { currentNode = currentNode->next; cout << currentNode->element << " "; if((i+1)%10 == 0) { cout << endl; } } cout << endl; } #endif
上述循环链表中的放方法均以通过测试:
main.cpp
#include <iostream> #include <string> #include <time.h> #include "E:\back_up\code\c_plus_code\digui\external_file\linearlist.h" #include "E:\back_up\code\c_plus_code\digui\external_file\arraylist.h" #include "E:\back_up\code\c_plus_code\digui\external_file\chain.h" #include "E:\back_up\code\c_plus_code\digui\external_file\circularlist.h" // 循环链表 using namespace std; // 实现友元函数 int main(int argc, char *argv[]) { circularList<double> c1(10); for(int i=0; i<10; i++) { c1.insert(i, i*2); } cout << "The list size is " << c1.size() << endl; c1.output(); c1.insert(3, 4.4); c1.insert(5, 3.4); cout << "The list size is " << c1.size() << endl; c1.output(); circularList<double> c2; c2 = c1; cout << "The list size is " << c2.size() << endl; c2.output(); c2.erase(0); c2.erase(5); cout << "The list size is " << c2.size() << endl; c2.output(); /* c1.insert(3, 4.4); c1.insert(0, 1.1); cout << "The list size is " << c1.size() << endl; c1.output(); c1.erase(0); c1.erase(5); cout << "The list size is " << c1.size() << endl; c1.output(); circularList<double> c2; c2 = c1; cout << "The list size is " << c2.size() << endl; c2.output(); return 0; }
运行结果:
给循环链表添加方法:
1.在链表的末尾插入元素, push_back()
2.在链表的末尾删除元素:
template<typename T> void circularList<T>::push_back(T x) { chainNode<T>* currentNode = headerNode->next; while(currentNode->next != headerNode) { currentNode = currentNode->next; } currentNode->next = new chainNode<T>(x, headerNode); listSize++; } template<typename T> void circularList<T>::pop_back() { chainNode<T>* currentNode = headerNode; for(int i=0; i<listSize-1; i++) { currentNode = currentNode->next; } delete currentNode->next; currentNode->next = headerNode; listSize--; }
------------------------------------------------------------分割线------------------------------------------------------------
双向链表:
如果每个元素节点既有一个指向后继的指针,又有一个指向前驱的指针,就会方便应用,这样的链表叫做双向链表,其中每个节点都有两个指针,next和previous 。定义一个双向链表,它有两个数据成员,firstNode和 lastNode,分别指向链表的首节点和尾节点。
例如,对于链表中元素的查找工作,当index<listSize/2时从左到右查找,否则从右到左进行查找
双项链表的实现:
双向链表的节点定义:
template<typename T> struct doubleChainNode // 双向链表的节点定义 { T element; // 数据域 doubleChainNode<T>* previous; // 指向前去的指针 doubleChainNode<T>* next; // 指向后继的指针 // 构造函数 doubleChainNode() { } doubleChainNode(T theElement) { this->element = theElement; } doubleChainNode(T theElement, doubleChainNode<T>* thePrevious, doubleChainNode<T>* theNext) { this->element = theElement; this->previous = thePrevious; this->next = theNext; } };
双向链表类的定义:
这里的双向链表doubleChain依然作为抽象类linearList(线性表)的派生类:
#ifndef DOUBLE_CHAIN_H #define DOUBLE_CHAIN_H #include <iostream> #include <string> #include "E:\back_up\code\c_plus_code\chain\external_file\linearlist.h" // ABC文件 #include <stddef.h> template<typename T> struct doubleChainNode // 双向链表的节点定义 { T element; // 数据域 doubleChainNode<T>* previous; // 指向前去的指针 doubleChainNode<T>* next; // 指向后继的指针 // 构造函数 doubleChainNode() { } doubleChainNode(T theElement) { this->element = theElement; } doubleChainNode(T theElement, doubleChainNode<T>* thePrevious, doubleChainNode<T>* theNext) { this->element = theElement; this->previous = thePrevious; this->next = theNext; } }; // 定义模板类 template<typename T> class doubleChain : public linearList<T> { private: doubleChainNode<T>* firstNode; // 指向链表的首节点 doubleChainNode<T>* lastNode; // 指向链表的尾节点 int listSize; public: doubleChain(int capacity=10); doubleChain(const doubleChain& d_chain); ~doubleChain(); // 析构函数 // ADT abstract data type bool empty() const; int size() const; T& get(int index) const; int indexOf(T x) const; void erase(int index); void clear(); void insert(int index, T x); void push_back(T x); // 在末尾插入元素 void pop_back(); // 在末尾删除元素 void output() const; }; template<typename T> doubleChain<T>::doubleChain(int capacity) { if(capacity<1) { cout << "The capacity is invalid" << endl; return; } firstNode = NULL; lastNode = NULL; listSize = 0; } template<typename T> doubleChain<T>::doubleChain(const doubleChain<T>& d_chain) { listSize = d_chain.listSize; if(listSize==0) // 复制空链表 { firstNode = NULL; lastNode = NULL; } else // 非空链表 { doubleChainNode<T>* sourceNode = d_chain.firstNode; firstNode = new doubleChainNode<T>(sourceNode->element, NULL, NULL); doubleChainNode<T>* targetNode = firstNode; while(sourceNode != NULL) { sourceNode = sourceNode->next; // sourcexuNode向后移动 targetNode->next = new doubleChainNode<T>(sourceNode->element, targetNode, NULL); targetNode = targetNode->next; lastNode = targetNode; } //lastNode = targetNode; } } template<typename T> doubleChain<T>::~doubleChain() // 析构函数 { doubleChainNode<T>* currentNode = firstNode; while(currentNode != NULL) { firstNode = currentNode->next; delete currentNode; currentNode = firstNode; } //delete currentNode; //listSize = 0; } template<typename T> bool doubleChain<T>::empty() const { return listSize==0; } template<typename T> int doubleChain<T>::size() const { return listSize; } template<typename T> T& doubleChain<T>::get(int index) const { // 判断index的合法性 // 双向链表的索引 if(index<listSize/2) // 从前向后找 { doubleChainNode<T>* currentNode = firstNode; int cnt=0; while(currentNode != NULL) { if(cnt == index) { return currentNode->element; } currentNode = currentNode->next; cnt++; } } else // 从后向前找 { doubleChainNode<T>* currentNode = lastNode; int cnt = 0; while(currentNode != NULL) { if(cnt == listSize-index-1) { return currentNode->element; } currentNode = currentNode->previous; cnt++; } } } template<typename T> int doubleChain<T>::indexOf(T x) const { doubleChainNode<T>* currentNode = firstNode; int cnt = 0; while(currentNode != NULL) // 从头找到尾,包含最后一个元素 { if(currentNode->element == x) { return cnt; } currentNode = currentNode->next; cnt++; } return -1; } template<typename T> void doubleChain<T>::insert(int index, T x) { if(index==0) // 在链表的头部插入元素 { if(listSize==0) { //doubleChainNode<T>* tempNode = firstNode->next; firstNode = new doubleChainNode<T>(x, NULL, NULL); // 第一个节点 lastNode = firstNode; } else { firstNode = new doubleChainNode<T>(x, NULL, firstNode); } } else if(index == listSize) // 最后一个位置 { //oubleChainNode<T>* tempNode = lastNode->previous; //lastNode = new doubleChainNode<T>(x, lastNode, NULL); lastNode->next = new doubleChainNode<T>(x, lastNode, NULL); lastNode = lastNode->next; } else { doubleChainNode<T>* currentNode = firstNode; for(int i=0; i<index; i++) { currentNode = currentNode->next; } // currentNode指向第index个节点 doubleChainNode<T>* former = currentNode->previous; former->next = new doubleChainNode<T>(x, former, currentNode); } listSize++; } template<typename T> void doubleChain<T>::erase(int index) // 测试通过 { // 检查index的合法性 if(index == 0) // 删除首个元素 { doubleChainNode<T>* currentNode = firstNode; firstNode = firstNode->next; firstNode->previous = NULL; delete currentNode; } else if(index==listSize-1) { doubleChainNode<T>* currentNode = lastNode; lastNode = lastNode->previous; lastNode->next = NULL; delete currentNode; } else { if(index<=listSize/2) // 从左至右查找 { doubleChainNode<T>* currentNode = firstNode; // int cnt=0; for(int i=0; i<index; i++) { currentNode = currentNode->next; } // currentNode指向第index个节点 doubleChainNode<T>* former = currentNode->previous; doubleChainNode<T>* latter = currentNode->next; former->next = latter; latter->previous = former; delete currentNode; } else { doubleChainNode<T>* currentNode = lastNode; // int cnt=0; for(int i=0; i<listSize-index-1; i++) { currentNode = currentNode->previous; } // currentNode指向第index个节点 doubleChainNode<T>* former = currentNode->previous; doubleChainNode<T>* latter = currentNode->next; former->next = latter; latter->previous = former; delete currentNode; } } listSize--; } template<typename T> void doubleChain<T>::clear() // 清除链表所有元素 { doubleChainNode<T>* currentNode = firstNode; while(currentNode != NULL) { firstNode = currentNode->next; delete currentNode; currentNode = firstNode; } listSize = 0; firstNode = NULL; lastNode = NULL; } template<typename T> void doubleChain<T>::push_back(T x) // 链表的右端插入一个元素 { if(listSize==0) // 空链表 { firstNode = new doubleChainNode<T>(x, NULL, NULL); lastNode = firstNode; listSize++; } else // 非空链表 { lastNode->next = new doubleChainNode<T>(x, lastNode, NULL); lastNode = lastNode->next; listSize++; } } template<typename T> void doubleChain<T>::pop_back() // 删除链表最右端的元素 { if(listSize==1) // 链表中仅有一个元素 { delete firstNode; firstNode = NULL; lastNode = NULL; listSize = 0; } else // 大于一个元素 { doubleChainNode<T>* currentNode = lastNode; lastNode = lastNode->previous; lastNode->next = NULL; delete currentNode; listSize--; } } template<typename T> void doubleChain<T>::output() const // 输出函数 { doubleChainNode<T>* currentNode = firstNode; for(int i=0; i<listSize; i++) { cout << currentNode->element << " "; if((i+1)%10==0) { //cout << endl; } currentNode = currentNode->next; } cout << endl; } #endif
双向链表的测试:
#include <iostream> #include <string> #include <time.h> #include "E:\back_up\code\c_plus_code\chain\external_file\linearlist.h" #include "E:\back_up\code\c_plus_code\chain\external_file\arraylist.h" #include "E:\back_up\code\c_plus_code\chain\external_file\chain.h" #include "E:\back_up\code\c_plus_code\chain\external_file\circularlist.h" // 循环链表 #include "E:\back_up\code\c_plus_code\chain\external_file\doublechain.h" // 双向链表 using namespace std; // 实现友元函数 int main(int argc, char *argv[]) { doubleChain<double> d_chain1; // 测试insert函数: cout << "--------------Insert test----------" << endl; for(int i=0; i<10; i++) { d_chain1.insert(i, i+1); } d_chain1.output(); d_chain1.insert(2, 1.1); d_chain1.output(); d_chain1.insert(7, 2.7); d_chain1.output(); cout << "----------------------------------" << endl; cout << "--------------erase test----------" << endl; cout << "The list size is " << d_chain1.size() << endl; d_chain1.erase(4); d_chain1.output(); cout << "-----------------------------------" << endl; cout << "---------------index test----------------" << endl; double find_m = 3; cout << "The index of " << find_m << " is " << d_chain1.indexOf(find_m) << endl; double find_n = 8; cout << "The index of " << find_n << " is " << d_chain1.indexOf(find_n) << endl; int index_test = 3; cout << "The index " << index_test << " is " << d_chain1.get(index_test) << endl; index_test = 5; cout << "The index " << index_test << " is " << d_chain1.get(index_test) << endl; cout << "-------------push_pop_back---------------------" << endl; d_chain1.push_back(5.20); d_chain1.output(); d_chain1.pop_back(); d_chain1.output(); cout << "-----------------------------------" << endl; cout << "Clearing the list....." << endl; d_chain1.clear(); cout << "The list is empty? " << d_chain1.empty() << endl; cout << "------Push_back and pop_back an element----------" << endl; d_chain1.push_back(5.20); d_chain1.output(); d_chain1.pop_back(); d_chain1.output(); return 0; }
测试结果:
拷贝构造函数的测试:
#include <iostream> #include <string> #include <time.h> #include "E:\back_up\code\c_plus_code\chain\external_file\linearlist.h" #include "E:\back_up\code\c_plus_code\chain\external_file\arraylist.h" #include "E:\back_up\code\c_plus_code\chain\external_file\chain.h" #include "E:\back_up\code\c_plus_code\chain\external_file\circularlist.h" // 循环链表 #include "E:\back_up\code\c_plus_code\chain\external_file\doublechain.h" // 双向链表 using namespace std; // 实现友元函数 int main(int argc, char *argv[]) { doubleChain<double> d_chain1; // 测试insert函数: cout << "--------------Insert test----------" << endl; for(int i=0; i<10; i++) { d_chain1.insert(i, i+1); } d_chain1.output(); cout << "---------------Copy constructor test---------------"<< endl; doubleChain<double> d_chain2; d_chain2 = d_chain1; d_chain2.output(); cout << "--------------Insert test----------" << endl; d_chain2.insert(2, 1.1); d_chain2.output(); d_chain2.insert(7, 2.7); d_chain2.output(); cout << "----------------------------------" << endl; cout << "--------------erase test----------" << endl; cout << "The list size is " << d_chain2.size() << endl; d_chain2.erase(4); d_chain2.output(); cout << "-----------------------------------" << endl; cout << "---------------index test----------------" << endl; double find_m = 3; cout << "The index of " << find_m << " is " << d_chain2.indexOf(find_m) << endl; double find_n = 8; cout << "The index of " << find_n << " is " << d_chain2.indexOf(find_n) << endl; int index_test = 3; cout << "The index " << index_test << " is " << d_chain2.get(index_test) << endl; index_test = 8; cout << "The index " << index_test << " is " << d_chain2.get(index_test) << endl; cout << "-------------push_pop_back---------------------" << endl; d_chain2.push_back(5.20); d_chain2.output(); d_chain2.pop_back(); d_chain2.output(); cout << "Clearing the list...." << endl; d_chain2.clear(); cout << "The list is empty? " << d_chain2.empty() << endl; return 0; }
测试结果:
----------------------------------------------------------end------------------------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)