写在前面

华为面试题库刷题第三次整理。

203. 移除链表元素

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6

输出: 1->2->3->4->5

解法:题目不难,重点是用多钟解法来解题。

递归

从尾部开始删除就不需要特别记录前一个节点,利用递归在这一点上就很讨巧。

代码:

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {if(!head) return NULL;//递归边界head->next = removeElements(head->next,val);//递去:到底端再开始删除操作return head->val==val?head->next:head;//递归式}
};

迭代

遍历链表,删除节点。每次判断的都是下一位,这样方便删除,然后再最后return位置特判头。

代码:

class Solution {
public:ListNode* removeElements(ListNode* head, int val) {if(!head) return NULL;ListNode *curr, * del;curr = head;while(curr->next){if(curr->next->val==val){//删除下一位del = curr->next;curr->next = del->next;delete del;}else{curr = curr->next;}}return head->val==val?head->next:head;//头元素特判}
};

538. 把二叉搜索树转换为累加树

给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

例如:

输入: 原始二叉搜索树:

        5

        /   \

      2   13

输出: 转换为累加树:

        18

        /   \

      20  13

解法:加上所有比这个节点大的,大的都在右边,所以从右往左遍历并累加就行了。

递归

代码:

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:int sum = 0;
public:TreeNode* convertBST(TreeNode* root) {if(root){convertBST(root->right);//遍历右子树sum += root->val;//累加root->val = sum;//更新数值convertBST(root->left);//遍历左子树}return root;}
};

迭代

其实和递归大同小异,只不过把递归过程放进了循环里面。

代码:

class Solution {
public:TreeNode* convertBST(TreeNode* root) {int sum = 0;TreeNode* t = root;vector<TreeNode*> s;while(!s.empty()||t){while(t){s.push_back(t);t = t->right;}t = s.back();s.pop_back();sum += t->val;t -> val = sum;t = t -> left;}return root;}
};

820. 单词的压缩编码

给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A。

例如,如果这个列表是 [“time”, “me”, “bell”],我们就可以将其表示为 S = “time#bell#” 和 indexes = [0, 2, 5]。

对于每一个索引,我们可以通过从字符串 S 中索引的位置开始读取字符串,直到 “#” 结束,来恢复我们之前的单词列表。

那么成功对给定单词列表进行编码的最小字符串长度是多少呢?

提示:

  1. 1 <= words.length <= 2000
  2. 1 <= words[i].length <= 7
  3. 每个单词都是小写字母 。

解法:

后缀字符串

一个数组如果是另外一个字符串的后缀字符串,就会被删除,因为数据的单词长度不超过7,穷举即可,为了方便除重和删除操作,我们这里利用set数据结构。

代码:

class Solution {
public:int minimumLengthEncoding(vector<string>& words) {int l = words.size();set<string> s(words.begin(),words.end());for(auto word:words)for(int i=1; i<word.length(); i++)s.erase(word.substr(i));int res = 0;for(auto i = s.begin(); i != s.end(); i++)res += (*i).length()+1;return res;}
};

字典树

上面那种方法明显不具有课扩展性,穷举单词的后缀字符串很繁琐,这题可以利用字典树来进行计算。

去找到是否不同的单词具有相同的后缀,我们将其反序之后放入一棵字典树中(前缀树)。例如,我们有 “time” 和 “me”,可以将 “emit” 和 “em” 放入字典树中。

然后,字典树的叶子节点(没有孩子的节点)就代表没有后缀的词,统计 sum(word.length + 1 for word in words)。

代码:

class Solution {
public:struct TreeNode {int len;map<char, TreeNode*> next;TreeNode() : len(0) {};};TreeNode* root;Solution() {root = new TreeNode();}int minimumLengthEncoding(vector<string>& words) {int res = 0;for (auto& w : words) {auto node = root;for (int i = w.size() - 1; i >= 0; --i) {char c = w[i];if (node->next.count(c) == 0) {node->next[c] = new TreeNode();}node = node->next[c];if (node->len > 0) {res -= node->len;node->len = 0;}}if (node->len == 0 && node->next.empty()) {node->len = 1 + w.size();res += node->len;}}return res;}
};

1109. 航班预订统计

这里有 n 个航班,它们分别从 1 到 n 进行编号。

我们这儿有一份航班预订表,表中第 i 条预订记录 bookings[i] = [i, j, k] 意味着我们在从 i 到 j 的每个航班上预订了 k 个座位。

请你返回一个长度为 n 的数组 answer,按航班编号顺序返回每个航班上预订的座位数。

提示:

  1. 1 <= bookings.length <= 20000
  2. 1 <= bookings[i][0] <= bookings[i][1] <= n <= 20000
  3. 1 <= bookings[i][2] <= 10000

解法:

暴力模拟(超时)

看范围就知道会爆了,不过还是手贱试了一下。

代码:

class Solution {
public:vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {vector<int> a(n+1,0);for(auto x : bookings){for(int i=x[0]; i<=x[1]; i++)a[i] += x[2];}a.erase(a.begin());return a;}
};

扫描法

  1. 换一种思路理解题意,将问题转换为:某公交车共有 n 站,第 i 条记录 bookings[i] = [i, j, k] 表示在 i 站上车 k 人,乘坐到 j 站,在 j+1 站下车,需要按照车站顺序返回每一站车上的人数
  2. 根据 1 的思路,定义 counter[] 数组记录每站的人数变化,counter[i] 表示第 i+1 站。遍历 bookings[]:bookings[i] = [i, j, k] 表示在 i 站增加 k 人即 counters[i-1] += k,在 j+1 站减少 k 人即 counters[j] -= k
  3. 遍历(整理)counter[] 数组,得到每站总人数: 每站的人数为前一站人数加上当前人数变化 counters[i] += counters[i - 1]

代码:

class Solution {
public:vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {vector<int> a(n,0);for(auto x : bookings){if(x[0]-1>=0)a[x[0]-1] += x[2];if(x[1]<n)a[x[1]] -= x[2];}for(int i=1; i<n; i++){a[i] += a[i-1];}return a;}
};

423. 从英文中重建数字

给定一个非空字符串,其中包含字母顺序打乱的英文单词表示的数字0-9。按升序输出原始的数字。

注意:

  1. 输入只包含小写英文字母。
  2. 输入保证合法并可以转换为原始的数字,这意味着像 “abc” 或 “zerone” 的输入是不允许的。
  3. 输入字符串的长度小于 50,000。

解法:我用了很笨的方法,就嗯解,写了10个函数,然后再判断顺序修改的时候发现了一些优化的思路,不过已经不想写了,从0-9顺序判断,会出错,因为是恰好全部能转换成数字,这样可能会抢占别的数字的字母,所以我们要先判断一些有唯一字母的数字,然后再判断其他数字,每次都要保证当前数字不会抢占别的数字。

代码:

class Solution {
public:bool checkZero(vector<int>& a){if(a[int('z'-'a')]>=1 && a[int('e'-'a')]>=1 && a[int('r'-'a')]>=1 && a[int('o'-'a')]>=1){a[int('z'-'a')]--;a[int('e'-'a')]--;a[int('r'-'a')]--;a[int('o'-'a')]--;return true;}return false;}bool checkOne(vector<int>& a){if(a[int('o'-'a')]>=1 && a[int('n'-'a')]>=1 && a[int('e'-'a')]>=1){a[int('o'-'a')]--;a[int('n'-'a')]--;a[int('e'-'a')]--;return true;}return false;}bool checkTwo(vector<int>& a){if(a[int('t'-'a')]>=1 && a[int('w'-'a')]>=1 && a[int('o'-'a')]>=1){a[int('t'-'a')]--;a[int('w'-'a')]--;a[int('o'-'a')]--;return true;}return false;}bool checkThree(vector<int>& a){if(a[int('t'-'a')]>=1 && a[int('h'-'a')]>=1 && a[int('t'-'a')]>=1 && a[int('e'-'a')]>=2){a[int('t'-'a')]--;a[int('h'-'a')]--;a[int('r'-'a')]--;a[int('e'-'a')]-=2;return true;}return false;}bool checkFour(vector<int>& a){if(a[int('f'-'a')]>=1 && a[int('o'-'a')]>=1 && a[int('u'-'a')]>=1 && a[int('r'-'a')]>=1){a[int('f'-'a')]--;a[int('o'-'a')]--;a[int('u'-'a')]--;a[int('r'-'a')]--;return true;}return false;}bool checkFive(vector<int>& a){if(a[int('f'-'a')]>=1 && a[int('i'-'a')]>=1 && a[int('v'-'a')]>=1 && a[int('e'-'a')]>=1){a[int('f'-'a')]--;a[int('i'-'a')]--;a[int('v'-'a')]--;a[int('e'-'a')]--;return true;}return false;}bool checkSix(vector<int>& a){if(a[int('s'-'a')]>=1 && a[int('i'-'a')]>=1 && a[int('x'-'a')]>=1){a[int('s'-'a')]--;a[int('i'-'a')]--;a[int('x'-'a')]--;return true;}return false;}bool checkSeven(vector<int>& a){if(a[int('s'-'a')]>=1 && a[int('v'-'a')]>=1 && a[int('n'-'a')]>=1 && a[int('e'-'a')]>=2){a[int('s'-'a')]--;a[int('v'-'a')]--;a[int('n'-'a')]--;a[int('e'-'a')]-=2;return true;}return false;}bool checkEight(vector<int>& a){if(a[int('e'-'a')]>=1 && a[int('i'-'a')]>=1 && a[int('g'-'a')]>=1 && a[int('h'-'a')]>=1 && a[int('t'-'a')]>=1){a[int('e'-'a')]--;a[int('i'-'a')]--;a[int('g'-'a')]--;a[int('h'-'a')]--;a[int('t'-'a')]--;return true;}return false;}bool checkNine(vector<int>& a){if(a[int('i'-'a')]>=1 && a[int('e'-'a')]>=1 && a[int('n'-'a')]>=2){a[int('i'-'a')]--;a[int('e'-'a')]--;a[int('n'-'a')]-=2;return true;}return false;}string originalDigits(string s) {if(s.empty()) return s;vector<int> a(30,0);for(char c:s)a[int(c-'a')]++;string res = "";while(checkZero(a))//顺序就按照上面的思路res += '0';while(checkSix(a))res += '6';while(checkEight(a))res += '8';while(checkTwo(a))res += '2';while(checkThree(a))res += '3';while(checkFour(a))res += '4';while(checkFive(a))res += '5';while(checkSeven(a))res += '7';while(checkOne(a))res += '1';while(checkNine(a))res += '9';sort(res.begin(),res.end());return res;}
};

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

解法:这题是很经典的一题,我们用多种方法来解决。

排序

时间复杂度O(NlogN)的解法,空间复杂度O(1)。

代码:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {sort(nums.rbegin(),nums.rend());return nums[k-1];}
};

可以维护一个小顶堆,然后保持堆大小不超过k,这样遍历结束时,堆顶就是答案。时间:O(Nlogk) 空间:O(k)。

代码:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {if(k<=0 || nums.empty()) return 0;priority_queue<int,vector<int>,greater<int> > pq;//小顶堆for(int i=0; i<nums.size(); i++){pq.push(nums[i]);if(pq.size()>k){//保持个数不超过kpq.pop();}}return pq.top();}
};

快速选择法

用快徘的思维解决第k小问题。时间:O(N),空间:O(1)。

  1. 从开头讲起,随机选择一个pivot,将其移动至数组的尾端
  2. 然后初始化两个指针,一个叫store_index,其值代表当前小于pivot的个数,在数组中,其左边的元素代表小于pivot的元数,其初始值为0;
  3. 另一个指针j是用来遍历的,起点为store_index,终点为right-1。
  4. 每次j的数值小于pivot的话,把j和store_index的值互换,store_index+=1,也就是把小于pivot的值放在store_index的左边,然后j继续遍历。
  5. j从store_index到right-1的遍历结束后,把最终store_index的位置和right(也就是pivot)的位置呼唤,使得此时pivot左边的值都小于pivot,右边的值都大于pivot。
  6. 然后判断top k_smallest与当前pivot位置的大小关系,若两者相等,则找到目标位置啦,若两者不等,则由其大小关系去判断是要选择pivot的左边还是右边部分继续前面的步骤直到找到目标位置。

代码:

class Solution { //最小堆的方法
public:int partition(int left, int right, int k_smallest, vector<int>& nums, int pivot_index){//首先把pivot_index放在最后的位置int pivot=nums[pivot_index];int tmp=nums[pivot_index];nums[pivot_index]=nums[right];nums[right]=tmp;//选定当前小于pivot的位置,记为iint i=left;for(int j=i;j<right;j++){if(nums[j]<pivot){int tmp=nums[j];nums[j]=nums[i];nums[i]=tmp;i+=1;}}tmp=nums[i];nums[i]=nums[right];nums[right]=tmp;return i;}int select(int left, int right, int k_smallest, vector<int>& nums){if(left==right){return nums[left];}int pivot_index = (rand() % (right-left+1))+ left;//随机化pivot,防止数组有序使算法退化pivot_index = partition(left, right, k_smallest, nums, pivot_index);if(pivot_index==k_smallest){return nums[pivot_index];}else if(k_smallest>pivot_index){return select(pivot_index+1, right,k_smallest, nums);}else{return select(left,pivot_index-1, k_smallest, nums);}}int findKthLargest(vector<int>& nums, int k) {return select(0, nums.size()-1, nums.size()-k, nums);}
};

593. 有效的正方形

给定二维空间中四点的坐标,返回四点是否可以构造一个正方形。

一个点的坐标(x,y)由一个有两个整数的整数数组表示。

注意:

  1. 所有输入整数都在 [-10000,10000] 范围内。
  2. 一个有效的正方形有四个等长的正长和四个等角(90度角)。
  3. 输入点没有顺序。

解法:题目乍一看属实不难,只是情况比较多,繁琐是这题的难点,我们理清一下思路,可以发现有三种情况需要考虑:

  1. 平90°的正方形,意味着四个边都在坐标线了。
  2. 斜45°的正方形。
  3. 其他角度的正方形。

代码:

class Solution {
public:int sqr(int a){return a*a;}bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) {vector<vector<int>> p = {p1,p2,p3,p4};sort(p.begin(),p.end());//情况1if(p[0][0]==p[1][0]){if(p[0][0]!=p[2][0] && p[2][0]==p[3][0] && abs(p[0][1]-p[1][1])==abs(p[2][1]-p[3][1]) && abs(p[2][0]-p[0][0])==abs(p[0][1]-p[1][1]) && max(p[0][1],p[1][1])==max(p[2][1],p[3][1])){return true;}return false;}//情况2if(p[1][0]==p[2][0]){if(p[1][0]-p[0][0] == p[3][0]-p[2][0] && abs(p[1][1]-p[0][1])==abs(p[3][1]-p[2][1])){return true;}return false;}//情况3if(double(p[0][0]-p[1][0]) * double(p[0][0]-p[2][0])/double(p[0][1]-p[1][1])/double(p[0][1]-p[2][1])==-1 && double(p[0][0]-p[1][0]) * double(p[1][0]-p[3][0])/double(p[0][1]-p[1][1])/double(p[1][1]-p[3][1])==-1 && double(p[1][0]-p[3][0]) * double(p[3][0]-p[2][0])/double(p[1][1]-p[3][1])/double(p[3][1]-p[2][1])==-1){//三个角是90°if(sqr(p[0][0]-p[1][0])+sqr(p[0][1]-p[1][1])==sqr(p[0][0]-p[2][0])+sqr(p[0][1]-p[2][1]) && sqr(p[0][0]-p[1][0])+sqr(p[0][1]-p[1][1])==sqr(p[1][0]-p[3][0])+sqr(p[1][1]-p[3][1]))//三条边相等return true;}return false;}
};

题目小结:这道题让我学到了不少小知识,在这里总结一下:

  1. 注意我的自定义函数sqr,一开始我写的是int sqr(int& a),然后报错了,主要原因是下面的判断语句里面调用sqr的时候是常数,不是变量,所以不需要引用。
  2. 一开始我的斜率相乘判断的时候,没有加double,导致出错,int除法会出现0,导致结果错误。
  3. 仔细看我的斜率判断条件,你会发现我先乘再除,然后不是两个斜率分别算出来相乘,因为double的原因,先除再乘会有误差,如果斜率乘积是-1,先乘再除不会影响结果。

    这些小细节的错误反应了我C++基础的不扎实哈哈,所谓细节决定成败,还要多努力,刷的题目越多,基础就会越扎实,加油!

细节决定成败!华为面试题库刷题整理(三)相关推荐

  1. 华为机试题库+题解【C语言版】

    文章目录 前言 1.字符串最后一个单词的长度[***] 描述 输入描述 输出描述 示例 解题代码 2.计算某字符出现次数[****] 描述 输入描述 输出描述 示例 解题代码 3. 明明的随机数[** ...

  2. 《华为机试》刷题之HJ84 统计大写字母个数

    <华为机试>刷题之统计大写字母个数 我不知道将去向何方,但我已在路上! 时光匆匆,虽未曾谋面,却相遇于斯,实在是莫大的缘分,感谢您的到访 ! 题目: 找出给定字符串中大写字符(即'A'-' ...

  3. c语言程序设计理论考试,《C语言程序设计》理论试题库-程序题100例

    <<C语言程序设计>理论试题库-程序题100例>由会员分享,可在线阅读,更多相关<<C语言程序设计>理论试题库-程序题100例(59页珍藏版)>请在人人 ...

  4. 北邮oj题库刷题计划(更新ing)

    北邮oj题库刷题计划(更新ing) 83. A + B Problem 84 Single Number 85. Three Points On A Line 120 日期 121 最值问题 122 ...

  5. 基于科目分类的题库刷题小程序系统

    基于科目分类的题库刷题小程序系统 使用场景 在线答题,是一种在线练习.考试.测评的智能答题系统,适用于企业培训.学生练习.评测考核.知识竞赛.模拟考试等场景. 简介 考研刷题小程序云开发实战.题库小程 ...

  6. 大学计算机基础-题库刷题-精选

    题库刷题: 写在前面: 这个是我准备应对学校转专业考试而刷的题库, 也是大学计算机的题库,同样适用于大学计算机这门课的期末考试. 精选了一些重要的题目. 目录 题库刷题: 写在前面: 题目1:(接下来 ...

  7. leetcode刷题(三)——容斥原理

    leetcode刷题系列三.这一节的内容主要是容斥原理的题目和题解. 百度百科上容斥原理的解释: 在计数时,必须注意没有重复,没有遗漏.为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法 ...

  8. 【C/C++】蓝桥杯算法必刷题(三)目标ICPC铜/蓝桥杯国一

    目录 前言 题解文章汇总 题目传送门:算法必刷题(三) 该题单中第一类考点:二进制 1018.有趣的二进制 1019.[NOIP2006]数列 1020.只能吃土豆的牛牛 该题单中第二类考点:思维 1 ...

  9. 大一python题库刷题训练_python实现合工大试题库自动刷题

    1 #coding= utf-8 2 importre3 importrequests4 importxlrd5 6 save_url = "http://tkkc.hfut.edu.cn/ ...

最新文章

  1. about ajax,About 4nf.org - Arvind Gupta | Ajaxify | The Ajax Plugin
  2. oracle错误:ORA-12545
  3. python to_excel新增sheet_Python使用xlrd和xlwt读写Excel的简单用法
  4. 解决: Spring Boot报错 This application has no explicit mapping ... a fallback
  5. 14. GD32F103C8T6入门教程-Systick定时器
  6. Exchange Server2010系列之二:部署三合一角色(CAS+HT+MBX)
  7. 用户、组和权限命令练习
  8. 使用WinDbg分析Windows dump文件方法
  9. 计算机如何从光盘启动不了,电脑如何设置光驱启动?开机设置光驱为第一启动的步骤...
  10. nginx 版本升级
  11. java设置随机数_java设置随机数教程
  12. 2019xman-shellmaster wp
  13. 区块链-WeBase企业部署
  14. mysql rpl_MySQL增强半同步参数rpl_semi_sync_master_wait_point值AFTER_SYNC和AFTER_COMMIT
  15. Python绘制双坐标图
  16. 基于javaweb+jsp的医院住院管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Bootstrap)
  17. 微商客源如何引流?坚持操作喜马拉雅日引50+粉丝
  18. 用C语言来输出菱形的形状
  19. 【中文分词】 FMM BMM (python)
  20. SQL Server 2005 SP3正式版下载

热门文章

  1. emplace_back和push_back的区别
  2. 计算机售电卡端口错误怎么弄,目前Z流行的插卡预付费电表怎么使用售电软件?...
  3. 服务器怎么架设虚拟主机,服务器怎么架设虚拟主机
  4. windows 11下:Docker DeskTop starting
  5. C中哈希开源hash代码uthash的原理与用法
  6. vue 腾讯地图点聚合 点标注添加信息窗口
  7. sharding-proxy和sharding-ui 简介与v5.0.0-beat版本搭建配置
  8. html5最新浏览器支持程度比较
  9. 探索迷宫问题 错误点总结
  10. 在vscode中打开vue项目,js文件中出现黄色波浪线