二.2王道课后题
第四部分
20
我的想法:每次更新完freq的值后,遍历链表中freq的值,最大的排在最前,并且排当前的位置.
由于依次插入的特性,一定是大的在前面,只要向前找就行
找到等于x的结点后,先将他摘下来,然后依次向前查找(因为后面的次序一定小于它),一直到头结点之前,插入到频率大于它的后面(实现值相同,先访问的在前)
这是一道包括了查找,删除,增加,以及初始化等操作的综合题
//typedef struct LinkList{
//报错
typedef struct LNode{
int data;
int freq; //频率
struct LNode *prior;
struct LNode *next;
}LNode,*LinkList;
void insertListForInit(LinkList &l){
l=new LNode;
l->prior=NULL;
l->next=NULL;
LNode *r=l;//尾指针
int a;
cout<<"生成单链表,输入999结束"<<endl;
scanf("%d",&a);
while(a!=999){
LNode *p=new LNode;
p->data=a;
p->freq=0;//初始频率0
p->prior=r;
r->next=p;
r=p;
scanf("%d",&a);
}
r->next=NULL;
}
代码
LNode* Locate(LinkList &l,int x){
LNode *p=l->next;
//如果有,找到了x的位置,没找到p为空
while(p!=NULL && p->data!=x){
p=p->next;
}
//如果没找到
if(!p){
cout<<"没有x"<<endl;
exit(0);//我感觉这里就写return NULL;就行
}
//找到了继续,之前的返回了,不用写else
p->freq++;
//摘出p
//保留p的前置指针
LNode *q=p->prior;
p->next->prior=p->prior;
p->prior->next=p->next;
//注意是小于等于,等于是不行的
//找到插入位置q后
while(q!=l &&q->freq<=p->freq){
q=q->prior;
}
//q后面插入q
q->next->prior=p;
p->next=q->next;
q->next=p;
p->prior=q;
return p;
}
演示
LinkList l;
cout<<"初始化,建议1234"<<endl;
insertListForInit(l);
Locate(l,1);
cout<<"输入1后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
Locate(l,2);
Locate(l,2);
cout<<"两次输入2后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
Locate(l,3);
Locate(l,3);
cout<<"两次输入3后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
结果
初始化,建议1234
生成单链表,输入999结束
1
2
3
4
999
输入1后,当前最前面的是1他的次序是1
两次输入2后,当前最前面的是2他的次序是2
两次输入3后,当前最前面的是3他的次序是3
21
我的想法比较简单,两遍遍历,第一遍知道长度,然后长度-k+1就是倒数第k个结点的位序.
这是我看了书之后的想法
倒数第k个元素,距离最后相差k-1个元素
当p从0走k个位置的时候,q开始出发
p和q差距k-1个元素
当p走到最后的时候,q到了倒数第k个位置
王道书上
p是走到了最后的后一个,此时p与q相差k个元素
代码
王道感觉比较完整
int findCountDownK02(LinkList l,int k){
int count=0;
LNode *p=l->next;
LNode *q=l->next;//p先走,q后走
while(p!=NULL){
if(count<k){
count++;
}else{
q=q->next;
}
p=p->next;
}
if(count<k){
return 0;
}
cout<<"倒数第"<<k<<"个元素是"<<q->data<<endl;
return 1;
}
int findCountDownK(LinkList l,int k){
LNode *p=l->next;
LNode *q=l->next;//p先走,q后走
int i=1;
while(p!=NULL && i<k){//p先走
i++;
p=p->next;
}
if(p==NULL){
return 0;//没有k个元素
}
while(p->next!=NULL){//一起走
q=q->next;
p=p->next;
}
cout<<"倒数第"<<k<<"个元素是"<<q->data<<endl;
return 1;
}
演示
LinkList l;
initList_H(l);
insertListByOrder_H(l,1,1);
insertListByOrder_H(l,2,2);
insertListByOrder_H(l,3,3);
insertListByOrder_H(l,4,4);
insertListByOrder_H(l,5,5);
insertListByOrder_H(l,6,6);
printList_H(l);
cout<<"倒数第3个"<<endl;
if(findCountDownK(l,1)){
cout<<"找到了"<<endl;
}else{
cout<<"没找到"<<endl;
}
结果
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
单链表的第6个值是:6
倒数第3个
倒数第1个元素是6
找到了
22
我一开始想成是字符串匹配问题,想复杂了,这里只是找共同的后缀.
先分别算出两个的长度,尾端对齐,从短的那边开始依次找,相同的即可.
注意要找到公共结点长度的前一个,比较next是否一样
代码
LNode* findCommon02(LinkList a, LinkList b){
LNode *p=a;
int lenA=length_H(a);
LNode *q=b;
int lenB=length_H(b);
//改善 不用if判断,通过for循环条件控制
//从头结点开始,因为要判断next,走长度差距位
for(;lenA>lenB;lenA--){
p=p->next;
}
for(;lenA<lenB;lenB--){
q=q->next;
}
//两者同时为空和非空,判断一个即可
while(p->next!=q->next && p->next!=NULL ){
p=p->next;
q=q->next;
}
if(p!=NULL){
return p;
}
return NULL;
}
我写的丑陋代码,可无视,我想错了,应该是公共结点,看地址
LNode* findCommon(LinkList a, LinkList b){
LNode *p=a->next;
int lenA=0;
LNode *q=b->next;
int lenB=0;
while(p!=NULL){
lenA++;
p=p->next;
}
while(q!=NULL){
lenB++;
q=q->next;
}
//改善
if(lenA>lenB){
p=a->next;
q=b->next;
for(int i=0;i<(lenA-lenB);i++){
p=p->next;
}
while(p->data!=q->data && p!=NULL ){
p=p->next;
q=q->next;
}
}
if(p!=NULL){
return p;
}
return NULL;
}
void insertNodeNext(LNode *a,LNode *b){
b->next=a->next;
a->next=b;
}
演示
LinkList l;
initList_H(l);
insertListByOrder_H(l,1,1);
insertListByOrder_H(l,2,2);
insertListByOrder_H(l,3,3);
//公共结点456
LNode *h=(LNode *)malloc(sizeof(LNode));
h->data=4;
LNode *j=(LNode *)malloc(sizeof(LNode));
j->data=5;
LNode *k=(LNode *)malloc(sizeof(LNode));
k->data=6;
//找到第3个位置的结点a
LNode *a=getValueByOrder_H(l,3);
//在a后面插入公共结点456
insertNodeNext(a,h);
insertNodeNext(h,j);
insertNodeNext(j,k);
cout<<"单链表1"<<endl;
printList_H(l);
LinkList l2;
initList_H(l2);
insertListByOrder_H(l2,1,7);
insertListByOrder_H(l2,2,8);
//找到第2个位置的结点b
LNode *b=getValueByOrder_H(l2,2);
//在b后面插入
insertNodeNext(b,h);
insertNodeNext(h,j);
insertNodeNext(j,k);
cout<<"单链表2"<<endl;
printList_H(l2);
if(findCommon02(l,l2)!=NULL){
cout<<"共同的数字是"<<findCommon(l,l2)->data<<endl;
}else{
cout<<"没有"<<endl;
}
结果
单链表1
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
单链表的第6个值是:6
单链表2
单链表的第1个值是:7
单链表的第2个值是:8
单链表的第3个值是:4
单链表的第4个值是:5
单链表的第5个值是:6
共同的数字是4
23
我的想法是带标志位(初始0)的遍历删除.时间复杂度o(n),每次遍历到,标志位++,当标志位>1的时候,删除改结点
我的想法不对,不是删除和某个数相同的绝对值,而是所有绝对值相同的都删除
时间复杂度尽可能高,怎么实现??
时间上尽可能高==空间换时间
书上:通过辅助数组,由于长度<=n,需要n+1位
代码
//这题用的指针来表示数组,可以学习一下
void deleteAbsEqual02(LinkList &l,int n){
//int a[n+1];
int* a=new int[n+1];
//初始化为0
for(int i=0;i<=n;i++){
*(a+i)=0;
}
LNode *p=l;
//删除一遍都要保留前指针
while(p->next!=NULL){
//三目运算符,也可以用abs(p->next->data)
int m=p->next->data>0?p->next->data:-p->next->data;
//完全可以用a[m]
if(*(a+m)==0){
*(a+m)=1;
p=p->next;
}
else{
//删除结点
LNode *r=p->next;
p->next=r->next;
free(r);
}
}
//数组是申请的
delete[] a;
}
错误示范
void deleteAbsEqual(LinkList &l,int x){
LNode *p=l;
int flag=0;
while(p->next!=NULL){
if(abs(p->next->data)==x){
flag++;
}
//删除该结点
if(flag>1 && abs(p->next->data)==x){
//别忘了释放空间
LNode *r=p->next;
p->next=p->next->next;
free(r);
}else{
p=p->next;
}
}
}
演示
LinkList l;
initList_H(l);
insertListByOrder_H(l,1,1);
insertListByOrder_H(l,2,3);
insertListByOrder_H(l,3,-3);
insertListByOrder_H(l,4,3);
insertListByOrder_H(l,5,5);
insertListByOrder_H(l,6,-5);
insertListByOrder_H(l,7,3);
printList_H(l);
cout<<"删除重复的3的绝对值"<<endl;
deleteAbsEqual02(l,3);
printList_H(l);
结果
单链表的第1个值是:1
单链表的第2个值是:3
单链表的第3个值是:-3
单链表的第4个值是:3
单链表的第5个值是:5
单链表的第6个值是:-5
单链表的第7个值是:3
删除重复的3的绝对值
单链表的第1个值是:1
单链表的第2个值是:3
24
若有环,最后一定会回到某个位置,他的next是前面出现过的值.
我的想法:辅助数组,保存所有的地址
比较重要的是,循环那段代码
代码
LNode* findCircle(LinkList l){
LNode *fast=l;
LNode *slow=l;
//fast!=NULL slow!=NUll也不行
//有可能空指针
//这个循环 很有讲究
while(slow!=NULL && fast->next!=NULL){
fast=fast->next->next;//走两步
slow=slow->next;
if(slow==fast) break; //相遇
}
while(slow==NULL ||fast->next==NULL) return NULL;
LNode *p1=l; //从头走
LNode *p2=fast; //相遇点
while(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;
演示
LinkList l;
initList_H(l);
insertListByOrder_H(l,1,1);
insertListByOrder_H(l,2,2);
insertListByOrder_H(l,3,3);
LNode *h=(LNode *)malloc(sizeof(LNode));
h->data=4;
LNode *j=(LNode *)malloc(sizeof(LNode));
j->data=5;
//找到第3个位置的结点a
LNode *a=getValueByOrder_H(l,3);
//在a后面插入公共结点45
insertNodeNext(a,h);
insertNodeNext(h,j);
LNode *b=getValueByOrder_H(l,5);
b->next=a;
//构建了一个12345---|
// ^-----|
//12345->3的环
//打印死循环
//printList_H(l);
cout<<"环的起点的值是"<<findCircle(l)->data<<endl;
结果
环的起点的值是3
25
想不到啥
空间复杂度1–>不能用数组,或者新建一个链表
书上:先遍历找到中间元素(两个指针),后半部分逆置(a1 a2 a3 … an an-1 an-2…)
依次从头和中间选择,插入
找中间元素的思想,也很重要
代码
void change(LinkList &l){
LNode *mid=l,*end=l;
//找中间位置 mid指向中间 end指向最后
while(end->next!=NULL){
mid=mid->next;
end=end->next;
if(end->next!=NULL){
end=end->next;
}
}
LNode *p=mid->next; // p为中间结点next为后半段起始
//逆置后半段 头插法,先让p->next=NULL
mid->next=NULL;
LNode *r;
while(p!=NULL){
r=p->next; //不断链
p->next=mid->next;
mid->next=p;
p=r;
}
//依次插入
LNode *s=l->next; //第一个结点
LNode *q=mid->next;//后半段开始结点
mid->next=NULL;//作为尾结点
while(q!=NULL){
r=q->next; //不断链
q->next=s->next;
s->next=q;
s=q->next;
q=r;
}
}
演示
LinkList l;
initList_H(l);
insertListByOrder_H(l,1,1);
insertListByOrder_H(l,2,2);
insertListByOrder_H(l,3,3);
insertListByOrder_H(l,4,4);
insertListByOrder_H(l,5,5);
printList_H(l);
cout<<"变换"<<endl;
change(l);
printList_H(l);
}
结果
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
变换
单链表的第1个值是:1
单链表的第2个值是:5
单链表的第3个值是:2
单链表的第4个值是:4
单链表的第5个值是:3