刷题笔记:AcWing
Zee_Chao
to be slim, to be smart
展开
-
AcWing 1.A+B
具体代码如下:#include <iostream>using namespace std;int main(){ int a, b; cin >> a >> b; cout << a+b; return 0;}原创 2019-05-09 13:25:46 · 540 阅读 · 0 评论 -
AcWing 48. 复杂链表的复刻(地址映射+原地复刻)
1.地址映射考虑将原链表的各个结点从头到尾依次进行编号。这样结点之间的指向关系就可以用编号来代替。在按一般链表的顺序复制好链表以后,就可以用编号的指向关系快速地定位每个新结点的额外指针的指向。这里为了方便对复制的链表进行编号,可以考虑用向量来依次存放每个新结点。至于原链表的结点与编号的关系可以考虑建立一个从结点地址到数字的映射关系。具体代码如下:/** * Definition...原创 2019-06-19 11:06:20 · 286 阅读 · 0 评论 -
AcWing 37. 树的子结构
题目要求实现两个过程,一个是判断是否为子结构,另一个是对A的遍历。假如题目规定空树是任何树的子结构,这两个过程可以在同一个递归函数中完成。如果B的根与A的某个子树的根数值相同,则判断左右子树是否同时满足要求。否则的话对A做遍历依次验证。然而题目规定空树不是任何树的子结构,那么一个递归函数已经不能满足要求了。因为递归的出口并不统一。B一上来根节点就为空,和子结构判断时遍历到B子树根节点为...原创 2019-06-02 15:45:12 · 263 阅读 · 0 评论 -
AcWing 39. 对称的二叉树
一棵二叉树为对称二叉树等价于其左右子树镜像对称。因此可以考虑使用递归来解决此问题。具体代码如下:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int...原创 2019-06-07 14:57:17 · 230 阅读 · 0 评论 -
AcWing 43. 不分行从上往下打印二叉树(层次遍历模板)
具体代码如下:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} *...原创 2019-06-13 12:26:00 · 205 阅读 · 0 评论 -
AcWing 36. 合并两个排序的链表(归并+混洗)
1.归并具体代码如下:/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public...原创 2019-06-01 16:12:09 · 178 阅读 · 0 评论 -
AcWing 35. 反转链表(两种链表两种通法,头插法+原地法)
1.无头结点的链表在分析整个问题之前,我们先做一个假设。假设“NULL”是一个特殊的“结点”,它只能被其他结点指向而不能指向其他结点。这里一定要强调把NULL作为结点看待。我们可以做个图:NULL 1->2->……->n->NULL其中,最左侧的NULL是新链表的NULL,我们期待的结果是:NULL<-1<-2<-……<...原创 2019-06-01 15:33:34 · 296 阅读 · 0 评论 -
AcWing 34. 链表中环的入口结点(快慢指针赛跑+地址映射)
1.快慢指针赛跑这是一个做起来很巧妙的算法,而且这个方法既不会改变原链表的结构,又不会需要额外的空间。具体的操作是:从头结点开始,令快指针每次移动两个单位,慢指针每次移动一个单位。如果链表有环则两个指针必定会在环上的某一个位置相遇。此时令快指针重回起点,然后让两个指针同时每次移动一个单位,当两个指针再次相遇时(必定会相遇),相遇的结点恰好就是环的入口结点。证明如下:假设链表...原创 2019-06-01 14:56:53 · 1386 阅读 · 0 评论 -
AcWing 33. 链表中倒数第k个节点 (快慢指针赛跑)
设置两个指针,快指针先行遍历k次,然后在同慢指针同步遍历。当快指针抵达链表尾部时,慢指针所指位置即链表中倒数第k个结点。遍历期间,要注意快指针先到达链表尾部的情况。具体代码如下:/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; *...原创 2019-05-29 14:37:42 · 207 阅读 · 0 评论 -
AcWing 44. 分行从上往下打印二叉树(计数法+标记法)
首先,我们要知道一件事。那就是:在用辅助队列进行二叉树层次遍历时,辅助队列中的所有节点在层次上要么均来自同一层,要么来自相邻两层。而且对于后者的情况,辅助队列中从队头到队尾的所有节点,前半部分节点所在层数距离根节点较近,而后半部分则较远。因此,本题的解题关键就在于如何正确地区分这两部分。可以想到的方法有两种。第一种是设置两个计数器,分别记录辅助队列中不同层中节点的个数并且动态维护。另一种则...原创 2019-06-14 16:19:45 · 224 阅读 · 0 评论 -
AcWing解题目录(C++)
# 试题名称 思想方法 难度 试题来源 1 A+B 简单 AcWing 2 01背包问题 简单 3 完全背包问题 简单 4 多重背包问题I 简单 5 多重背包问题II 中等 6 多重背包问题...原创 2019-06-09 18:08:42 · 737 阅读 · 0 评论 -
AcWing 53. 最小的k个数(堆+快速排序的轴点选择函数)
1.基于堆的实现创建一个最大堆,控制其最大规模为k。若堆的规模小于k则不断将数组元素压入到堆中,否则则要根据当前元素和堆顶元素的大小来判断是否进行“交换”操作(堆顶元素弹出,当前数组元素压入)。不断重复此操作就可以将原数组中所有最小的k个数全部保留到堆中了。具体代码如下:class Solution {public: vector<int> getLeast...原创 2019-07-02 14:50:10 · 226 阅读 · 0 评论 -
AcWing 51. 数字排列(DFS(有重复排列+无重复排列))
OJ上这道题的描述并不清晰。实际上,它的要求是输出所有“不重复的排列方式”。1.next_permutation()(无重复排列)要解决数组无重复排列的问题,那么这个方法是再合适不过的了。在使用这个函数前注意要引入<algorithm>头文件并且为了保证可以枚举所有情况要实现对数组进行“由小到大”的排序。具体代码如下:class Solution {public...原创 2019-06-21 22:48:24 · 404 阅读 · 0 评论 -
AcWing 47. 二叉树中和为某一值的路径(DFS)
具体代码如下:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} *...原创 2019-06-17 14:57:46 · 241 阅读 · 0 评论 -
AcWing 41. 包含min函数的栈
除了维护基本的栈s以外,还有额外维护一个存放当前栈中最小元素的栈ms。假设当前需要入栈s的元素为x,则当ms为空或者x<=ms.top()时,x也要压入到ms中。假设当前栈s顶部元素为x,则出栈时若x==ms.top()则ms也要进行出栈操作。需要注意的是,当最小栈的入栈条件中x必须要小于等于ms.top(),不可以是严格小于。否则的话当多个最小元素入栈后,再进行出栈操作会出错。...原创 2019-06-11 13:08:46 · 208 阅读 · 0 评论 -
AcWing 46. 二叉搜索树的后序遍历序列
后序遍历序列从前往后可以分成三部分,分别是:左子树的后序遍历序列、右子树的后序遍历序列和单一的根节点。由于该树是一个BST,因此左子树中的节点的值一定比根节点的值小,右子树节点的值一定比根节点的值大。根据这些就可以设计出判断BST后序遍历序列的算法。首先提取出序列的最后一个节点(必然为根节点)。然后从前向后遍历序列根据节点值与根节点值的大小关系来分割序列并判断当前序列是否满足BST后序遍历...原创 2019-06-16 13:03:57 · 215 阅读 · 0 评论 -
AcWing 49. 二叉搜索树与双向链表(前驱后继+中序遍历)
1.利用前驱后继对于一个二叉树,可以将其分为左子树、根节点和右子树三部分。根据题目要求可知将BST化为一个排好序的双向链表实际上就是将其中序遍历序列对应的各个节点连接起来。同时还可以知道若根的左右子树非空,则根节点在链表中的前驱结点一定在其左子树中,后继结点一定在其右子树中。那么可以先找到并标记根节点的前驱和后继,然后连接。同时递归执行这一过程便能达到要求。具体代码如下:/**...原创 2019-06-20 13:47:52 · 339 阅读 · 0 评论 -
AcWing 40. 顺时针打印矩阵
题目的主要难度在于如何控制遍历方向的变化。首先,可以考虑建立两个方向数组(一个控制行的移动,另一个控制列的移动)。另外,由于题目的要求,这两个方向数组的控制的移动方向必须按照“右、下、左、上”的顺序排列。这样,方向的选取就可以按照“循环位移”的方式来进行,即当前位置加1后再对4取模。那么,另一个问题是如何发现改变方向的时机呢?很多人容易想到根据当前可访问的行或列的界限来设置循环控制。然而这...原创 2019-06-10 14:03:40 · 195 阅读 · 0 评论 -
AcWing 45. 之字形打印二叉树(双栈法+reverse方法)
1.双栈法根据经验,我们可以发现这道题需要对层次遍历的模板进行一定的改动。如果我们将根节点所在层定义为第0层,可以发现凡是偶数层都是从左往右打印,而奇数层却恰好相反。考虑到下一层与上一层节点的关系以及他们的相反输出次序,可以试着引入栈来进行反向输出的操作。那么问题来了,要怎么去设计对栈的控制呢?这里的方案是引入两个栈,一个负责偶数层节点,记作stack0,另一个负责奇数层的,记作sta...原创 2019-06-15 13:18:44 · 312 阅读 · 0 评论 -
AcWing 38. 二叉树的镜像
具体代码如下:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} *...原创 2019-06-06 11:35:10 · 178 阅读 · 0 评论 -
AcWing 32. 调整数组顺序使奇数位于偶数前面
简单来说就是设置两个指针,一个从前向后遍历,另一个从后向前遍历。当前面的指针遇到偶数,后面的指针遇到奇数且前一个指针在后一个指针之前是交换两个数字。具体代码如下:class Solution {public: void reOrderArray(vector<int> &array) { int i = 0, j = array.siz...原创 2019-05-28 21:50:06 · 193 阅读 · 0 评论 -
AcWing 22. 旋转数组的最小数字(二分法)
这个问题投机的做法就是将数组排序然后输出最小元素。但这种做法并不推荐,原因就不解释了。这道题最好的做法是使用二分法。在解决这个问题前,先假设数组是一个元素互异且递增的数组。那么其中一定满足nums[n]>nums[0]。当此数组发生了旋转后,则必有nums[n]<nums[0]。此时,我们可以利用这一条件结合二分法来查找最小元素。如果nums[mid]<nums[0],则...原创 2019-05-16 13:55:13 · 176 阅读 · 0 评论 -
AcWing 29. 删除链表中重复的节点
这个问题有很多种解法。最直观简单的就是建立映射用以统计每个结点的出现次数,最后根据结果重构链表。这里介绍一种空间复杂度为O(1)的方法。具体代码如下:/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(...原创 2019-05-20 17:20:51 · 248 阅读 · 0 评论 -
AcWing 21. 斐波那契数列(迭代算法(或DP算法))
斐波那契数列的求法有很多种,这里只列举一种最简单的方法。我们先假设斐波那契数列是如下排列的:下标 0 1 2 3 4 5 …… 数据 0 1 1 2 3 5 …… 我们先设置两个变量f和g,并分别初始化为0和1以代表第0号元素和第1号元素。然后我们可以很容易地发现,只要不断重复进行如下操作f ...原创 2019-05-15 13:50:20 · 731 阅读 · 0 评论 -
AcWing 20. 用两个栈实现队列
设置两个栈,一个用来负责输入,另一个用来负责输出。对于push操作就是将元素压入输入栈即可。empty操作则是判断两个栈是否同时为空。peek和pop操作大同小异。那就是如果输出栈为空,则要先将输入栈的元素全部弹出并按照弹出顺序再压入到输出栈中,此时在输出栈中,从栈顶到栈底的顺序就是队列中从队头到队尾的顺序。从而只需返回或者弹出输出栈的栈顶元素即可。具体代码如下:class My...原创 2019-05-15 13:35:25 · 128 阅读 · 0 评论 -
AcWing 19. 二叉树的下一个节点(中序遍历的后继结点)
要想解决这个问题就必须要先知道当前节点与其后继节点之间究竟存在什么关系。由数据结构的知识可以得知:如果当前节点存在右子树,则其后继节点一定是右子树上最左端的节点。如果不存在右子树,则其后继节点一定是其所属最小左子树的父亲。因此就得到了求后继节点的具体方法:如果当前节点存在右子树,则将指针指向有孩子节点,然后不断向左子树遍历找到左孩子为空的节点并返回。如果当前节点不存在右子树,则判断其父...原创 2019-05-15 13:10:23 · 251 阅读 · 0 评论 -
AcWing 18. 重建二叉树(前序+中序)
常规的做法是先从先序遍历序列中选出根节点,然后再由中序遍历序列得到左右子树节点,最后递归重建二叉树。具体的做法是:首先,先序遍历序列的第一个元素一定是根节点元素;然后,在中序遍历序列中找到此元素的位置,此时该位置左侧的元素排序为根节点的左子树的中序遍历,右侧的元素排序为根节点的右子树的中序遍历。由左子树和右子树的节点个数可以对先序遍历序列进行进一步划分,假设左子树的中序遍历序列的长度是l,则在...原创 2019-05-15 12:27:17 · 237 阅读 · 0 评论 -
AcWing 28. 在O(1)时间删除链表结点
由于单向链表无法在O(1)的时间内得知其前驱结点,因此考虑将待删除结点的值替换成其下一个结点的值,然后将下一个结点删除即可。具体代码如下:/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x)...原创 2019-05-19 14:55:27 · 195 阅读 · 0 评论 -
AcWing 13.找出数组中重复的数字(循环交换)
1.方法1简单来说就是开辟一个空间为n的向量用来做数字出现次数的统计。由于题目只要求返回任意一个重复的数字,因此只需遍历统计向量找到首个记录出现次数大于1的数字即可。具体代码如下:class Solution {public: int duplicateInArray(vector<int>& nums) { int n = nums...原创 2019-05-09 16:18:18 · 379 阅读 · 0 评论 -
AcWing 23. 矩阵中的路径(DFS)
这是一道比较经典的深度优先搜索问题。首先,记录下每个字符串的起点位置。然后从每个位置开始进行深度优先搜索。具体代码如下:class Solution {public: int dx[4] = {0, 0, -1, 1}; int dy[4] = {-1, 1, 0, 0}; bool dfs(vector<vector<char>...原创 2019-05-16 14:57:02 · 444 阅读 · 0 评论 -
AcWing 14. 不修改数组找出重复的数字(分治法)
如果不将思考题考虑在内的话,那么AcWing 13.找出数组中重复的数字中的方法1的代码是可以完全移植过来的。这里不再赘述。不过,由于这道题的样例里面不存在含有超出范围的数据以及全部数据均出现一次的情况,因此代码还可以进一步简化。简化后的具体代码如下:class Solution {public: int duplicateInArray(vector<int>...原创 2019-05-12 15:19:44 · 396 阅读 · 0 评论 -
AcWing 42. 栈的压入、弹出序列
解题原理参考我的另一篇博文。具体代码如下:class Solution {public: bool isPopOrder(vector<int> pushV,vector<int> popV) { stack<int> s; int i = 0; for(auto &e: pushV...原创 2019-06-12 15:14:46 · 217 阅读 · 0 评论 -
AcWing 27. 数值的整数次方
1.方法1根据题意只需要按常规的循环处理就行了。具体代码如下:class Solution {public: double Power(double base, int exponent) { int n = abs(exponent); //取绝对值可以简化代码,等输出时再判断是否取倒数返回 double ans = 1.; ...原创 2019-05-18 20:57:03 · 320 阅读 · 0 评论 -
AcWing 26. 二进制中1的个数
1.方法1容易理解的方法就是判断二进制最低位是否为1,然后计数器加一再将数据右移1位,直到数据变成0。需要注意的是,输入数据可能是负数。负数在右移时最高位会持续补1,这样的话程序会进入死循环。因此需要将输入数据强制类型转换为无符号整数,使得其右移时最高位补0(相关知识参考计算机组成原理的内容)。这里由于我们只关心输入数据的二进制表示,所以不必担心强制类型转换会带来问题。由于每一次右移都...原创 2019-05-18 20:49:01 · 302 阅读 · 0 评论 -
AcWing 25. 剪绳子(贪心+DP)
1.贪心算法具体的证明可以参考题解。简单来说就是尽可能地把绳子剪成长度为3的段,直至余下的部分为4或者2。具体代码如下:class Solution {public: int maxProductAfterCutting(int length) { if(length < 4) return length - 1; int n = ...原创 2019-05-18 18:22:26 · 259 阅读 · 0 评论 -
AcWing 24. 机器人的运动范围(DFS+BFS)
1.DFS从起点位置开始深入,遇到合法位置则计数器自增,否则返回。由于题目不需要回溯,因此访问数组不必还原。具体代码如下:class Solution {public: int dx[2] = {0, 1}; //根据题意可以发现只需要有两个方向即可 int dy[2] = {1, 0}; //计算数位和 int getSum(int ...原创 2019-05-17 15:56:47 · 275 阅读 · 0 评论 -
AcWing 17. 从尾到头打印链表(递归+栈)
如果我们将输出或者打印看做一个操作的话,那么这个问题是一个操作顺序与输入顺序相反的问题。这样的问题可以考虑使用递归算法或者用栈来存放输入数据。1.递归设置遍历指针访问到NULL时作为递归出口。然后将数据存入结果数组中。具体代码如下:/** * Definition for singly-linked list. * struct ListNode { * int...原创 2019-05-13 14:18:22 · 190 阅读 · 0 评论 -
AcWing 16. 替换空格
具体代码如下:class Solution {public: string replaceSpaces(string &str) { string ans; for(auto &e: str){ if(e == ' ') ans += "%20"; else ans += e; ...原创 2019-05-13 14:02:54 · 349 阅读 · 0 评论 -
AcWing 15. 二维数组中的查找(非递归算法和递归算法)
1.非递归算法观察此二维数组(以下称为“矩阵”),可以发现该矩阵的副对角线上的每一个元素均满足一个规律,那就是其左侧在同一行的元素均比其小,下侧在同一列的元素均比其大。于是,从副对角线入手进行查找就可以通过一次比较操作排除一整行和一整列的元素。从而大幅缩短时间开销。具体操作就是,当前位置元素如果等于目标元素则直接返回true,小于目标元素则将遍历指针下移一个单位,大于目标元素则将遍历指针左...原创 2019-05-13 13:54:58 · 362 阅读 · 0 评论 -
AcWing 52. 数组中出现次数超过一半的数字(基于中位数和众数的线性复杂度的解法)
这道题最直观的解法就是使用排序算法将数组变为一个有序数组。显然,满足题意的数字必然是此时数组的中位数。但是,我们仍然清楚,即使是最快的排序算法,复杂度也不能低于O(nlogn)。不过,以下两个方法都可以在线性复杂度内解决此问题。1.基于快速排序的轴点选择函数的算法(中位数)快速排序的轴点选择函数会将序列中小于轴点值的数值放置到轴点左侧,大于等于轴点值的数值放置到轴点右侧。我们令每一次...原创 2019-06-28 15:44:37 · 247 阅读 · 0 评论