双向循环链表
[转载整理]C语言链表实例
[转载整理]C语⾔链表实例 C语⾔链表有单链表、双向链表、循环链表。
单链表由数据域和指针域组成,数据域存放数据,指针域存放该数据类型的指针便于找到下⼀个节点。
双链表则含有头指针域、数据域和尾指针域,域单链表不同,双链表可以从后⼀个节点找到前⼀个节点,⼆单链表则不⾏。
循环链表就是在单链表的基础上,将头结点的地址指针存放在最后⼀个节点的指针域⾥以,此形成循环。
此外还有双向循环链表,它同时具有双向链表和循环链表的功能。
单链表如:链表节点的数据结构定义struct node{int num;struct node *p;} ;在此链表节点的定义中,除⼀个整型的成员外,成员p是指向与节点类型完全相同的指针。
※在链表节点的数据结构中,⾮常特殊的⼀点就是结构体内的指针域的数据类型使⽤了未定义成功的数据类型。
这是在C中唯⼀规定可以先使⽤后定义的数据结构。
链表实例代码:1// 原⽂地址 /wireless-dragon/p/5170565.html2 #include<stdio.h>3 #include<stdlib.h>4 #include<string.h>56 typedef int elemType;//定义存⼊的数据的类型可以是int char78 typedef struct NODE{ //定义链表的结构类型9 elemType element;10struct NODE *next;11 }Node;1213/************************************************************************/14/* 以下是关于线性表链接存储(单链表)操作的19种算法 */1516/* 1.初始化线性表,即置单链表的表头指针为空 */17/* 2.创建线性表,此函数输⼊负数终⽌读取数据*/18/* 3.打印链表,链表的遍历*/19/* 4.清除线性表L中的所有元素,即释放单链表L中所有的结点,使之成为⼀个空表 */20/* 5.返回单链表的长度 */21/* 6.检查单链表是否为空,若为空则返回1,否则返回0 */22/* 7.返回单链表中第pos个结点中的元素,若pos超出范围,则停⽌程序运⾏ */23/* 8.从单链表中查找具有给定值x的第⼀个元素,若查找成功则返回该结点data域的存储地址,否则返回NULL */24/* 9.把单链表中第pos个结点的值修改为x的值,若修改成功返回1,否则返回0 */25/* 10.向单链表的表头插⼊⼀个元素 */26/* 11.向单链表的末尾添加⼀个元素 */27/* 12.向单链表中第pos个结点位置插⼊元素为x的结点,若插⼊成功返回1,否则返回0 */28/* 13.向有序单链表中插⼊元素x结点,使得插⼊后仍然有序 */29/* 14.从单链表中删除表头结点,并把该结点的值返回,若删除失败则停⽌程序运⾏ */30/* 15.从单链表中删除表尾结点并返回它的值,若删除失败则停⽌程序运⾏ */31/* 16.从单链表中删除第pos个结点并返回它的值,若删除失败则停⽌程序运⾏ */32/* 17.从单链表中删除值为x的第⼀个结点,若删除成功则返回1,否则返回0 */33/* 18.交换2个元素的位置 */34/* 19.将线性表进⾏冒排序 */35363738/*注意检查分配到的动态内存是否为空*/3940414243/* 1.初始化线性表,即置单链表的表头指针为空 */44void initList(Node **pNode)45 {46 *pNode=NULL;47 printf("initList函数执⾏,初始化成功\n");48 }4950/* 2.创建线性表,此函数输⼊负数终⽌读取数据*/51 Node *creatList(Node *pHead)52 {53 Node *p1,*p2;54 p1=p2=(Node *)malloc(sizeof(Node));55if(p1 == NULL || p2 ==NULL)57 printf("内存分配失败\n");58 exit(0);59 }60 memset(p1,0,sizeof(Node));6162 scanf("%d",&p1->element);63 p1->next=NULL;6465while(p1->element >0) //输⼊的值⼤于0则继续,否则停⽌66 {67if(pHead == NULL)//空表,接⼊表头68 {69 pHead=p1;70 }71else72 {73 p2->next=p1;74 }7576 p2=p1;77 p1=(Node *)malloc(sizeof(Node));7879if(p1==NULL||p2==NULL)80 {81 printf("内存分配失败\n");82 exit(0);83 }84 memset(p1,0,sizeof(Node));85 scanf("%d",&p1->element);86 p1->next=NULL;87 }88 printf("CreatList函数执⾏,链表创建成功\n");89return pHead;90 }9192/* 3.打印链表,链表的遍历*/93void printList(Node *pHead)94 {95if(NULL==pHead)96 {97 printf("PrintList函数执⾏,链表为空\n");98 }99else100 {101while(NULL!=pHead)102 {103 printf("%d\n",pHead->element);104 pHead=pHead->next;105 }106 }107108 }109110111/* 4.清除线性表L中的所有元素,即释放单链表L中所有的结点,使之成为⼀个空表 */ 112void clearList(Node *pHead)113 {114 Node *pNext;115116if(pHead==NULL)117 {118 printf("clearList函数执⾏,链表为空\n");119return;120 }121while(pHead->next!=NULL)122 {123 pNext=pHead->next;124free(pHead);125 pHead=pNext;126 }127 printf("clearList函数执⾏,链表已经清除!\n");128129 }130131/* 5.返回链表的长度*/132int sizeList(Node *pHead)133 {134int size=0;135136while(pHead!=NULL)137 {138 size++;139 pHead=pHead->next;141 printf("sizelist函数执⾏,链表长度为%d\n",size);142return size;143 }144145/* 6.检查单链表是否为空,若为空则返回1,否则返回0 */146int isEmptyList(Node *pHead)147 {148if(pHead==NULL)149 {150 printf("isEmptylist函数执⾏,链表为空!\n");151return1;152 }153154else155 printf("isEmptylist函数执⾏,链表⾮空!\n");156return0;157158 }159160/* 7.返回链表中第post节点的数据,若post超出范围,则停⽌程序运⾏*/161int getElement(Node *pHead,int pos)162 {163int i=0;164if(pos<1)165 {166 printf("getElement函数执⾏,pos值⾮法!");167return0;168 }169if(pHead==NULL)170 {171 printf("getElement函数执⾏,链表为空!");172 }173174while (pHead!=NULL)175 {176 ++i;177if(i==pos)178 {179break;180 }181 pHead=pHead->next;182 }183if(i<pos)184 {185 printf("getElement函数执⾏,pos值超出链表长度\n");186return0;187 }188 printf("getElement函数执⾏,位置%d中的元素为%d\n",pos,pHead->element);189190return1;191 }192193//8.从单⼀链表中查找具有给定值x的第⼀个元素,若查找成功后,返回该节点data域的存储位置,否则返回NULL 194 elemType *getElemAddr(Node *pHead,elemType x)195 {196if(NULL==pHead)197 {198 printf("getEleAddr函数执⾏,链表为空");199return NULL;200 }201if(x<0)202 {203 printf("getEleAddr函数执⾏,给定值x不合法\n");204return NULL;205 }206while((pHead->element!=x)&&(NULL!=pHead->next))//判断链表是否为空,并且是否存在所查找的元素207 {208 pHead=pHead->next;209 }210if(pHead->element!=x)211 {212 printf("getElemAddr函数执⾏,在链表中没有找到x值\n");213return NULL;214 }215else216 {217 printf("getElemAddr函数执⾏,元素%d的地址为0x%x\n",x,&(pHead->element));218 }219return &(pHead->element);220221 }222223224/*9.修改链表中第pos个点X的值,如果修改成功,则返回1,否则返回0*/225int modifyElem(Node *pNode,int pos,elemType x)226 {227 Node *pHead;228 pHead=pNode;229int i=0;230if(NULL==pHead)231 {232 printf("modifyElem函数执⾏,链表为空\n");233return0;234 }235236if(pos<1)237 {238 printf("modifyElem函数执⾏,pos值⾮法\n");239return0;240 }241242while(pHead!= NULL)243 {244 ++i;245if(i==pos)246 {247break;248 }249 pHead=pHead->next;250 }251252if(i<pos)253 {254 printf("modifyElem函数执⾏,pos值超出链表长度\n");255return0;256 }257 pNode=pHead;258 pNode->element=x;259 printf("modifyElem函数执⾏,修改第%d点的元素为%d\n",pos,x);260261return1;262263 }264265/* 10.向单链表的表头插⼊⼀个元素 */266int insertHeadList(Node **pNode,elemType insertElem)267 {268 Node *pInsert;269 pInsert=(Node *)malloc(sizeof(Node));270if(pInsert==NULL) exit(1);271 memset(pInsert,0,sizeof(Node));272 pInsert->element=insertElem;273 pInsert->next=*pNode;274 *pNode=pInsert;275 printf("insertHeadList函数执⾏,向表头插⼊元素%d成功\n",insertElem);276return1;277 }278279/* 11.向单链表的末尾添加⼀个元素 */280int insertLastList(Node *pNode,elemType insertElem)281 {282 Node *pInsert;283 Node *pHead;284 Node *pTmp;285286 pHead=pNode;287 pTmp=pHead;288 pInsert=(Node *)malloc(sizeof(Node));289if(pInsert==NULL) exit(1);290 memset(pInsert,0,sizeof(Node));291 pInsert->element=insertElem;292 pInsert->next=NULL;293while(pHead->next!=NULL)294 {295 pHead=pHead->next;296 }297 pHead->next=pInsert;298 printf("insertLastList函数执⾏,向表尾插⼊元素%d成功!\n",insertElem);299return1;300 }301302/* 12.向单链表中第pos个结点位置插⼊元素为x的结点,若插⼊成功返回1,否则返回0*/ 303int isAddPos(Node *pNode,int pos,elemType x)304 {305 Node *pHead;306 pHead=pNode;307 Node *pTmp;308int i=0;309310if(NULL==pHead)311 {312 printf("AddPos函数执⾏,链表为空\n");313return0;314 }315316if(pos<1)317 {318 printf("AddPos函数执⾏,pos值⾮法\n");319return0;320 }321322while(pHead!=NULL)323 {324 ++i;325if(i==pos)326break;327 pHead=pHead->next;328 }329330if(i<pos)331 {332 printf("AddPos函数执⾏,pos值超出链表长度\n");333return0;334 }335336 pTmp=(Node *)malloc(sizeof(Node));337if(pTmp==NULL) exit(1);338 memset(pTmp,0,sizeof(Node));339 pTmp->next=pHead->next;340 pHead->next=pTmp;341 pTmp->element=x;342343 printf("AddPos函数执⾏成功,向节点%d后插⼊数值%d\n",pos,x); 344return1;345 }346347/* 13.向有序单链表中插⼊元素x结点,使得插⼊后仍然有序 */348int OrrderList(Node *pNode,elemType x)349 {350//注意如果此数值要排到⾏尾要修改本代码351 Node *pHead;352 pHead=pNode;353 Node *pTmp;354355if(NULL==pHead)356 {357 printf("OrrderList函数执⾏,链表为空\n");358return0;359 }360361if(x<1)362 {363 printf("OrrderList函数执⾏,x值⾮法\n");364return0;365 }366367while(pHead!=NULL)368 {369if((pHead->element)>=x)370break;371 pHead=pHead->next;372 }373374375if(pHead==NULL)376 {377 printf("OrrderList函数查找完毕,该函数中没有该值\n");378return0;379 }380381382 pTmp=(Node *)malloc(sizeof(Node));383if(pTmp==NULL) exit(1);384 memset(pTmp,0,sizeof(Node));385 pTmp->next=pHead->next;386 pHead->next=pTmp;387 pTmp->element=x;388389 printf("OrrderList函数成功插⼊数值%d\n",x);390return1;391 }392393/*14.从单链表中删除表头结点,并把该结点的值返回,若删除失败则停⽌程序运⾏*/ 394int DelHeadList(Node **pList)395 {396 Node *pHead;397 pHead=*pList;398if(pHead!=NULL)399 printf("DelHeadList函数执⾏,函数⾸元素为%d删除成功\n",pHead->element); 400else401 {402 printf("DelHeadList函数执⾏,链表为空!");403return0;404 }405 *pList=pHead->next;406return1;407 }408409/* 15.从单链表中删除表尾结点并返回它的值,若删除失败则停⽌程序运⾏ */410int DelLastList(Node *pNode)411 {412 Node *pHead;413 Node *pTmp;414415 pHead=pNode;416while(pHead->next!=NULL)417 {418 pTmp=pHead;419 pHead=pHead->next;420 }421 printf("链表尾删除元素%d成功!\n",pHead->element);422free(pHead);423 pTmp->next=NULL;424return1;425 }426427/* 16.从单链表中删除第pos个结点并返回它的值,若删除失败则停⽌程序运⾏ */ 428int DelPos(Node *pNode,int pos)429 {430 Node *pHead;431 pHead=pNode;432 Node *pTmp;433434int i=0;435436if(NULL==pHead)437 {438 printf("DelPos函数执⾏,链表为空\n");439return0;440 }441442if(pos<1)443 {444 printf("DelPos函数执⾏,pos值⾮法\n");445return0;446 }447448while(pHead!=NULL)449 {450 ++i;451if(i==pos)452break;453 pTmp=pHead;454 pHead=pHead->next;455 }456457if(i<pos)458 {459 printf("DelPos函数执⾏,pos值超出链表长度\n");460return0;461 }462 printf("DelPos函数执⾏成功,节点%d删除数值%d\n",pos,pHead->element); 463 pTmp->next=pHead->next;464free(pHead);465return1;466 }467468/* 17.从单链表中删除值为x的第⼀个结点,若删除成功则返回1,否则返回0 */469int Delx(Node **pNode,int x)470 {471 Node *pHead;472 Node *pTmp;473 pHead=*pNode;474int i=0;475476if(NULL==pHead)477 {478 printf("Delx函数执⾏,链表为空");479return0;480 }481if(x<0)482 {483 printf("Delx函数执⾏,给定值x不合法\n");484return0;485 }486while((pHead->element!=x)&&(NULL!=pHead->next))//判断链表是否为空,并且是否存在所查找的元素487 {488 ++i;489 pTmp=pHead;490 pHead=pHead->next;491 }492if(pHead->element!=x)493 {494 printf("Delx函数执⾏,在链表中没有找到x值\n");495return0;496 }497if((i==0)&&(NULL!=pHead->next))498 {499 printf("Delx函数执⾏,在链表⾸部找到此元素,此元素已经被删除\n");500 *pNode=pHead->next;501free(pHead);502return1;503 }504 printf("Delx函数执⾏,⾸个为%d元素被删除\n",x);505 pTmp->next=pHead->next;506free(pHead);507return1;508 }509510/* 18.交换2个元素的位置 */511int exchange2pos(Node *pNode,int pos1,int pos2)512 {513 Node *pHead;514int *pTmp;515int *pInsert;516int a;517int i=0;518519if(pos1<1||pos2<1)520 {521 printf("DelPos函数执⾏,pos值⾮法\n");522return0;523 }524525 pHead=pNode;526while(pHead!=NULL)527 {528 ++i;529if(i==pos1)530break;531 pHead=pHead->next;532 }533534if(i<pos1)535 {536 printf("DelPos函数执⾏,pos1值超出链表长度\n");537return0;538 }539540 pTmp=&(pHead->element);541 i=0;542 pHead=pNode;543while(pHead!=NULL)544 {545 ++i;546if(i==pos2)547break;548 pHead=pHead->next;549 }550551if(i<pos2)552 {553 printf("DelPos函数执⾏,pos2值超出链表长度\n");554return0;555 }556557 pInsert=&(pHead->element);558 a=*pTmp;559 *pTmp=*pInsert;560 *pInsert=a;561562 printf("DelPos函数执⾏,交换第%d个和第%d个pos点的值\n",pos1,pos2); 563return1;564 }565566int swap(int *p1,int *p2)567 {568int a;569if(*p1>*p2)570 {571 a=*p1;572 *p1=*p2;573 *p2=a;574 }575return0;576 }577578/* 19.将线性表进⾏冒泡排序 */579int Arrange(Node *pNode)580 {581 Node *pHead;582 pHead=pNode;583584int a=0,i,j;585586if(NULL==pHead)587 {588 printf("Arrange函数执⾏,链表为空\n");589return0;590 }591592while(pHead!=NULL)593 {594 ++a;595 pHead=pHead->next;596 }597598 pHead=pNode;599for(i=0;i<a-1;i++)600 {601for(j=1;j<a-i;j++)602 {603 swap(&(pHead->element),&(pHead->next->element));604 pHead=pHead->next;605 }606 pHead=pNode;607 }608 printf("Arrange函数执⾏,链表排序完毕!\n");609return0;610 }611612int main()613 {614 Node *pList=NULL;615int length=0;616617 elemType posElem;618619 initList(&pList);620 printList(pList);621622 pList=creatList(pList);623 printList(pList);624625 sizeList(pList);626 printList(pList);627628 isEmptyList(pList);629630631 posElem=getElement(pList,3);632 printList(pList);633634 getElemAddr(pList,5);635636 modifyElem(pList,4,1);637 printList(pList);638639 insertHeadList(&pList,5);640 printList(pList);641642 insertLastList(pList,10);643 printList(pList);644645 isAddPos(pList,4,5); 646 printList(pList);647648 OrrderList(pList,6);649 printList(pList);650651 DelHeadList(&pList); 652 printList(pList);653654 DelLastList(pList);655 printList(pList);656657 DelPos(pList,5);658 printList(pList);659660 Delx(&pList,5);661 printList(pList);662663 exchange2pos(pList,2,5); 664 printList(pList);665666 Arrange(pList);667 printList(pList);668669 clearList(pList);670return0;671 }。
循环双链表特点
循环双链表特点循环双链表是一种特殊的数据结构,它具有循环和双向链表的特点。
循环双链表中的每个节点都包含两个指针,一个指向前一个节点,一个指向后一个节点。
最后一个节点的后指针指向头节点,头节点的前指针指向最后一个节点,从而形成了一个闭环。
循环双链表的特点如下:1. 双向性:每个节点都有两个指针,分别指向前一个节点和后一个节点。
这样可以方便地在任意位置插入或删除节点,而不需要像单链表那样需要遍历找到前驱节点。
2. 循环性:循环双链表是一个闭环,即最后一个节点的后指针指向头节点,头节点的前指针指向最后一个节点。
这样可以方便地进行循环遍历,不需要判断是否到达了链表的末尾。
3. 动态性:循环双链表可以动态地增加或删除节点,而不需要预先指定链表的长度。
4. 灵活性:循环双链表可以在任意位置插入或删除节点,不受限于只能在链表的头部或尾部进行操作。
这样可以方便地实现栈、队列等数据结构。
5. 代码实现简单:相比于其他数据结构,循环双链表的代码实现相对简单,只需要处理好节点之间的指针关系即可。
循环双链表的应用领域非常广泛,特别是在需要频繁插入和删除节点的场景中,循环双链表能够提供高效的插入和删除操作。
下面以几个具体的应用场景来展开对循环双链表的解释和扩展。
1. 缓存替换算法:循环双链表可以用于实现LRU(Least Recently Used)缓存替换算法。
LRU算法中,当缓存满时,需要替换掉最近最少使用的数据。
循环双链表可以维护数据的访问顺序,每次访问一个数据时,将其移到链表的头部;当缓存满时,删除链表尾部的数据即可。
这样就可以保证链表头部的数据是最近访问的数据,尾部的数据是最久未访问的数据。
2. 轮播图:循环双链表可以用于实现轮播图功能。
轮播图需要循环展示多张图片,循环双链表正好可以满足这个需求。
每个节点表示一张图片,节点之间通过指针连接起来形成一个循环链表。
通过不断地遍历链表,可以实现图片的自动切换。
3. 约瑟夫环问题:循环双链表可以用于解决约瑟夫环问题。
List-DuLinkedList-将单向循环链表改为双向的
List-DuLinkedList-将单向循环链表改为双向的2.32 已知有一个单向循环链表,其每个结点中含有三个域:prior,data和next,其中data为数据域,next为指向后继结点的指针域,prior也为指针域,但它的值为NULL。
试编写算法将此单向循环链表改写为双向循环链表,即使prior成为指向前驱结点的指针域#include <stdio.h> #include <malloc.h>#define N 6typedef struct DuLNode { //双向循环链表结点char data;struct DuLNode *prior;struct DuLNode *next;} DuLNode, *DuLinkedList;void createLinkedList(DuLinkedList &L) { //创建双向循环链表(暂时只设NEXT指针)DuLinkedList p; int i;L = (DuLinkedList)malloc(sizeof(DuLNode));L->data = NULL;L->next = L;L->prior = NULL;for(i=N; i>0; i--) {p = (DuLinkedList)malloc(sizeof(DuLNode));p->data = getchar(); p->next = L->next; L->next = p;}}void singleToDu(DuLinkedList &L) { //将Prior指针添加上DuLinkedList p, q;p = L;q = p->next;while(q != L) {q->prior = p; p = q; q = q->next;}q->prior = p;}void printList(DuLinkedList L) { //打印双向循环链表DuLinkedList p;if(L->prior) {printf("链表的前驱指针存在,现采用反向遍历\n");p = L->prior;while(p != L) {printf("%c ", p->data); p = p->prior;}} else {printf("链表的前驱指针不存在,将采用正向遍历\n");p = L->next;while(p != L) {printf("%c ", p->data); p = p->next;}}}void main() {DuLinkedList L;printf("请输入%d个结点的值:\n", N); createLinkedList(L);printList(L);singleToDu(L); printf("\n");printList(L); printf("\n");}。
c语言双循环的简单例子
c语言双循环的简单例子从代码所示为常见的for语句双重循环,循环原理如下1,对于给二维数组赋值部分,第一次i=0,判断i<3值为真,然后执行二重循环语句,j=0,判断j<2结果为真,因此执行下面的循环体语句,a[0][0]=0+0=0,j++2,第二次循环依旧是在j循环部分执行,j=1,判断j<2结果为真,因此执行下面的循环体语句,a[0][1]=0+1=1,j++3,第三次循环依旧是在j循环部分执行,j=2,判断j<2结果为假,退出j部分循环,i++4,第四次循环,i=1,判断i<3结果为真,然后执行j部分二重循环语句,j=0;判断j<2结果为真,因此执行下面的循环体语句,a[1][0]=1+0=1,j++5,第五次循环,i=1,判断j<2结果为真,执行循环体语句,a[1][1]=1+1=2,j++本文实例讲述了C++双向循环列表用法。
分享给大家供大家参考。
具体如下:1 2 3 4 5 6 7 8 9 1/*双向循环链表*/#include <iostream> using namespace std;1 1 12 13 14 15 16 17 18 19 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 //结构体构造链表的指针域和数据域struct ChainNode{int data; //节点数据ChainNode *left; //节点的前驱指针ChainNode *right; //节点的后继指针};////////////创建n个双向循环链表并返回链表头指针///////// ChainNode* CreateNode(int n){ChainNode *head = NULL; //链表头节点ChainNode *pCur=NULL,*pNew=NULL; //当前节点,新建节点//初始化头结点的前驱和后继节点都为NULLif(n<1) //没有节点返回头节点{return head;}//创建头节点并将器左右指针指向空head = new ChainNode;head->left = NULL;head->right = NULL;2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5head->data = 0;pCur = head;//为防止指针互指带来的混乱,用pCur节点保存了头节点也表示当前指针移动到了头指针//创建n个节点并连接成链表for(int i=0; i<n; i++){pNew = new ChainNode; //创建一个新节点cout<<"请输入数据:";cin>>pNew->data;pCur->right = pNew; //头指针的右指针指向新建节点pNew->left = pCur; //新建节点的左指针执行头节点pNew->right = NULL; //用于最后和头指针进行交换pCur = pNew; //指针往下移动}//最后将头指针的左指针指向最后一个节点,//最后一个节点的有指针指向头指针,构成循环head->left = pCur;pCur->right = head;return head;}5 4 5 5 56 57 58 59 6 0 6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 7 0 7 1 7 2 7 3 7 4 //////////////输出链表头节点///////////////////////void OutList(ChainNode *head) //参数为头指针从头指针开始{cout<<"链表元素输出如下:"<<endl;ChainNode *pCur = head->right;//重第一个节点开始输出//没有指向空节点,则链表没结束输出链表元素while(pCur->right != head){cout<<pCur->data<<" ";pCur = pCur->right;//当前节点指向下一个节点可以遍历链表}cout<<pCur->data<<endl;//输入最后一个元素,它的右指针执行head}///////在双向循环链表后添加n个节点//////ChainNode* AddNode(ChainNode* head, int n){ChainNode *pNew,*pCur;//新添加节点和当前节点5 767 7 78 79 8 0 8 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9 9 0 9 1 9 2 9 3 9 4 9 5 9pCur = head;//移动到最节点while(pCur->right != head){pCur = pCur->right;//当前节点往下移动一直移到最后一个节点}//新添加n个节点并插入链表for(int i=0; i<n; i++){pNew = new ChainNode;cout<<"输入要添加的节点元素:";cin>>pNew->data;pCur->right = pNew; //头指针的右指针指向新建节点pNew->left = pCur; //新建节点的左指针执行头节点pNew->right = NULL; //用于最后和头指针进行交换pCur = pNew; //指针往下移动}//最后将头指针的左指针指向最后一个节点,//最后一个节点的有指针指向头指针,构成循环head->left = pCur;9 7 9 8 9 9 1 0 0 1 0 1 1 0 2 1 0 3 1 0 4 1 0 5 1 0 6 1 0 7 1 0 8 1 0 9 1 1 0 1 1 1pCur->right = head;return head;}/////在双向循环链表中删除一个节点///////ChainNode* DeleteNode(ChainNode* head, unsigned num) //删除第num个节点{ChainNode *pNew,*pCur,*temp;//新添加节点和当前节点 ,临时交换节点pCur = head;int ncount = 0;//移动到第num-1个节点while(1){ncount++;pCur = pCur->right; //当前节点往下移动if(num == ncount){break; //此时pCur还是指向了第num个节点}}1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1 7 1 1 8 1 1 9 1 2 0 1 2 1 1 2 2 1 2 3 1 2 4 1 2 5 1//当前节点的前一个节点的右指针指向当前节点的下一个节点//当前节点的下一个节点的左指针指向当前节点的上一个节点构成连接//最后删除当前节点(pCur->left)->right = pCur->right;(pCur->right)->left = pCur->left;delete pCur;return head;}int main(){int num;//创建num个节点并显示cout<<"输入要创建的链表节点个数:";cin>>num;ChainNode *head = CreateNode(num);OutList(head);//往链表后添加n个节点int addnum;cout<<"输入要添加的节点个数:";cin>>addnum;AddNode(head, addnum);6 1 27 1 28 1 29 1 3 0 1 3 1 1 3 2 1 3 3 1 3 4 1 3 5 1 3 6 1 3 7 1 3 8 1 3 9 1 4OutList(head);//删除链表的第del个元素int del;cout<<"输入要删除的第几个位置的节点:"; cin>>del;DeleteNode (head, del);OutList(head);system("pause");return0;}。
双向链表
第8讲 双向链表● 循环单链表的出现,虽然能够实现从任一结点出发沿着链能找到其前趋结点,但时间耗费是O (n) 。
● 如果希望从表中快速确定某一个结点的前趋,另一个解决方法就是在单链表的每个结点里再增加一个指向其前趋的指针域prior 。
这样形成的链表中就有两条方向不同的链,我们称之为双向链表。
● 双向链表的结构定义如下:typedef struct DNode{ ElemType data ;struct DNode *prior ,*next ;}DNode, * DoubleList ;● 双向链表的结点结构如图所示。
图:双链表的结点结构注:● 双向链表也是由头指针唯一确定的,● 增加头结点能使双链表的某些运算变得方便● 由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。
● 设指针p 指向双链表中某一结点,则有下式成立:p->prior->next = p = p->next->prior●在双向链表中,那些只涉及后继指针的算法,如求表长度、取元素、元素定位等,与单链表中相应的算法相同,● 但对于前插和删除操作则涉及到前驱和后继两个方向的指针变化,因此与单链表中的算法不同。
1、 双向链表的前插操作【算法思想】欲在双向链表第i 个结点之前插入一个的新的结点,则指针的变化情况如图所示:… p …s->prior=p->prior; ①p->prior->next=s;②s->next=p; ③p->prior=s;④【算法描述】int DlinkIns(DoubleList L,int i,ElemType e){DNode *s,*p;… /*先检查待插入的位置i是否合法(实现方法同单链表的前插操作)*/… /*若位置i合法,则找到第i个结点并让指针p指向它*/s=(DNode*)malloc(sizeof(DNode));if (s){ s->data=e;s->prior=p->prior; ①p->prior->next=s; ②s->next=p; ③p->prior=s; ④r eturn TRUE;}else return FALSE;}2、双向链表的删除操作【算法思想】欲删除双向链表中的第i个结点,则指针的变化情况如图所示:p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);【算法描述】int DlinkDel(DoubleList L,int i,ElemType *e){DNode *p;… /*先检查待插入的位置i 是否合法(实现方法同单链表的删除操作)*/… /*若位置i 合法,则找到第i 个结点并让指针p 指向它*/*e=p->data;p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);return TRUE;}3、 双向循环链表双向链表可以有循环表,称为双向循环链表。
数据结构-chap2 (3)循环链表
head->next=NULL; while(p!=NULL) {r=p->next; head->next=p; p=r; } return(head); }∥invert
∥p为工作指针,指向第一个元素
∥臵空链表 ∥将原链表的元素按头插法插入 ∥暂存p的后继 ∥头结点的指针域指向新插入的结点 ∥恢复待处理结点
A.插入、删除不需要移动元素
B.可随机访问任一元素 C.不必事先估计存储空间 D.所需空间与线性长度成正比
试述头指针、头结点、元素结点、首元结点的区别。 单链表中,增加一个头结点的目的是为了( )。 【江苏大学 2005 一.3(2分)】 A.使单链表至少有一个结点 B.标识表结点中首结点的位臵
C.方便运算的实现
【解答】单循环链表中无论设置尾指针还是头指针都可以
遍历到表中任一个结点。设置尾指针时,若在表尾进行插 入元素或删除第一元素,操作可在O(1)时间内完成;若只 设置头指针,在表尾进行插入或删除操作,需要遍历整个 链表,时间复杂度为O(n)。
在循环链表中寻找结点的直接后继很简单,只需要O(1); 但要寻找结点的直接前趋需要循环一遍,需要O(n)。
C. (p->rlink)->llink=p
D. p->rlink=(p->llink)->llink
p->rlink=(p->rlink)->rlink
p->llink=(p->rlink)->rlink;
【西安电子科技大学 1998 一、1(2分)】
试述头指针、头结点、元素结点、首元结点的区别。 •在单链表、双链表、单循环链表中,若知道指针p指向某结点, 能否删除该结点,时间复杂度如何? 【解答】以上三种链表中,若知道指针p指向某结点,都能 删除该结点。
链表(单链表 双向循环)实验报告讲解
数据结构实验报告T1223-3-21余帅实验一实验题目:仅仅做链表部分难度从上到下1.双向链表,带表头,线性表常规操作。
2.循环表,带表头,线性表常规操作。
3.单链表,带表头,线性表常规操作。
实验目的:了解和掌握线性表的逻辑结构和链式存储结构,掌握单链表的基本算法及相关的时间性能分析。
实验要求:常规操作至少有:1.数据输入或建立2.遍历3.插入4.删除必须能多次反复运行实验主要步骤:1、分析、理解给出的示例程序。
2、调试程序,并设计输入数据,测试程序的如下功能:1.数据输入或建立2.遍历3.插入4.删除单链表示意图:headhead head 创建删除双向循环链表示意图:创建程序代码://单链表#include<iostream.h>#include<windows.h>const MAX=5;enum returninfo{success,fail,overflow,underflow,range_error}; int defaultdata[MAX]={11,22,33,44,55};class node{public:int data;node *next;};class linklist{private:node *headp;protected:int count;public:linklist();~linklist();bool empty();void clearlist();returninfo create(void);returninfo insert(int position,const int &item);returninfo remove(int position) ;returninfo traverse(void);};linklist::linklist(){headp = new node;headp->next = NULL;count=0;}linklist::~linklist(){clearlist();delete headp;}bool linklist::empty(){if(headp->next==NULL)return true;elsereturn false;}void linklist::clearlist(){node *searchp=headp->next,*followp=headp;while(searchp->next!=NULL){followp=searchp;searchp=searchp->next;delete followp;}headp->next = NULL;count = 0;}returninfo linklist::create(){node *searchp=headp,*newnodep;for(int i=0;i<MAX;i++){newnodep = new node;newnodep->data = defaultdata[i];newnodep->next = NULL;searchp->next = newnodep;searchp = searchp->next;count++;}searchp->next = NULL;traverse();return success;}returninfo linklist::insert(int position,const int &item) //插入一个结点{if(position<=0 || position>=count)return range_error;node *newnodep=new node,*searchp=headp->next,*followp=headp;for(int i=1; i<position && searchp!=NULL;i++){followp=searchp;searchp=searchp->next;}newnodep->data=item; //给数据赋值newnodep->next=followp->next; //注意此处的次序相关性followp->next=newnodep;count++; //计数器加一return success;}returninfo linklist::remove(int position) //删除一个结点{if(empty())return underflow;if(position<=0||position>=count+1)return range_error;node *searchp=headp->next,*followp=headp; //这里两个指针的初始值设计一前一后for(int i=1; i<position && searchp!=NULL;i++){followp=searchp;searchp=searchp->next;}followp->next=searchp->next; //删除结点的实际语句delete searchp; //释放该结点count--; //计数器减一return success;}returninfo linklist::traverse(void){node *searchp;if(empty())return underflow;searchp = headp->next;cout<<"连表中的数据为:"<<endl;while(searchp!=NULL){cout<<searchp->data<<" ";searchp = searchp->next;}cout<<endl;return success;}class interfacebase{public:linklist listface; //定义一个对象Cskillstudyonfacevoid clearscreen(void);void showmenu(void);void processmenu(void);};void interfacebase::clearscreen(void){system("cls");}void interfacebase::showmenu(void){cout<<"================================"<<endl;cout<<" 功能菜单 "<<endl;cout<<" 1.创建链表 "<<endl;cout<<" 2.增加结点 "<<endl;cout<<" 3.删除结点 "<<endl;cout<<" 4.遍历链表 "<<endl;cout<<" 0.结束程序 "<<endl;cout<<"======================================"<<endl;cout<<"请输入您的选择:";}void interfacebase::processmenu(void){int returnvalue,item,position;char menuchoice;cin >>menuchoice;switch(menuchoice) //根据用户的选择进行相应的操作{case '1':returnvalue=listface.create();if(returnvalue==success)cout<<"链表创建已完成"<<endl;break;case '2':cout<<"请输入插入位置:"<<endl;cin>>position;cout<<"请输入插入数据:"<<endl;cin>>item;returnvalue = listface.insert(position,item);if(returnvalue==range_error)cout<<"数据个数超出范围"<<endl;elsecout<<"操作成功!!!"<<endl;break;case '3':cout<<"输入你要删除的位置:"<<endl;cin>>position;returnvalue = listface.remove(position);if(returnvalue==underflow)cout<<"链表已空"<<endl;else if(returnvalue==range_error)cout<<"删除的数据位置超区范围"<<endl;elsecout<<"操作成功!!!"<<endl;break;case '4':listface.traverse();break;case '0':cout<<endl<<endl<<"您已经成功退出本系统,欢迎再次使用!!!"<<endl;system("pause");exit(1);default:cout<<"对不起,您输入的功能编号有错!请重新输入!!!"<<endl;break;}}void main(){interfacebase interfacenow;linklist listnow;system("color f0");interfacenow.clearscreen();while(1){interfacenow.showmenu();interfacenow.processmenu();system("pause");interfacenow.clearscreen();}}/* 功能:用双向循环链表存储数据1.创建链表2.增加结点3.删除结点4.遍历链表制作人:余帅内容:239行*/#include<iostream.h>#include<windows.h>const MAX=5;enum returninfo{success,fail,overflow,underflow,range_error}; int defaultdata[MAX]={11,22,33,44,55};class node{public:int data;node * next; //指向后续节点node * pre; //指向前面的节点};class linklist{private:node *headp;protected:int count;public:linklist();~linklist();bool empty();void clearlist();returninfo create(void);returninfo insert(int position,const int &item);returninfo remove(int position) ;returninfo traverse(void);};linklist::linklist(){headp = new node;headp->next = NULL;headp->pre = NULL;count=0;}linklist::~linklist(){clearlist();delete headp;}bool linklist::empty(){if(headp->next==NULL)return true;elsereturn false;}void linklist::clearlist(){node *searchp=headp->next,*followp=headp;while(searchp->next!=NULL){followp=searchp;searchp=searchp->next;delete followp;}headp->next = NULL;headp->pre = NULL;count = 0;}returninfo linklist::create(){node *searchp=headp,*newnodep;for(int i=0;i<MAX;i++){newnodep = new node;newnodep->data = defaultdata[i];newnodep->next = NULL;searchp->next = newnodep;newnodep->pre = searchp;searchp = searchp->next;count++;}searchp->next = headp;headp->pre = searchp;traverse();return success;}returninfo linklist::insert(int position,const int &item) //插入一个结点{if(position<=0 || position>count+1)return range_error;node *newnodep=new node;node *searchp=headp->next,*followp=headp;for(int i=1; i<position && searchp!=NULL;i++){followp=searchp;searchp=searchp->next;}newnodep->data=item; //给数据赋值newnodep->next = searchp;searchp->pre = newnodep;followp->next = newnodep;newnodep->pre = followp;count++; //计数器加一return success;}returninfo linklist::remove(int position) //删除一个结点{if(empty())return underflow;if(position<=0||position>=count+1)return range_error;node *searchp=headp->next,*followp=headp; //这里两个指针的初始值设计一前一后for(int i=1; i<position && searchp!=NULL;i++){followp=searchp;searchp=searchp->next;}followp->next=searchp->next; //删除结点的实际语句searchp->next->pre = followp;delete searchp; //释放该结点count--; //计数器减一return success;}returninfo linklist::traverse(void){node *searchp1,*searchp2;if(empty())return underflow;searchp1 = headp;searchp2 = headp;cout<<"连表中的数据为:"<<endl;cout<<"从左至右读取:";while (searchp1->next!=headp ) {searchp1 = searchp1 ->next;cout << searchp1->data<<" ";}cout<<endl;cout<<"从右至左读取:";while (searchp2->pre!=headp ) {searchp2 = searchp2 ->pre;cout << searchp2->data<<" ";}cout<<endl;return success;}class interfacebase{public:linklist listface; //定义一个对象Cskillstudyonface void clearscreen(void);void showmenu(void);void processmenu(void);};void interfacebase::clearscreen(void){system("cls");}void interfacebase::showmenu(void){cout<<"================================"<<endl;cout<<" 功能菜单 "<<endl;cout<<" 1.创建链表 "<<endl;cout<<" 2.增加结点 "<<endl;cout<<" 3.删除结点 "<<endl;cout<<" 4.遍历链表 "<<endl;cout<<" 0.结束程序 "<<endl;cout<<"======================================"<<endl;cout<<"请输入您的选择:";}void interfacebase::processmenu(void){int returnvalue,item,position;char menuchoice;cin >>menuchoice;switch(menuchoice) //根据用户的选择进行相应的操作{case '1':returnvalue=listface.create();if(returnvalue==success)cout<<"链表创建已完成"<<endl;break;case '2':cout<<"请输入插入位置:"<<endl;cin>>position;cout<<"请输入插入数据:"<<endl;cin>>item;returnvalue = listface.insert(position,item);if(returnvalue==range_error)cout<<"数据个数超出范围"<<endl;elsecout<<"操作成功!!!"<<endl;break;case '3':cout<<"输入你要删除的位置:"<<endl;cin>>position;returnvalue = listface.remove(position);if(returnvalue==underflow)cout<<"链表已空"<<endl;else if(returnvalue==range_error)cout<<"删除的数据位置超区范围"<<endl;elsecout<<"操作成功!!!"<<endl;break;case '4':listface.traverse();break;case '0':cout<<endl<<endl<<"您已经成功退出本系统,欢迎再次使用!!!"<<endl;system("pause");exit(1);default:cout<<"对不起,您输入的功能编号有错!请重新输入!!!"<<endl;break;}}void main(){interfacebase interfacenow;linklist listnow;system("color f0");interfacenow.clearscreen();while(1){interfacenow.showmenu();interfacenow.processmenu();system("pause");interfacenow.clearscreen();}}运行结果:1.创建链表:2.增加结点3.删除结点心得体会:本次实验使我们对链表的实质了解更加明确了,对链表的一些基本操作也更加熟练了。
《数据结构C语言版》----第02章
p size=0
head
a0
a1
(a)
...
a n 1 ∧
3.顺序表操作的效率分析
时间效率分析: 算法时间主要耗费在移动元素的操作上,因此计算时间复 杂度的基本操作(最深层语句频度) T(n)= O(移动元素次数) 而移动元素的个数取决于插入或删除元素的位置i. 若i=size,则根本无需移动(特别快); 若i=0,则表中元素全部要后移(特别慢); 应当考虑在各种位置插入(共n+1种可能)的平均移动次 数才合理。
(3)带头结点单链表和不带头结点单链表的比较
1).在带头结点单链表第一个数据元素前插入结点
p head s p head data next a0 x
∧
a1
…
an-1
∧
(a) 插入前
data next a0
∧
a1
…
an-1
∧
s
x
(b) 插入后
2).删除带头结点单链表第一个数据元素结点
p data next head
(5)取数据元素ListGet(L,
i, x)
int ListGet(SeqList L, int i, DataType *x) { if(i < 0 || i > L.size-1) { printf("参数i不合法! \n"); return 0; } else { *x = L.list[i]; return 1; } }
带头结点的双向循环链表操作集
带头结点的双向循环链表操作集带头结点的双向循环链表操作集1. 链表的定义链表是一种数据结构,它由一系列节点组成,每个节点存储数据和指向下一个节点的指针。
链表可以分为单向链表和双向链表。
在双向链表中,每个节点有两个指针,一个指向前一个节点,另一个指向后一个节点。
2. 链表的基本操作2.1 链表的创建创建一个带头结点的双向循环链表,可以按照以下步骤进行:1. 创建头结点2. 将头结点的前指针和后指针均指向自身,完成循环链接的闭合3. 将头结点作为链表的起始节点2.2 链表的遍历链表的遍历是指按照某种顺序遍历链表中的所有节点。
可以使用循环或递归的方法进行遍历,其具体步骤如下:1. 先将指针指向链表的起始节点2. 依次访问每个节点,并将指针指向下一个节点,直到指针指向空节点为止2.3 链表的插入链表的插入是指将一个新的节点插入到链表中的某个位置。
如果要在第i个位置插入一个新节点,需要进行以下操作:1. 新建一个节点,并将要插入的数据存储在其中2. 找到第i-1个节点,并将它的后指针指向新节点3. 将新节点的前指针指向第i-1个节点,后指针指向第i个节点4. 如果插入位置是链表的末尾,则需要将新节点的后指针指向头结点,完成循环链接的闭合2.4 链表的删除链表的删除是指将链表中某个节点删除。
如果要删除第i个节点,需要进行以下操作:1. 找到第i个节点2. 将第i-1个节点的后指针指向第i+1个节点3. 将第i+1个节点的前指针指向第i-1个节点4. 释放第i个节点所占用的内存空间3. 链表的应用链表常常被用于各种算法和数据结构中,如栈、队列、哈希表、图等。
链表具有无需预先分配内存空间,插入和删除操作效率高等优点,在某些场合可以取代数组进行操作。
4. 链表的优化在实际使用中,链表的优化也是非常重要的,可以采用以下方法进行优化:1. 在插入和删除操作频繁的场合,可以选用跳表、B树等数据结构进行优化2. 在查询操作频繁的场合,可以选用哈希表等数据结构进行优化3. 可以使用链表的迭代器进行遍历操作,比单纯使用指针更加方便和安全5. 总结带头结点的双向循环链表是一种常用的数据结构,具有插入和删除操作效率高、可以减少分配内存空间等优点。
双向循环链表
7 人 6 人 4 人
1
双向链表(Doubly Linked List)
如果在一个应用问题中经常要求检测指针向前驱和后继方向移动, 为保证移动的时间复杂度达到最小,就必须采用双向链表表示。
双向链表的结点结构:
左链指针 数据 右链指针
前驱结点
lLink
data
rLink
后继结点
template <class Type> class DblNode { private: Type data; DblNode <Type> * lLink, * rLink; }
2014-7-15 2
带头结点的双向循环链表 :
first
e0
e1
current
…
en-1
first 空表 游标结点: * current 游标结点的前驱结点: * ( current -> lLink ) 游标结点的后继结点: * ( current -> rLink )
2014-7-15 3
双向循环链表的类定义:
调查结果:
讲课进度: 偏快 27 人 适中 20 人 偏慢 2 人 课程难易: 太简单 1 人 偏难 9 人 太繁 1 人 讲解方法: 多讲理论、原理、方法,少讲具体程序 少讲理论,多讲程序和C++内容 多讲具体应用的完整实例 其他: 规定交作业时间,促进学生做作业; 作业太多,要少而精; 最好能现场编程、调试;
2014-7-15 4
2014-7-15
5
template <class Type> class DblList { public: DblList ( Type uniqueVal ); ~DblList ( ); int Length ( ) const; int IsEmpty ( ) { return first->rLink==first ;} int Find ( const Type & target ); Type getData ( ) const; void Firster ( ) { current = first; } int First ( ); int Next ( ); int ) { return current != NULL;} void Insert ( const Type & value ); void Remove ( ) ; private: DblNode <Type> * first, * current; }
双向循环链表中结点的交换
双向循环链表中结点的交换
双向循环链表是一种常见的数据结构,其中每个结点都有前驱和后继指针,可以实现快速的插入和删除操作。
在某些情况下,需要对双向循环链表中的结点进行交换操作,例如将两个相邻的结点位置互换。
下面介绍一种简单的方法来实现双向循环链表中结点的交换。
假设双向循环链表中有结点A、B、C、D,它们的前驱和后继指针分别为prev和next。
要将结点B和结点C进行交换操作,可以按照以下步骤进行:
1. 将结点B的前驱指针指向结点C的前驱结点A,将结点B的后继指针指向结点C的后继结点D,将结点C的前驱指针指向结点B,将结点C的后继指针指向结点B。
2. 将结点B的前驱结点A的后继指针指向结点C,将结点D的前驱指针指向结点B。
完成以上两个步骤后,双向循环链表中结点B和结点C的位置就会互换,同时它们的前驱和后继指针也会相应地更新。
这种方法比较简单,但需要注意一些细节,例如交换的结点不能是链表的头结点或尾结点,否则需要进行额外的处理。
- 1 -。
第三章 链表
第三章 链表
第三章 链表
知识点
单链表的结点形式、组织方法和特点 单链表的基本运算和相应的算法 循环链表的组织方法和基本运算算法 双链表的结点形式、组织方法和特点 双链表的基本运算和相应的算法 顺序表与链表比较,各自的优、缺点 链表的应用 用十字链表表示稀疏矩阵
第三章 链表
难点
双链表插入、删除运算的算法 利用链接结构的特点设计有效算法,解决与链表结 构相关的应用问题
第三章 链表
算法分析
此算法的关键是while循环语句,开始时p 指针指向头结点,每一循环都修改指针 值,让它指向下一个结点,同时将计数 链表长度的变量count加1。 这样每循环一次就向后推移一个结点, 直到p所指结点*p的链域值为NULL为止。 空指针NULL起标志的作用,若无此标志, 尾结点链域的值为“无定义”,上述算 法中的while语句在做最后一次判断时将 出现“运行错”,这是应予避免的。
第三章 链表
3.3.2 链队列
链队列需要两个指针,其中队首指针front指向 链表的表头,队尾指针rear指向链表的表尾。 一般插入时只修改队尾结点的指针和队尾指针 rear,删除时只修改队首指针front。当将第一个 元素插入空队列或删除了最后一个元素而使队 列为空时,front和rear都需要修改。 front rear
当需要从单链表上删除结点时,就要通过删 除运算来完成。 删除单链表上一个其值为x的结点的主要操作 是:
1) 用遍历的方法在单链表上找到该结点; 2) 从单链表上删除该结点。
欲从单链表上删除一个结点,需修改该结点 的前一个结点的指针,如下面的图所示。
第三章 链表
q head
p x ∧
假设指针q指向待删除结点的前一个结点,指 针p指向要删除的结点,删除该结点的操作如 下:将该结点的前一个结点*q的链域指向*p 的后继结点(即q->next=p->next)。
计算机专业基础综合数据结构(线性表)历年真题试卷汇编1
计算机专业基础综合数据结构(线性表)历年真题试卷汇编1(总分:70.00,做题时间:90分钟)一、单项选择题(总题数:9,分数:18.00)1.对于双向循环链表,在P指针所指的结点之后插入s指针所指结点的操作应为( )。
【北京工业大学2004一、1(3分)】(分数:2.00)A.P一>right=s;s一>left=p;p->right一>left=s;s一>right=p一>right;B.P一>right=s;p->right一>left=s; s一>left=p; s一>right=p一>fight;C.s一>left=p; s一>right=p一>right;P一>right=-s;P一>right一>left=s;D.s一>left=p; s一>right=p一>fight;P一>right一>left=s;P一>right=s;√解析:解析:双链表在p指向的结点前或结点后插入结点都可以,但是必须避免“断链”。
本例A和B第一个语句就将p的原后继断链,没必要再浪费时间看这两个选择答案后边的其他语句。
2.设双向循环链表中结点的结构有数据域data,指针域pre和next,链表不带头结点。
若在指针P所指结点之后插入结点S,则应执行下列( )操作。
【南京理工大学2005一、3 (1分)】【北京交通大学2006一、1(2分)】(分数:2.00)A.P一>next=s;s一≥pre=p;P一>next一>pre=s;s一>next=p一>next;B.P一>next=s;P一>next->pre=s;s一≥pre=p;s一>next=p一>next;C.s一>pre=p;s一>nex=p一>next;P一>next=s;P一>next->pre=s;D.s一≥pre=p;s->next=p一>next;P一>next一>pre=s;P一>next=s;√解析:3.在下列双向链表中,已知指针pa指向结点A,若在A、C之间插入指针pb所指的结点B,则依次执行的【华中科技大学2006二、4(2分)】(1)pb一>next=pa->next;(2)pb一>prior=pa;语句序列可以是( )。
数据结构试题及答案
一、判断1.带表头节点的双向循环链表为空时,表头的前驱和后继指针指向HEAD。
2.带表头结点的双向链表为空时,表头的前趋和后继指针分别指向NULL。
3.带表头结点的双向循环链表为空时,表头的前驱和后继指针分别指向NULL4.环形链表是线性结构,首尾相连,使用连续地址储存。
5.环形队列是线性结构,但它不是环形,利用特殊的循环算法解决“假溢出”。
6.环形队列是线性结构,象循环链表一样头尾相连接,它能解决“假溢出”。
7.环形队列是线性结构,能利用特殊的循环算法解决“假溢出”。
8.广义表E(a ,E)的表长是2,表深度是无穷大。
9.广义表E(a ,E)的表深度是2,表长是2。
10.广义表G(a ,G)的表深度是2,表长是2。
11.广义表G(a ,G)的深度是2,表长是2。
12.满BT是特殊的BT树,它的定义是递归的。
13.BT是特殊的树,它的定义是递归的。
14.平衡树是特殊的BT树,它的定义是递归的。
15..若已知BT的先根序和中根序、后根序和中根序,可唯一确定一个BT。
16.若已知BT的先根序遍历和后根序遍历,可唯一确定一个BT。
17.平衡BT是特殊的BT,它的定义是递归的。
18.BST是特殊的BT,它的中序遍历是有序的。
19.何时使用PRIM法,何时使用KRUSKAL法取决于图是稀疏还是稠密20.PRIM和KRUSKAL均可以对图求MST,但是PRIM法方便。
21.希尔排序是插入排序的变型,插入排序是稳定的,但希尔排序是不稳定的。
22.希尔排序是插入排序的变型。
插入排序是稳定的,所以希尔排序是稳定的。
23.快速排序的稳定性取决于枢轴的选择。
24.快速排序的枢轴有多种选择方法,取首尾节点是较简单的做法。
25.冒泡排序不但稳定,而且速度很快。
二、名词解释与问答26.线性结构:唯一的首节点,唯一的尾节点,除首尾外其它节点既有前驱也有后继,首无前驱,尾无后继。
27.线性结构中端操作受限的含义是什么?操作仅在指定的位置。
循环双链表的判空条件
循环双链表的判空条件循环双链表(Circular Doubly Linked List)是双向链表(Doubly Linked List)的一种特殊形式,其最后一个结点的 next 指向头结点,头结点的 prev 指向最后一个结点,从而形成了闭环。
循环双链表具有双向遍历的特性,可以实现快速插入、删除等操作,因此被广泛应用于数据结构和算法中。
在使用循环双链表时,判空条件是非常重要的,它决定了程序是否能够正常运行。
判空条件通常是根据链表的头结点来判断的,因为头结点是循环双链表的入口,它包含了链表的长度、头尾结点等信息。
不同的编程语言和应用场景可能会有不同的判空条件,下面给出几种常见的判空条件及其实现方式。
1. 头结点为空头结点为空是最常见的循环双链表判空条件,它表示整个链表为空。
在实现循环双链表时,通常会初始化一个头结点,并使其指向自己,也就是构成一个至少包含一个结点的循环双链表。
这样,在判空时只需要判断头结点的next 指针是否指向自己即可。
C++ 代码实现:``` template<typename T> class CircularDoublyLinkedList { private: struct Node { T data; Node *prev, *next;Node(const T& x = T(), Node* p = nullptr, Node* n = nullptr) : data(x), prev(p), next(n) {} };Node *head;public: CircularDoublyLinkedList() :head(new Node()) { head->prev = head->next = head; }bool empty() const { returnhead->next == head; } }; ```2. 结点数量为零结点数量为零是另一种常见的循环双链表判空条件,它表示链表中没有任何结点。
循环链表和双向链表
b.head->next = NULL; //此时,b中已只剩第一个结点(头), 为其置空表标志
return k; //返回结果链表中的元素个数
}
为了进一步说明上述程序,举一个程序运行的例子, 其各次循环的运行结果如图5-6所示
p
7 0 3 2 -9 3 1 5
^
(a)A(x)=p5(x)=7+3x2-9x3+x5,进入循环前
该程序不断比较A链和B链中的一对结点的指数值 (称其为当前结点)。开始时A链和B链中参加比较
的当前结点都是它们的第一个元素。
主循环while结束后,可能出现下列3种情况:①A
链和B链同时被处理完;②只有B链处理完;③只有A
链处理完。 对第一和第二种情况,不需要“善后”处理。对第 三种情况,B链中尚有未被处理完的结点,需将其挂 接在结果链的尾部。循环外的“if(q 不为空)将q
p = p->next; } // if (x==0) … else … q0 = q; q = q->next; delete q0; //将q所指结点从表中删除并释放,令q新指向原所 指的下一个 } // if (p->exp > q->exp ) … else … } //while if (q!=NULL) p0->next = q;
为处理方便,在具体存储多项式时,我们规定:
所存储的多项式已约简,即已合并同类项,不 保留0系数项,各项按指数的升序排列。 (二)多项式加法实现—直接操作链表 为操作方便,我采用带头结点的非循环链表,下面给 出一个例子说明多项式的这种表示法。
设有一个一元5次多项式: P5(x)=7+3x-9x3+x5
数据结构实验五:链表实验
一,实验题目实验五:假设有一个单向循环链表,其结点包含三个域:data,pre和next,其中data为数据域next为指针域,其值为后继结点的地址,pre也为指针域,其初值为空(null),试设计算法将此单向循环链改为双向循环链表。
二,问题分析本程序要求将一个包含三个域:data,pre和next,其中data为数据域next为指针域,其值为后继结点的地址,pre也为指针域的单向循环链表改为双向循环链表。
完成这些问题需要解决的是空链表的生成,链表元素的输入和输出,将单向链表改为双向链表等。
1,数据的输入形式和输出形式:单链表的元素均为int型,输出的单链表元素也为int型。
输出链表指针所指向的前一个或后一个链表元素时,选择1或2。
2,结果的输出形式:首先输出单链表的元素,将单链表转换为双向链表后,输出p指针所指的前一个或后一个链表元素。
3,测试数据:(1)输入的单链表元素为:23,553,87,0;(2)输入的单链表元素为:34,7650;三,概要设计1,为了实现上述程序功能,需要:(1)构造一个空的单链表L;(2)逐个输入单链表元素;(3)输出单链表元素;(4)将单链表转换为双向链表;(5)进行简单的双向链表功能测试Array 2,本程序包含5个函数:(1)主函数:main();(2)建立空链表函数:creatnull();(3)输入单链表元素函数:input();(4)转换为双向循环链表函数:setdouble(5)单链表输出函数:display();个函数间的关系如右图所示:四,详细设计1,链表的结构类型定义:typedef struct dnode{int data;struct dnode *pre,*next;}dlinklist;2,建立空链表伪代码:dlinklist *creatnull(){dlinklist *L;L=(dlinklist*)malloc(sizeof(dlinklist));L->next=NULL;L->pre=NULL;L->data=NULL;return L;}3,建单链表伪代码:dlinklist* input(dlinklist *L,int x){dlinklist *s,*r;r=L->next; s=creatnull(); s->data=x; L->next=s; s->next=r;return L;}4,将单链表转换为双向循环链表的伪代码:dlinklist *setdouble(dlinklist *L){dlinklist *s,*t; s=L; t=L->next;while(t!=NULL){t->pre=s; s=s->next; t=t->next;}return L;}五,源程序#include "stdio.h"#include "malloc.h"typedef struct dnode{ //链表的结构类型定义int data; //数据域struct dnode *pre,*next;}dlinklist;dlinklist *creatnull(){ //建立空链表,即建立空结点dlinklist *L;L=(dlinklist*)malloc(sizeof(dlinklist)); //为新节点申请空间L->next=NULL;L->pre=NULL;L->data=NULL; //置空return L;}dlinklist* input(dlinklist *L,int x) //头插法建单链表{dlinklist *s,*r;r=L->next; //将L指向的下一个节点赋值给rs=creatnull(); //建立空点结s->data=x; //将x值赋给新建结点的data域L->next=s; //将s赋值给L指向的下一个结点s->next=r; //将r赋给s所指向的下一个结点,完成插入return L;}dlinklist *setdouble(dlinklist *L){ //将单链表转换为双向循环链表dlinklist *s,*t;s=L;t=L->next;while(t!=NULL){t->pre=s;s=s->next;t=t->next;}return L;}void display(dlinklist *k) //输出顺序表中数据{k=k->next;while(k!=NULL){printf("%d ",k->data);k=k->next;}}void main(){dlinklist *p,*q; //定义两个指针型变量p,qint x,i=0;p=creatnull(); //建立空链表q=p; //使q也指向头结点printf("输入顺序表的元素,(输入0表示输入结束):\n");while(x!=0){p=input(p,x); //当x不等于0时,输入链表元素xscanf("%d",&x);}printf("输入的顺序表元素为:\n");p=setdouble(p); //将单链表转换为双向循环链表display(p); //输出链表表元素do{printf("\n");printf("1,输出下一个数据:\n");printf("2,输出上一个数据:\n");printf("请选择要输出的是顺序表元素的上一个还是下一个元素:\n");scanf("%d",&i);switch(i) //选择语句{case 1: //当i为1时,输出q指针所指的下一个链表元素q=q->next;if(q!=NULL)printf("\t链表元素为%d\n",q->data);else{printf("\t为表尾元素");q=p;}break;case 2: //当i为2时,输出q指针所指的前一个链表元素q=q->pre;if(q->pre!=NULL)printf("\t链表元素为%d\n",q->data);else{printf("\t为表头元素");q=p;}break;}}while(1);}六,调试分析如下语句所示,当输入顺序表元素时,没有输入语句“scanf(“%d”,&x)“则输出的错误结果:即要输入的链表元素为54,43,23.但输出是却多出一个元素。
链表指针
指针与链表程序中的变量一经说明,计算机操作系统就会在内存空间中分配相应的存贮单元,其中变量名是存贮单元的地址,而变量的值是存贮单元的内容,且该存贮单元自始至终都被该变量所占用,直到程序结束。
如果变量是局部变量,那么在它的作用域内,一经说明也占有一定的存贮单元,直到退出其作用域为止。
这样的变量,在程序执行过程中,不能随时使用随时分配存贮空间,也不能在程序执行的过程中,释放这些空间。
也就是说,一旦给这些变量分配存贮空间,无论程序是否还需要使用,它们都要占用一定的存贮空间,以便给用户存贮数据。
我们称具有这样特点的存贮为静态存贮,它所对应的变量称为静态变量。
如字符类型、数组类型、记录类型等。
这类变量的优点是存贮方便,查找容易,可以通过一个简单的公式随机存取表中的任一元素,逻辑关系上相邻的两个元素在物理位置上也是相邻的,很容易找到前趋与后继元素;缺点是在线性表的长度不确定时,必须分配足够大的存储空间,经常浪费了宝贵的存储资源;而线性表的容量一经定义确定后就难以扩充;在插入和删除线性表的元素时,需要移动大量的元素,时间效率也比较差。
2、动态存贮在程序执行过程中,通过向操作系统申请存贮空间或释放存贮空间的命令,达到动态管理计算机的存贮空间,以保证存贮空间的充分利用。
存贮空间可以随时申请、随时释放,这样的存贮方式称为动态存贮,其变量称为动态变量。
指针变量即为动态变量。
动态存储所需要的空间可以是不连续的,这样有利于充分利用零散的小空间。
但缺无法用O(1)的时间实现存取了。
如何用这些零散的空间存储数组这些大规模数据呢?如何表示这些数据之间的逻辑关系呢?为了表示这些物理存储单元之间的逻辑关系,对于每个数据元素来说,除了要存储它本身的信息(数据域d ata)外,还要存储它的直接后继元素的存储位置(指针域,一般用l in k或ne xt表示)。
我们往往把这两部分信息合在一起称为一个“结点node”。
N个结点链接在一起就构成了一个链表。
数据结构中链表及常见操作
链表1 定义链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。
链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向明上一个或下一个节点的位置的链接("links")。
链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。
而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。
链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。
链表有很多种不同的类型:单向链表,双向链表以及循环链表。
2 结构2.1 单向链表链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。
这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。
一个单向链表的节点被分成两个部分。
第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。
单向链表只可向一个方向遍历。
链表最基本的结构是在每个节点保存数据和到下一个节点的地址,在最后一个节点保存一个特殊的结束标记,另外在一个固定的位置保存指向第一个节点的指针,有的时候也会同时储存指向最后一个节点的指针。
一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
19
双向循环链表的输出算法
template<class Type> void dbclist<Type>::print_list(ostream& out) const { dnode<Type> * current; for (current=header->right; //从第1个结点开始 current!=header; //到头结点为止 current=current->right) //依次将current后移 out << current->data << " "; }
2.7 双向链表
20
② q->right->left=q->left
2.7 双向链表
18
双向循环链表的删除算法
template<class Type> dbclist<Type>& dbclist<Type>::erease(int k, Type& x) { if (k < 1 || k > n) throw out_bounds(); // k值越界 dnode<Type> *q = header->right; // 指针q指向第k个元素 for (int index=1; index<k; index++) q=q->right; q->left->right=q->right; //q指向的第k个元素被删除 q->right->left=q->left; x = q->data; // 用x保存原表中第k个元素 delete q; // 释放第k个元素结点 n--; // 表长度减1 return *this; }
//当前结点 //下一结点 //已是空表 //current初始化 //若表非空则依次删除表中结点
//n实际控制循环次数
}
header
a1 current next current
2.7 双向链表
a2 current next
… …
an
10
current next
查找位置k元素并存于x中
• 基本思想:从第1个结点开始依次向后扫描,直到找到位置k 上的元素,并存于x中,且返回ture,位置k无效返回false。 k k k
… …
an right_end
2.7 双向链表
4
双向链表类定义—结点类
template <class Type> class dblist; //前视类定义 template <class Type> class dnode { friend class dblist<Type>; //友类声明 private: Type data; //数据 dnode<Type> *left, *right; //左、右指针 };
ak+1 a1
④
①
x y
② ③ ③ p->right->left=y
② y->right=p->right ④ p->right=y
2.7 双向链表
16
双向循环链表的插入算法
template<class Type> dbclist<Type>& dbclist<Type>::insert(int k, const Type& x)
current
while (current->data != x) { current = current->next; index++; }
2.7 双向链表
13
查找元素x位置算法
template<class Type> int dbclist<Type>::locate(const Type& x) const { dnode<Type> *current = header->right; //current指向位置1 int index = 1; // index记录元素位置 header->data = x; //用于循环终止条件 while (current->data != x) { //找元素x所在的结点 current = current->right; index++; } return ((current==header) ? 0 : index) ;
index=1
header
a1
current
… …
index=k
ak data
… …
an
current
while (index < k ){ current = current->next; index++;}
2.7 双向链表
11
查找位置k元素并存于x中算法
template<class Type> bool dbclist<Type>::retrieve(int k, Type& x) const { if (k < 1 || k > n) return false; //k值越界 dnode<Type> *current=header->right; //current从第1个元素开始 int index = 1; while (index < k ) { //找表中第k个元素 current = current->right; index++; } x = current->data; //取第k个元素值赋予x return true; }
链表指针的方向?
单链表? 循环链表?
单向的 优点?
双向的 双向链表
2.7 双向链表
1
双向链表
• 双向链表 是指在前驱和后继方向都能游历(遍历)的 链表。 • 在双向链表中,每个结点有两个指针域,一个指向直 接后继元素结点,另一个指向直接前趋元素结点。
2.7 双向链表
2
双向链表结点结构
存储数 据元素
p->right->left= y; p->right=y; n++; return * this; }
2.7 双向链表
//表长度加1
17
双向循环链表的删除
k header a1 … … k ak ① ak-1 q a ak1 ②
aa2 k+1 ak+1
k … … an
① q->left->right=q->right
左指针 left
数据域 data
右指针 right
存储前驱 结点地址
结点图示
存储后继 结点地址
2.7 双向链表
3
双向链表结构
• 双向链表中有指向头结点和尾结点的两个指针 left_end和right_end。 • 双向链表中双指针的设置,可以快速的找到某 结点的前趋和后继结点。 a1 left_end a2 a3
{ //位置k后插入结点 if (k < 0||k > n) throw out_bounds( ); // k值越界 dnode<Type> *p =header; // 指针p初始化为头结点 for (int index=1; index<=k ; index++) p=p->right; // 搜索位置k dnode<Type> *y=new dnode<Type>; //申请新结点y y->data = x; y->left =p; y->right=p->right; //插入结点y
2.7 双向链表
5
双向链表类定义—双向链表类
template <class Type> class dblist { private: int n; //表长度 dnode<Type> *left_end, *right_end; //表头,表尾指针 public: dblist ( ) {left_end=right_end = 0;}; //构造函数 ~dblist ( ); //析构函数 bool empty( ) const {return n==0;} //测试表是否为空 int size( ) const {return n;} //表的长度 bool retrieve(int k, Type& x) const; //查找表位置k处的元素 int locate(const Type& x) const; //计算元素x在表中的位置 dblist<Type>& insert(int k, const Type& x); //插入结点 dblist<Type>& erase(int k, Type& x); //删除结点 void print_list(ostream& out) const; //输出双链表 };
2.7 双向链表
6
双向循环链表
• 双向链表通常采用带表头结点的循环链表形式。
header
a1
非空表
a2
… …
an
header
空表
2.7 双向链表
7
双向循环链表类定义—双向循环链表类
template <class Type> class dbclist { private: int n; //表长度 dnode<Type> *header; //表头,表尾指针 public: dbclist ( ); //构造函数 ~dbclist ( ) {erase();delete header}; //析构函数 void erase(); //删除表结点 bool empty( ) const {return n==0;} //测试表是否为空 int size( ) const {return n;} //表的长度 bool retrieve(int k, Type& x) const; //查找表位置k处的元素 int locate(const Type& x) const; //计算元素x在表中的位置 dblist<Type>& insert(int k, const Type& x); //插入结点 dblist<Type>& erase(int k, Type& x); //删除结点 void print_list(ostream& out) const; //输出双链表 };