左神算法中级班第三课[C++代码]

  • 第一题:流水线打包问题[阿里原题]
    • 代码
  • 第二题
    • 代码
  • 第三题:打印螺旋矩阵
    • 代码
  • 第四题
    • 代码
  • 第五题:判读aim是否在矩阵中
    • 代码
  • 第七题:topK问题
    • 代码
  • 第七题加题:自定义堆解题
    • 代码

声明:博客根据左神在毕站的讲课视频整理,代码是根据左神的java代码版本改写为c++版本
代码为自己动手改写,未经应用不能转载

github代码链接:
https://github.com/hjfenghj/zuoshen_middlecalss

第一题:流水线打包问题[阿里原题]

技巧分析: 以某个元素为分割点,求左右两侧信息的技巧

  • 思路
    遍历数组,记录每个位置左右两侧(不包括此位置)的信息。这里需要记录左侧所有数据的平均值于全部数组平均值大小的关系;
    左侧的元素平均值记为L_avg,右侧的元素平均值记为R_avg,数组全部元素平均值记为avg;
    如果L_avg>avg,表示需要从左侧拿出物体放在此位置
    如果R_avg>avg, 表示需要从右侧拿出物体放在此位置
    所以一共右4中情况
  1. L = L_avg - avg < 0 且 R = R_avg- avg < 0
    需要从此位置往两侧拿物体,因为每个位置每次只能搬动一个物体进行移动,所以位置i处需要调度abs(L) + abs®次才能到达均值
    2)L = L_avg - avg > 0 且 R = R_avg- avg > 0
    需要从两侧拿物体放在i位置上,可以同时进行,所以这里需要调度max(abs(L),abs®)轮
    3)L = L_avg - avg 与 R = R_avg- avg 异号,需要从一侧搬出物体放在位置i上,右需要从位置i上取出物体放在另一侧;因为考虑到位置i初始时可能为零的情况,第一轮不能同时完成,所以需要max(abs(L),abs®)轮次

代码

  • code
#include<iostream>
#include<vector>using namespace std;int get_res(vector<int> Arr)
{int ans = 0;int LeftSum = 0;//当前索引左侧元素和int AllSum = 0;for (int i = 0; i < Arr.size(); i++){AllSum += Arr[i];}int avg = AllSum / Arr.size();int RightSum = AllSum - LeftSum; //当前索引左侧元素和,起始索引为-1for (int i = 0; i < Arr.size(); i++){RightSum = RightSum - Arr[i] - LeftSum;if (i > 0)LeftSum = LeftSum + Arr[i - 1];int L = (i > 0) ? LeftSum - avg * (i - 1) : 0;int R = RightSum - avg * (Arr.size() - i - Arr[i]);if (R > 0 && L > 0)ans = max(ans, R + L);elseans = max(ans, max(abs(R), abs(L)));}return ans;
}

第二题

技巧:大局观,宏观意识

  • 思路
    两个指针,都从位置【0,0】开始;一个指针往左移动,撞墙以后往下移动;一个指针往下移动,撞墙以后往右移动;当两个指针到达结束位置的时候返回

代码

  • code
#include<iostream>
#include<vector>
using namespace std;class PROBLEM02
{public:vector<int> ans;void process(vector<vector<int>> Arr,int a,int b,int c,int d,bool flag){//从右上角角往左下角移动if (flag){while (a != c+1)ans.push_back(Arr[a++][b--]);}//从左下角往右上角移动else{while (c != a - 1)ans.push_back(Arr[c--][d++]);}return;}void get_res(vector<vector<int>> Arr){bool flag = true;int Lx = 0;int Ly = 0;int Dx = 0;int Dy = 0;while (Lx != Arr.size()){process(Arr, Lx, Ly, Dx, Dy, flag);//两个指针坐标迁移Lx = Ly == Arr[0].size() - 1 ? Lx+1 : Lx;Ly = Ly == Arr[0].size()-1 ? Ly : Ly+1;Dy = Dx == Arr.size() - 1 ? Dy+1 : Dy;Dx = Dx == Arr.size() - 1 ? Dx : Dx+1;flag = !flag;}return;}
};int main()
{vector<vector<int>> Arr(4, vector<int>(4, 0));for (int i = 0; i < 4; i++){Arr[i] = vector<int>{ 1 + i * 4,2 + i * 4,3 + i * 4,4 + i * 4 };}for (int i = 0; i < Arr.size(); i++){for (int j = 0; j < Arr[0].size(); j++){cout << Arr[i][j] << " ";}cout << endl;}cout << "==============" << endl;PROBLEM02 P;P.get_res(Arr);for (int i = 0; i < P.ans.size(); i++)cout << P.ans[i] << " ";return 0;
}

第三题:打印螺旋矩阵

技巧:大局观,宏观意识
矩阵的子矩阵可以由左上角和右下角两个位置确定

  • 思路1
    螺旋举着的打印时一圈一圈的打印,也就是每次打印一个子矩阵的外圈;所以使用两个指针,一个指针从左上角开始,一个指针从右下角开始;打印确定的矩阵的外圈;

  • 思路2
    常规思路

代码

  • code
#include<iostream>
#include<vector>using namespace std;class PROBLEM03_1
{public:vector<int> ans;//(a,b)左上角点,(c,d)右下角点void process(vector<vector<int>> Arr,int a,int b,int c,int d){//同行if (a == c){while (b <= d){ans.push_back(Arr[a][b]);b++;}}//同列if (b == d){while (a <= c){ans.push_back(Arr[a][b]);a++;}}//其他情况for (int j = b; j < d;j++)ans.push_back(Arr[a][j]);for (int i = a; i < c; i++)ans.push_back(Arr[i][d]);for (int j = d; j > b; j--)ans.push_back(Arr[c][j]);for (int i = c; i > a; i--)ans.push_back(Arr[i][b]);   return;}void get_res(vector<vector<int>> Arr){int a = 0;int b = 0;int c = Arr.size()-1;int d = Arr[0].size()-1;while (a <= c && b <= d){process(Arr, a++, b++, c--, d--);}for (int i = 0; i < ans.size(); i++)cout << ans[i] << " ";cout << endl;return;}
};class PROBLEM_03_2
{public:vector<int> ans;void get_res(vector<vector<int>> Arr){int i = 0;//实时记录目前打印到的位置int j = 0;int idx = 0;//记录现在是第几圈,0表示第一圈int flag = (min(Arr.size(), Arr[0].size()) + 1) / 2;//一共需要打印多少圈while (idx < flag){//0+idx表示起始列,从左往右打印,递增列for (j = 0 + idx; j < Arr[0].size() - idx - 1; j++)ans.push_back(Arr[i][j]);//自上往下打印,递增行for (i = 0 + idx; i < Arr.size() - idx - 1; i++)ans.push_back(Arr[i][j]);//自右往左打印,递减列for (j; j >= 0 + idx + 1; j--)ans.push_back(Arr[i][j]);//自下网上打印,递减行for (i; i >= 0 + idx + 1; i--)ans.push_back(Arr[i][j]);//更新起始位置和圈数i++;idx++;}for (int i = 0; i < ans.size(); i++)cout << ans[i] << " ";return;}
};

第四题

技巧:大局观,宏观意识

  • 思路
    每次都是四个点的互换(写为固定的函数),根据每一圈的边长,确定每个外圈需要调用互换函数的次数;左上角和右下角两个点确定一个矩形;

代码

class PROBLEM04
{public:void get_res(vector<vector<int>>& Arr){int flag = (min(Arr[0].size(), Arr.size()) + 1)/2;int count = 0;//层数int idx1 = 0;int idy1 = 0;int idx2 = Arr.size()-1;int idy2 = Arr[0].size()-1;while (count < flag){int a = idx1 + count;int b = idy1 + count;int c = idx2 - count;int d = idy2 - count;for (int offset = 0; (offset+a) < Arr.size() - count - 1; offset++){int temp = 0;temp = Arr[a][b + offset];Arr[a][b + offset] = Arr[c - offset][b];Arr[c - offset][b] = Arr[c][d - offset];Arr[c][d - offset] = Arr[a + offset][d];Arr[a + offset][d] = temp;}count++;}for (int i = 0; i < Arr.size(); i++){for (int j = 0; j < Arr[0].size(); j++){cout << Arr[i][j] << " ";}cout << endl;}}
};

第五题:判读aim是否在矩阵中

技巧:遍历顺序的重要性

  • 思路
    思路很简单,从左上角开始遍历,判断与target的大小关系,如果大于target,就往左遍历;如果小于target,就往下遍历;当找到目标的时候返回,或者越界的时候返回;

代码

  • code
class PROBLEM05
{public:int flag = false;//是否找到的标志//返回行号,如果down一直没找到,返回-1int Down(vector<vector<int>> Arr, int i, int j, int target){while (i < Arr.size()){if (Arr[i][j] > target)return i;else if (Arr[i][j] == target)flag = true;i++;}return -1;}//返回列,如果down一直没找到,返回-1int Left(vector<vector<int>> Arr, int i, int j, int target){while (j >= 0){if (Arr[i][j] < target)return j;else if (Arr[i][j] == target)flag = true;j--;}return -1;}bool process(vector<vector<int>> Arr, int aim){int L1 = Arr.size();int L2 = Arr[0].size();//作为全局的变量,实时更新当前位置int idx_r = 0;int idx_c = L2 - 1;while (!flag && idx_r != -1 && idx_c != -1){if (Arr[idx_r][idx_c] == aim)return true;//小于当前值往下搜素,遇到大于目标值返回else if (Arr[idx_r][idx_c] < aim)idx_r = Down(Arr, idx_r, idx_c, aim);//大于当前值往左搜索, 遇到小于当前值返回elseidx_c = Left(Arr, idx_r, idx_c, aim);}return flag;}
};class PROBLEM05_2
{public:bool process(vector<vector<int>> Arr, int target){int row = 1;int col = Arr[0].size()-1;while (row < Arr.size() && col > -1){if (Arr[row][col] == target)return true;else if (Arr[row][col] > target)col--;elserow++;}return false;}
};

第七题:topK问题

小根堆的使用

  • 思路
    哈希表记录字符出现的频率,然后构建小根堆,根据词频记录前K个;
    当小根堆长度达到K以后,根据堆顶元素的词频与待进入字符词频比较,如果待进入字符的词频到达小根堆的门槛,就把堆顶弹出,把待进入的字符压入小根堆,然后堆内部会重新进行排列,将目前词频最小的字符推在堆顶

代码

class Node
{public:string str;int times;Node(string s, int t){str = s;times = t;}
};class PROBLEM07
{public:static bool compare(Node N1, Node N2){return N2.times < N1.times;}vector<string> get_res(vector<string> S,int T){//词频统计map<string, int> M;for (auto str : S){if (M.find(str) != M.end())M[str]++;elseM[str] = 1;}//使用小根堆priority_queue<Node,vector<Node>,decltype(&compare)> PQ(compare);for (auto kv : M){if (PQ.size() < T){               PQ.emplace(Node(kv.first, kv.second));}else{if (PQ.top().times < kv.second){PQ.pop();PQ.emplace(Node(kv.first, kv.second));}}}vector<string> res;for (int i = 0; i < T;i++){res.push_back(PQ.top().str);PQ.pop();}return res;}
};
int main()
{vector<string>  S{ "a","b","c","f","s","a","b","c","r","a","b","f","f","f","f","f","s","s","s" };PROBLEM07 P;vector<string> res = P.get_res(S, 2);for (int i = 0; i < res.size(); i++)cout << res[i] << " ";return 0;
}

第七题加题:自定义堆解题

第七题中直接给定了所有字符,可以直接将不达标的字符删去,但是如果字符时源源不断地流来的话,因为不知道以后的数据是什么情况,就需要将原来不能进入小根堆的字符 以及其词频也储存起来;


设计投票系统,实时显示前K高的人

  • 思路
    1)词频表
    2)堆位置表,可以看作是数组中的索引值(以中序遍历的方式遍历二叉树形成的数组)
    3)最小堆

来一个字符,通过词频表判断字符是否出现过,词频表中添加该字符,如果没出现过那么在堆里边的位置一定为-1;然后判断字符词频有没有达到堆顶门槛,如果达到了,就把堆顶元素与新元素互换,然后堆堆使用heapify方法将首节点往下沉,将最小词频字符拿到堆顶,并实时更新堆位置表;如果堆没有满,将新字符放在堆尾(在堆位置表数组中体现,然后堆该位置调用heapinsert方法,将最小词频字符浮在堆顶)

代码

  • code
#include<iostream>
#include<vector>
#include<queue>
#include<map>using namespace std;class Node
{public:string str;int times;Node(string s){str = s;times = 1;}Node(){str = "wuming";times = 1;}
};class PROBLEM07
{public:map<string, Node> maptime;//词频表map<string, int> heaplocal;//堆位置更新vector<Node> heap;int K;//堆目前的长度PROBLEM07(int size){heap.resize(size);K = 0;}void add(string s){Node cur = Node(s);int local_idx = -1;   //初始化新进入的字符串的堆位置为-1;//词频表更新//如果新加入的字符以前出现过,那么一定存在他的堆位置if (maptime.find(s) != maptime.end()){maptime[s].times++;local_idx = heaplocal[s];}//如果新加入的字符以前没出现过else{maptime.emplace(s, cur);heaplocal.emplace(s, -1);}//新进入的字符串不在堆上if (local_idx == -1){//堆已经满了if (heap.size() == K){//新入元素的次数可以把堆顶干翻if (maptime[s].times > heap[0].times){heaplocal[heap[0].str] = -1;heap[0] = maptime[s];heapify(0, K);//堆顶元素下沉,K代表此时的堆长度}}//堆还没有满else{heaplocal[s] = K;heap[K] = maptime[s];heap_insert(K++);//堆底元素上浮}}//新进入的字符在堆上else{heapify(local_idx, K);//从local_idx位置下沉}}void swap(int idx1,int idx2){Node temp;temp = heap[idx1];heap[idx1] = heap[idx2];heap[idx2] = temp;heaplocal[heap[idx1].str] = idx2;heaplocal[heap[idx2].str] = idx1;}//下沉void heapify(int s_idx, int e_idx){int L = s_idx * 2 + 1;int R = s_idx * 2 + 2;int smallest = s_idx;while (L < e_idx){if (heap[L].times < heap[s_idx].times){smallest = L;}if (R < e_idx && heap[R].times < heap[smallest].times)smallest = R;if (smallest != s_idx)swap(smallest, s_idx);elsebreak;s_idx = smallest;L = s_idx * 2 + 1;R = s_idx * 2 + 2;}}//元素上浮void heap_insert(int s_idx){while (s_idx != 0){int parent = (s_idx - 1) / 2;if (heap[s_idx].times < heap[parent].times){swap(parent, s_idx);s_idx = parent;}elsebreak;}return;}void print_topK_str(){cout << "now top 2 is:" << " ";for (int i = 0; i < heap.size(); i++){cout << heap[i].str << " ";}cout << endl;}
};int main()
{vector<string>  S{ "a","b","c","f","s","a","b","c","r","a","b","f","f","f","f","f","s","s","s" };PROBLEM07 P(2);for (int i = 0; i < S.size(); i++){P.add(S[i]);P.print_topK_str();}return 0;
}

左神算法中级班第三课[C++代码]相关推荐

  1. 左神算法初级班笔记4:二叉树

    文章目录 01 | 实现二叉树的先序.中序.后序遍历,包括递归方式和非递归 方式 1.递归版本: 2. 非递归版本: 02 | 在二叉树中找到一个节点的后继节点 03 | 介绍二叉树的序列化和反序列化 ...

  2. 《左神算法初级班》第四节课:二叉树结构

    目录: 1)二叉树结构 2)二叉树的递归与非递归遍历 3)打印二叉树 4)判断搜索二叉树 5)判断完全二叉树 6)判断平衡二叉树 7)折纸问题 8)二叉树节点的前驱节点与后继节点 9)二叉树的序列化和 ...

  3. 【搞定左神算法初级班】第4节:二叉树及相关常见面试题

    目 录: 题目1:实现二叉树的先序.中序.后序遍历[递归方式和非递归方式] 题目2:在二叉树中找到一个节点的后继节点 题目3:介绍二叉树的序列化和反序列化 题目4:折纸问题 题目5:判断一棵二叉树是否 ...

  4. 左神算法基础班4_4_3在二叉树中找到一个节点的后继节点

    Problem: 在二叉树中找到一个节点的后继节点 [题目] 现在有一种新的二叉树节点类型如下: public class Node { public int value; public Node l ...

  5. 左神算法基础班3_13深度拷贝含有随机指针的链表

    Problem: 复制含有随机指针节点的链表 [题目] 一种特殊的链表节点类描述如下: public class Node { public int value; public Node next; ...

  6. 左神算法进阶班5_3求公司的最大活跃度

    [题目] 一个公司的上下节关系是一棵多叉树,这个公司要举办晚会,你作为组织者已经摸清了大家的心理: 一个员工的直接上级如果到场,这个员工肯定不会来. 每个员工都有一个活跃度的值,决定谁来你会给这个员工 ...

  7. 0206.BFPRT在一大堆数中求其前k大或前k小的问题,简称TOP-K问题(左神算法基础班源码)

    package basic_class_02;/*** * 在一大堆数中求其前k大或前k小的问题,简称TOP-K问题.* 而目前解决TOP-K问题最有效的算法即是BFPRT算法**/ public c ...

  8. 13.在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。(左神算法基础班源码)

    package basic_class_01; /*** *小和问题在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和.求一个数组的小和.例子:[1,3,4,2,5]1左边比1小的数 ...

  9. 左神算法(一)下修改版

    序言: 此篇内容紧跟在左神算法(一)上修改版之后. 左神算法(一)上修改版 左神算法(一)下修改版 左神算法(二) 七.二叉树的基本算法 1.二叉树 2.二叉树的先序.中序.后序遍历 先序:任何子树的 ...

最新文章

  1. [征询意见][投票]先集中力量做好一个开源项目
  2. js实现图片轮播(终结版)
  3. 陀螺仪c语言算法,陀螺仪c程序.docx
  4. 26.智能指针和动态内存
  5. linux服务器下降,linux - 远程升级Ubuntu:如何最大程度地降低丢失服务器的风险? - Ubuntu问答...
  6. 信息学奥赛一本通(2049:【例5.19】字符串判等)
  7. azure api 管理_Azure Cosmos DB和MongoDB API入门
  8. 鸿蒙个人用户怎么申请,鸿蒙OS来了,这些机型的用户可以申请
  9. sqlserver201无效的许可证书_sql提示无效数字
  10. 灵悟礼品网上专卖店——分析类似项目的布局和商品的分类模式
  11. android之socket编程实例
  12. 常用的网络操作系统有哪些?它们各具有什么特点?
  13. 学习类App原型制作分享-Wokabulary
  14. 应聘阿里的前车之鉴:从被回绝的系列原因出发,解读应聘阿里注意事项
  15. 个人愚见: React 和 Vue 区别
  16. I帧,P帧,B帧 压缩率对比
  17. 微信瑞文智力测试1分_答完瑞文智力测试的题之后,怎样看智商是多少?
  18. python开发要学哪些内容_Python开发工程师需要学习哪些内容?
  19. scp时提示【Read-only file system】的解决方案
  20. 车险杀手锏——高速大数据在车险定价中的应用

热门文章

  1. Python自动翻译英语论文PDF(三十九)
  2. 做移动端电子签名发现canvas的 一些坑
  3. Dell服务器组建阵列-Raid(无阵列卡)
  4. windows开启SMB
  5. linux系统的版本(转载)
  6. 【微服务】—— 统一网关Gateway
  7. RC滤波器(高通/低通)
  8. 数据湖与数据仓库:主要差异
  9. Android 去掉锁屏
  10. QtScrcpy手机投屏电脑利器连接Android设备