自己有自己想象中的那么努力吗?还热爱技术吗?能跟学习的日子交朋友吗?
总数:10
2021.7.10
JZ1 二维数组中的查找
一维数组的处理方法:二分、排序、快慢指针、双向指针、动态规划
有序的数组很显然应该想到二分,如果二分之后,无法确定下一次二分应该往哪边分,由此无法进行二分下去。如果我们找个位置,每次都能确定的往哪个部分二分,即可达到我们想要的结果。
JZ2 替换空格
C++中如果直接在string变量后面接字符的话,string内部会做这个处理消耗一定的性能,可以通过遍历一遍字符串确认有多少个空格,从而计算需要空间大小为字符串长度+空格数量2+1,然后自己分配空间,这样子的性能相对较高
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @return string字符串
/
string replaceSpace(string s) {
// write code here
string str;
for (int i=0; i<s.size(); i++){
if (s[i] !=' '){
str+= s[i];
}else{
str+="%20";
}
}
return str;
}
};
class Solution {
public:
/*
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @return string字符串
*/
string replaceSpace(string s) {
// write code here
for (int i=0; i<s.size(); i++){
if (s[i] == ' '){
s.replace(i,1,"%20");//从i位置开始的1个字符替换为%20
}
}
return s;
}
};
JZ3 从尾到头打印链表
反转:栈、递归、std::reverse()
JZ4 重建二叉树
注意数组中的指针移动方式可以为:数组名+移动步长,参数传递尽量使用引用而不是指针。
JZ5 用两个栈实现队列
如果我知道队列是FIFO,栈是FILO,但是这道题我还是不知道怎么写怎么办?
对于这种感觉不难,但是又不会写的,方法就是模拟。
807 保持城市天际线
代码写的很优美:一个二重循环,同时求行和列的最大值,初始化时声明vector的大小和初试值。
2021.7.11
JZ6 旋转数组的最小数字
这是一道对二分查找算法灵活运用的一道题目。
二分查找算法不限于运用在有序数组上。如果能够明确二分之后,答案存在于二分的某一侧,就可以使用二分。这种二分查找难就难在,arr[mid]跟谁比.
我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。
一般的比较原则有:
如果有目标值target,那么直接让arr[mid] 和 target 比较即可。如果没有目标值,一般可以考虑端点。这里我们把target 看作是右端点,分析arr[mid]大于、小于、等于 target的处理情况。
要有target,对于3种情况要有明确是last和first的变化公式!!!
JZ7 斐波那契数列
注意时间复杂度的分析。
JZ10 矩形覆盖
可以得出:f[n] = f[n-1] + f[n-2],初始条件f[1] = 1, f[2] =2
所以代码可用递归,记忆递归,和动态规划和递推
JZ11 二进制中1的个数
理解了为什么负数使用补码表示
精妙的解法:一次去掉最右边的一个1,即有多少个1就循环几次
正数左右移都补0,负数左移补0,右移补1,符号位也跟着移动
JZ12 数值的整数次方
以上的3种方法都很有看头,第一种方法我能想到,第二种也是常规思路,这种一个递归入口的不会造成幂函数的时间复杂度。第三种就属于二进制中的套路了。
JZ13 调整数组顺序使奇数位于偶数前
很多数组中有关顺序的要求都可以从排序算法中找方法;快排就是双向指针,空间复杂度为1,时间复杂度为N2的那个就是插入排序。重开数组,两次遍历的那个就相当于归并。
JZ14 链表中倒数最后k个结点
一提到倒数的k个数,第一想到的就是栈,递归,现在还有快慢指针
JZ15 反转链表
1.很简单:将链表值存vector中,将vector反转,用值再构造链表
2.头插法反转
JZ16 合并两个排序的链表
和归并排序的合并方法一样。
另外对于这种线性的数组,一般也都可以使用递归来做。
JZ17 树的子结构
这里先区分一下子树和树的子结构:子树是原树的某一结点为树根,下面的所有子节点和原树相同,而子结构这可以是树中任意的一部分,空树不是任一树的子树,子结构也是。这里还要注意空节点的处理
JZ18 二叉树的镜像
典型的二叉树的遍历变形而来的
JZ19 顺时针打印矩阵
每一行代码都要会默写
JZ20 包含min函数的栈
我想到要用一个线性序列来存储,但没想那么深入:
JZ21 栈的压入、弹出序列
一道模拟题
JZ23 二叉搜索树的后序遍历序列
这个和我的思路一样,但要注意两点:1.处理边界问题,空序列不是后序遍历序列,只有一个元素的序列可以是前中后序遍历序列中的任何一种。2.将递归函数拿出来,主函数用于边界情况的处理。
JZ24 二叉树中和为某一值的路径
很简单的二叉树遍历
JZ25 复杂链表的复制
第一种方法,先复制常规链表,接着记录各个节点的随机存储位置,然后使用二重循环复制随机指针。
第二种 在每个节点后面添加复制节点,这样的好处是随即节点的复制变得非常容易,之后拆分两个来连在一起的链表。
JZ26 二叉搜索树与双向链表
这里我原来的思路是模拟中序遍历,但是不能用当前节点的左指针指向左孩子遍历的返回值,右指针指向右孩子遍历的返回值,那是后续遍历的做法。
这个和线索化二叉树很象,可以看看这个二叉树的线索化
JZ27字符串的排列
我本来想的是将加入过的字符删掉,这样是O(N)的时间复杂度,而且再也找不到了,这是最烂的想法,可以交换或标记已被选入,我想的是深搜加回溯。另一个方法非常轻便,一次搞一个字符串,搞好后加进去。
C++标准库中有全排列的库函数 next_permutation
JZ28数组中出现次数超过一半的数字
hashmap存储法(标记法),排序法,但没想到之前用于求两个相同,3个相同的消去法
JZ29 最小的K个数
大顶堆,C++里面用priority_queue实现堆,这里的时间复杂度是O(nlongk),空间复杂度:O(k)。这里的分析很精彩。
快排的思想,其实是二分的思想,这里的时间复杂度是平均O(n),最坏O(n^2)
JZ30 连续子数组的最大和
很常规的动态规划题目,也可以将记录数组改为一个变量来节省时间。
JZ32 把数组排成最小的数
这里面讲了仿函数、匿名和具名lambda表达式在排序函数中的用法。贪心算法我没怎么看懂。
JZ33 丑数
递推的很好,要经常看
JZ34 第一个只出现一次的字符位置
map和bitset的使用
JZ35 数组中的逆序对
归并排序的思想
JZ36 两个链表的第一个公共结点
JZ37 数字在排序数组中出现的次数
有序数组二分 一看到有序就要想到二分。明白了查找目标范围的下界left和上界right 的概念。最终target的数目等于right - left。也可以直接用STL中的upper_bound(), lower_bound()库函数。
JZ38 二叉树的深度
深搜和广搜,时间复杂度贺空间复杂度都能达到O(n)级别
JZ39 平衡二叉树
这个自底向上的判断平衡二叉树的方法改编自树的深度的递归遍历,这里depth是递归主体,用以返回传入参数为根节点的树的深度,这里他把不是平衡二叉树的节点的高度返回为-1是值得学习的,二在判断返回值时可以直接判断这个。这样在左子树不行的情况下就直接返回了,右子树就不用管了,相当于剪枝。
J40 数组中只出现一次的两个数字
异或
J41 和为S的连续正数序列
前缀和 由力扣hot100得到的启发
滑动窗口(分为固定大小和动态大小) 这里也要注意连续。
J42 和为S的两个数字
找一个数在数组中存在与否,如果数组是有序的且只查一次:二分,如果数组无序且要查多次:hash表
这个题在有序数组里可以用双指针法
J43 左旋转字符串
重开数组,按新数组的枚举次序将原数组中的数按规律放到新数组中。
另一种反转单词的方法更好一些,时间复杂度为1
J44 翻转单词序列
很经典的题目
J45 扑克牌顺子
判断有无重复值可以用set这个数据结构,set底层用的是红黑树。
J46 孩子们的游戏(圆圈中最后剩下的数)
1.模拟
学学代码中STL的用法
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if (n <= 0) return -1;
list<int> lt;
for (int i=0; i<n; ++i)
lt.push_back(i);
int index = 0;
while (n > 1) {
index = (index + m - 1) % n;
auto it = lt.begin();
std::advance(it, index); // 让it向后移动index个位置
lt.erase(it);
--n;
}
return lt.back();
}
};
方法二:递归
J47 求1+2+3+...+n
1.求和公式
2.一重循环:不能用
3.递归代替循环:没有比较,无法退出循环。
4.可以用 &&的特性实现if判断
JZ48 不用加减乘除做加法
流程图很明白了,没有进的加法 ^,有进的加法 &后左移 ,这两者循环,直到没有进,^的结果就是最后的结果。
J49 把字符串转换成整数
这里并不难,但应该是应用程度较高的地方:首先去掉首部的格,检查是否全是格,记录首部有没有负号,有的话记录。如果接下来的不是正负号又不是数字,就跳出。将字符数字转化数字。如果是正数且此时已经达到了INT的正最大,就返回正最大,如果是负的超过了最大,就返回负最大加一。
红黑树(一)之 原理和算法详细介绍
红黑树高度不会超过logN的证明,红黑树调整在O(1)的时间内完成的证明,红黑树插入和删除的操作,主要弄懂原理即可,代码不做要求。
set和map底层都是红黑树
J50 数组中重复的数字
对于重复性问题可以想到set,遍历数组依次加入集合,若集合中存在该元素则直接返回该元素,否则将该元素加入集合。
JZ51 构建乘积数组
1.用除法,两个复杂度都很低。一时半会还没想到
2.不让用除法的话,用两个数组做双向数组。
JZ52 正则表达式匹配
注意这里是str与pattern配对。首先想特殊的:全是0的,当然true,其次一边是0,一边不是0的,如果第一个字符串空了,第二个字符串非空,还是可能匹配成功的,因*能匹配0次啊。
然后进入正题:
J54 字符流中第一个不重复的字符
判断数组中的元素是否重复,首选set和hash,同时这里还有先进先出的属性,所以选队列。这里的标准代码写的还是很好的。
JZ55 链表中环的入口结点
1.暴力解法 每遍历一个
JZ56 删除链表中重复的结点
这个题应该分两部分:一找出重复节点,二将重复节点删除。
这里有序的能一趟成功。没序的用set记录。
JZ59 按之字形顺序打印二叉树
层序遍历+栈
JZ60 把二叉树打印成多行
做题=解法+模板,意思就是说,对于一道题目,首先明白正确的解法已经解决该问题70%,剩下的就直接套模板。
当bfs用在层序遍历上。
JZ61 序列化二叉树
序列化即将内存中的二叉树按特定格式存入硬盘,反序列化即通过一定格式的字符串复现二叉树。前中后三种顺序遍历和层序遍历都行。
JZ62 二叉搜索树的第k个结点
一:将搜索树中序遍历,然后找就行了,时间复杂度是O(N)
二:在遍历时,按左、中、右的中序序列遍历。在遍历中计数,计到第L个结束递归。
JZ63 数据流中的中位数
这种动态插入有序数组的方法使用插入排序很好。
应该是先分解题目,共有几个要做的步骤,根据步骤选择合适的数据结构。
JZ64 滑动窗口的最大值
暴力法:窗大小n,数组大小m,时间复杂度m*n。
大顶堆:这里应该使用双端队列
双端队列的代码如下:
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> ret;
if (num.size() == 0 || size < 1 || num.size() < size) return ret;
int n = num.size();
deque<int> dq;
for (int i = 0; i < n; ++i) {
while (!dq.empty() && num[dq.back()] < num[i]) {
dq.pop_back();
}
dq.push_back(i);
// 判断队列的头部的下标是否过期
if (dq.front() + size <= i) {
dq.pop_front();
}
// 判断是否形成了窗口
if (i + 1 >= size) {
ret.push_back(num[dq.front()]);
}
}
return ret;
}
};
不知道时间复杂度是O(N)的原因。
JZ65 矩阵中的路径
深搜,剪枝,回溯,标记等方法。
JZ66 机器人的运动范围
这里讲的是BFS和DFS,动态规划应该也行。
JZ67 剪绳子
剪绳子这里没法弄清楚要剪多少段,每段多长,如果遍历的话这些会非常麻烦。这里能使用暴力递归,参照斐波那契改进的方法改进一下,用记忆化递归。最好的方法是参照背包的方法的动态规划。
网友评论