(立下flag)每日10道前端面试题-08 十道算法简单算法

1.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5
输出: 2
示例 2:

输入: [1,3,5,6], 2
输出: 1
示例 3:

输入: [1,3,5,6], 7
输出: 4
示例 4:

输入: [1,3,5,6], 0
输出: 0

解一:

还是indexOf开头,如果存在返回索引值,否则就push一下给它生米煮成熟饭,再排序,重新返回索引值。


解二:

老实方法用for遍历,存在(=)和不存在(>)两种情况可以一起处理。


解三:

使用二分法,也是这道题真正想考察的方法。

二分法理解起来很简单,小学就学过。但是实际编码时,边界防范、左右中位数的取舍、while中的条件以及是否+1何处+1可以说是要处处设防。

2.. 外观数列

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

  1. 1

  2. 11

  3. 21

  4. 1211

  5. 111221

1 被读作  "one 1"  ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2",  "one 1" ("一个二" ,  "一个一") , 即 1211。

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

 

示例 1:

输入: 1
输出: "1"
解释:这是一个基本样例。
示例 2:

输入: 4
输出: "1211"
解释:当 n = 3 时,序列是 "21",其中我们有 "2" 和 "1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12" 和 "11" 组合在一起,也就是 "1211"。

解一:
通过正则合并相同元素完成累加

解二:

这种类型的题目用字典不是特别爽吗????

3. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

解题思路:
示例: [a, b , c, d , e]

解答这类题目, 省略不掉遍历, 因此我们先从遍历方式说起

通常我们遍历子串或者子序列有三种遍历方式

以某个节点为开头的所有子序列: 如 [a],[a, b],[ a, b, c] ... 再从以 b 为开头的子序列开始遍历 [b] [b, c]。
根据子序列的长度为标杆,如先遍历出子序列长度为 1 的子序列,在遍历出长度为 2 的 等等。
以子序列的结束节点为基准,先遍历出以某个节点为结束的所有子序列,因为每个节点都可能会是子序列的结束节点,因此要遍历下整个序列,如: 以 b 为结束点的所有子序列: [a , b] [b] 以 c 为结束点的所有子序列: [a, b, c] [b, c] [ c ]。
第一种遍历方式通常用于暴力解法, 第二种遍历方式 leetcode (5. 最长回文子串 ) 中的解法就用到了。

第三种遍历方式 因为可以产生递推关系, 采用动态规划时, 经常通过此种遍历方式, 如 背包问题, 最大公共子串 , 这里的动态规划解法也是以 先遍历出 以某个节点为结束节点的所有子序列 的思路

对于刚接触动态规划的, 我感觉熟悉第三种遍历方式是需要抓住的核心

因为我们通常的惯性思维是以子序列的开头为基准,先遍历出以 a 为开头的所有子序列,再遍历出以 b 为开头的...但是动态规划为了找到不同子序列之间的递推关系,恰恰是以子序列的结束点为基准的,这点开阔了我们的思路。

我在网上看不少解答时,直接阅读其代码,总是感觉很理解很吃力,因为好多没有写清楚,一些遍历到底代表什么意思,看了许久仍不知所以然,下面的代码中摘录了 维基中的解释,感觉比较清楚,供大家理解参考。

代码:

第二块代码和 第一块代码 思路实现是完全一样的,但是如果第一次看到这类题目,直接阅读 第二块代码,理解起来很难,尤其是 如果改成 if (sum > 0 ) 对于刚接触的这题目的比较不好理解。

4. 最后一个单词的长度

给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。

如果不存在最后一个单词,请返回 0 。

说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。

 

示例:

输入: "Hello World"
输出: 5

解一:使用trim

解法二:遍历
从右往左最后一个字符位置开始,求最后一个单词的开头和结尾字符索引位置,相减即为所求
结尾字符记录索引为end,初始化为字符串结尾字符索引
遇到空字符,end--
否则
end < 0,越界,字符串为空或不存在,返回0
end > 0
继续end--判断字符串是否为空
不为空,则说明找到了最后一个单词的结尾字符索引位置
开头字符初始化为 start = end,依旧向前遍历
不为空,则 end--
为空,则说明找到了最后一个单词的首字符位置 start
结果
end - start

5. 加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

示例 2:

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

解法一:使用es10 bigint

  • 已知传入参数必定为数值型数组

  • 将参数转换为字符型(Array.prototype.join())

  • 由字符型变为BigInt型(BigInt构造函数))

  • 进行专属于BigInt类型的数学计算

  • 计算结果再次变为字符型(String构造函数)

  • 将字符型数值变更为数组(String.prototype.split())

以下是相应的代码实现:

方法二:小学生加法
我们将问题简化为第i位(0 <= i < n)加一或加0。
而加一的结果分为两种情况:

加一后需要进位
加一后不需要进位
对于第二种情况,我们直接返回digits就可以。
对于第一种情况,我们将指针移到前面一位。继续同样处理。
而边界情况即,首位也产生进位,首位产生进位只会是999....9999这种情况,我们也很容易得出结果。

6. 二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。

输入为 非空 字符串且只包含数字 1 和 0。

 

示例 1:

输入: a = "11", b = "1"
输出: "100"
示例 2:

输入: a = "1010", b = "1011"
输出: "10101"
 

提示:

每个字符串仅由字符 '0' 或 '1' 组成。
1 <= a.length, b.length <= 10^4
字符串如果不是 "0" ,就都不含前导零。

解法一:利用BigInt解决溢出的问题

解法二:小学生加法

  • 先对两个字符串按照最长长度,在前面用'0'补全长度

  • 然后对字符串从后向前遍历,对两个字符串相同位置的字符进行求和

  • 如果和 >=2,说说明发生了进位,保存进位状态为true,下一次求和要 + 1

  • 如果和 < 2,说明没有发生进位,保存进位状态为false,下一次求和要 + 0

  • 遍历结束后,如果进位状态为 true, 说明第一位字符串仍然发生了进位,则拼上一个1

  • 时间复杂度O(n)

  • 代码:

7. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2
示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
  由于返回类型是整数,小数部分将被舍去。
 

解一:

现成的sqrt函数来一个。

解二:

最耿直的自增暴力解。


解三:

牛顿法,只用知道迭代公式就好了(原理在数学规划课上学得差不多了):

8. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入:2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶

  2. 2 阶

示例 2:

输入:3
输出:3
解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶

  2. 1 阶 + 2 阶

  3. 2 阶 + 1 阶

解法:动态规划
本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和

爬上 n-1n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
爬上 n-2n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
所以我们得到公式 dp[n] = dp[n-1] + dp[n-2]dp[n]=dp[n−1]+dp[n−2]
同时需要初始化 dp[0]=1dp[0]=1 和 dp[1]=1dp[1]=1
时间复杂度:O(n)O(n)

9. 删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2
示例 2:

输入: 1->1->2->3->3
输出: 1->2->3

思路
标签:链表
指定 cur 指针指向头部 head
当 cur 和 cur.next 的存在为循环结束条件,当二者有一个不存在时说明链表没有去重复的必要了
当 cur.val 和 cur.next.val 相等时说明需要去重,则将 cur 的下一个指针指向下一个的下一个,这样就能达到去重复的效果
如果不相等则 cur 移动到下一个位置继续循环
时间复杂度:O(n)O(n)

10. 合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

 

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
 

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出: [1,2,2,3,5,6]

解一:

数组拼接后sort排序。缺点是没有利用nums1和nums2本身是有序数组的优势。

解法二:双指针 + 从后向前
思路
两个数组从小到大排序
且题目要求 修改nums1为合并排好序的nums1+nums2
双指针
两个分别指向两个数组尾部的指针
从后向前
比较两指针位置的值
大的一定是结果数组的最大值
一一填充到 nums1的末尾
遍历完后
当 n > 0 时
说明 nums2 中还有剩余没有比较的数字
将其插入替换 nums1 数组前面n个数字即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C语言实现存储管理中的FIFO算法和LRU算法的代码示例: FIFO算法: ```c #include <stdio.h> #define MAXSIZE 3 int main() { int pages[MAXSIZE], frames[MAXSIZE], temp[MAXSIZE]; int i, j, k, page_faults = 0, flag = 0, pos = 0; printf("Enter the reference string: "); for(i = 0; i < MAXSIZE; ++i){ scanf("%d", &pages[i]); } for(i = 0; i < MAXSIZE; ++i){ frames[i] = -1; } for(i = 0; i < MAXSIZE; ++i){ flag = 0; for(j = 0; j < MAXSIZE; ++j){ if(frames[j] == pages[i]){ flag = 1; break; } } if(flag == 0){ frames[pos] = pages[i]; pos = (pos + 1) % MAXSIZE; ++page_faults; } printf("\n"); for(j = 0; j < MAXSIZE; ++j){ printf("%d\t", frames[j]); } } printf("\nTotal page faults: %d", page_faults); return 0; } ``` LRU算法: ```c #include <stdio.h> #define MAXSIZE 3 int main() { int pages[MAXSIZE], frames[MAXSIZE], temp[MAXSIZE]; int i, j, k, page_faults = 0, flag = 0, pos = 0, max, min; printf("Enter the reference string: "); for(i = 0; i < MAXSIZE; ++i){ scanf("%d", &pages[i]); } for(i = 0; i < MAXSIZE; ++i){ frames[i] = -1; } for(i = 0; i < MAXSIZE; ++i){ flag = 0; for(j = 0; j < MAXSIZE; ++j){ if(frames[j] == pages[i]){ flag = 1; break; } } if(flag == 0){ for(j = 0; j < MAXSIZE; ++j){ temp[j] = 0; for(k = i - 1; k >= 0; --k){ if(frames[j] == pages[k]){ temp[j] = 1; break; } } } flag = 0; for(j = 0; j < MAXSIZE; ++j){ if(temp[j] == 0){ pos = j; flag = 1; break; } } if(flag == 0){ max = temp[0]; pos = 0; for(j = 1; j < MAXSIZE; ++j){ if(temp[j] > max){ max = temp[j]; pos = j; } } } frames[pos] = pages[i]; ++page_faults; } printf("\n"); for(j = 0; j < MAXSIZE; ++j){ printf("%d\t", frames[j]); } } printf("\nTotal page faults: %d", page_faults); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值