两数之和

链接:

https://leetcode.cn/problems/add-two-numbers/

题解

两数相加,需要返回头指针,所以就两个指针headtail来进行表示,最后返回head,计算过程用 tail作用。

因为两个链表可能长度不一样,所以while的条件很重要。然后就是对于其中某个指针有没有值的判断,用三目运算符。接着就是对于头指针是否为空,因为初始化的时候头指针和尾指针都是空指针。然后是原来两个指针的更新逻辑。以及进位逻辑。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// 初始化结果链表的头节点和尾节点
ListNode* head = nullptr; // 初始化头节点
ListNode* tail = nullptr; // 初始化尾节点
int carry = 0; // 进位符号
while (l1 || l2) { // 当其中一个链表指针不为空
int n1 = l1 ? l1->val : 0; // 创建一个新的值存储l1
int n2 = l2 ? l2->val : 0; // 创建一个新的值存储l2
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) { //如果两个数分别是99和88,那么head和tail同时移动到末尾的时候。还需要创建一个新的节点
tail->next = new ListNode(carry);
}
}
return head;
}
};

反转链表

https://leetcode.cn/problems/reverse-linked-list/submissions/575965011/

代码如下:

用三个指针实现,curr,next和prev,curr指向当前,next指向下一个,prev指向curr的前一个,(一开始curr指向传入的头指针)然后prev和next是空指针,更新逻辑是:

  • 保存当前节点curr的下一个指针为next
  • 令当前节点curr指向前一个节点prev
  • 将prev更新为curr
  • 将当前指针更新为之前保存的next
  • 直到当前指针为空,因为在循环中更新了,所以返回prev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev=nullptr;
ListNode* next=nullptr;
ListNode* current=head;
while(current!=nullptr){
next=current->next;//保存下一个节点的指针
current->next=prev;//将当前节点指向之前的指针
prev=current;//将上一个节点的指针向前移动
current=next;//将当前节点的指针向前移动(和上一句的顺序有关系)
}
return prev;//最后返回的是有数值的指针。
}
};

C++中一个结构体的对象可以用.或者->访问,但是一个结构体指针的对象只能用->,不能用.

C++中类的结尾要有分号

删除单向链表的倒数第n个节点

双指针法

思路是用两个距离为n的指针,每次同时更新这两个指针,当后面的指针为空的时候前面的指针刚好到达倒数第n个。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//双指针
ListNode* tail=head; //尾节点
ListNode* front=head;//待删除结点的前一个节点,初始化为头结点
ListNode* del=head;//待删除的节点
//将尾节点向后移动N个,保持尾节点和待删除的节点相差n
for(int i=0;i<n;i++){
tail=tail->next;
}
//将双指针都向后更新,直到尾指针为空
while(tail!=nullptr){

front=del; //更新待删除节点的前一个指针指向待删除节点
del=del->next;//待删除节点向后更新
tail=tail->next;//将尾节点向后更新
}
//单独判断只有一个结点的情况,此时上述while循环并未进入,
if(del==head){
return head->next; //返回头节点的下一个,即空指针。
}
//如果不是只有一个节点的情况,就可以将节点删除。
front->next=del->next; //删除节点

return head;
}

};

合并两个升序链表

合并两个升序链表

解法一:遍历

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {

ListNode*p1=list1;//第一个链表
ListNode*p2=list2;//第二个链表
ListNode dummy(0); //哑结点,方便找回结果链表的初始节点
ListNode* result=&dummy;//结果的链表的指针,初始化为哑结点的地址
while(p1&&p2){ //循环条件:两个链表都不为空
if(p1->val<p2->val){ //如果链表1的值大于链表2的值,将其值加入结果链表中
result->next=p1; //,将其值加入结果链表中
p1=p1->next; //将添加过一个值的那个链表的指针向后移动
result=result->next; //将结果链表的指针向后移动
}else{
result->next=p2;

p2=p2->next;
result=result->next;
}
}
//最后跳出来循环的时候,由于链表不等长,可能一个链表不为空,此时判断一下,直接把那个链表给接起来。
p1==nullptr?result->next=p2:result->next=p1;
return dummy.next; //返回哑结点的next指针
}
};

解法二:递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(!list1){ //边界判断,如果指针为空,返回另一个指针
return list2;
}
else if(!list2){
return list1;
}
else if(list1->val<list2->val){ //比较值,如果其中一个链表的值比较小
list1->next=mergeTwoLists(list2,list1->next); //就将这个链表的指针接到计算后的结果,即看后面的结果,如果遇到空指针,再向后回溯
return list1;
}
else{
list2->next=mergeTwoLists(list1,list2->next);
return list2;
}

}
};

两两交换链表中的节点

两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

迭代法

思路:用一个哑结点 dummyHead放在头节点前,再用一个指针 tmp去遍历,从dummyHead开始,终止条件是指针tmp指向空或者下一个指针指向空。

在循环的内部,可以初始化两个节点,指向tmp的下一个和下下一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy=new ListNode(0);//哑结点,初始化为0
dummy->next=head; //让哑结点指向头节点

ListNode*tmp=dummy;//定义一个指针,用来遍历
while((tmp->next!=nullptr)&&(tmp->next->next!=nullptr)){
ListNode*back=tmp->next;
ListNode*forward=back->next;

//交换
back->next=forward->next; //先把back指向forward的下一个,防止丢失
forward->next=back; //再把forward指向back
tmp->next = forward;//这一步很关键,是为了链表整体还是连接起来,作用是把交换后的节点对连接到前面的链表部分 ,如果没有这一步, 链表就会乱
tmp=back;//此时back已经通过交换移动到了前面,所以tmp更新为back
}
return dummy->next;//返回原来头指针的下一个

}

};

递归

为了得到交换后的链表,我们可以认为,递归的条件是递归的终止条件是链表中没有节点,或者链表中只有一个节点,此时无法进行交换。如果链表中至少有两个节点,则在两两交换链表中的节点之后,原始链表的头节点变成新的链表的第二个节点,原始链表的第二个节点变成新的链表的头节点。链表中的其余节点的两两交换可以递归地实现。在对链表中的其余节点递归地两两交换之后,更新节点之间的指针关系,即可完成整个链表的两两交换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
//如果只有一个或者没有节点,直接返回
if(head==nullptr||head->next==nullptr){ //注意此处的两个条件的顺序,一旦变为head->next==nullptr||head==nullptr会报错
return head;
}
//否则
//交换头节点和头节点的下一个节点
ListNode* newhead=head->next;
head->next=swapPairs(newhead->next); //此时用以下递归
newhead->next=head;


return newhead;

}

};

求数组中和为目标值的下标

方法一(暴力遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) { //函数返回值是int类型的数组
int n=nums.size(); //获取数组长度
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(nums[i]+nums[j]==target){
return {i,j}; //返回一个由i和j组成的数组
}
}
}
return {}; //如果没有找到,返回空数组
}
};

方法二(哈希表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) { //函数返回值是int类型的数组
unordered_map<int,int>myHash; //建立一个键值类型都是整型的hash表,命名为myHash.其中键为数组的值,值为数组的下标
for(int i=0;i<nums.size();i++){ //遍历这个数组
auto it=myHash.find(target-nums[i]);//查找第i个元素所对应的“另一半”是否在myHash的值中
if(it!=myHash.end()){ //如果第i个元素的“另一半”在myHash的值中,证明匹配成功,直接返回
return {i,it->second}; //返回下标i和hash表元素it的值(it->first是键)
}
//如果第i个元素的“另一半”不在myHash的值中,证明未匹配成功,将当前元素加入Hash表中
myHash[nums[i]]=i;
}
return {}; //如果没有找到,返回空数组
}
};

35.搜索插入位置

[https://leetcode.cn/problems/search-insert-position/description/]

二分法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;

while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) //
return mid;
if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}

return left; // 未找到时返回插入位置
}
};

  1. 删除有序数组中的重复项