<Init:从零开始的leetcode编程之旅>(2)
题目2:两数相加
题目说明
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
class Solution {
public:
ListNode* l3 = new ListNode(0);
ListNode* p = l3;
bool flag = false;
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
while (l1 && l2) {
l3->val = l1->val + l2->val;
if (flag == true) {
l3->val += 1;
flag = false;
}
if (l3->val >= 10) {
l3->val %= 10;
flag = true;
}
if (l2->next || l1->next) {
l3->next = new ListNode(0);
}
else if (flag) {
l3->next = new ListNode(1);
}
else {
l3->next = NULL;
}
l3 = l3->next; l1 = l1->next; l2 = l2->next;
}
while (l1) {
l3->val = l1->val;
if (flag == true) {
l3->val += 1;
flag = false;
}
if (l3->val >= 10) {
l3->val %= 10;
flag = true;
}
if (l1->next) {
l3->next = new ListNode(0);
}
else if (flag) {
l3->next = new ListNode(1);
}
else {
l3->next = NULL;
}
l3 = l3->next; l1 = l1->next;
}
while (l2) {
l3->val = l2->val;
if (flag == true) {
l3->val += 1;
flag = false;
}
if (l3->val >= 10) {
l3->val %= 10;
flag = true;
}
if (l2->next) {
l3->next = new ListNode(0);
}
else if (flag) {
l3->next = new ListNode(1);
}
else {
l3->next = NULL;
}
l3 = l3->next; l2 = l2->next;
}
return p;
}
};
思路与问题:
用链表实现一个队列,同时做好位数间的进位,没什么好说的,暴力求解,注意末位存在进位的情况
大佬的代码
//思路1其实没啥区别
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = nullptr, *tail = nullptr;
int carry = 0;
while (l1 || l2) {
int n1 = l1 ? l1->val: 0;
int n2 = l2 ? l2->val: 0;
int sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
carry = sum / 10;
if (l1) {
l1 = l1->next;
}
if (l2) {
l2 = l2->next;
}
}
if (carry > 0) {
tail->next = new ListNode(carry);
}
return head;
}
};
//思路2--递归
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
return dfs(l1, l2, 0);
}
ListNode* dfs(ListNode* l, ListNode* r, int i) {
if (!l && !r && !i) return nullptr;
int sum = (l ? l->val : 0) + (r ? r->val : 0) + i;
ListNode* node = new ListNode(sum % 10);
node->next = dfs(l ? l->next : nullptr, r ? r->next : nullptr, sum / 10);
return node;
}
};
学到许多:
递归思路比较值得借鉴,特别是用递归创建线性表的操作极其漂亮,并且由于单递归同样是O(n)复杂度,且思路往往更加和逻辑,注意加强对单递归的理解和使用。
题目19:删除链表的倒数第N个结点
题目说明
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
思路与问题:
- 遍历第一次,记录长度,动态创建数组,将每个结点的指针存入数组,然后查找对应位置
- 注意边界问题的处理,比如只有一个元素则可以直接返回
- 不采用动态创建,而是创建一个足够大的数组就可以只遍历一次
自解代码
class Solution {
private:
int length = 0;
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int cnt = 0;
ListNode* p = head;
while (p) {
length++;
p = p->next;
}
ListNode** lists = new ListNode*[length];
p = head;
while (p) {
lists[cnt++] = p;
p = p->next;
}
if (length == n ) head = head->next;
else { lists[length - n - 1]->next = lists[length - n]->next; }
delete[]lists;
return head;
}
};
大佬的代码
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode first = dummy;
ListNode second = dummy;
// Advances first pointer so that the gap between first and second is n nodes apart
for (int i = 1; i <= n + 1; i++) {
first = first.next;
}
// Move first to the end, maintaining the gap
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}
学到许多:
我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。现在,这两个指针被 nn 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。此时第二个指针将指向从最后一个结点数起的第 nn 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。
这个思路本质上和常用的邻接指针pq相似,要打破思维定势。
题目<偶然遇到,非leetcode>:数组元素循环右移
题目说明
任务描述
本关任务:编写一个算法,将数组A中的n个元素A[0]至A[n-1]循环右移k位。
要求算法时间复杂度为O(n),空间复杂度为O(1)
相关知识
为了完成本关任务,你需要掌握:1.一维数组,2.数组的遍历。
编程要求
根据提示,在右侧编辑器填写算法函数,实现将大小为n的数据元素数组a中的元素循环右移k位。要求函数原型为: void ShiftRightCircular(ElemType a[],int n,int k) 其中k的值可为负整数,表示循环左移;ElemType已经在后台测试程序中定义。
测试说明
平台会自动读取输入数据,对你编写的代码进行测试,并输出结果。
测试输入:
10,5;(输入的是n与k的值) 预期输出:6 7 8 9 10 1 2 3 4 5测试输入:
10,-3; 预期输出:4 5 6 7 8 9 10 1 2 3
代码求解
class Solution{
public:
void ShiftRightCircular(ElemType* A, int n, int k)
{
k = k > 0 ? k % 10 : (10 + k) % 10;
int i = 0, j = n - k - 1;
for (int t = 0; t < ((n%k==0)?(n/k-1):(n / k)); t++) {
for (i = t*k, j = n - k ; i < k*(t+1); i++, j++) {
A[i] = A[i] + A[j];
A[j] = A[i] - A[j];
A[i] = A[i] - A[j];
}
}
}
}
学到许多
- 本题完成并不难,相对困难的是保证就地计算和线性时间复杂度,对于一个新人(我),可能会默认线性复杂度就是一层循环,但是这是显然不对的,如本次是二层循环,第一层n/k次,第二层k次,相乘仍是O(n)
- 这其实也是分治算法的基础,理解本题有助于分析理解分治算法
- 注意如何实现数据交换的就地运算,除了本题使用到的“加减法”,还有“乘除法”,“位运算法”等等

浙公网安备 33010602011771号