数据结构第五篇——线性表的链式存储之循环链表
♥注:未经博主同意,不得转载。
链表是另一种形式的链式存储结构,它是线性链表的一种变形。在线性链表中,每个结点的指针都指向它的下一个结点,最后一个结点的指针域为空,表示链表的结束。若使最后一个结点的指针指向头结点,则链表呈环状,这样的链表称为循环链表。
循环链表当然也分为单链表和双向列表。
通常情况下,在循环链表中设立尾指针而不设头指针,可使某些操作简化。
循环链表的定义和操作与单链表类似,只是循环结束条件有所不同,下面只给出单循环链表的定义和部分操作,至于双向循环链表可能会在以后的项目中出现,到时再进一步探讨。
单循环链表定义如下:
1 typedef int Data; 2 3 struct Node 4 { 5 Data data; //数据 6 Node* next; //指向后继的指针 7 }; 8 9 class CList 10 { 11 Node* head; //创建头结点 12 public: 13 CList() //构造函数 14 { 15 head = new Node; 16 head->next = head; //头尾相接 17 } 18 ~CList(); //析构函数 19 20 Data GetElem(int i); //取第i个元素的值 21 bool IsEmpty(); //判断是否为空链表 22 void Create(Data* a , int n); //创建长度为n的循环单链表(头插法) 23 void Create1(Data* a , int n); //创建长度为n的循环单链表(尾插法) 24 Node* Locate(Data e,int* i); //查找值为e的结点,返回指向e的指针 25 void Insert(Data x,int i); //将数据元素x插入到第i个位置 26 Data Delete(int i); //删除第i个元素 27 //int _Delete(Data e); //删除值为e的第一个元素 28 int Size(); //返回链表的长度 29 void Clear(); //清空 30 void Print(); //显示元素 31 };
函数功能实现如下:
1 //计算链表长度 2 int CList::Size() 3 { 4 Node* p; //创建指针p 5 int k; 6 p=head->next; //p指向第一个元素结点 7 k=0; 8 while(p!=head) 9 { 10 p=p->next; 11 ++k; 12 } 13 return k; 14 } 15 16 //显示所有元素 17 void CList::Print() 18 { 19 Node* p; //创建指针p 20 p=head->next; //p指向第一个元素结点 21 while(p!=head) 22 { 23 cout<<p->data<<" "; 24 p=p->next; 25 } 26 cout<<endl; 27 } 28 29 //取元素 30 Data CList::GetElem(int i) 31 { 32 if(head->next == NULL) //为空链表 33 { 34 cout<<"此链表为空"<<endl; 35 exit(0); 36 } 37 else 38 { 39 Node* p; //创建指针p 40 int k; 41 p=head; //p指向头结点 42 k=0; 43 while(p&&k<i) //p移到i的位置 44 { 45 p=p->next; 46 k++; 47 } 48 return (p->data); 49 } 50 } //此算法的时间复杂度为O(n) 51 52 //插入操作 53 void CList::Insert(Data x,int i) 54 { 55 Node* p=head; 56 int k=0; 57 while(p&&k<i-1) // 将p指到第i个位置 58 { 59 p=p->next; 60 ++k; 61 62 if(p==head) 63 p=p->next; 64 } 65 Node* s = new Node; //创建此结点 66 if(!s) 67 { 68 cout<<"空间分配失败"<<endl; 69 exit(0); 70 } 71 72 s->data=x; //将元素存入创建的结点 73 s->next=p->next; 74 p->next=s; 75 } 76 77 //删除操作 78 Data CList::Delete(int i) 79 { 80 Node* p = head; 81 int k=0; 82 while(p&&k<i-1) //将p指到要删除的位置 83 { 84 p=p->next; 85 ++k; 86 87 if(p==head) 88 p=p->next; 89 } 90 Node* q = p->next; //暂存删除结点 91 92 p->next = q->next; //将结点隔离出来 93 Data e=q->data; //将删除的元素储存起来 94 delete q; //释放将要删除的结点 95 return e; 96 } 97 98 //判断链表是否为空 99 bool CList::IsEmpty() 100 { 101 if(head->next==NULL) 102 { 103 cout<<"此链表为空"<<endl; 104 return true; 105 } 106 else 107 { 108 cout<<"此链表非空"<<endl; 109 return false; 110 } 111 } 112 113 //建立单循环链表 114 //第一种是头插法 115 void CList::Create(Data* a,int n) 116 { 117 Node* p=NULL; 118 Node* q=NULL; 119 for(int i=n-1;i>=0;--i) 120 { 121 p=new Node; //创建新结点 122 p->data=a[i]; //将元素存入结点 123 124 p->next=head->next; //将新加入结点指向头结点后面 125 head->next=p; //将头结点指向新加入的结点 126 } 127 } 128 129 //第二种是尾插法 130 void CList::Create1(Data* a,int n) 131 { 132 Node* p=NULL; 133 Node* q=head; //创建中间结点指针 134 for(int i=0;i<n;++i) 135 { 136 p=new Node; //创建储存元素的新结点 137 p->data=a[i]; //将元素存入创建的结点 138 p->next=q->next; //插入到终端结点之后 139 q->next=p; //终端结点指向新建结点 140 q=p; //q指向新建结点 141 } 142 p->next=NULL; 143 } 144 145 //查找给定值的结点 146 Node* CList::Locate(Data e,int *i) 147 { 148 *i=1; 149 Node* p=head->next; 150 151 while(p!=head) //p不为空 152 { 153 if(p->data==e) //找到元素 154 return p; 155 else 156 { 157 p=p->next; 158 ++(*i); 159 } 160 } 161 cout<<"当前链表中无此元素"<<endl; 162 exit(0); 163 return NULL; 164 } 165 166 //清空单循环链表 167 //保留表头结点,把链表中的 168 //其余所有结点全部释放。 169 void CList::Clear() 170 { 171 Node* p=NULL; 172 Node* q=NULL; 173 p=head->next; 174 while(p!=head) 175 { 176 q=p; 177 p=p->next; 178 delete q; 179 } 180 head->next = NULL; 181 } 182 183 //析构函数 184 //释放链表中的所有元素。 185 CList::~CList() 186 { 187 Node* p; 188 p=head; 189 while(p!=head) 190 { 191 p=p->next; 192 delete head; 193 head=p; 194 } 195 }
测试程序则放在main函数里:
1 int main() 2 { 3 int i=0; 4 Data e; 5 int a[8]={2,4,6,8,5,1,7,9}; 6 7 CList list; //创建链表类 8 list.IsEmpty(); //判断链表是否为空 9 list.Create(a,8); //将数据插入 10 list.Print(); //显示 11 cout<<"链表长度:"<<list.Size()<<endl; 12 13 cout<<"输入要插入的元素和位置:"; 14 cin>>e>>i; 15 list.Insert(e,i); //插入数据 16 list.Print(); 17 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 18 19 cout<<"输入要查找的元素值:"; 20 cin>>e; 21 list.Locate(e,&i); //查找某元素 22 cout<<"这是第"<<i<<"个元素"<<endl<<endl; 23 24 list.IsEmpty(); //判断链表是否为空 25 26 cout<<"输入要查找的元素位置:"; 27 cin>>i; 28 e=list.GetElem(i); //查找第i个位置的元素 29 cout<<"这个元素值为:"<<e<<endl<<endl; 30 31 list.IsEmpty(); //判断链表是否为空 32 33 cout<<"输入要删除的元素位置:"; 34 cin>>i; 35 e=list.Delete(i); //删除第i个位置的元素 36 cout<<"这个元素值为:"<<e<<endl; 37 list.Print(); 38 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 39 40 cout<<"输入要删除的元素位置:"; 41 cin>>i; 42 e=list.Delete(i); //删除第i个位置的元素 43 cout<<"这个元素值为:"<<e<<endl; 44 list.Print(); 45 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 46 47 list.Clear(); 48 list.IsEmpty(); //判断链表是否为空 49 50 return 0; 51 }
测试情况如下:
到此,线性表的几种基本实现已经结束了,下方可查看几种线性表的具体实现:
1.顺序存储:http://www.cnblogs.com/tenjl-exv/p/7469316.html
2.单链表:http://www.cnblogs.com/tenjl-exv/p/7470075.html
3.双向链表:http://www.cnblogs.com/tenjl-exv/p/7475518.html
这里说一下循环链表的优点:
① 从表中任意结点出发均可访问到表中其他结点,这使得某些操作在循环链表上容易实现。
② 插入删除操作中不需区分尾节点还是中间结点,以使操作简化