LeetCode-2-两数之和设计文档
题目地址
题目解析
本题是给出两个链表存储的数,要求进行相加,有点类似以前做过的高精度运算不过那个是用顺序表实现的
本题是创建一个新的链表来存储结果
需要考虑的情况有如下
长度相等的链表相加
长度不相等的链表相加
需要考虑的机制:
进位保存
算法解析
进位机制
使用一个变量表示溢出量(进位的数,初始化为0)
创建新的节点时,考虑分别指向两个链表的指针的值和溢出量之和为新节点的值
链表相加
使用两个指针分别指向两个链表
每次创建完结果链表的节点时,分别判断这两个指针是否指向末尾再考虑向后迭代
当其中一个链表的指针指向尾(NULL)的时候,在进行加法运算的时候,令其值为0
代码实现
void addNode(ListNode* tail,int value){
// 由于频繁使用新建节点,这里进行封装
ListNode* new_node = new ListNode(value);
tail->next = new_node;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ptr_l1 = l1; // 指向l1首节点
ListNode* ptr_l2 = l2; // 指向l2首节点
ListNode* list = new ListNode;
ListNode* list_ptr = list;
int carry_bit_value = 0; // 存储进位数据
while(true){
if(ptr_l1 && ptr_l2){
// ptr_l1 != NULL && ptr_l2 != NULL
// 正常遍历
addNode(list_ptr,(ptr_l1->val+ptr_l2->val+ carry_bit_value)%10);
// 进位值更新
carry_bit_value = (ptr_l1->val+ptr_l2->val+carry_bit_value)/10;
// 整体向后迭代
ptr_l1 = ptr_l1->next;
ptr_l2 = ptr_l2->next;
list_ptr = list_ptr->next;
}
else if(ptr_l1 && !ptr_l2){
// ptr_l1 != NULL && ptr_l2 == NULL
// 链表l1比链表l2长的情况
addNode(list_ptr,(ptr_l1->val + carry_bit_value)%10);
carry_bit_value = (ptr_l1->val + carry_bit_value)/10;
// 只对l1的指针和list的指针进行向后迭代
ptr_l1 = ptr_l1->next;
list_ptr = list_ptr->next;
}
else if(!ptr_l1 && ptr_l2){
// ptr_l1 == NULL && ptr_l2 != NULL
// 链表l2比链表l1长的情况
addNode(list_ptr,(ptr_l2->val + carry_bit_value)%10);
carry_bit_value = (ptr_l2->val + carry_bit_value)/10;
// 只对l1的指针和list的指针进行向后迭代
ptr_l2 = ptr_l2->next;
list_ptr = list_ptr->next;
}else{
// 两个链表都遍历完毕,考虑是否有进位再据此考虑是否创建新节点
if(carry_bit_value){
addNode(list_ptr,carry_bit_value);
break;
}
}
}
return list;
}
代码优化
第一次提交的代码运行太慢,看了一下效率最高的一个样例,他是将加法和进位值的修改封装在一起了,我对原有的代码进行了一点修改
void addNode(ListNode* tail,int value){
// 由于频繁使用新建节点,这里进行封装
ListNode* new_node = new ListNode(value);
tail->next = new_node;
}
int sum(int val,int val1,int &cm){
int sumed=val+val1+cm,bit=0;
if(sumed>9){
cm=1;
bit=sumed-10;
}else{
cm=0;
bit=sumed;
}
return bit;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ptr_l1 = l1; // 指向l1首节点
ListNode* ptr_l2 = l2; // 指向l2首节点
ListNode* list = new ListNode;
ListNode* list_ptr = list;
int carry_bit_value = 0; // 存储进位数据
while(true){
if(ptr_l1 && ptr_l2){
// ptr_l1 != NULL && ptr_l2 != NULL
// 正常遍历
addNode(list_ptr,sum(ptr_l1->val,ptr_l2->val,carry_bit_value));
// // 进位值更新
// carry_bit_value = (ptr_l1->val+ptr_l2->val+carry_bit_value)/10;
// 整体向后迭代
ptr_l1 = ptr_l1->next;
ptr_l2 = ptr_l2->next;
list_ptr = list_ptr->next;
continue;
}
else if(ptr_l1 && !ptr_l2){
// ptr_l1 != NULL && ptr_l2 == NULL
// 链表l1比链表l2长的情况
addNode(list_ptr,sum(ptr_l1->val,0,carry_bit_value));
// carry_bit_value = (ptr_l1->val + carry_bit_value)/10;
// 只对l1的指针和list的指针进行向后迭代
ptr_l1 = ptr_l1->next;
list_ptr = list_ptr->next;
continue;
}
else if(!ptr_l1 && ptr_l2){
// ptr_l1 == NULL && ptr_l2 != NULL
// 链表l2比链表l1长的情况
addNode(list_ptr,sum(ptr_l2->val,0,carry_bit_value)%10);
// carry_bit_value = (ptr_l2->val + carry_bit_value)/10;
// 只对l1的指针和list的指针进行向后迭代
ptr_l2 = ptr_l2->next;
list_ptr = list_ptr->next;
continue;
}else{
// 两个链表都遍历完毕,考虑是否有进位再据此考虑是否创建新节点
if(carry_bit_value){
addNode(list_ptr,carry_bit_value);
}
break;
}
}
list_ptr = list;
list = list->next;
delete list_ptr;
list_ptr = NULL;
return list;
}
以上修改的代码,去除多余的运算完加法又对进位值赋值的过程,同时对每个条件执行完后进行截断操作,执行效率提高了一些
不过这个这个执行统计在某些情况下貌似并不那么严谨:第二次提交的时候用时又回到32ms了
总结
这次做题的过程中还是应该先写题目分析,分析出每种情况及对应的解决办法再开始写代码,我以前一直有个坏毛病,就是没有考虑到所有情况就上手敲代码,导致敲代码的过程中出现没有考虑到的情况就需要大量修改整体的结构,浪费了大量的时间。
写程序不论是大程序还是小程序,在编写之前都要对整体的架构进行一个设计,考虑各种各样的情况。是所谓大局观,从宏观和微观的角度分别解构一个事物,才能对其有一个深刻的了解