Leetcode学习笔记(2)
题目1 ID面试题 01.04
给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。
回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。
回文串不一定是字典当中的单词。
示例1:
输入:"tactcoa"
输出:true(排列有"tacocat"、"atcocta",等等)
我的解答:
bool canPermutePalindrome(char* s){ int num[200]={0}; int i,flag=0; for(i=0;i<strlen(s);i++){ num[s[i]]++; } for(i=0;i<200;i++){ if(num[i]%2!=0){ flag++; } } if(flag>1){ return false; } return true; }
思路跟之前做过的一个回文数类似,不过这里的题目要求的是给出的字母能不能组成回文字符串,而回文字符串想要组成,那么奇数的字母必须要小于等于1个,想到这里之后,再将得到的字母都储存到数组里面,本来数组大小设置的是小写字母26的,但测试用例里面有\,所以我直接扩展成了200,空间换时间,而且最后空间占用也挺少,内存消耗 :6.7 MB, 在所有 C 提交中击败了100.00%的用户
题目2ID697
给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
示例 1:
输入: [1, 2, 2, 3, 1]
输出: 2
解释:
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.
示例 2:
输入: [1,2,2,3,1,4,2]
输出: 6
注意:
nums.length 在1到50,000区间范围内。
nums[i] 是一个在0到49,999范围内的整数。
我的解答:
这个题目分为两步,第一步是求输入数组的度,第二步是求出与输入数组拥有相同大小度的最短连续子数组。因为题目里面告诉我们输入数组的范围是1--50000,数据不是很大,可以考虑使用空间交换时间。因为度是出现频次最高的数字,所以我们只需要知道频次最高的数字的开始和最远处,即起点固定,每次只更新远处即可。
int findShortestSubArray(int* nums, int numsSize){ int mark[50000]={0},start[50000]={0},end[50000]={0}; int i,count=0; int min=50000;//[1] numsSize=1 nums[0]=1 for(i=0;i<numsSize;i++){//mark[1]=1,count=1;mark[1]=1;start[1]=0,end[1]=1; mark[nums[i]]++; if(mark[nums[i]]>count){ count=mark[nums[i]]; } if(mark[nums[i]]==1){ start[nums[i]]=i; end[nums[i]]=i; }else{ end[nums[i]]=i; } } for(i=0;i<50000;i++){ if(mark[i]==count){ if((end[i]-start[i])<min){ min=end[i]-start[i]; } } } min++; return min; }
题目3 ID206
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
我的解答:
在做https://leetcode-cn.com/problems/palindrome-linked-list-lcci/ 回文链表的时候,需要使用到快慢指针和反转链表,所以先去找了一道反转链表的题目来做。思考了好久反转链表的方式,看了b站的视频:https://www.bilibili.com/video/av24376909?from=search&seid=6943227570952146957 顺利理解(虽然是无声的,但是认真看几分钟就能看懂),写出代码一次通过。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* reverseList(struct ListNode* head){ struct ListNode *curr=head,*p=NULL,*q=NULL; while(curr){ p=curr; curr=p->next; p->next=q; q=p; } curr=p; return curr; }
题目4 ID面试题 02.06
编写一个函数,检查输入的链表是否是回文的。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
我的解答:
此题在上一道反转链表的基础上做的,先使用快慢指针,即快指针一次走两步,慢指针一次走一步,当快指针到终点的时候,慢指针必然在中点,将中点往后的链表反转,然后与输入链表进行比较即可。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ bool isPalindrome(struct ListNode* head){ struct ListNode* fir=head,*sec=head,*p=NULL,*q=NULL,*curr=head; if(fir==NULL||fir->next==NULL){ return true; } while(sec&&sec->next){ fir=fir->next; sec=sec->next->next; } while(fir){ p=fir; fir=p->next; p->next=q; q=p; } fir=q; while(curr&&fir){ if(curr->val!=fir->val){ return false; } curr=curr->next; fir=fir->next; } return true; }
执行用时 :4 ms, 在所有 C 提交中击败了100.00%的用户
内存消耗 :10.9 MB, 在所有 C 提交中击败了100.00%的用户
题目5 ID888
爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 块糖的大小,B[j] 是鲍勃拥有的第 j 块糖的大小。
因为他们是朋友,所以他们想交换一个糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)
返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。
如果有多个答案,你可以返回其中任何一个。保证答案存在。
示例 1:
输入:A = [1,1], B = [2,2]
输出:[1,2]
示例 2:
输入:A = [1,2], B = [2,3]
输出:[1,2]
示例 3:
输入:A = [2], B = [1,3]
输出:[2,3]
示例 4:
输入:A = [1,2,5], B = [2,4]
输出:[5,4]
提示:
1 <= A.length <= 10000
1 <= B.length <= 10000
1 <= A[i] <= 100000
1 <= B[i] <= 100000
保证爱丽丝与鲍勃的糖果总量不同。
答案肯定存在。
我的解答:
求出输入两个数组和的平均值,就是最后要两人要达到的糖果量,然后寻找能达成目的的数组值即可
/** * Note: The returned array must be malloced, assume caller calls free(). */ int* fairCandySwap(int* A, int ASize, int* B, int BSize, int* returnSize){ int* re=(int*)malloc(sizeof(int)*2); int i,j; int Asum=0,Bsum=0; int ave=0; int flag=0; for(i=0;i<ASize;i++){ Asum+=A[i]; } for(i=0;i<BSize;i++){ Bsum+=B[i]; } ave=(Asum+Bsum)/2; for(i=0;i<ASize;i++){ for(j=0;j<BSize;j++){ if(Asum-A[i]+B[j]==ave){ re[0]=A[i]; re[1]=B[j]; flag=1; break; } } if(flag==1){ break; } } *returnSize=2; return re; }
题目6 ID409
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
我的解答:
常规思路,存储每个字母出现的次数,偶数的话直接加到length里面,奇数的话需要判断是第一次出现还是二次以上出现,写出代码即可
int longestPalindrome(char * s){ int num[200]={0}; int flag=0; int i; int length=0; for(i=0;i<strlen(s);i++){ num[s[i]]++; } for(i=0;i<200;i++){ if(num[i]%2==0){ length+=num[i]; }else{ if(flag==0){ length+=num[i]; flag=1; }else{ length+=num[i]-1; } } } return length; }
题目7 ID155
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) -- 将元素 x 推入栈中。
pop() -- 删除栈顶的元素。
top() -- 获取栈顶元素。
getMin() -- 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
我的解答:
题目告诉我们要在常数时间内检索到最小元素的栈,所以我们在将数据存入的时候进行处理,调用getMin()直接输出最小值即可,栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成,这里我们多加一个min[],使用min[]数组来储存每一次存入数据的时候的当前的最小值,需要特殊处理的是第一次存入数据的时候,(代码最开始因为首次压入数据没有处理好而出错)每一次存入数据都需要跟上一次的最小值进行比较,第一次存入的时候没有上一次的最小值,我们手动设定一个大值即可,让第一次的数据被成功存入最小值
typedef struct { int data[1000]; int top; int min[1000]; } MinStack; /** initialize your data structure here. */ int flag=0; MinStack* minStackCreate() { MinStack* obj=(MinStack*)malloc(sizeof(MinStack)); obj->top=-1; return obj; } void minStackPush(MinStack* obj, int x) { if(obj->top+1<1000){ long min; if(obj->top==-1){ min=2147483649; }else{ min=obj->min[obj->top]; } obj->top++; obj->data[obj->top]=x; if(x<min){ obj->min[obj->top]=x; }else{ obj->min[obj->top]=min; } } } void minStackPop(MinStack* obj) { if(obj->top>-1){ obj->top--; } } int minStackTop(MinStack* obj) { if(obj->top>-1){ return obj->data[obj->top]; } return; } int minStackGetMin(MinStack* obj) { if(obj->top>-1){ return obj->min[obj->top]; } return; } void minStackFree(MinStack* obj) { free(obj); } /** * Your MinStack struct will be instantiated and called as such: * MinStack* obj = minStackCreate(); * minStackPush(obj, x); * minStackPop(obj); * int param_3 = minStackTop(obj); * int param_4 = minStackGetMin(obj); * minStackFree(obj); */
题目8 ID217
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
我的解答:
第一次做的时候用数组模拟哈希表,开2000的数组,1000储存正数,1000储存负数,
bool containsDuplicate(int* nums, int numsSize){ int num[2000]={0}; int i; for(i=0;i<numsSize;i++){ if(nums[i]<0){ num[nums[i]+1000]++; }else{ num[nums[i]]++; } } for(i=0;i<2000;i++){ if(num[i]>1){ return true; } } return false; }
题目测试数据输入极大[-1200000005,-1200000005],证明空间换时间不可行
换一种方法为:
bool containsDuplicate(int* nums, int numsSize){ int i,j; int temp; if(numsSize==1){ return false; } for(i=0;i<numsSize;i++){ temp=nums[i]; for(j=i+1;j<numsSize;j++){ if(temp==nums[j]){ return true; } } } return false; }
输入的数据过多导致时间超出限制,说明要找到两全其美的办法
我们先对输入的数组进行排序,然后比较数组相邻两个值是否相等,使用C语言自带的qsort函数排序。
int cmpfunc(int *a,int *b){ return(*a - *b); } bool containsDuplicate(int* nums, int numsSize){ int i; if(numsSize==1||numsSize==0){ return false; } qsort(nums,numsSize,sizeof(int),cmpfunc); for(i=1;i<numsSize;i++){ if(nums[i]==nums[i-1]){ return true; } } return false; }
题目9 ID896
如果数组是单调递增或单调递减的,那么它是单调的。
如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递减的。
当给定的数组 A 是单调数组时返回 true,否则返回 false。
示例 1:
输入:[1,2,2,3]
输出:true
示例 2:
输入:[6,5,4,4]
输出:true
示例 3:
输入:[1,3,2]
输出:false
示例 4:
输入:[1,2,4,5]
输出:true
示例 5:
输入:[1,1,1]
输出:true
提示:
1 <= A.length <= 50000
-100000 <= A[i] <= 100000
我的解答:
最开始的时候分情况进行讨论,也就是递增数列,递减数列,常数数列,列出几种情况通过
bool isMonotonic(int* A, int ASize){ int i=0; int check; if(ASize==1){ return true; } check=A[ASize-1]-A[0]; if(check>0){ for(i=0;i<ASize-1;i++){ if(A[i]>A[i+1]){ return false; } } }else if(check<0){ for(i=0;i<ASize-1;i++){ if(A[i]<A[i+1]){ return false; } } }else{ for(i=0;i<ASize-1;i++){ if(A[i]!=A[i+1]){ return false; } } } return true; }
学习评论区里面的解法,虽然看上去我们需要讨论的情况很多,但是返回false的情况只是左侧的数字不一直大于等于右侧数字,或者右侧数字不一直大于等于左侧数字的时候(当然特殊的只有一个数的时候还是需要讨论),所以我们可以用两个常数a,b来记录这种规律,感觉不太容易想到,学习思路
bool isMonotonic(int* A, int ASize){ int i=0; int a=0,b=0; if(ASize==1){ return true; } for(i=0;i<ASize-1;i++){ if(A[i]>A[i+1]){ a=1; } if(A[i]<A[i+1]){ b=1; } } if(a+b==2){ return false; }else{ return true; } }
题目10 ID面试题53 - I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
限制:
0 <= 数组长度 <= 50000
我的解答:
这道题目比较简单,因为是排好序的数组,为了令其找完target之后立刻退出,使用goto语句
执行用时 :8 ms, 在所有 C 提交中击败了100.00%的用户
内存消耗 :8.5 MB, 在所有 C 提交中击败了100.00%的用户
int search(int* nums, int numsSize, int target){ int length=0,i; for(i=0;i<numsSize;i++){ if(nums[i]==target){ while(i<numsSize&&nums[i]==target){ i++; length++; } goto end; } } end:return length; }
题目11 ID1005
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。
示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。
示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
提示:
1 <= A.length <= 10000
1 <= K <= 10000
-100 <= A[i] <= 100
我的解答:
常规做法,看了题目之后能想到每次反转的都是那个数组里面的最小值,这样才能让整个数组的和最大,每次遍历循环求最小值及其下标再翻转即可。
int largestSumAfterKNegations(int* A, int ASize, int K){ int i,j; int min,min_x,sum=0; for(i=0;i<K;i++){ min=A[0]; min_x=0; for(j=0;j<ASize;j++){ if(A[j]<min){ min=A[j]; min_x=j; } } if(min>0||min<0){ A[min_x]=-A[min_x]; } } for(i=0;i<ASize;i++){ sum+=A[i]; } return sum; }
题目12 ID492
作为一位web开发者, 懂得怎样去规划一个页面的尺寸是很重要的。 现给定一个具体的矩形页面面积,你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面。要求:
1. 你设计的矩形页面必须等于给定的目标面积。
2. 宽度 W 不应大于长度 L,换言之,要求 L >= W 。
3. 长度 L 和宽度 W 之间的差距应当尽可能小。
你需要按顺序输出你设计的页面的长度 L 和宽度 W。
示例:
输入: 4
输出: [2, 2]
解释: 目标面积是 4, 所有可能的构造方案有 [1,4], [2,2], [4,1]。
但是根据要求2,[1,4] 不符合要求; 根据要求3,[2,2] 比 [4,1] 更能符合要求. 所以输出长度 L 为 2, 宽度 W 为 2。
说明:
给定的面积不大于 10,000,000 且为正整数。
你设计的页面的长度和宽度必须都是正整数。
我的解答:
从面积的开方处开始递减,寻找能够被整除的数字,最快找到的就是距离最近的。
/** * Note: The returned array must be malloced, assume caller calls free(). */ int* constructRectangle(int area, int* returnSize){ int left,right,i; int* a=(int*)malloc(sizeof(int)*2); *returnSize=2; for(i=(int)sqrt(area);i>0;i--){ if(area%i==0){ a[0]=area/i; a[1]=i; return a; } } return; }
题目13 ID551
给定一个字符串来代表一个学生的出勤记录,这个记录仅包含以下三个字符:
'A' : Absent,缺勤
'L' : Late,迟到
'P' : Present,到场
如果一个学生的出勤记录中不超过一个'A'(缺勤)并且不超过两个连续的'L'(迟到),那么这个学生会被奖赏。
你需要根据这个学生的出勤记录判断他是否会被奖赏。
示例 1:
输入: "PPALLP"
输出: True
示例 2:
输入: "PPALLL"
输出: False
我的解答:
根据题目意思写出相应代码,需要注意的是缺勤是否第一次判断需要放在开始
bool checkRecord(char * s){ int a=0,b=0,i; for(i=0;i<strlen(s);i++){ if(a==1&&s[i]=='A'){ return false; } if(s[i]=='A'){ a=1; } if((i+2)<strlen(s)&&s[i]==s[i+1]&&s[i]==s[i+2]&&s[i]=='L'){ return false; } } return true; }
题目14 ID994
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
示例 1:
输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
提示:
1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j] 仅为 0、1 或 2
我的解答:
理清楚思路之后其实比较简单,这种做法叫做BFS吗?从每一个腐烂橘子处,检查四个相邻方向是否有新鲜橘子,有的话将其感染为3,因为在本轮感染中,刚被感染的橘子是不能感染其他橘子的,他们需要在下一轮开始的时候才感染其他新鲜橘子。对于检查是否有永远不会被感染的新鲜橘子,我们只需要在腐烂橘子不再增加的时候,循环检测一下是否还有新鲜橘子即可。我们需要做的事情主要是将标记为3的橘子变成2,以及将2的橘子四周的新鲜橘子变为3。
int orangesRotting(int** grid, int gridSize, int* gridColSize){ int i,j,k; int m,n; int flag; int count=0; int num[4][2]={{1,0},{0,1},{-1,0},{0,-1}}; while(1){ flag=1; for(i=0;i<gridSize;i++){ for(j=0;j<gridColSize[i];j++){ if(grid[i][j]==2){ for(k=0;k<4;k++){ m=i+num[k][0]; n=j+num[k][1]; if(m>=0&&n>=0&&m<gridSize&&n<gridColSize[i]&&grid[m][n]==1){ grid[m][n]=3; flag=0; } } } } } for(i=0;i<gridSize;i++){ for(j=0;j<gridColSize[0];j++){ if(grid[i][j]==3){ grid[i][j]=2; } } } if(flag==1){ break; }else{ count++; } } for(i=0;i<gridSize;i++){ for(j=0;j<gridColSize[i];j++){ if(grid[i][j]==1){ return -1; } } } return count; }
题目15 ID263
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例 1:
输入: 6
输出: true
解释: 6 = 2 × 3
示例 2:
输入: 8
输出: true
解释: 8 = 2 × 2 × 2
示例 3:
输入: 14
输出: false
解释: 14 不是丑数,因为它包含了另外一个质因数 7。
说明:
1 是丑数。
输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]。
我的解答:
做leetcode做多了之后对于这种没有用到什么数据结构的题目感觉越来越得心应手了,很简单的题目,直接看代码吧。
bool isUgly(int num){ int flag; int test=num; if(test==0){ return false; } while(test!=1){ flag=1; if(test%2==0){ test/=2; flag=0; } if(test%3==0){ test/=3; flag=0; } if(test%5==0){ test/=5; flag=0; } if(flag){ break; } } if(flag==1){ return false; }else{ return true; } }
题目16 ID796
给定两个字符串, A 和 B。
A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' 。如果在若干次旋转操作之后,A 能变成B,那么返回True。
示例 1:
输入: A = 'abcde', B = 'cdeab'
输出: true
示例 2:
输入: A = 'abcde', B = 'abced'
输出: false
注意:
A 和 B 长度不超过 100。
我的解答:
看到一个python的解法,觉得好强啊,因为A能够经过旋转变成B,那么A+A一定包含有B
class Solution(object): def rotateString(self, A, B): """ :type A: str :type B: str :rtype: bool """ return len(A)==len(B) and B in (A+A)
另外是自己用C语言的解法,刚好今天老师讲了约瑟夫生者死者问题,感觉这个题目如果是用C语言的链表构成环形能够做到上面python的效果,如果是使用数组的话,将数组模拟成环形,需要index取余,然后就能进行字符串的比较了。
bool rotateString(char * A, char * B){ int i,j; int length=strlen(A); if(strlen(A)!=strlen(B)){ return false; } if(strlen(A)==0){ return true; } for(i=0;i<length;i++){ int tmp=i; for(j=0;j<length;j++){ if(A[tmp++]!=B[j]){ break; } tmp%=length; } if(j==length){ return true; } } return false; }
题目17 ID83
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
我的解答:
这道题目练习链表的基本操作,因为题目已经给出了是排序链表,所以我们只需要比较相邻两个节点的val是否相同即可,如果相同则删除后一个节点,不同的话则往后继续检查,需要注意的地方是链表的边界操作
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* deleteDuplicates(struct ListNode* head){ struct ListNode* test=head; while(test!=NULL){ while(test->next!=NULL&&test->val==test->next->val){ if(test->next->next==NULL){ test->next=NULL; }else{ test->next=test->next->next; } } test=test->next; } return head; }
题目18 ID345
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1:
输入: "hello"
输出: "holle"
示例 2:
输入: "leetcode"
输出: "leotcede"
说明:
元音字母不包含字母"y"。
我的解答:
元音字母不是只包含aeiou吗?为啥y还要专门提示一下。这道题需要注意的地方是元音字母有大小写之分,然后我们用双指针法,一个从开始,一个从末尾,依次反转即可
bool check(char ch){ switch(ch){ case 'A': case 'E': case 'I': case 'O': case 'U': case 'a': case 'e': case 'i': case 'o': case 'u': return true; default: return false; } } char * reverseVowels(char * s){ int p,q; char* tmp; p=0; q=strlen(s)-1; while(p<q){ while(!check(s[p])&&p<q){ p++; } while(!check(s[q])&&p<q){ q--; } tmp=s[p]; s[p]=s[q]; s[q]=tmp; q--; p++; } return s; }
题目19 ID70
假设你正在爬楼梯。需要 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 阶
我的解答:
先使用递归求解,但是递归太消耗时间了,当输入的楼梯数是36的时候就超时了
int climbStairs(int n){ if(n==0){ return 0; }else if(n==1){ return 1; }else if(n==2){ return 2; }else{ return climbStairs(n-2)+climbStairs(n-1); } }
当阶梯数为1,2,3,4时对应的上楼梯方案是1,2,3,5,可以看到当前方案数是前两次之和,所以使用动态规划
int climbStairs(int n){ if(n==0){ return 0; }else if(n==1){ return 1; }else if(n==2){ return 2; } long a=1,b=2; int i; long tmp; for(i=3;i<=n;i++){ tmp=a+b; a=b; b=tmp; } return tmp; }
题目20 ID342
给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。
示例 1:
输入: 16
输出: true
示例 2:
输入: 5
输出: false
进阶:
你能不使用循环或者递归来完成本题吗?
我的解答:
先用循环来做:
bool isPowerOfFour(int num){ int tmp=num; if(num==0){ return false; } while(tmp%4==0){ tmp/=4; } if(tmp==1){ return true; }else{ return false; } }
因为题目说不使用循环或者递归来完成,使用数学方法,既然是4的幂次方,那这个数可以写成num=4^n,接着转换成n=log4num,再化简为n=1/2*log2num,因为n一定是一个整数,所以log2num一定是一个偶数,在C语言中按对数换底公式达到这个目的,在一定范围内认为结果是一个整数
#include<math.h> bool isPowerOfFour(int num){ if(num==0){ return false; } double x = log((double)num) / log(4.0), e = 1e-3; int ifloor = floor(x), iceil = ceil(x); if (x-ifloor < e || iceil-x < e) return true; else return false; }
题目21 ID205
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s = "egg", t = "add"
输出: true
示例 2:
输入: s = "foo", t = "bar"
输出: false
示例 3:
输入: s = "paper", t = "title"
输出: true
说明:
你可以假设 s 和 t 具有相同的长度。
我的解答:
评论里面有一句话挺好,大概意思是:一个人说法语,另一个人说英语,要想知道他们是不是说的同一个意思,有一个办法就是将他们说的都翻译成中文,看是否相同就行了,在这道题里也是这样,将每一个字母翻译成第一次出现的数字下标,进行比较。
bool isIsomorphic(char * s, char * t){ int i; while(s[i]){ if(find(s,i)!=find(t,i)){ return false; } i++; } return true; } int find(char* ch,int end){ int i; for(i=0;i<=end;i++){ if(ch[i]==ch[end]){ return i; } } return end; }
题目22 ID147
对链表进行插入排序。
(好像GIF放进来就动不了了)
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
我的解答:
对链表进行操作,将原链表上面的每一个节点摘取下来,进行比较排序之后放进新链表,最后返回新链表头结点。插入的时候有三种情况,第一种是在头结点之前,第二种是插在中间,第三种是插在末尾,第二种和第三种实际操作是相同的,所以分两种情况进行插入,剩下的就是链表的操作了。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* insertionSortList(struct ListNode* head){ if(head==NULL||head->next==NULL){ return head; } struct ListNode* result=NULL; struct ListNode* pnew=NULL; while(head){ pnew=head; head=head->next; pnew->next=NULL; //摘取单个节点,将其在新链表上重新排序 if(result==NULL){ result=pnew; }else{ struct ListNode* p=result; struct ListNode* tmp=p; while(p&&p->val<pnew->val){ tmp=p; p=p->next; } //插头结点 if(p==result){ pnew->next=result; result=pnew; }else{ //插中间或末尾 tmp->next=pnew; pnew->next=p; } } } return result; }
接着坚持努力吧!