17、题目:设计一个算法用于判断带头结点的循环双链表是否对称
思路:让p从左向右扫描,q从右向左扫描,直到它们指向同一个结点或相邻结点为止。(p==q,p->next=q或q->next=p)。若它们所指结点值相同,则继续进行下去,否则返回0。
typedef struct DNode{ //定义双链表结点的类型
ElemType data; //数据域
struct DNode *prior, *next; //前驱和后继指针
}DNode, *DLinklist;
int Symmetry(DLinklist L){
DNode *p=L->next, *q=L->prior;
while(p!=q || q->next!=p){
if(p->data == q->data){
p = p->next;
q = q->next;
}
else
return 0;
}
return 1;
}
18、题目:有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之后,要求链接后的链表仍保持循环链表形式
思路:将h1的尾指针指向h2,将h2的尾指针指向h1
LinkList Link(LinkList &h1, LinkList &h2){
LNode *r1, *r2; //r1和r2分别指向两个链表的尾指针
r1 = h1;
while(r1->next!=h1) //寻找h1的尾结点
r1 = r1->next;
r2 = h2;
while(r2->next!=h2) //寻找h2的尾结点
r2 = r2->next;
r1->next = h2; //将h2链接到h1后
r2->next = h1; //令h2的尾结点指向h1
return h1;
}
19、题目:设有一个带头结点的循环单链表,其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表为空,再删除头结点。
思路:对于循环单链表L,在不空时循环;每循环一次查找一个最小结点(由minp指向最小值结点,minpre指向其前驱结点)并删除它。最后释放头结点。时间复杂度为O(n²)
typedef struct node{ //链表结点的结构定义
int data; //数据域
struct node *next; //指针域
}LNode, *LinkList;
void Min_Delete(LinkList &L){
LNode *pre, *p, *minpre, *minp;
while(L->next!=L){ //表不为空,循环
pre=L;
p=L->next; //p为工作指针,pre指向其前驱
minpre=pre;
minp=p; //minp指向最小值结点,minpre指向其前驱
while(p!=L){ //循环一趟,查找最小值结点
if(p->data<minp->data){
minp=p;
minpre=pre;
}
p=p->next; //查找下一个结点
pre=pre->next;
}
//找到最小值结点,输出并删除
printf("%d", minp->data);
minpre->next=minp->next;
free(minp);
}
//释放头结点
free(L);
}
20、题目:设有一头指针为L的带有表头结点的非循环双向链表,其每个结点中除有prior(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq。在链表被起用前,其值均初始化为零。每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点的最后,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型。
DLinklist Locate(DLinklist &L, ElemType x){
DNode *p=L->next, *q;
while(p!=NULL && p->data!=x)
p = p->next; //查找值为x的结点
if(!p){
printf("不存在值为x的结点\n");
exit(0);
} else{
p->freq++;
if(p->next!=NULL)
p->next->pred = p->pred;
p->pred->next = p->next; //将p结点从链表上摘下,这包括p->next==NULL
q = p->pred;
if(q!=L && q->freq<=p->freq;)
q = q->pred; //找到q比p频率更高的结点,然后将p插在q之后
p->next = q->next;
q->next->pred = p; //将p结点插入,一定是排在同频率的第一个
p->pred = q;
q->next = p;
}
return p; //返回值为x的结点的指针
}
21、
算法的详细实现步骤如下:
①count=0,p和q指向链表表头结点的下一个结点。
②若p为空,转⑤。
③若count等于k,则q指向下一个结点;否则,count=count+1.
④p指向下一个结点,转②
⑤若count等于k,则查找成功,输出该结点的data域的值,返回1;否则,说明k值超过了线性表的长度,查找失败,返回0.
⑥算法结束。
typedef int ElemType; //链表数据的类型定义
typedef struct LNode{ //链表结点的结构定义
ElemType data; //结点数据
struct LNode *link; //结点链接指针
}LNode, *LinkList;
int Search_k(LinkList list, int k){
LNode *p=list->link, *q=list->link; //指针p、q指示第一个结点
int count = 0;
while(p!=NULL){ //遍历链表直到最后一个结点
if(count<k)
count++; //计数,若count<k,p结点继续向后移动,q结点不动
else
q = q->link; //若count=k,p和q结点同时向后移动
p = p->link;
}
if(count<k)
return 0; //查找失败返回0
else{
printf("%d", q->data); //查找成功,输出该结点的值,并返回1
return 1;
}
}
22、
//定义链表
typedef struct Node{
char data;
struct Node *next;
}SNode;
//求链表的表长
int listlen(SNode *head){
int len = 0;
while(head->next!=NULL){
len++;
head = head->next;
}
return len;
}
//方法一:
SNode* find_addr(SNode *str1, SNode *str2){
SNode *p, *q;
int m, n;
m = listlen(str1); //求str1的长度
n = listlen(str2); //求str2的长度
for(p=str1; m>n; m--) //若m>n,使p指向链表的第m-n+1个结点
p = p->next;
for(q=str2; m<n, n--) //若m<n,使q指向链表的第n-m+1个结点
q = q->next;
while(p->next!=NULL && p->next!=q->next){ //链表不为空,且没有找到共同后缀,将指针p个q同步往后移
p = p->next;
q = q->next;
}
return p->next; //返回共同后缀的起始地址
}
//方法二:
SNode* find_addr(SNode *str1, SNode *str2){
LNode *longlenth, *shortlength;
int m, n, dist;
m = listlen(str1);
n = listlen(str2);
if(m>=n){ //
longstr = str1;
shortstr = str2;
dist = m-n; //
} else{ //
longstr = str2;
shortstr = str1;
dist = n-m; //
}
while(dist--){ //
longstr = longstr->next;
}
while(longstr->next!=NULL && longstr->next!=shortstr->next){
longstr = longstr->next;
shortstr = shortstr->next;
}
return longstr->next;
}
//时间复杂度为O(m+n)
23、
/*
用空间换时间。使用辅助数组记录链表中已出现的数值,从而只需对链表进行一趟扫描。
因为|data|<=n,故辅助数组q的大小为n+1,各元素的初值均为0.依次扫描链表中的各结点,同时检查q[|data|]的值。
若为0,则保留该结点,并令q[|data|]=1;否则,删除该结点
*/
typedef struct node{
int data;
struct node *link;
}NODE;
typedef NODE *PNODE;
void func(PNODE h, int n){
PNODE p=h, r;
int *q, m;
q = (int *)malloc(sizeof(n+1)); //申请n+1个位置的辅助空间
for(int i=0; i<n+1; i++) //数组元素的初值都置为0
*(q+i)=0;
while(p->link!=NULL){
if(p->link->data > 0) //取结点的data的绝对值,并赋值给m
m=p->link->data;
else
m=-p->link->data;
if(*(q+m)==0){ //判断该结点的data是否已经出现过
*(q+m)=1;
p=p->link;
} else{ //之前出现过,删除该结点
r=p->link;
p->link=r->link;
free(r);
}
}
free(q);
}
24、题目:设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回,否则返回NULL。
/*
设置快慢两个指针分别为fast和slow,初始时都指向head指针。
slow每次走一步,即slow=slow->next,fast每次走两步,即fast=fast->next->next。
经过若干次操作后,如果两个指针在环上相遇,说明链表有环;否则,反之。
设头结点到环的入口点距离为a,入口点沿环的方向到相遇点距离为x,环长为r,相遇时fast绕过了n圈。
则有2(a+x)=a+n*r+x,即a=n*r-x,即从头结点到环的入口点的距离等于n倍的环长减去环的入口点到相遇点的距离。
可以设置两个指针,一个指向head,一个指向相遇点,两个指针同步移动(均为一次走一步),相遇点即为环的入口点。
*/
typedef struct LNode{
int data;
struct LNode *next;
}LNode;
LNode *FindLoopStart(LNode *head){
LNode *fast=head, *slow=head; //设置快慢两个指针
while(slow!=NULL && fast->next!=NULL){
slow=slow->next; //慢指针每次走一步
fast=fast->next->next; //快指针每次走两步
if(slow==fast) break; //两个指针相遇,说明链表有闭环
}
if(slow==NULL || fast->next==NULL)
return NULL; //链表没有环,返回NULL
LNode *p=head, *q=slow; //分别指向开始点、相遇点
while(p!=q){
p=p->next;
q=q->next;
}
return p; //返回入口点
}
25、
/*
①找出链表L的中间结点,设置两个指针p和q,指针p每次走一步,指针q每次走两步,当指针q到达链尾时,指针p正好在链表的中间结点。
②然后将L的后半段结点原地逆置。
③从单链表前后两段中依次各取一个结点,按要求重新排。
*/
void change_List(NODE *h){
NODE *p, *q, *r, *s;
p=q=h;
while(q->next!=NULL){ //寻找中间结点
p=p->next; //p每次走一步
q=q->next->next; //q每次走两步
}
q=p->next;
p->next=NULL;
while(q!=NULL){ //将链表后半段逆置
r=q->next; //r指向后半段的下一个结点,保证不断链
q->next=p->next; //利用头插法
p->next=q;
q=r;
}
s=h->next;
q=p->next;
p->next=NULL;
while(q!=NULL){ //将链表后半段的结点插入到指定的位置
r=q->next; //r指向后半段的下一个结点,保证不断链
q->next=s->next; //将q所指结点插入到s所指结点之后
s->next=q;
s=q->next; //s指向前半段的下一个插入点
q=r;
}
}
友情链接: