大数相乘-写的比较麻烦

#include<stdio.h>
#include<string.h>


void strtoint(char *str, int * cheng, int n)
{
int i;
for(i = 0;i<n;i++)
{
cheng[n-i-1] = str[i] -'0';
}
for(i = 0;i<n;i++)
{
printf("%d ",cheng[i]);
}
printf("\n");
}
int  getresult(int *cheng1,int * cheng2,int len1,int len2,int *sum)
{
int i,j;
int tmp = 0;


for(i = 0;i<len1+len2;i++)
{
sum[i] = 0;
}


for(i = 0 ;i<len2;i++)
{
for(j=0;j<len1;j++)
{
sum[i+j] +=  (cheng2[i] * cheng1[j] %10 + tmp);
printf("sum1[%d] = %d\n",i+j,sum[i+j]);
int tmp1 = sum[i+j];
sum[i+j] = sum[i+j] %10;
printf("sum[%d] = %d\n",i+j,sum[i+j]);
tmp  = tmp1 /10;
printf("tmp = %d\n",tmp);
tmp += cheng2[i] * cheng1[j]/10;
printf("tmp2 = %d\n",tmp);
}
if(tmp != 0) sum[i+j-1] = tmp;


int m;
for(m = 0;m<i+j;m++)
{
printf("%d ",sum[m]);
}
printf("\n");


}
if(tmp == 0) return i+j -2;
else {
return i+j -1   ;
}
}
int main()
{
char str1[1024];
char str2[1024];
char strsum[1024];
int cheng1[1024];
int cheng2[1024];
int sum[1024];
scanf("%s",str1);
scanf("%s",str2);
int len1 = strlen(str1);
int len2 = strlen(str2);
strtoint(str1,cheng1,len1);
strtoint(str2,cheng2,len2);
int num = getresult(cheng1,cheng2,len1,len2,sum);
int i;
printf("num = %d\n",num);
for(i=num;i>=0;i--)
{
printf("%d ",sum[i]);
}
}
<think>好的,用户需要我用C语言实现基于链表的大数处理程序,包含输入存储、加减乘,并且要求结果正确、界面友好和效率高。首先,我需要考虑如何用链表来存储大数大数可能超过普通整型的范围,所以每个节点存储一位数字比较合理。不过,用户提到的引用里,C++的例子用的是每个节点存一位,而C语言的双向链表也是类似结构,所以可能采用双向链表更方便操作,尤其是在减法的时候可能需要反向遍历。 接下来是输入和存储。用户需要友好的界面,所以得设计一个输入函数,能够读取用户输入的数字字符串,然后逐个字符转换为整数,存入链表。这里要注意处理前导零的情况,还有输入的合法性,比如非数字字符需要提示错误。双向链表的每个节点有前驱和后继指针,这样在加减法时可以从低位到高位处理,但存储的时候可能需要逆序存储,比如输入"1234"存为4->3->2->1,这样进位处理更方便。不过用户引用的代码里,乘法实现中似乎是从头开始相乘,可能需要考虑顺序问题,所以存储方式需要统一。 然后是加法。加法需要从最低位开始逐位相加,处理进位。因为链表是双向的,可以从尾部开始向前遍历,同时构建结果链表。需要考虑两个数的位数不同时的处理,以及最后的进位是否新增节点。 减法相对复杂,因为需要处理借位,还要判断两个数的大小,确保用大数减去小数,否则结果会是负数。所以需要先实现一个比较函数,判断两个大数的大小。如果被减数小于减数,则交换它们,并在结果前加上负号。减法过程中,借位的处理需要小心,特别是连续借位的情况。 乘法的话,参考用户提供的C++代码,每个节点相乘,然后累加到相应的位置。这里可能需要双层循环,外层遍历被乘数的每一位,内层遍历乘数的每一位,乘积的结果加到对应的位置,并处理进位。由于链表是双向的,可能需要从尾部开始处理,或者按顺序处理,但要注意数位的对齐。例如,被乘数的第i位和乘数的第j位相乘,结果应该放在第i+j的位置上。这里可能需要预先创建一个足够长的结果链表,初始化为0,然后逐步累加。 关于效率,用户提到要高效,所以需要考虑算法的时间复杂度。加法减法都是O(n),乘法如果是普通的竖式乘法,则是O(n^2),这可能不够高效,但对于教学或简单应用来说已经足够。如果需要更高效率,可以考虑Karatsuba算法,但实现起来复杂,可能用户的需求是基础实现,所以先按普通乘法处理。 界面友好方面,需要提供清晰的输入提示,输出结果时要正确显示大数,包括可能的负号。输入函数需要处理用户输入的错误,比如非数字字符,并提示重新输入。此外,程序应该有菜单驱动,让用户选择加减乘运算,并显示结果。 现在要考虑结构体的定义。根据引用中的C语言结构体,定义每个节点包含digit,前后指针,而大数结构体包含头尾指针和位数。所以结构体可能如下: struct Node { int digit; struct Node *prev; struct Node *next; }; struct BigNumber { int sign; // 正负号 struct Node *head; struct Node *tail; int length; }; 不过是否需要存储符号位,取决于是否需要处理负数。用户的引用中提到加减乘除,可能暂时不需要负数,但减法可能产生负数结果,所以需要考虑符号处理。 输入函数需要将字符串转换为链表,比如输入"12345",存储为5->4->3->2->1,头节点是5,尾节点是1。但这样在输出时需要反向遍历,从尾到头,或者头到尾,但存储是逆序的。或者可能存储为1->2->3->4->5,这样高位在前,但处理进位可能需要从尾部添加节点,比如加法时进位会在最后面。这里可能需要统一存储方式,比如逆序存储,即低位在链表头部,高位在尾部,这样加减乘的时候处理进位更方便,因为操作是从低位开始,逐位处理。例如,加法时,两个链表的当前节点相加,进位传递到下一个节点。因此,逆序存储可能更合适。 例如,数字123存储为3 -> 2 -> 1,这样在相加时,从头节点开始处理,依次是3+对应节点的值,进位加到下一位。这样处理起来比较自然,不需要反向遍历链表。 接下来是具体函数的实现: 1. 输入函数:读取字符串,检查合法性,创建链表,逆序存储每位数字。 2. 加法函数:遍历两个链表的每一位,相加,处理进位。 3. 减法函数:先比较大小,决定符号,然后逐位相减,处理借位。 4. 乘法函数:双重循环,每位相乘,结果累加到正确的位置,处理进位。 5. 输出函数:将链表逆序输出为字符串,注意符号。 对于乘法,参考用户提供的C++代码,他们创建了一个结果链表,长度是两数长度之和,初始化为0。然后每个被乘数的位和乘数的位相乘,结果加到对应的位置,并处理进位。例如,被乘数的第i位(从0开始)和乘数的第j位相乘,结果应该加到结果的第i+j位。这里可能需要将结果链表视为按顺序存储每一位,但因为是逆序存储,被乘数的第i位对应链表中的第i个节点,乘数的第j位对应链表中的第j个节点,那么他们的乘积会影响结果的第i+j位。所以,在实现时,需要为结果链表预先分配足够的空间,初始化为0,然后在每次乘积后更新相应的节点值,并处理进位。 例如,被乘数a的节点pa(第i位),乘数b的节点pb(第j位),乘积为pa->digit * pb->digit,这个值应该加到结果链表的第i+j位的节点上。然后处理进位,进位会加到i+j+1位。由于链表是逆序存储的,第i位在a中是第i个节点,从头部开始数,同理b的第j个节点,那么结果的第i+j位可以通过同时移动i+j步吗?或者需要另一种方式定位。可能更有效的是,在创建结果链表时,每个节点初始化为0,然后遍历a的每个节点,对于每个a的节点i,遍历b的每个节点j,计算乘积,然后找到结果链表中对应的i+j的位置,将乘积加上去,并处理进位。 不过,这样的实现可能比较麻烦,因为链表不支持随机访问,必须顺序遍历到i+j的位置。所以,可能需要在乘法函数中使用一个临时数组来记录各位的累加值,然后再将数组转换为链表,这样可能更高效。但用户要求使用链表,所以可能需要另一种方法。 或者,可以像引用中的C++代码那样,使用两个循环:外层循环遍历被乘数的每一位,内层循环遍历乘数的每一位,并在结果链表的对应位置累加乘积。在C++的例子中,结果链表是预先初始化为足够长度的0,然后每次乘积后更新对应的节点,进位处理是立即进行的。这里可能需要类似的方法,在C语言中,创建结果链表时,长度是size + num.size(即被乘数和乘数的位数之和),然后每个节点初始化为0。然后对于每个被乘数的节点pa,从头部开始(即低位),乘数的节点pb,从头部开始,同时移动pr指针到结果链表的对应位置,即初始时pr指向结果链表的头部,然后在每次外层循环开始时,pr的位置需要调整到当前被乘数位的位置。例如,外层循环处理第i位时,pr从结果链表的第i位开始,内层循环处理乘数的每一位j,结果的位置是i+j。但链表无法直接索引,所以可能需要每次外层循环开始时,pr移动到当前i的位置,然后内层循环中,每处理一个乘数位,pr移动到下一个节点,这样i+j的位置就被正确累加。例如,外层循环中,pa是a的第i位,内层循环pb是b的第j位,乘积加到结果的第i+j位。在链表结构中,结果链表在创建时是size + num.size长度,初始为0。当处理pa时,pr初始指向结果链表的第i位,然后随着pb的循环,每次处理一个j,pr移动到下一个节点,相当于i+j的位置。这可能需要在外层循环中,保存pr的起始位置,然后每次内层循环后,pr回到起始位置的下一个节点。这可能有点复杂,需要仔细处理指针的移动。 例如,在C++的代码中,res的head被初始化为足够长的链表,每个节点是0。然后对于每个pa,temp = pr(即当前外层循环的起始位置),然后对于每个pb,乘积加上进位和当前pr的值,然后pr移动到下一个节点,这样相当于在结果链表中,每个pa对应的乘数循环处理完后,pr回到temp->next,即下一个外层循环的起始位置。这可能类似于我们在C语言中需要实现的逻辑。 因此,在C语言中,实现乘法函数时,可以创建结果链表,长度是a的位数加上b的位数,初始为0。然后,对于a的每个节点(从head开始,即低位),遍历b的每个节点,计算乘积,并累加到结果链表的对应位置,同时处理进位。例如,使用两个指针,外层循环pa遍历a的每个节点,内层循环pb遍历b的每个节点,同时维护一个当前结果节点的指针pr,初始时,每个外层循环开始时,pr指向结果链表的当前起始位置(比如,第i位的位置),然后每次内层循环,pr移动一位,处理第i+j位。具体实现可能需要一个临时指针来保存起始位置,以便每次外层循环后,pr可以回到正确的位置。 现在,关于符号处理,加法减法需要考虑两个数的符号,比如同为正是相加,不同符号则转换为减法。但用户的需求可能暂时不需要处理负数,只需处理非负数的加减乘。但减法可能产生负数,所以需要比较两个数的大小,并在结果中添加负号。例如,实现一个比较函数,比较两个大数的绝对值大小,如果a < b,则交换并标记结果为负。 对于界面友好,需要有一个菜单系统,让用户选择操作,输入两个大数,然后显示结果。输入时,可能需要循环读取字符,直到遇到换行符,并过滤非数字字符。例如,用户输入"123a56",应该提示错误,并要求重新输入。 代码结构的大致步骤: 1. 定义结构体Node和BigNumber。 2. 实现输入函数:读取字符串,创建逆序链表,处理符号(如果有)。 3. 实现加法函数:处理两个正数相加。 4. 实现减法函数:比较大小,处理借位,处理符号。 5. 实现乘法函数:使用双重循环累加乘积。 6. 实现输出函数:将链表逆序转换为字符串,处理符号。 7. 实现比较函数:比较两个大数的绝对值大小。 8. 实现菜单界面,让用户选择操作,输入数字,显示结果。 可能的难点在于链表的操作,尤其是乘法的实现,以及进位借位的处理。此外,内存管理需要注意,避免内存泄漏,每次创建节点后要正确释放。 现在,测试各个函数的情况: 加法:例如,999 + 2 = 1001。链表存储为9->9->9和2。相加时,从最低位开始,9+2=11,进位1,然后下一位9+0+1=10,进位1,最后9+0+1=10,进位1,最后添加进位1,结果为1->0->0->1,逆序输出为1001。 减法:例如,1000 - 999 = 1。比较发现1000 > 999,所以直接相减。每位相减,借位处理正确。 乘法:123 * 45 = 5535。存储为3->2->1和5->4。乘积计算时,3*5=15,放在第0位,进位1;3*4=12 + 进位1=13,第1位3,进位1;接着2*5=10加到第1位变成3+10=13,进位1,然后2*4=8+进位1=9,放在第2位;1*5=5加到第2位变成9+5=14,进位1,1*4=4+1=5放在第3位。最终结果链表是5->3->5->5,逆序输出5535。 现在,编代码的大致步骤: 结构体定义: typedef struct Node { int digit; struct Node *prev; struct Node *next; } Node; typedef struct BigNumber { int sign; // 0 正,1 负 Node *head; Node *tail; int length; } BigNumber; 输入函数: BigNumber* input_big_number() { 读取字符串,检查符号,过滤非数字字符,创建链表,逆序存储。 } 加法函数: BigNumber* add(BigNumber *a, BigNumber *b) { 处理符号,如果符号不同则转换为减法。 创建结果链表,逐位相加,处理进位。 } 减法函数: BigNumber* subtract(BigNumber *a, BigNumber *b) { 比较绝对值大小,确定符号。 逐位相减,处理借位。 } 乘法函数: BigNumber* multiply(BigNumber *a, BigNumber *b) { 创建结果链表,长度a->length + b->length,初始为0。 双重循环,逐位相乘,累加到结果链表,处理进位。 } 比较函数: int compare(BigNumber *a, BigNumber *b) { 比较位数,位数相同则逐位比较。 } 输出函数: void print_big_number(BigNumber *num) { 处理符号,逆序遍历链表,输出数字。 } 内存释放函数: void free_big_number(BigNumber *num) { 释放所有节点。 } 菜单函数: void menu() { 显示选项,读取输入,调用相应函数。 } 需要处理的情况: - 输入为0时,链表应有一个节点0。 - 处理前导零,例如输入"000123"应存储为3->2->1->0->0->0,但需要优化为123。 - 符号处理,但用户可能暂时不需要负数,但减法可能产生负数,所以需支持符号。 可能的问题:链表操作时,指针移动错误,导致结果不正确;内存泄漏;未处理进位或借位导致结果错误。 现在,编具体的代码示例: 例如,输入函数的实现: BigNumber* input_big_number() { char buffer[1024]; printf("请输入一个大数: "); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return NULL; } // 处理符号 int sign = 0; int start = 0; if (buffer[0] == '-') { sign = 1; start = 1; } else if (buffer[0] == '+') { start = 1; } // 过滤非数字字符 BigNumber *num = (BigNumber*)malloc(sizeof(BigNumber)); num->sign = sign; num->head = NULL; num->tail = NULL; num->length = 0; for (int i = strlen(buffer) - 1; i >= start; i--) { if (isdigit(buffer[i])) { int digit = buffer[i] - '0'; Node *node = (Node*)malloc(sizeof(Node)); node->digit = digit; node->prev = NULL; node->next = num->head; if (num->head != NULL) { num->head->prev = node; } else { num->tail = node; } num->head = node; num->length++; } else if (buffer[i] != '\n' && buffer[i] != '\r') { printf("输入包含非法字符,请重新输入。\n"); free_big_number(num); return input_big_number(); } } // 去除前导零 while (num->length > 1 && num->tail->digit == 0) { Node *temp = num->tail; num->tail = temp->prev; num->tail->next = NULL; free(temp); num->length--; } if (num->length == 0) { // 输入为空或全是非数字字符 Node *node = (Node*)malloc(sizeof(Node)); node->digit = 0; node->prev = node->next = NULL; num->head = num->tail = node; num->length = 1; num->sign = 0; } return num; } 加法函数需要考虑进位: BigNumber* add(BigNumber *a, BigNumber *b) { // 假设a和b都是正数 BigNumber *result = (BigNumber*)malloc(sizeof(BigNumber)); result->sign = 0; result->head = result->tail = NULL; result->length = 0; Node *pa = a->head; Node *pb = b->head; int carry = 0; while (pa != NULL || pb != NULL || carry != 0) { int sum = carry; if (pa != NULL) { sum += pa->digit; pa = pa->next; } if (pb != NULL) { sum += pb->digit; pb = pb->next; } carry = sum / 10; Node *node = (Node*)malloc(sizeof(Node)); node->digit = sum % 10; node->next = NULL; node->prev = result->tail; if (result->tail != NULL) { result->tail->next = node; } else { result->head = node; } result->tail = node; result->length++; } return result; } 乘法函数的实现参考: BigNumber* multiply(BigNumber *a, BigNumber *b) { BigNumber *result = create_empty_number(a->length + b->length); Node *pa = a->head; Node *pr_start = result->head; for (int i = 0; pa != NULL; pa = pa->next, i++) { int carry = 0; Node *pb = b->head; Node *pr = pr_start; for (int j = 0; pb != NULL; pb = pb->next, j++) { int product = pa->digit * pb->digit + carry + pr->digit; pr->digit = product % 10; carry = product / 10; if (pr->next == NULL && (carry != 0 || pb->next != NULL)) { // 需要扩展结果链表,但预先创建了足够长度,所以可能不需要 // 这里假设结果链表长度足够 } pr = pr->next; } // 处理剩余进位 while (carry > 0) { pr->digit += carry; carry = pr->digit / 10; pr->digit %= 10; pr = pr->next; } pr_start = pr_start->next; } // 去除前导零 while (result->length > 1 && result->tail->digit == 0) { Node *temp = result->tail; result->tail = temp->prev; result->tail->next = NULL; free(temp); result->length--; } return result; } 但这里可能需要一个创建空链表的函数,预先分配足够的节点: BigNumber* create_empty_number(int length) { BigNumber *num = (BigNumber*)malloc(sizeof(BigNumber)); num->sign = 0; num->head = NULL; num->tail = NULL; num->length = 0; for (int i = 0; i < length; i++) { Node *node = (Node*)malloc(sizeof(Node)); node->digit = 0; node->prev = num->tail; node->next = NULL; if (num->tail != NULL) { num->tail->next = node; } else { num->head = node; } num->tail = node; num->length++; } return num; } 这可能存在错误,需要调试。此外,乘法中的指针移动需要仔细处理。 总之,实现这样的程序需要仔细处理链表的每个操作,确保指针正确移动,进位借位正确处理,以及内存管理正确。此外,界面友好需要良好的输入输出提示和错误处理。</think>以下是用C语言实现基于链表的大数运算的详细解决方案,包含输入存储、加减乘运算和友好界面: ### 一、数据结构定义 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> typedef struct Node { int digit; struct Node *prev; struct Node *next; } Node; typedef struct BigNumber { int sign; // 0: 正数 1: 负数 Node *head; Node *tail; int length; } BigNumber; ``` ### 二、核心功能实现 #### 1. 输入与存储 ```c BigNumber* create_number(const char* str) { BigNumber* num = (BigNumber*)malloc(sizeof(BigNumber)); num->head = num->tail = NULL; num->length = 0; num->sign = 0; int start = 0; if (str[0] == '-') { num->sign = 1; start = 1; } // 逆序存储数字,便于运算 for (int i = strlen(str)-1; i >= start; i--) { if (isdigit(str[i])) { Node* node = (Node*)malloc(sizeof(Node)); node->digit = str[i] - '0'; node->prev = NULL; node->next = num->head; if (num->head) num->head->prev = node; else num->tail = node; num->head = node; num->length++; } } return num; } ``` #### 2. 加法运算 ```c BigNumber* add(BigNumber* a, BigNumber* b) { BigNumber* result = (BigNumber*)malloc(sizeof(BigNumber)); result->head = result->tail = NULL; result->length = 0; Node *pa = a->head, *pb = b->head; int carry = 0; while (pa || pb || carry) { int sum = carry; if (pa) { sum += pa->digit; pa = pa->next; } if (pb) { sum += pb->digit; pb = pb->next; } Node* node = (Node*)malloc(sizeof(Node)); node->digit = sum % 10; node->next = NULL; if (result->tail) { result->tail->next = node; node->prev = result->tail; } else { result->head = node; node->prev = NULL; } result->tail = node; result->length++; carry = sum / 10; } return result; } ``` #### 3. 减法运算 ```c // 比较绝对值大小 int compare(BigNumber* a, BigNumber* b) { if (a->length != b->length) return a->length > b->length ? 1 : -1; Node *pa = a->tail, *pb = b->tail; while (pa) { if (pa->digit != pb->digit) return pa->digit > pb->digit ? 1 : -1; pa = pa->prev; pb = pb->prev; } return 0; } BigNumber* subtract(BigNumber* a, BigNumber* b) { BigNumber* result = (BigNumber*)malloc(sizeof(BigNumber)); result->head = result->tail = NULL; result->length = 0; int cmp = compare(a, b); if (cmp < 0) { // 确保a >= b BigNumber* temp = a; a = b; b = temp; result->sign = 1; } Node *pa = a->head, *pb = b->head; int borrow = 0; while (pa || pb) { int x = pa ? pa->digit : 0; int y = pb ? pb->digit : 0; if (borrow) { x -= 1; borrow = 0; } if (x < y) { x += 10; borrow = 1; } Node* node = (Node*)malloc(sizeof(Node)); node->digit = x - y; node->next = NULL; if (result->tail) { result->tail->next = node; node->prev = result->tail; } else { result->head = node; } result->tail = node; result->length++; if (pa) pa = pa->next; if (pb) pb = pb->next; } // 去除前导零 while (result->length > 1 && result->tail->digit == 0) { Node* temp = result->tail; result->tail = temp->prev; result->tail->next = NULL; free(temp); result->length--; } return result; } ``` #### 4. 乘法运算 ```c BigNumber* multiply(BigNumber* a, BigNumber* b) { BigNumber* result = (BigNumber*)malloc(sizeof(BigNumber)); result->head = result->tail = NULL; result->length = a->length + b->length; // 初始化结果链表 for (int i = 0; i < result->length; i++) { Node* node = (Node*)malloc(sizeof(Node)); node->digit = 0; node->prev = result->tail; node->next = NULL; if (result->tail) result->tail->next = node; else result->head = node; result->tail = node; } Node *pa = a->head, *pr_start = result->head; for (int i = 0; pa; i++, pa = pa->next) { Node *pb = b->head, *pr = pr_start; int carry = 0; for (int j = 0; pb; j++, pb = pb->next) { int product = pa->digit * pb->digit + carry + pr->digit; pr->digit = product % 10; carry = product / 10; pr = pr->next; } while (carry) { pr->digit += carry; carry = pr->digit / 10; pr->digit %= 10; pr = pr->next; } pr_start = pr_start->next; } // 去除前导零 while (result->length > 1 && result->tail->digit == 0) { Node* temp = result->tail; result->tail = temp->prev; result->tail->next = NULL; free(temp); result->length--; } return result; } ``` ### 三、界面与辅助函数 ```c void print_number(BigNumber* num) { if (num->sign) printf("-"); Node* current = num->tail; while (current) { printf("%d", current->digit); current = current->prev; } printf("\n"); } void menu() { printf("\n===== 大数计算器 =====\n"); printf("1. 加法\n2. 减法\n3. 乘法\n4. 退出\n"); } int main() { int choice; char str1[1000], str2[1000]; while (1) { menu(); printf("请选择操作: "); scanf("%d", &choice); getchar(); // 清除缓冲区 if (choice == 4) break; printf("输入第一个数: "); fgets(str1, sizeof(str1), stdin); str1[strcspn(str1, "\n")] = 0; printf("输入第二个数: "); fgets(str2, sizeof(str2), stdin); str2[strcspn(str2, "\n")] = 0; BigNumber* num1 = create_number(str1); BigNumber* num2 = create_number(str2); BigNumber* result = NULL; switch (choice) { case 1: result = add(num1, num2); break; case 2: result = subtract(num1, num2); break; case 3: result = multiply(num1, num2); break; } printf("结果: "); print_number(result); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值