初步学习动态规划、KMP算法、贪心算法
贪心算法:通过局部最优解得到全局最优解
动态规划:记日记 + 递归, 以最小情况为底逐步展开
KMP算法:
1.买卖股票的最佳时机 II
给你一个整数数组
prices
,其中prices[i]
表示某支股票第i
天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4] 输出:7 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。 总利润为 4 + 3 = 7 。
我的思路
求导后,积分所有正的区域
我的算法
int maxProfit(int* prices, int pricesSize) {
int* diff_prices = malloc(sizeof(int)* (pricesSize-1));
int profit = 0;
for(int i =0; i<pricesSize-1; i++){
profit += (diff_prices[i] = prices[i+1] - prices[i]) > 0 ?diff_prices[i]:0;
}
return profit;
}
算法学习
动态规划,必须学习
2.轮转数组
我的思路
三次反转数组,反转数组这种最基本的算法需要牢记。
我的算法
void swap(int* a, int* b){
int tmp = *a;
*a = *b;
*b = tmp;
}
void reverse(int* nums, int numsSize){
int start = 0, end = numsSize -1;
while(start < end){
swap(&nums[start++], &nums[end--]);
}
}
void rotate(int* nums, int numsSize, int k) {
k %= numsSize;
reverse(nums, numsSize);
reverse(nums, k);
reverse(nums+k, numsSize-k);
}
3. 跳跃游戏
我的思路
用一个数字来表达能够到达的位置,而不用单独开一个数组来表示。因为这道题的特性,只需要知道最大的地方能不能到达,而不用知道中间的细节,
我的算法
bool canJump(int* nums, int numsSize) {
int max = 0;
for(int i = 0; i<numsSize-1; i++){
max = max>nums[i]+ i?max:nums[i]+ i;
if(max <= i){
return false;
}
}
return true;
}
4. 跳跃游戏 II
给定一个长度为
n
的 0 索引整数数组nums
。初始位置为nums[0]
。每个元素
nums[i]
表示从索引i
向前跳转的最大长度。换句话说,如果你在nums[i]
处,你可以跳转到任意nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达
nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达nums[n - 1]
。示例 1:
输入: nums = [2,3,1,1,4] 输出: 2
我的思路
第一次自己写动态规划
总结 :
动态规划的递归函数有三种情况:
1. 最小情况处理,这里是到达了末尾,返回0.
2. 日记本记录直接使用。
3. 如果不是前两种情况,计算所有后面能够到达的值,计算方法是递归调用函数计算。并且比较计算出来的值,取最好的结果(最大利润或者最小步数)
我的算法
int count(int* nums, int* diary, int numsSize, int i){ //函数意义:从i出发到n-1的最少跳
int min = INT_MAX;
int tmp = 0;
//如果已经到达了,返回0
if(i == numsSize-1){
return 0;
}
//如果日记本有记录,直接返回记录
if(diary[i] != -1){
return diary[i];
}
//没有记录,需要算后面所有位置到n-1的最小跳
else{
//比较所有,计算最小值
for(int j = 1; j<= nums[i] && i+j < numsSize; j++){
diary[i+j] = count(nums, diary, numsSize, i+j);
if( diary[i+j] < min){
min = diary[i+j]+1;
}
}
//算出了最小值,返回min
return min;
}
}
int jump(int* nums, int numsSize) {
int diary[numsSize];
for(int i = 0; i<numsSize; i++){
diary[i] = -1;
}
return count(nums, diary, numsSize, 0);
}
*算法学习(非常重要,要多复习,理解)
标答使用的是贪心算法,每次选择能够达到的最远处。注意这里最远处能够覆盖其他所有的情况,所以不用考虑其他的,考虑最远的即可。(需要深度思考一下为什么动态规划的题目不能这样做)
#define max(a, b) a>b?a:b
int jump(int* nums, int numsSize) {
int max = 0, end = 0, step =0;
for(int i = 0; i<numsSize-1; i++){
max = max(max, i+nums[i]);
if(i == end){
end = max;
step++;
}
}
return step;
}
5. H 指数
我的思路
我的算法
算法学习
6. 最后一个单词的长度
我的思路
进单词,单独设置一个循环把这个单词耗尽,出来的时候才进行对last的赋值
注意,单词结束的标志除了空格还有'0'
我的算法
int lengthOfLastWord(char* s) {
int length = 0, last = 0;
while(*s != 0){
if(*s != ' '){
while(*s != ' ' && *s != 0){
length++;
s++;
}
last = length;
length = 0;
}
else{
s++;
}
}
return last;
}
7. 最长公共前缀
我的思路
发现有字符串到结尾了或者当前的位置不全一样,就发出退出信号,用lenght同时表达检查的长度和返回的长度。
我的算法
char* longestCommonPrefix(char** strs, int strsSize) {
int length = 0, cease = 0;
while(1){
for(int i = 0; i< strsSize; i++){
if(strs[0][length] != strs[i][length] || strs[i][length] == 0){
cease = 1;
break;
}
}
if(cease){
break;
}
length++;
}
strs[0][length] = 0;
return strs[0];
}
8. 找出字符串中第一个匹配项的下标
我的思路
我的算法
算法学习
9. 验证回文(双指针)
我的思路
暴力求解
为了方便判别,先对字符串进行预处理,把所有非目标字符换成空格,这件事情可以在确定数组长度的时候做,顺便把大小写给统一了,一举两得。
我的算法
char tolower(char c){
if(c>='A' && c <= 'Z'){
c += 'a'-'A';
}
return c;
}
int isAor1(char c){
if(c>='a' && c<='z'){
return 1;
}
else if(c>='0' && c<='9'){
return 1;
}
else{
return 0;
}
}
bool isPalindrome(char* s) {
int length;
int isP = 1;
for(length = 0; s[length] != 0; length++){
s[length] = tolower(s[length]);
if(!isAor1(s[length])){
s[length] = ' ';
}
}
int start = 0, end = length-1;
while(start < end){
//跳过所有空格,但是注意不要越界
while(start<=length-1 && !isAor1(s[start])){
start++;
}
while(end>=0&&!isAor1(s[end])){
end--;
}
//当start<end的时候才是有效的判断
//当start>end的时候说明已经通过检验了!
if(start<end && s[start] != s[end]){
isP = 0;
break;
}
start++; end--;
}
return isP;
}