在写链表的过程中,顺便写一下emplace_back和push_back的区别,刚开始用的时候就发现题解里经常用的是emplace_back,但是我实测用push_back好像并没有什么区别,写题的时候遇到不知道是哪报错,顺便查了一下两者的区别。
比较通俗一点的解释就是emplace_back 只会调用一次构造函数,不会在原地构造完之后再次调用复制构造函数,也就是emplace_back 的优势在于可以避免创建临时对象,造成性能损失。
题意:两个非空链表分别对应一个整数,每个节点存储整数上的某一位,要求将这两个数相加,并需要将和以链表形式返回。
思路:
本来想的是再创建一个新的链表,然后把链表l1和l2的相加的结果建造一个新链表,但是写着写着突然发现要用好多链表,感觉这样很浪费内存,突发奇想能不能放在l1里,然后再避开l1为空指针的情况,最后再进位。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* bianli = l1;
ListNode* ans = l1;
while(l1 || l2)
{
if(l1 && l2)
{
l1 -> val = l1 -> val + l2 -> val;
l2 = l2 -> next;
}
if(l1 -> next == NULL)
{
l1 -> next = l2;
break;
}
l1 = l1 -> next;
}
int temp = 0;
while(bianli)
{
bianli -> val += temp;
temp = 0;
while(bianli -> val >= 10)
{
temp ++;
bianli -> val -= 10;
}
if(temp && bianli -> next == NULL)
{
ListNode* tem = new ListNode(temp);
temp = 0;
bianli -> next = tem;
}
bianli = bianli -> next;
}
return ans;
}
题目大意:给定一个单链表的头节点,原始排列顺序为L1 -> L2 -> L3 -> L4 -> … -> Ln
需要更改排列顺序为L1 -> Ln -> L2 -> Ln-1 -> L3 -> Ln-2 -> …
比较简单的一个做法就是:将链表转换成ListNode* 形式的线性表,用Vector进行存储,此时我们能够很轻松地根据vector数组的下表进行查找,因为是线性表,所以可以通过下标方便快捷地对链表节点进行查询。
里面有个小坑就是当用左右指针来进行更改的时候,左指针可能会大于右指针,如果不及时进行判断,链表末尾直接会错乱,因此一定需要进行判断。
void reorderList(ListNode* head) {
vector<ListNode* >a;
ListNode* cur = head;
if(head == NULL)
return ;
while(head)
{
a.emplace_back(head);
head = head -> next;
}
int right = a.size() - 1, left = 0, count = 1;
while(left < right)
{
a[left] -> next = a[right];
left ++;
//此时,如果不进行判断,则会出现left == right 的情况,即到达最终节点
if(left == right)
break;
a[right] -> next = a[left];
right --;
}
//需要手动给最后一个节点的next赋值为空,否则链表带环
a[left] -> next = NULL;
return ;
}
第二种做法-栈
这种做法也是偶然看到的, 就是建立一个类型为ListNode*的Stack容器,但是做法大同小异,主要的思路是:Stack容器的特性就是先进后出,因此只需要在遍历链表的过程中,将遇到的节点按照顺序全部压入堆栈当中,最后再从堆栈中一个个弹出即可,但是也需要进行判断,当两个节点相等时也要及时停止。
第三种做法-找链表中点 -> 链表反转 -> 链表合并
在找链表中点的时候,用的是快慢指针法,要注意的是,在判断fast 的next 以及next -> next 指针时,next指针应该放前面。
ListNode* middleList(ListNode* head)
{
if(head == NULL)
return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast->next!= nullptr && fast->next->next!= nullptr)
{
fast = fast -> next -> next;
slow = slow -> next;
}
return slow;
}
ListNode* reverseList(ListNode* head)
{
ListNode* cur = head;// cur -> 4
ListNode* pre = nullptr;// pre -> 3
while(cur != NULL)
{
ListNode* temp = cur -> next;
cur -> next = pre;
pre = cur;
cur = temp;
}
return pre;
}
void MergeList(ListNode* l1, ListNode* l2)
{
ListNode* cur;
while(l2 != NULL)
{
ListNode* temp1 = l1 -> next;
ListNode* temp2 = l2 -> next;
l1 -> next = l2;
l2 -> next = temp1;
l1 = temp1;
l2 = temp2;
}
return ;
}
void reorderList(ListNode* head) {
if(head == NULL)
return ;
ListNode* middle = middleList(head);
ListNode* rightlist = reverseList(middle -> next);
middle -> next = NULL;
MergeList(head, rightlist);
return ;
}
因为刚做完反转链表的题,感觉之前都是动不动就抄题解,有点怕这么下去会一直持续抄题解,有点怕了,这个题目认认真真debug了一下,发现没有那么难。
思路:找到要反转的那一截链表的前节点和反转后的链表的最末尾的节点,让反转完的节点能够连接成功。
ListNode* reverseBetween(ListNode* head, int left, int right) {
if(head == NULL)
return NULL;
int count = 1;
ListNode* pre = nullptr;
//pre代表的是反转之前的 最后一个节点
ListNode* headd = head;
while(count < left && headd)
{
pre = headd;
headd = headd -> next;
count ++;
}
ListNode* prev = nullptr;
ListNode* temp = nullptr;
ListNode* last = headd;
//last 代表的是反转后的最末尾的节点
for(int i = left; i <= right ;i++)
{
temp = headd -> next;
headd -> next = prev;
prev = headd;
headd = temp;
}
if(pre)
pre -> next = prev;
else
head = prev;
if(last)
last -> next = temp;
return head;
}
想到的第一种做法:新建一个虚拟指针,将这个虚拟指针用于新建一个链表,然后将新建链表返回。
ListNode* swapPairs(ListNode* head) {
ListNode* newhead = new ListNode(-1);
ListNode* bianli = newhead;
while(head != NULL && head -> next != NULL)
{
ListNode* now = head;
ListNode* n_next = head -> next;
head = head -> next -> next;
newhead -> next = n_next;
newhead = newhead -> next;
newhead -> next = now;
newhead = newhead -> next;
}
if(head && head -> next == NULL)
{
newhead -> next = head;
newhead = newhead -> next;
}
newhead -> next = NULL;
return bianli -> next;
}
第二种办法:建立线性表
ListNode* swapPairs(ListNode* head) {
vector<ListNode*>a;
ListNode* pre;
if(head == NULL)
return NULL;
while(head)
{
a.emplace_back(head);
head = head -> next;
}
int length = a.size() - 1;
for (int i = 0; i < length; i += 2)
{
ListNode* temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
}
for (int i = 0; i < length ; i ++)
a[i] -> next = a[i + 1];
a[length] -> next = nullptr;
return a[0];
}
题解新方法:递归(还没学会,先挖个坑)
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
大概题意就是给个正整数组,可以选择数组中任意一个数,减小到恰好一半,要求将数组减小到一半以下的最少操作次数。
很容易就能想到,如果要大幅度减小,那么肯定每次都要减小当前数组中最大的数,因此问题就转换成了如何找到当前数组中的最大数,此时就要用到优先队列。
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。
普通的优先队列默认输出最大值,也就是大根堆。
用法与普通队列差不多(用到了再写,主要需要注意的是:priority_queue在弹出队首元素时,使用的函数为top();
int halveArray(vector<int>& nums) {
priority_queue<double>Q(nums.begin(), nums.end());
double sum = 0, count = 0;
int ans = 0;
for (int i = 0; i < nums.size() ; i++)
sum += nums[i];
count = sum;
while(count > sum / 2.0)
{
double now = Q.top();
Q.pop();
count = count - (now / 2.0);
now /= 2.0;
Q.push(now);
ans ++;
}
return ans;
}
题意:桌子上n 张卡, 卡有正面,背面两个数字。可以翻转任意卡,选择一张,如果这张卡背面的数字与任何一张卡正面的数字都不一样,就是想要的数字,要找 想要的数字里的最小值
如果没有一个符合 输出0
最多1000 张卡
题解的思路很震撼:如果有一张卡,正面反面都是同一个数字,那么这个数字必定重复,绝对不是答案,只需要把所有的卡中,正面反面是同一个数字的这个数字找出来,除了这个数字以外的都是答案。
(没想过这个题这么简单)
int flipgame(vector<int>& fronts, vector<int>& backs) {
int length = fronts.size();
int num = 2001;
set<int>a;
bool Jud_Same[length + 1];
for (int i = 0; i < length ; i++)
if(fronts[i] == backs[i])
a.insert(fronts[i]);
for (int i = 0; i < length ; i++)
{
if(a.find(fronts[i]) == a.end())
num = min(num, fronts[i]);
if(a.find(backs[i]) == a.end())
num = min(num, backs[i]);
}
return num == 2001 ? 0 : num;
}
C++ 语言中,注释分为两种,一种注释是块注释,即以 /* 开头 */ 结尾的注释, 其中所有的内容都会被注释,
另一种注释是:// 开头的注释,注释单行
vector<string> removeComments(vector<string>& source) {
string s = "";
vector<string>ans;
string temp = "";
for (int i = 0; i < source.size() ;i ++)
s += source[i] + "\n";
for (int i = 0; i < s.size() ; i++)
{
if(s[i] == '/' && s[i + 1] == '*')
{
s[i] = 0;
s[i + 1] = 0;
i += 2;
while(i < s.size())
if(s[i] == '*' && i + 1 < s.size() && s[i + 1] == '/')
{
s[i] = s[i + 1] = 0;
i++;
break;
}
else
s[i++] = 0;
}
if(s[i] == '/' && i + 1 < s.size() && s[i + 1] == '/')
while(i < s.size())
if(s[i] == '\n')
break;
else
s[i++] = 0;
}
for (int i = 0; i < s.size(); i++)
{
if(s[i] && s[i] != '\n')
temp += s[i];
else if(s[i] == '\n' && temp != "")
{
ans.emplace_back(temp);
temp = "";
}
}
return ans;
}
这个题目看错题了…没看到一定要连续,附上不需要连续的代码:
int maxAbsoluteSum(vector<int>& nums) {
int n = nums.size();
int dp_zheng[n][2];
int dp_fu[n][2];
dp_zheng[0][0] = 0;
dp_zheng[0][1] = nums[0];
dp_fu[0][0] = 0;
dp_fu[0][1] = nums[0];
for (int i = 1; i < n; i++)
{
dp_zheng[i][0] = max(0, max(dp_zheng[i - 1][1], dp_zheng[i - 1][0]));
cout << "dp_zheng[i][0]" << dp_zheng[i][0] << endl;
dp_zheng[i][1] = max(0, max(dp_zheng[i - 1][0] + nums[i] , dp_zheng[i - 1][1] + nums[i]));
cout << "dp_zheng[i][1]" << dp_zheng[i][1] << endl;
dp_fu[i][0] = min(0, min(dp_fu[i - 1][1], dp_fu[i - 1][0]));
cout << "dp_fu[i][0]" << dp_fu[i][0] << endl;
dp_fu[i][1] = min(0, min(dp_fu[i - 1][1] + nums[i], dp_fu[i - 1][0] + nums[i]));
cout << "dp_fu[i][1]" << dp_fu[i][1] << endl << endl;
}
int ans_zheng = max(abs(dp_zheng[n - 1][0]), abs(dp_zheng[n - 1][1]));
int ans_fu = max(abs(dp_fu[n - 1][0]), abs(dp_fu[n - 1][1]));
return max(ans_fu, ans_zheng);
}
不连续的比较好写,实际上就是加上这个数结果 > 0 就加进来
int maxAbsoluteSum(vector<int>& nums) {
int positive_max = 0, positive_sum = 0;
int negative_max = 0, negative_sum = 0;
for (int i = 0; i < nums.size(); i++)
{
positive_sum = max(0, positive_sum + nums[i]);
positive_max = max(positive_max, positive_sum);
negative_sum = min(0, negative_sum + nums[i]);
negative_max = min(negative_max, negative_sum);
}
return max(positive_max, -negative_max);
}