21. 合并两个有序链表
双指针往后遍历即可
/**
* 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* result = new ListNode(0);
ListNode* cur = result;
while(list1 != NULL && list2 !=NULL){
if(list1->val < list2->val){
cur->next = list1;
list1 = list1->next;
}else{
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
if(list1 != NULL){
cur->next = list1;
}
if(list2 != NULL){
cur->next = list2;
}
return result->next;
}
};
2. 两数相加
思路也是双链表,我采取最暴力的解题办法,对于进一位得情况,用单独标志位表示是否进1,
/**
* 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* result = new ListNode;
ListNode* cur = result;
bool flag = false;
while(l1 != nullptr && l2 != nullptr){
ListNode* temp = new ListNode;
int va = l1->val + l2->val;
if(flag)
va++;
if(va<10){
temp->val = va;
cur->next = temp;
flag = false;
}else{
flag = true;
temp->val = va%10;
cur->next = temp;
}
cur = cur->next;
l1 = l1->next;
l2 = l2->next;
}
if(!flag)
cur->next = l1 ? l1 : l2;
else{
l1 = l1 ? l1:l2;
while(l1){
if(flag){
int temp = l1->val + 1;
flag = temp>9 ? true:false;
l1->val = temp%10;
cur->next = l1;
}else{
cur->next = l1;
}
l1 = l1->next;
cur = cur->next;
}
if(flag){
ListNode* last = new ListNode(1);
cur->next = last;
}
}
return result->next;
}
};
25.K个一组反转链表
之前做个类似题,好像是K个反转数组,这里普通思路很简单,遍历,定义新的链表,不断加节点就好。两个主要点,首先就是满足k,单独定义一个flag就好。其次就是k的反转,利用栈特性就好,每次反转,遍历数据压入栈,然后遍历栈,依次弹出加入结果中就好
/**
* 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:
bool flag(ListNode* node, int k){
while(k-->0){
if(node == nullptr)
return false;
node = node->next;
}
return true;
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* result = new ListNode;
ListNode* i = result;
stack<int> s;
ListNode* cur = head;
while(flag(cur, k)){
int temp = k;
while(temp-->0){
s.push(cur->val);
cur = cur->next;
}
while(!s.empty()){
ListNode* node = new ListNode(s.top());
s.pop();
i->next = node;
i = i->next;
}
}
i->next = cur;
return result->next;;
}
};
138.随即链表的复制
这题主要是random的确定。暴力思路很简单,就是遍历判断random是否为null,如果不是,那就在遍历链表,找到指向得节点位置。这题主要是镜像关系,需要四个Node标志位置。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
Node* result = new Node(1);
Node* i =result;
Node* cur = head;
while(cur){ //
Node* temp = new Node(cur->val);
i->next = temp;
i = i->next;
cur = cur->next;
}
cur = head;
i = result->next;
Node* temp = head;
Node* temp_result = i;
while(cur){
if(cur->random == NULL){
cur = cur->next;
i = i->next;
continue;
}
while(temp && temp != cur->random){
temp = temp->next;
temp_result = temp_result->next;
}
i->random = temp_result;
temp = head;
temp_result = result->next;
cur = cur->next;
i = i->next;
}
return result->next;
}
};
23. 合并k个升序链表
暴力很简单,遍历所有链表,val放到vector中,然后排序,利用vec构建链表就好
/**
* 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* mergeKLists(vector<ListNode*>& lists) {
ListNode* result = new ListNode;
ListNode* cur = result;
vector<int> temp;
for(int i=0; i<lists.size(); i++){
ListNode* n = lists[i];
while(n){
temp.emplace_back(n->val);
n = n->next;
}
}
sort(temp.begin(), temp.end());
reverse(temp.begin(), temp.end());
while(!temp.empty()){
ListNode* t = new ListNode(temp.back());
temp.pop_back();
cur->next = t;
cur = cur->next;
}
return result->next;
}
};
152. 乘积最大子数组
在数组中找最大乘积得子数组,但问题就是,这里的数组中会包含负数,直接利用那种求和动规思想做不对。因此遇到负数就要考虑一个问题,之前的子数组中是不是还有负数?之前有负数,乘积为负,不会放在dp中,但此时加上i两个负数就会负负得正,因此我们采取两个dp数组,一个取最大值,另一个取最小值。最小值就会保存那个负数乘积,然后每次更新,最大dp取三者最大值,最小dp取三者最小值,三个数就是num[i], mindp[i-1]*num[i];maxdp[i-1]*num[i]。就能写出动规过程了,最后在maxdp中找最大值即可。
然后会发现,每次i只与i-1有关,因此可以利用滚动数组思想,只维护两个数字。代表最大值和最小值,result一直更新。
class Solution {
public:
int maxProduct(vector<int>& nums) {
//vector<long> ma(nums.begin(), nums.end()), mi(nums.begin(), nums.end());
int result = nums[0];
long ma = nums[0], mi=nums[0];
for(int i=1; i<nums.size(); i++){
long tmax = ma, tmin = mi;
ma = max(tmax*nums[i], max((long)nums[i], tmin*nums[i]));
mi = min(tmin*nums[i], min((long)nums[i], tmax*nums[i]));
if(mi < INT_MIN)
mi = nums[i];
result = max((int)ma, result);
}
//result = max((int)ma, result);
return result;
}
};
写的时候犯了个错误,每次递推中,需要把上一次得两个dp记录下来使用,两个局部变量,因为ma递推后值修改,紧接着mi递归需要用原始上一轮的ma,因此需要临时变量保存两个dp。不利用临时变量,mi递推就会用更新后的ma,这是错的。
1081. 不同字符的最小子序列
这题,看着不难,但做的时候很难理解,第一眼以为是求“含不同字符的最大连续序列”,但看一眼子序列概念,麻了。而且还有个最小子序列,所以该题核心点在于
- 序列最小,例如aba,要的是ab,而不是ba
- 子序列,不一定是连续的,可以删除,相对位置不变就好
思路确实不好想。顺序遍历(左往右),第一个字母肯定加入result,其次,第二个字母,就要看,result的back字母是不是唯一,也就是剩下字符中还有没有该字母,如果没有,那back不能变(因为要保证加入所有字母),加入第二个字母就好。如果还有(说明这个back字母位置可以调整),就要判断back和第二个字母大小了,因为要最小子序列,如果back大,并且后面要有该字母,那我们是不是就可以用后面的那个字母?这样得到的子序列才是小的。所以把back弹出。如果back小,那正常将新字母加入即可。
所以关键点在于,如何保证最小,就是贪心思想,从左往右遍历,每次都要保证,back小于新字母(大于就要更新,这里说的是剩余字符串中还有back的情况,如果没有back,那就没办法了,哪怕back大,也不能动),这样就清晰很多了。
综上所述,需要两个辅助数组,一是表示剩余字母的数量(更新时,判断该字母是否还有?有才能替换,没有得话,即使back大也不能替换)。而是表示result中已经存在的字母,一个去重数组。
class Solution {
public:
string smallestSubsequence(string s) {
vector<int> left(26), in_ans(26);
for(auto c:s)
left[c-'a']++; //每个字母有几个
string ans;
for(char c : s){
left[c-'a']--; //表示用一个字母
if(in_ans[c-'a']) //是否在结果中,如果在(用过了),直接跳过。
continue;
while(!ans.empty() && c<ans.back() && left[ans.back()-'a']){
//新字母小于back,并且后面还有该字母,需要将back弹出,最后新字母会加入,保证“最小”
in_ans[ans.back()-'a'] = 0;
ans.pop_back();
}
ans += c; //加入新字母
in_ans[c-'a'] = 1; //表示该字母用过了,满足子序列条件
}
return ans;
}
};