之前收藏了极客时间的算法训练营3期 共10周,计划每周内容分3p博客来记录学习,主要形式为
方法类型1
题1
题解
题2
题解
方法类型2
题1
题解
……
题目大体来自leetcode 和 acwing
主要记录和理解代码,所以基本完全搬运了视频题解代码,
个人学习感受体现在大致思路的总结和注释上。
目录
数组原理
题1
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = 0;
for(int i = 0; i < nums.size(); i++){
if(i == 0 || nums[i] != nums[i - 1]){//过滤了每个和之前位置值相等的值
nums[n] = nums[i];
n++;
}
}
return n;
}
};
题2
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i] != 0){//不是0的数往前走
nums[n] = nums[i];
n++;//0的位置会被覆盖成后来的非零数
}
}
while(n < nums.size()){
nums[n] = 0;//不是传统意义上把0挪过去,而是直接赋值了新的0
n++;
}
}
};
题3
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m-1;
int j = n-1;
for(int k = n+m-1; k >= 0; k--){//倒序来放置数据不会覆盖原数据
if(j < 0||(i >= 0 && nums1[i] >= nums2[j])){//nums2出界
nums1[k] = nums1[i];
i--;
}
else{
nums1[k] = nums2[j];
j--;
}
}
}
};
链表
题1
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* last = nullptr;//第一个结点的前一个自然是空指针
while(head != nullptr){
ListNode* headnext = head->next;//操作每一对结点翻转后,head就会被last推进而覆盖,所以先把后一个结点储存。
head->next = last;
last = head;
head = headnext;
}
return last;
}
};
题2
大致思路
1.每个组都要翻转
2.各个组要重新连接
3要判断组是否完整
class Solution {
public:
//1,分组
//2,翻转
//3, 连接
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* protect = new ListNode(0);//通过实例理解了保护结点的作用,这里保护结点的作用是提供了链表的入口,因为它不参与翻转运算,但是会连接到链表上,解决了链表入口本来是空指针导致返回数据时找不到入口费劲的问题。
ListNode* last = protect; //赋值保护结点为第一个结点,本来应该是空指针
while(head != nullptr){
ListNode *end = getEnd(head,k);//需要判定是足够一组,还是末尾的不足一组的情况
if (end == nullptr) break;
ListNode* nextGroupHead = end->next;//设定了一个末尾,方便判定结束
//此时,链表被操作部分为 last > head > … >end > nextGrouphead
reverseList(head,nextGroupHead);//对于每个小块就是翻转操作
//此时,链表被操作部分为 last > end > … >head > nextGrouphead
last->next = end;//last是上个小块的末尾哈
head->next = nextGroupHead;//把该连接的连接上
last = head;//推进一下
head = nextGroupHead;
}
return protect->next;
}
private:
ListNode* getEnd(ListNode* head,int k){
while(head != nullptr){
k--;//第一次就是走过自己这步
if(k == 0) return head;
head = head->next;
}
return nullptr;
}
void reverseList(ListNode* head,ListNode* stop){
ListNode* last = head;//因为不是完整的链表操作,不必关心最后的连接问题,所以直接让头指针做最后一个结点就行,至于头结点指向哪里,返回到主函数另有安排。
head = head->next;
while(head != stop){
ListNode* headnext = head->next;
head->next = last;
last = head;
head = headnext;
}
}
};
题3
大致思路
1.需要邻值,就把相邻的值放在一起,就是顺序排列
2.需要保留原数据的顺序,所以用索引来标记
3.需要考虑出现顺序,所以倒序来操作,每次删除最后一个数据,
4.rank数组存储rank[i] = x; i是第i个数据,这个是出现顺序,x是它在链表里的结点位置。链表Linklist[ i ] 就是 第i个出现的数据所在的链表结点。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#define MAX 100010
struct Node {
double val;
int idx;
Node* pre;
Node* next;
};
int n, i , tmp[MAX], rk[MAX], ans[MAX];
double a[MAX];
Node* pos[MAX];
void m_sort(int q[], int l, int r) {
if (l >= r) return;
int mid = l + r >> 1;
m_sort(q, l, mid);
m_sort(q, mid + 1, r);
int k = 0;
int i = l;
int j = mid + 1;
while (i <= mid && j <= r) {
if (a[q[i]] <= a[q[j]]) tmp[k++] = q[i++];//是原数据在rk[i]的位置上要顺序排列
else tmp[k++] = q[j++];
}
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
Node* AddNode(Node* node, int idx) {
Node* newnode = new Node();
newnode->val = a[idx];//此处idx 就是rk[i];
newnode->idx = idx;
node->next->pre = newnode;
newnode->next = node->next;
node->next = newnode;
newnode->pre = node;
return newnode;
}
void DeleteNode(Node* node) {
node->pre->next = node->next;
node->next->pre = node->pre;
delete node;
}
int main() {
cin >> n;
for (i = 1; i <= n; i++) {
scanf("%lf", &a[i]);//先按照出现顺序保存全部的数据,因为题意里出现了出现顺序的条件
rk[i] = i;//默认是顺序,但是顺序不好操作,没有特性,所以后续要排序
}
m_sort(rk, 1, n);//学艺不精,没有调用库函数来解决,排序了一下rk值,rk值作为索引来标记在链表中,相应位置的结点序号。
//如,rk[1] 就是第一个链表结点,rk[1]的值是一个数,这个数带入到a[]中,就找到了那个数,a[rk[1]]就是第一个链表结点的值
Node head;
Node tail;
head.next = &tail;
tail.pre = &head;//构建了一个双向链表
head.val = a[rk[1]] - 1e9 * 2 - 1;//视频中给出的+1e9并不足以排除所有特殊情况,如1e9和-1e9,遇到这种情况会越界访问头结点尾结点
tail.val = a[rk[n]] + 1e9 * 2 + 1;//用了双浮点,后来看用long long也行。
for (i = 1; i <= n; i++) {//pos[rk[i]],把第i个数据放到它该到的位置上
pos[rk[i]] = AddNode(tail.pre, rk[i]);//每次插到后面,因为排序排的是顺序
}
for (i = n; i > 1; i--) {
Node* curr = pos[i];
if (curr->val - curr->pre->val <= curr->next->val - curr->val)//判断了是和前面的近还是和后面的近
ans[i] = curr->pre->idx;
else ans[i] = curr->next->idx;
DeleteNode(curr);//根据题意,只要某个数之前出现的数,那么从最后一个开始的话,每操作一个就不要这个结点了。
}
for (i = 2; i <= n; i++) {
printf("%.0lf %d\n", abs(a[ans[i]] - a[i]), ans[i]);
}
}