algorithm
本文最后更新于:1 年前
- [[#1.1递归|1.1递归]]
- [[#2.1 两数之和|2.1 两数之和]]
- [[#2.2 合并两个有序数组|2.2 合并两个有序数组]]
- [[#2.3 移动零|2.3 移动零]]
- [[#2.4 找到所有数组中消失的数字|2.4 找到所有数组中消失的数字]]
- [[#2.1 枚举|2.1 枚举]]
- [[#2.1 枚举#1. abc|1. abc]]
- [[#2.1 枚举#2. 反序数|2. 反序数]]
- [[#2.1 枚举#3. 对称平方数|3. 对称平方数]]
- [[#2.1 枚举#4. 与 7 无关的数|4. 与 7 无关的数]]
- [[#2.1 枚举#5. 百鸡问题|5. 百鸡问题]]
- [[#2.1 枚举#6. Old Bill|6. Old Bill]]
- [[#2.2 模拟|2.2 模拟]]
- [[#2.2 模拟#1. 图形排版|1. 图形排版]]
- [[#1. 图形排版#1. 输出梯形|1. 输出梯形]]
- [[#1. 图形排版#2. 叠筐|2. 叠筐]]
- [[#2.2 模拟#2. 日期|2. 日期]]
- [[#2. 日期#1. 今天是第几天|1. 今天是第几天]]
- [[#2. 日期#2. 打印日期|2. 打印日期]]
- [[#2. 日期#3. 日期累加|3. 日期累加]]
- [[#3. 日期累加#套路|套路]]
- [[#3. 日期累加#技巧|技巧]]
- [[#3. 日期累加#代码|代码]]
- [[#2.2 模拟#3. 其他|3. 其他]]
- [[#3. 其他#1. 剩余的树|1. 剩余的树]]
- [[#3. 其他#2. 手机键盘|2. 手机键盘]]
- [[#3. 其他#3. xxx_定律|3. xxx_定律]]
- [[#2.2 模拟#1. 图形排版|1. 图形排版]]
- [[#3.1 排序|3.1 排序]]
- [[#3.1 排序#1. 排序|1. 排序]]
- [[#1. 排序#大部分排序方法|大部分排序方法]]
- [[#3.1 排序#2. 成绩排序|2. 成绩排序]]
- [[#3.1 排序#3. 成绩排序 2|3. 成绩排序 2]]
- [[#3.1 排序#1. 排序|1. 排序]]
- [[#3.2 查找|3.2 查找]]
- [[#3.2 查找#1. 找 x|1. 找 x]]
- [[#3.2 查找#2. 查找|2. 查找]]
- [[#3.2 查找#3. extrenum_index|3. extrenum_index]]
- [[#3.2 查找#4. 找位置|4. 找位置]]
- [[#4.1 字符串处理|4.1 字符串处理]]
- [[#4.2 字符串匹配|4.2 字符串匹配]]
- [[#5.1 向量|5.1 向量]]
- [[#5.2 队列|5.2 队列]]
- [[#5.3 栈|5.3 栈]]
- [[#简单题|简单题]]
1. 基础知识
1.1递归
***自顶向下递归
自底向上迭代
问题特点
- 一个问题的解可以分解为几个子问题的解。
- 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样。
- 存在基线终止条件。
爬楼梯问题:
解法 1:纯递归
1 |
|
解法 2:递归并采用 HashMap 存储已求值
1 |
|
解法 3:迭代自底向上循环累加
1 |
|
总结:
对于多次重复出现的值,可以通过 HashMap 存储,后续先扫描 HashMap 是否存在再做行动。
2. LeetCode
2.1 两数之和
1 |
|
2.2 合并两个有序数组
1 |
|
2.3 移动零
1 |
|
2.4 找到所有数组中消失的数字
1 |
|
王道机试指南
第二章暴力求解
2.1 枚举
1. abc
三重循环暴力求解
1 |
|
2. 反序数
1 |
|
3. 对称平方数
判断一个数是否为对称数核心:
while (j) {
sum = sum * 10 + j % 10;
j /= 10;
}
j 为对称数则 sum 等于 j*j
1 |
|
4. 与 7 无关的数
1 |
|
5. 百鸡问题
1 |
|
6. Old Bill
1 |
|
方法 2:
1 |
|
2.2 模拟
1. 图形排版
1. 输出梯形
1 |
|
2. 叠筐
1 |
|
2. 日期
1. 今天是第几天
1 |
|
2. 打印日期
1 |
|
3. 日期累加
套路
以前大一的时候面对这个题,就是单纯按月份纯算,算的可谓是焦头烂额。现在学习了新的方法:
- 计算是当年的第几天
- 这个数值 sum 加上需要累加的天数
- 计算进位的多少年,确定年份
- 根据剩下的第几天反解出这是几月几日
- 输出
技巧
用到的技巧包括打表、巧用 bool。
提前写出来每个月有多少天、每年有多少天。
还有判断是否闰年函数,用它能够得到 bool 值,0 和 1 可以分别对应于平年和闰年,所以上面的可以构造成二维数组。
代码
1 |
|
3. 其他
1. 剩余的树
1 |
|
1. 使用一个 l+1 长度的 array 存储所有树的存在状态,初始化所有树的状态为真;
2. 根据区间循环判每棵树的状态,若为真则修改树输出总数的状态为假并将树的总数自减;
3. 输出剩余树的数量。
2. 手机键盘
- 用一个数组按顺序保存每个字母所需要的时间段
- 循环每个输入的字母求和总次数,判断前后两字符是否在一个按键上,若果是则加两个时间段
1 |
|
3. xxx_定律
既可递归实现也可 while 迭代迭代实现。
1 |
|
第三章排序与查找
3.1 排序
1. 排序
> 冒泡排序:
> 1. 升序:外层循环递减,内层循环递增直到外层循环变量;
> 2. 降序:外层循环递增,内层循环从外层循环变量开始递增。
1 |
|
大部分排序方法
1 |
|
2. 成绩排序
- 定义一个结构体包含学号与成绩
- 写一个比较器 Compare
- 使用内置 sort 算法,设置迭代头和尾(地址)以及比较规则(比较器)
1 |
|
3. 成绩排序 2
方法 1:
sort 是不稳定排序,stable_sort 才是稳定排序,稳定排序不改变输入的顺序
1 |
|
方法 2:
用一个编号保存每个学生的顺序,排序比较器里成绩相等的按照编号升序排
1 |
|
3.2 查找
1. 找 x
方法 1:
用 flag 标志是否找到
1 |
|
方法 2:
设置初始默认为-1,找到则修改状态
1 |
|
2. 查找
方法 1:复杂度 O(n)
1 |
|
方法 2:复杂度 O(m·n)
1 |
|
3. extrenum_index
方法 1:
空间复杂度为 O(1)
1 |
|
方法 2:
1 |
|
4. 找位置
时间复杂度为 O (n)
1. 用一个额外的矢量 orderS 不重复的添加字符,以保证输出时字符顺序
2. 用 map 的 key 记录字符,value 记录重复出现的次数
3. 最后按照 orderS 的顺序遍历输出
1 |
|
第四章字符串
4.1 字符串处理
1. 特殊乘法
1 |
|
2. 密码翻译
1 |
|
3. 简单密码
1 |
|
4. 统计字符
1 |
|
5. 字母统计
1 |
|
4.2 字符串匹配
第五章基础数据结构
5.1 向量
完数与盈数
1 |
|
- 思路:获取一个数的所有因子可以通过循环取余,判断余数是否为零来获得,若为零则为因子,反之不为因子
- 细节:关键在于格式输出的问题
5.2 队列
5.3 栈
剑指offer
简单题
1.数组中重复的数字
桶排序思想
- 首先定义一个与输入长度相等的数组,并将所有值置零;
- 遍历输入向量,对遇到的值作为数组下标进行自增;
- 判断自增后的值是否大于1,若是则为重复数则直接返回。
1 |
|
位置重排
- 遍历数组,遇到数组元素与下标相同的不用管。
- 遇到数组元素与下标不同,就将其交换到属于它的位置,交换前检查那个位置是否有相同的元素,若有则重复。
- 遍历结束完全交换也没重复,则返回-1.
1 |
|
哈希表
- 遍历数组,将没有出现过的元素加入哈希表。
- 遇到的元素在哈希表中出现过就是重复数组。
- 遍历结束也没找到就返回-1.
1 |
|
2.替换空格
python直接用字符串函数
replace
全局替换即可
遍历覆盖法
定义一个新字符串,遍历原字符串,遇到空格新字符串加要求替换的字符,否则添加原字符。
1 |
|
3.从尾到头打印链表
1.数组逆向拷贝
- 遍历链表,用一个数组顺序添加节点值
- 逆向遍历数组,添加数组值到新数组中
1 |
|
2.递归
- 从表头开始往后递归进入每一个节点。
- 遇到尾节点后开始返回,每次返回依次添加一个值进入输出数组。
- 直到递归返回表头
1 |
|
3.栈
- 我们可以顺序遍历链表,将链表的值push到栈中。
- 然后再依次弹出栈中的元素,加入到数组中,即可实现链表逆序。
1 |
|
4.用两个栈实现队列
解题思路:
借助栈的先进后出规则模拟实现队列的先进先出
- **当插入时,直接插入 stack1
- 当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素
图解:
代码展示:
1 |
|
复杂度分析:
时间复杂度:对于插入和删除操作,时间复杂度均为 O(1)。插入不多说,对于删除操作,虽然看起来是 O(n) 的时间复杂度,但是仔细考虑下每个元素只会「至多被插入和弹出 stack2 一次」,因此均摊下来每个元素被删除的时间复杂度仍为 O(1)。
空间复杂度O(N):辅助栈的空间,最差的情况下两个栈共存储N个元素.
5.旋转数组的最小数字
求解思路:
- 暴力法
- 特殊情况,如果数组为空,则直接返回0
- 创建最小值 min
- 遍历数组每一个元素nums,并更新最小值 min = min(min,num)
- 遍历结束,直接返回 min
时间复杂度O(N):N表示数组的长度,遍历整个数组O(N)
空间复杂度O(1):仅使用一个额外空间变量O(1)
1 |
|
- 二分法
- 初始化:定义二分端点双指针
i,j
,m=(i+j)/ 2
为每次二分的中点( “/“ 代表向下取整除法)- 循环二分:循环不成立则按照以下规则更新双指针:
- 当 array[m] > array[j] 时: m 一定在左排序数组中,即旋转点 x 一定在 [m + 1, j] 闭区间内,因此执行 i = m + 1;
- 当 array[m] < array[j] 时: m 一定在右排序数组中,即旋转点 x 一定在[i, m]闭区间内,因此执行 j = m;
- 当 array[m] = array[j] 时: 无法判断 mm 在哪个排序数组中,即无法判断旋转点 x 在 [i, m] 还是 [m + 1, j] 区间中。解决方案: 执行 j = j - 1 缩小判断范围
- 返回值: 当 i = j 时跳出二分循环,并返回旋转点的值 array[i] 即可。
时间复杂度O(logN):N表示数组的长度,二分查找O(logN)
空间复杂度O(1):仅使用常数(i, j, m)额外空间变量O(1)
1 |
|
6.二进制中1的个数
求解思路:
- 循环按位比较法(推荐使用)
知识点:位运算
计算机的数字由二进制表示,我们平常的运算是对整个数字进行运算,但是还可以按照二进制的每一位分别进行运算。常见运算有位与、位或、移位、位异或等。
思路:
我们可以检查该数字的二进制每一位是否为1,如果遍历二进制每一位呢?可以考虑移位运算,每次移动一位就可以。至于怎么统计到1呢?我们都只知道数字1与数字相位与运算,其实只是最后一位为1就是1,最后一位为0就是0,这样我们只需要将数字1移位运算,就可以遍历二进制的每一位,再去做位与运算,结果为1的就是二进制中为1的。
具体做法:
- 遍历二进制的32位,通过移位0-31次实现。
- 将移位后的1与数字进行位与运算,从而判断数字的二进制每一位结果,结果为1就记录一次。
复杂度分析:
- 时间复杂度:O(k)O(k)O(k),kkk为int型的32位,一次遍历
- 空间复杂度:O(1)O(1)O(1),常数级变量,没有额外辅助空间
1 |
|
- 暴力法
- 判断数n是否为负数,负数则转化为相同二进制表示的正数($P_b = 2^N+n,N为二进制位数$)
- 通过连除法,用结果累加n%2的余数,并更新n=n/2
1 |
|
- 位运算优化法(扩展思路)
思路:
有一个性质:$n&(n−1)$,会将n的二进制中最低位由1变成0
我们可以不断让当前的 n与 n−1做位与运算,直到 n的二进制全部变为 0 停止。因为每次运算会使得 n 的最低位的 1 被翻转成0,因此运算次数就等于 n 的二进制位中 1 的个数,由此统计1的个数。
具体做法:
- 使用循环检查n是否为0.
- 不为0就与n−1做位与运算,去掉二进制最后一位的1,并统计次数。
图示:
1 |
|