6-1 用单向链表完成多项式运算
本函数完成一个多项式的输出。题目要求的功能以及本函数具体涉及到多项式链表的创建以及多项式加、减和乘法运算所需的功能模块请在本函数实现之前一并自行完成,并将代码插入在合适的位置。
知识点:
- 本题通过链表实现多项式运算,涉及到链表的创建(初始链表、加法、减法、乘法),增加元素(加法、乘法),删除元素(加法、减法)
- 题目要求将多项式按指数高低输出,因此涉及到链表的排序
参考代码片段1:基于链表的冒泡排序算法
定义实现链表的冒泡排序,根据指数从高到低排序,可参考之前PTA的程序填空题对应的链表冒泡排序算法。
时间复杂度为O(n*n)
polynomial* bubbleSort(polynomial* head)
{/* h 是带空头结点的链表的头指针 */
polynomial* p, *q;
polynomial* h = (polynomial*)malloc(sizeof(polynomial));
h->next = head;
int flag_swap;
if (head == NULL) return NULL;
do{
flag_swap = 0;
p = h;
while (p->next->next){
//冒泡排序的条件。链表的冒泡排序可以直接将每个节点的数据进行交换即可,不需要交换两个节点的地址,方便操作。
if (p->next->exp < p->next->next->exp){
flag_swap++;
int exp = p->next->next->exp;
int coef = p->next->next->coef;
p->next->next->exp = p->next->exp;
p->next->next->coef = p->next->coef;
p->next->exp = exp;
p->next->coef = coef;
}
else p = p->next;
}
} while (flag_swap > 0);
return h->next;
}
参考代码片段2:实现存储多项式链表的创建
时间复杂度为O(n)
/* 提示:递交的答案将会自动插入此处 */
polynomial* create_polynomial() {
int n;
polynomial* head = (polynomial*)malloc(sizeof(polynomial));
if(scanf("%d",&n)!=EOF) {
int a,m;
polynomial* p, *q = head;
int i;
for(i=0;i<n;i++) {
p = (polynomial*)malloc(sizeof(polynomial));
scanf("%d%d",&a,&m);
p->coef = a;
p->exp = m;
p->next = NULL;
q->next = p;
q = q->next;
}
}
polynomial* q = head;
//创建链表后直接将其排序
bubbleSort(q->next);
q = q->next;
//为过滤指数相同的节点,该语句将其删除
while(q->next) {
if(q->exp == q->next->exp) {
polynomial* p = q->next;
q->coef = q->coef + p->coef;
q->next = p->next;
free(p);
}
else {
q = q->next;
}
}
return head->next;
}
参考代码片段3: 实现两个多项式的加法
两个指数自高到低排序的多项式相加,其操作思想类似于归并排序的merge操作,可创建新的链表存储这两个多项式相加的结果。将两部分指数相同的节点进行系数相加,指数不变,存入新的节点中,最后依次存入两个多项式中未操作过的节点即可。时间复杂度为O(n)
polynomial* add_polynomial(polynomial *head_polyA, polynomial *head_polyB) {
polynomial* head = (polynomial*)malloc(sizeof(polynomial)), *q=head;
polynomial* p_A = head_polyA;
polynomial* p_B = head_polyB;
//两个多项式均未被过滤完
while(p_A&&p_B) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
if(p_A->exp==p_B->exp) {
p->coef = p_A->coef+p_B->coef;
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
p_A = p_A->next;
p_B = p_B->next;
}
else if(p_A->exp>p_B->exp) {
p->coef = p_A->coef;
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
p_A = p_A->next;
}
else {
p->coef = p_B->coef;
p->exp = p_B->exp;
p->next = NULL;
q->next = p;
q = q->next;
p_B = p_B->next;
}
}
//此时p_B指向空节点,即B多项式被过滤完,只需要存储A中剩余的节点
while(p_A) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
p->coef = p_A->coef;
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
p_A = p_A->next;
}
while(p_B) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
p->coef = p_B->coef;
p->exp = p_B->exp;
p->next = NULL;
q->next = p;
q = q->next;
p_B = p_B->next;
}
return head->next;
}
参考代码片段4:实现两个多项式的减法
多项式减法操作需考虑两个指数相同的项的合并,如果系数差为0则直接删除。其他操作类似于多项式加法,即第二个多项式系数取相反数即可。时间复杂度为O(n)
polynomial* subtract_polynomial(polynomial *head_polyA, polynomial *head_polyB) {
polynomial* head = (polynomial*)malloc(sizeof(polynomial)), *q=head;
head->next = NULL;
polynomial* p_A = head_polyA;
polynomial* p_B = head_polyB;
while(p_A&&p_B) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
if(p_A->coef == 0) {
p_A = p_A->next;
continue;
}
if(p_B->coef == 0) {
p_B = p_B->next;
continue;
}
if(p_A->exp==p_B->exp) {
p->coef = p_A->coef-p_B->coef;
//抵消后系数为0
if(p->coef==0) {
free(p);
}
else {
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
q->next = NULL;
}
p_A = p_A->next;
p_B = p_B->next;
}
else if(p_A->exp>p_B->exp) {
p->coef = p_A->coef;
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
q->next = NULL;
p_A = p_A->next;
}
else {
p->coef = -p_B->coef;
p->exp = p_B->exp;
p->next = NULL;
q->next = p;
q = q->next;
q->next = NULL;
p_B = p_B->next;
}
}
while(p_A) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
p->coef = p_A->coef;
p->exp = p_A->exp;
p->next = NULL;
q->next = p;
q = q->next;
q->next = NULL;
p_A = p_A->next;
}
while(p_B) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
//系数取相反数
p->coef = -p_B->coef;
p->exp = p_B->exp;
p->next = NULL;
q->next = p;
q = q->next;
q->next = NULL;
p_B = p_B->next;
}
return head->next;
}
参考代码片段5:实现多项式链表的乘法
需要依次遍历两个多项式的每个节点并将对应节点的系数相乘,指数相加,存入新的链表中。其时间复杂度为O(n*n)。
polynomial* multiply_polynomial(polynomial *head_polyA, polynomial *head_polyB) {
polynomial* head = (polynomial*)malloc(sizeof(polynomial)), *q=head;
polynomial* p_A = head_polyA;
polynomial* p_B = head_polyB;
while(p_A) {
while(p_B) {
polynomial* p = (polynomial*)malloc(sizeof(polynomial));
p->coef = p_A->coef*p_B->coef;
p->exp = p_A->exp+p_B->exp;
//printf("%d %d\n",p->coef,p->exp);
p->next = NULL;
q->next = p;
q = q->next;
p_B = p_B->next;
}
p_B = head_polyB;
p_A = p_A->next;
}
//排序,下段语句重复,可删除
q = head;
bubbleSort(q->next);
//指数相乘后可能有重复的指数,需要将重复指数节点合并。
while(q->next) {
if(q->exp == q->next->exp) {
polynomial* p = q->next;
q->next = p->next;
q->coef = q->coef + p->coef;
free(p);
}
else {
q = q->next;
}
}
return head->next;
}
参考代码片段6:实现多项式链表的打印
打印多项式需考虑较多的情况
- 多项式为空,输出0
- 多项式只有一个常数项,直接输出该项。
- 多项式有多个项,需要分为两部分输出结果:
(1) 首项:正数需省略’+‘字符
(2) 非首项:正数需要添加’+'字符 - 多项式需结合上述三种情况下系数为0,1,-1以及指数为0 1时的特殊情况。
针对以上情况,总结如下:
系数 | 指数 | 是否首项 | 输出格式 |
---|---|---|---|
- | - | - | 正常输出 |
0 | - | - | 省略输出 |
1或-1 | 1 | 否 | 省略系数及指数:+x^1或-x^1–>+x或-x |
1或-1 | 1 | 是 | 省略系数、指数及前置加号:+x^1或-x^1–>x或-x |
正数k | 1 | 是 | 省略前置加号:+kx^1–>kx |
正数k | 0 | 否 | 省略x: +kx^0–>+k |
正数k | 0 | 是 | 省略x及前置加号:+k–>k |
负数k | 0 | - | 省略x: kx^0–>k |
另外,测试不通过时,建议尝试以下几种情况:
- 两多项式的指数都不相同,包括指数为0
- 常数(包括0)与1个多项式
- 一个多项式与一个常数(包括0)
- 两多项式的指数都为正数
- 两多项式的指数有正负不一样或一样
- 两多项式的项数不同,指数有正负不一样或一样
- 两多项式的项数和系数任意
void print_polynomial(polynomial *head) {
if(bubbleSort(head) == NULL) {
printf("0\n");
return;
}
polynomial *p = head;
//head
while(p&&p->coef == 0)
p = p->next;
if(p == NULL) {
printf("0\n");
return;
}
if(p->next == NULL&& p->exp==0) {
if(p->coef==-1)
printf("-1\n");
else if(p->coef==1)
printf("1\n");
else printf("%d\n",p->coef);
return;
}
else {
//系数化简
if(p->coef==-1)
printf("-");
else if(p->coef==1)
printf("");
else
printf("%d",p->coef);
//指数化简
if(p->exp == 1)
printf("x");
else if(p->exp == 0)
printf("");
else
printf("x^%d",p->exp);
}
p = p->next;
//middle
while(p&&p->next) {
if(p->coef==0);
else {
if(p->coef==-1)
printf("-");
else if(p->coef==1)
printf("+");
else if(p->coef<0)
printf("%d",p->coef);
else
printf("+%d",p->coef);
if(p->exp == 1)
printf("x",p->exp);
else if(p->exp == 0)
printf("",p->exp);
else
printf("x^%d",p->exp);
}
p = p->next;
}
//tail
if(p==NULL) {
printf("\n");
return;
}
if(p->coef==0);
else {
if(p->coef==1) {
if(p->exp == 1) printf("+x");
else if(p->exp == 0) printf("+1");
else printf("+x^%d",p->exp);
}
else if(p->coef == -1) {
if(p->exp == 1) printf("-x");
else if(p->exp == 0) printf("-1");
else printf("-x^%d",p->exp);
}
else if(p->coef >0){
if(p->exp == 1) printf("+%dx",p->coef);
else if(p->exp == 0) printf("+%d",p->coef);
else printf("+%dx^%d",p->coef,p->exp);
}
else {
if(p->exp == 1) printf("%dx",p->coef);
else if(p->exp == 0) printf("%d",p->coef);
else printf("%dx^%d",p->coef,p->exp);
}
}
printf("\n");
}
6-2 用单向循环链表实现猴子选大王
一群猴子要选新猴王。新猴王的选择方法是:让n只候选猴子围成一圈,从某位置起顺序编号为1~n号。每只猴子预先设定一个数(或称定数),用最后一只猴子的定数d,从第一只猴子开始报数,报到d的猴子即退出圈子;当某只猴子退出时,就用它的定数决定它后面的第几只猴子将在下次退出。如此不断循环,最后剩下的一只猴子就选为猴王。请输出猴子退出圈子的次序以及当选的猴王编号。
参考思路:
本题通过循环链表即可实现。需注意题目要求删除链表时需删除单循环链表的p所指的下一个结点。
参考代码:
//创建一个循环链表,将链表的头部指向最后一只猴子。
linklist *CreateCircle( int n ) {
linklist* head = (linklist*)malloc(sizeof(linklist));
linklist* p=head, *q;
int i=0;
for(;i<n;i++) {
q = (linklist*)malloc(sizeof(linklist));
scanf("%d",&q->mydata);
q->number =i+1;
q->next = NULL;
p->next = q;
p = p->next;
}
head = head->next;
//将尾部节点指向创建的链表的头部,实现循环链表结构
p->next = head;
//将尾部节点作为头部。
return p;
}
/* 删除单循环链表的p所指的下一个结点 */
//删除链表时需访问当前删除的节点对应的定数,因此不需要释放空间。
linklist *DeleteNext(linklist *p) {
if(p->next!=p) {
linklist *q = p->next;
p->next = q->next;
printf("Delete No:%d\n",q->number);
return q;
}
else {
return NULL;
}
}
int KingOfMonkey(int n,linklist *head) {
linklist* p=head, *q;
//定数
int settled_number = p->mydata;
while(p!=p->next) {
int cnt = 0;
//循环找到settled_number找到下一个猴子
while(cnt<settled_number-1) {
cnt++;
p = p->next;
}
//将对应的猴子移出链表
q = DeleteNext(p);
if(q)
settled_number = q->mydata;
}
return p->number;
}
6-3 单链表分段逆转
给定一个带头结点的单链表和一个整数K,要求你将链表中的每K个结点做一次逆转。例如给定单链表 1→2→3→4→5→6 和 K=3,你需要将链表改造成 3→2→1→6→5→4;如果 K=4,则应该得到 4→3→2→1→5→6。
参考思路及代码:
void K_Reverse( List L, int K )//前k个结点前插,后面若还有的话就连上去
{
int cnt;
int len = 0;
List p=L, r, s, t ,q = p->Next,tmp;
while(q)//q指向首结点
{
q = q->Next;
len++;
}
if(!len || K>len||K<=1)//len=0根本不需要逆转
return;
for(int i=0;i<len/K;i++)
{
cnt=1;
r= p->Next;//r是已经逆转的首节点
s =r->Next;//s是未逆转链表的首结点
t = r;//t指向每一轮逆转的第一个结点,逆转后就变成了最后一个结点
while(cnt!=K)
{
tmp = s->Next;
s->Next = r;
r =s;
s =tmp;
cnt++;
}//跳出while()说明一轮逆转结束
p ->Next = r;//p->next指向新的头节点
t ->Next =s;//逆转后的k个结点的尾结点指向下一个逆转的k个节点尾节点
p = t;//p指向k个结点的尾结点
}
}