leetcode 14天算法入门 C语言实现
第一天 二分查找
二分法讲解 : 把答案所在的区间逐渐缩小,直到区间内只有答案。
图解:
704. 二分查找
思路:二分思想,不用解释直接上手
代码:
int search(int* nums, int numsSize, int target){
int left=0;
int right=numsSize-1;
while(left<=right)
{
int mid=(right-left)/2+left;
if(nums[mid]==target)
{
return mid;
}
if(nums[mid]<target)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
return -1;
}
278. 第一个错误的版本
思路:还是基本的二分思想,当mid是真时,说明mid处为真,则left=mid+1,如此不断缩小间距,当left>=right时left处指的一定是第一个错误版本(此处不在证明)
代码:
int firstBadVersion(int n) {
int left=1;
int right=n;
while(left<right)
{
int mid=(right-left)/2+left;
bool tmp=isBadVersion(mid);
if(!tmp)
{
left=mid+1;
}
else
{
right=mid;
}
}
return left;
}
35. 搜索插入位置
思路:*nums*[*p**o**s*?1]<*target*≤*nums*[*p**o**s*] 只要需要理解这个概念就ok了
代码:
int searchInsert(int* nums, int numsSize, int target){
int left=0;
int right=numsSize-1;
int mid=0;
while(left<=right)
{
mid=(right-left)/2+left;
if(nums[mid]==target)
{
return mid;
}
if(nums[mid]>target)
{
right=mid-1;
}
else
{
left=mid+1;
}
}
return left;
}
第二天 双指针
双指针法讲解:双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
977. 有序数组的平方
思路:1.每个数都平方,平方后在排序,当然这就和双指针以及题目的非递减条件无关了.
2.最大的平方数一定出自左端或右端,所以使用双指针,一个左端一个右端,左右交替选最大平方值.
代码:
int mul(int a)
{
return a*a;
}
int* sortedSquares(int* nums, int numsSize, int* returnSize){
int left=0;
int right=numsSize-1;
*returnSize=numsSize;
int *answer=(int *)malloc(sizeof(int)*numsSize);
int loc=numsSize-1;
while(left<=right)
{
if(mul(nums[left])>mul(nums[right]))
{
answer[loc]=mul(nums[left]);
left++;
}
else
{
answer[loc]=mul(nums[right]);
right--;
}
loc--;
}
return answer;
}
189. 轮转数组
思路:k的左边反转,k的右边反转,最后对于数组整体反转,即可以完成实现
代码:
void swap(int *a,int *b)
{
int tmp=*a;
*a=*b;
*b=tmp;
}
void exchange(int *nums,int left,int right)
{
while(left<right)
{
swap(nums+left,nums+right);
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k){
k=k%numsSize;
exchange(nums,0,numsSize-1-k);
exchange(nums,numsSize-k,numsSize-1);
exchange(nums,0,numsSize-1);
}
第三天 双指针
283. 移动零
思路:注释部分是我第一次写的,使用的两个指针来记录0和非0的位置,进行移动操作
非注释时对该代码的简化
代码:
void swap(int *a,int *b)
{
int tmp=*a;
*a=*b;
*b=tmp;
}
void moveZeroes(int* nums, int numsSize){
int left = 0, right = 0;
while (right < numsSize) {
if (nums[right]) {
swap(nums + left, nums + right);
left++;
}
right++;
}
}
167. 两数之和 II - 输入有序数组
思路:左指针,右指针,大于目标数,右指针左移.小于目标数,左指针右移.
代码:
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
int left=0;
int right=numbersSize-1;
int *answer=(int *)malloc(sizeof(int)*2);
*returnSize=2;
while(1)
{
if(numbers[left]+numbers[right]==target)
{
answer[0]=left+1;
answer[1]=right+1;
break;
}
else if(numbers[left]+numbers[right]<target)
{
left++;
}
else
{
right--;
}
}
return answer;
}
第四天 双指针
344. 反转字符串
思路:就左指针与右指针交换即可
代码:
void swap(char *a, char *b) {
char t = *a;
*a = *b, *b = t;
}
void reverseString(char* s, int sSize){
int left=0,right=sSize-1;
while(left<right)
{
swap(s + left, s + right);
left++;
right--;
}
}
557. 反转字符串中的单词 III
思路:难点就在于寻找每一个单词
代码:
void swap(char *a,char *b)
{
char tmp=*a;
*a=*b;
*b=tmp;
}
void revers(char *arr,int begin,int end)
{
while(begin<end)
{
swap(arr+begin,arr+end);
begin++;
end--;
}
}
char * reverseWords(char * s){
int begin=0;
int last=0;
int len=strlen(s);
while(last<=len)
{
while(s[begin]==' ')
{
begin++;
}
while(s[last]!=' '&&s[last]!='\0')
{
last++;
}
revers(s,begin,last-1);
if(s[last]=='\0')
{
break;
}
begin=last+1;
last+=1;
}
return s;
}
第五天 双指针
876. 链表的中间结点
思路:一个快指针一次走两步(最后一次可能走一步,因为两个中间节点的第二个),一个慢指针一次走一步,当快指针的next=null时,此时慢指针指向中间节点.
代码:
struct ListNode* middleNode(struct ListNode* head){
struct ListNode*slow=head;
struct ListNode*fast=head;
while(fast->next&&fast->next->next)
{
fast=fast->next->next;
slow=slow->next;
}
if(fast->next)
{
slow=slow->next;
}
return slow;
}
19. 删除链表的倒数第 N 个结点
思路:找到该节点,删除即可.
代码:
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode*fast=head;
struct ListNode*guard=(struct ListNode*)malloc(sizeof( struct ListNode));
int i=1;
struct ListNode* slow=guard;
slow->next=head;
while(fast->next)
{
if(i>=n)
{
slow=slow->next;
}
i++;
fast=fast->next;
}
struct ListNode*tmp=slow->next;
slow->next=tmp->next;
head=guard->next;
free(tmp);
free(guard);
return head;
}
第六天 滑动窗口
567. 字符串的排列
思路:采用滑动窗口,判断窗口中的数量是否与s1相同
代码:
bool check(int s1_arr[],int s2_arr[])
{
for(int i=0;i<26;i++)
{
if(s1_arr[i]!=s2_arr[i])
{
return false;
}
}
return true;
}
bool checkInclusion(char * s1, char * s2){
int n=strlen(s1);
int m=strlen(s2);
if(n>m)
{
return false;
}
int s1_arr[26];
int s2_arr[26];
memset(s1_arr,0,sizeof(s1_arr));
memset(s2_arr,0,sizeof(s2_arr));
for(int i=0;i<n;i++)
{
s1_arr[*(s1+i)-'a']++;
}
for(int i=0;i<n;i++)
{
s2_arr[*(s2+i)-'a']++;
}
if(check(s1_arr,s2_arr))
{
return true;
}
for(int i=n;i<m;i++)
{
s2_arr[*(s2+i)-'a']++;
s2_arr[*(s2+i-n)-'a']--;
if(check(s1_arr,s2_arr))
{
return true;
}
}
return false;
}
3. 无重复字符的最长子串
思路:计算窗口的最大值即可
代码:
int lengthOfLongestSubstring(char * s){
int left=0;
int right=0;
int max=0;
int len=strlen(s);
int j=0,i=0;
int same=0;
while(right<len)
{
if(left<right)
{
same=0;
for(j=left;j<right;j++)
{
if(s[j]==s[right])
{
same=1;
break;
}
}
if(same)
left=j+1;
}
max=max>(right-left+1)?max:(right-left+1);
right++;
}
return max;
}
第七天 广度优先搜索 / 深度优先搜索
733. 图像渲染
思路:先修改最初的起点,再在修改起点上下左右的点,再然后再把四个点都作为起点,依次进行.
代码:
void dfs(int **image,int color,int curcolor,int row,int col,int sr,int sc)
{
if(sr<row&&sr>=0&&sc<col&&sc>=0)
{
if(image[sr][sc]==curcolor)
{
image[sr][sc]=color;
dfs(image,color,curcolor,row,col,sr-1,sc);
dfs(image,color,curcolor,row,col,sr+1,sc);
dfs(image,color,curcolor,row,col,sr,sc-1);
dfs(image,color,curcolor,row,col,sr,sc+1);
}
}
return ;
}
int** floodFill(int** image, int imageSize, int* imageColSize, int sr, int sc, int color, int* returnSize, int** returnColumnSizes){
int row =imageSize;
int col=imageColSize[0];
*returnSize=row;
*returnColumnSizes=(int *)malloc(sizeof(int)*row);
for(int i=0;i<row;i++)
{
(*returnColumnSizes)[i]=col;
}
int curcolor=image[sr][sc];
if(curcolor!=color)
{
dfs(image,color,curcolor,row,col,sr,sc);
}
return image;
}
695. 岛屿的最大面积
思路:深度递归即可,每次遍历到1就修改为0.选出最大值即可
代码:
int dfs(int **grid,int gr,int gc,int row, int col)
{
if(gr<row&&gr>=0&&gc<col&&gc>=0)
{
if(grid[gr][gc]==1)
{
grid[gr][gc]=0;
int tmp1=dfs(grid,gr-1,gc,row,col);
int tmp2=dfs(grid,gr+1,gc,row,col);
int tmp3=dfs(grid,gr,gc-1,row,col);
int tmp4=dfs(grid,gr,gc+1,row,col);
return 1+tmp1+tmp2+tmp3+tmp4;
}
else
{
return 0;
}
}
return 0;
}
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
int max=0;
int row=gridSize;
int col=*gridColSize;
for(int i=0;i<gridSize;i++)
{
for(int j=0;j<*gridColSize;j++)
{
if(grid[i][j]==1)
{
int tmp = dfs(grid,i,j,row,col);
max=max>tmp?max:tmp;
}
}
}
return max;
}
第八天 广度优先搜索 / 深度优先搜索
617. 合并二叉树
思路:遍历每一个节点即可.
代码:
struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2){
if(!root1&&!root2)
{
return NULL;
}
if(!root1)
{
return root2;
}
if(!root2)
{
return root1;
}
struct TreeNode* head=(struct TreeNode*)malloc(sizeof(struct TreeNode));
head->val=root1->val+root2->val;
head->left= mergeTrees(root1->left,root2->left);
head->right= mergeTrees(root1->right,root2->right);
return head;
}
116. 填充每个节点的下一个右侧节点指针
思路:根据图可以理解,最难的部分是判断5->6,使用 if(root->next&&root->right) root->right->next=root->next->left; 即可解决.
代码:
struct Node* connect(struct Node* root) {
if (root == NULL||root->left==NULL)
return root;
root->left->next=root->right;
if(root->next&&root->right)
{
root->right->next=root->next->left;
}
root->left=connect(root->left);
root->right=connect(root->right);
return root;
}
第九天 广度优先搜索 / 深度优先搜索
542. 01 矩阵
思路:这题代码写不出来 参考https://leetcode.cn/problems/01-matrix/solution/54201ju-zhen-zhong-deng-cyu-yan-dfsjie-f-r8gt/
代码:
#define MIN(a,b)(a<b?a:b)
int** updateMatrix(int** mat, int matSize, int* matColSize, int* returnSize, int** returnColumnSizes){
*returnSize=matSize;
int** ans=(int**)malloc(matSize*sizeof(int*));
(*returnColumnSizes)=(int*)malloc(sizeof(int*)*matSize);
memcpy(*returnColumnSizes,matColSize,matSize*sizeof(int));
for(int i=0;i<matSize;i++){
ans[i]=(int*)calloc(matColSize[0],sizeof(int));
for(int j=0;j<matColSize[i];j++){
if(mat[i][j]!=0)
ans[i][j]=9999;
}
}
for(int x=0;x<matSize;x++){
for(int y=0;y<matColSize[0];y++){
if(x-1>=0)
ans[x][y]=MIN(ans[x][y],ans[x-1][y]+1);
if(y-1>=0)
ans[x][y]=MIN(ans[x][y],ans[x][y-1]+1);
}
}
for(int x=matSize-1;x>=0;x--){
for(int y=matColSize[x]-1;y>=0;y--){
if(x+1<matSize)
ans[x][y]=MIN(ans[x][y],ans[x+1][y]+1);
if(y+1<matColSize[x])
ans[x][y]=MIN(ans[x][y],ans[x][y+1]+1);
}
}
return ans;
}
994. 腐烂的橘子
思路:这题代码也写不出, 参考https://leetcode.cn/problems/rotting-oranges/solution/by-nirvana-10-djc3/
代码:
#define MAX 100
bool IsInArea(int row, int col, int rowSize, int colSize)
{
if (row >= 0 && row < rowSize && col >= 0 && col < colSize) {
return true;
}
return false;
}
int orangesRotting(int** grid, int gridSize, int* gridColSize){
int **dis = (int **)malloc(sizeof(int *) * gridSize);
for (int i = 0; i < gridSize; i++) {
dis[i] = (int *)malloc(sizeof(int) * gridColSize[i]);
for (int j = 0; j < gridColSize[i]; j++) {
dis[i][j] = -1;
}
}
int **q = (int **)malloc(sizeof(int *) * MAX);
for (int i = 0; i < MAX; i++) {
q[i] = (int *)malloc(sizeof(int) * 2);
}
int front = 0;
int rear = 0;
int freshCnt = 0;
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < gridColSize[i]; j++) {
if (grid[i][j] == 2) {
q[rear][0] = i;
q[rear][1] = j;
rear++;
dis[i][j] = 0;
} else if (grid[i][j] == 1) {
freshCnt++;
}
}
}
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int ans = 0;
while (front < rear) {
int r = q[front][0];
int c = q[front][1];
front++;
for (int i = 0; i < 4; i++) {
int x = r + dir[i][0];
int y = c + dir[i][1];
if (IsInArea(x, y, gridSize, gridColSize[0]) == false || dis[x][y] >= 0 || grid[x][y] == 0) {
continue;
}
dis[x][y] = dis[r][c] + 1;
ans = dis[x][y];
q[rear][0] = x;
q[rear][1] = y;
rear++;
freshCnt--;
if (freshCnt == 0) {
break;
}
}
}
for (int i = 0; i < MAX; i++) {
free(q[i]);
}
free(q);
for (int i = 0; i < gridSize; i++) {
free(dis[i]);
}
free(dis);
if (freshCnt > 0) {
return -1;
} else {
return ans;
}
}
第十天 递归 / 回溯
21. 合并两个有序链表
思路:归并思想,每次取两个对比.排序
代码:
struct ListNode* mergeTwoLists1(struct ListNode* list1, struct ListNode* list2,struct ListNode*tmp)
{
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
if(list1->val<list2->val)
{
tmp=list1;
tmp->next=mergeTwoLists1(list1->next,list2,tmp->next);
}
else
{
tmp=list2;
tmp->next=mergeTwoLists1(list1,list2->next,tmp->next);
}
return tmp;
}
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode*guard=(struct ListNode*)malloc(sizeof(struct ListNode));
guard->next=NULL;
struct ListNode*tmp=guard;
tmp->next=mergeTwoLists1(list1,list2,tmp->next);
tmp=guard->next;
free(guard);
return tmp;
}
206. 反转链表
思路:每次进行一个尾插尾删即可.
代码:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode*tmp=head,*tail;;
if(tmp==NULL||tmp->next==NULL)
{
return head;
}
struct ListNode *guard=(struct ListNode *)malloc(sizeof(struct ListNode));
guard->next=NULL;
tail=guard->next;
while(tmp!=NULL)
{
tail=tmp;
tmp=tmp->next;
tail->next=guard->next;
guard->next=tail;
}
head=guard->next;
free(guard);
return head;
}
第十一天 递归 / 回溯
77. 组合
思路:参考官方 :https://leetcode.cn/problems/combinations/solution/zu-he-by-leetcode-solution/
代码:
int* temp;
int tempSize;
int** ans;
int ansSize;
void dfs(int cur, int n, int k) {
if (tempSize + (n - cur + 1) < k) {
return;
}
if (tempSize == k) {
int* tmp = malloc(sizeof(int) * k);
for (int i = 0; i < k; i++) {
tmp[i] = temp[i];
}
ans[ansSize++] = tmp;
return;
}
temp[tempSize++] = cur;
dfs(cur + 1, n, k);
tempSize--;
dfs(cur + 1, n, k);
}
int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {
temp = malloc(sizeof(int) * k);
ans = malloc(sizeof(int*) * 10001);
tempSize = ansSize = 0;
dfs(1, n, k);
*returnSize = ansSize;
*returnColumnSizes = malloc(sizeof(int) * ansSize);
for (int i = 0; i < ansSize; i++) {
(*returnColumnSizes)[i] = k;
}
return ans;
}
784. 字母大小写全排列
思路:参考代码 https://leetcode.cn/problems/letter-case-permutation/solution/c-by-eeeugene-0i5o/
代码:
char ** letterCasePermutation(char * s, int* returnSize){
int len = strlen(s);
char **ret = malloc(sizeof(char *)* pow(2,len));
ret[0] = malloc(sizeof(char) * len + 1);
strcpy(ret[0],s);
int i,j,k;
int size = 1;
for(i = 0;i < len;i++){
k = size;
if(isalpha(s[i])){
for(j = 0;j < k;j++){
ret[k + j] = malloc(sizeof(char) * (len+1));
strcpy(ret[j + k],ret[j]);
ret[k + j][i] = (isupper(s[i]) != 0) ? s[i] + 32 : s[i] - 32;
size++;
}
}
}
*returnSize = size;
return ret;
}
46. 全排列
思路:参考代码:https://leetcode.cn/problems/permutations/solution/quan-pai-lie-cyu-yan-xiang-jie-chao-ji-x-pywy/
代码:
void swap(int * nums,int indexA,int indexB)
{
int temp = nums[indexA];
nums[indexA]= nums[indexB];
nums[indexB]= temp;
}
void prem(int* nums, int numsSize, int* returnSize, int** returnColumnSizes,int** returnNums,int offset)
{
if(offset == numsSize)
{
returnNums[*returnSize] = (int *)malloc(sizeof(int ) * numsSize);
memcpy(returnNums[*returnSize],nums,sizeof(int) * numsSize );
(*returnColumnSizes)[*returnSize] = numsSize;
*returnSize = *returnSize + 1;
}
else
{
int i;
for(i = offset; i < numsSize; i++)
{
swap(nums,i,offset);
prem(nums,numsSize,returnSize,returnColumnSizes,returnNums,offset+1);
swap(nums,i,offset);
}
}
}
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
{
int **returnNums = (int **)malloc(sizeof(int *) * 721);
*returnColumnSizes= (int *)malloc(sizeof(int ) * 721);
*returnSize = 0;
prem(nums,numsSize,returnSize,returnColumnSizes,returnNums,0);
return returnNums;
}
第 十二 天 动态规划
70. 爬楼梯
思路:爬一节楼梯方法数1 爬两节楼梯方法数2,爬3=爬1+爬2, 爬4=爬2+爬3;以此类推
代码:
int climbStairs(int n){
if(n==1)
{
return 1;
}
if(n==2)
{
return 2;
}
int a=1;
int b=2;
int sum=0;
while(n>2)
{
sum=a+b;
a=b;
b=sum;
n--;
}
return sum;
}
120. 三角形最小路径和
思路:最小路径和,即每次选最小的值即可
举例:
每次选两个数中的最小值,加到上一行,依次进行,最顶层的数一定是最小数.
代码:
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
for(int i=0;i<triangleSize;i++)
{
triangleColSize[i]=i+1;
}
int *answer=(int *)malloc(sizeof(int)*triangleSize);
for(int i=0;i<triangleSize;i++)
{
answer[i]=triangle[triangleSize-1][i];
}
for(int i=triangleSize-1-1;i>=0;i--)
{
for(int j=0;j<triangleColSize[i];j++)
{
answer[j]=answer[j]<answer[j+1]?triangle[i][j]+answer[j]:triangle[i][j]+answer[j+1];
}
}
return answer[0];
}
第 十三天 位运算
231. 2 的幂
思路:如果是2的次方数,则2进制序列中一定只有一个1,判断1的个数即可.
代码:
bool isPowerOfTwo(int n){
if(n<=0)
{
return false;
}
int count=0;
while(n)
{
n=n&(n-1);
count++;
}
if(count==1)
{
return true;
}
return false;
}
191. 位1的个数
思路:特殊解法: n&(n-1)的次数即为1的个数.
代码:
int hammingWeight(uint32_t n) {
int count=0;
while(n)
{
n=n&(n-1);
count++;
}
return count;
}
第 十四天 位运算
190. 颠倒二进制位
思路:分治思想,一次分为两部分进行倒置.
代码:
const uint32_t M1 = 0x55555555;
const uint32_t M2 = 0x33333333;
const uint32_t M4 = 0x0f0f0f0f;
const uint32_t M8 = 0x00ff00ff;
uint32_t reverseBits(uint32_t n) {
n = n >> 1 & M1 | (n & M1) << 1;
n = n >> 2 & M2 | (n & M2) << 2;
n = n >> 4 & M4 | (n & M4) << 4;
n = n >> 8 & M8 | (n & M8) << 8;
return n >> 16 | n << 16;
}
136. 只出现一次的数字
思路:两个相同的按位异或=0;
代码:
int singleNumber(int* nums, int numsSize){
int a=nums[0];
for(int j=1;j<numsSize;j++)
{
a=a^nums[j];
}
return a;
}
95041221e6.png)
思路:特殊解法: n&(n-1)的次数即为1的个数.
代码:
int hammingWeight(uint32_t n) {
int count=0;
while(n)
{
n=n&(n-1);
count++;
}
return count;
}
第 十四天 位运算
190. 颠倒二进制位
思路:分治思想,一次分为两部分进行倒置.
代码:
const uint32_t M1 = 0x55555555;
const uint32_t M2 = 0x33333333;
const uint32_t M4 = 0x0f0f0f0f;
const uint32_t M8 = 0x00ff00ff;
uint32_t reverseBits(uint32_t n) {
n = n >> 1 & M1 | (n & M1) << 1;
n = n >> 2 & M2 | (n & M2) << 2;
n = n >> 4 & M4 | (n & M4) << 4;
n = n >> 8 & M8 | (n & M8) << 8;
return n >> 16 | n << 16;
}
136. 只出现一次的数字
思路:两个相同的按位异或=0;
代码:
int singleNumber(int* nums, int numsSize){
int a=nums[0];
for(int j=1;j<numsSize;j++)
{
a=a^nums[j];
}
return a;
}
第九天,第十天的代码没有写出来,等下一次补一篇博客 思路与代码. 欢迎各位博主多多点评,希望各位大佬提出给优秀的建议或思路.
|