【Leetcode Sheet】Weekly Practice 1

Leetcode Test

822 翻转卡片游戏(8.2)

在桌子上有 n 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。

我们可以先翻转任意张卡片,然后选择其中一张卡片。

如果选中的那张卡片背面的数字 x 与任意一张卡片的正面的数字都不同,那么这个数字是我们想要的数字。

哪个数是这些想要的数字中最小的数(找到这些数中的最小值)呢?如果没有一个数字符合要求的,输出 0

其中, fronts[i]backs[i] 分别代表第 i 张卡片的正面和背面的数字。

如果我们通过翻转卡片来交换正面与背面上的数,那么当初在正面的数就变成背面的数,背面的数就变成正面的数。

提示:

  • n == fronts.length == backs.length
  • 1 <= n <= 1000
  • 1 <= fronts[i], backs[i] <= 2000
int flipgame(int* fronts, int frontsSize, int* backs, int backsSize){
    /*
    如果fronts==backs,则对应的数不可能成为预选
    因为如果i本身正反相等,当前反面已经等于正面
    同时如果backs[i]是其他正反相等里面的数,则一定等于某一个正面值
    否则均可以是预选

    遍历卡片的预选,选择最小的数字,需要考虑正反
    */
    int min=2000;
    bool same[2001];    //hash,标记数值是否可能成为预选
    for(int i=0;i<2001;i++){
        same[i]=0;
    }
    for(int i=0;i<frontsSize;i++){
        if(fronts[i]==backs[i]) same[fronts[i]]=1;  //make a sign,标记数值不可能
    }
    for(int i=0;i<frontsSize;i++){
        if(fronts[i]<min && same[fronts[i]]==0) min=fronts[i];
        if(backs[i]<min && same[backs[i]]==0) min=backs[i];
    }
    return min%2000;
}

722 删除注释(8.3)

给一个 C++ 程序,删除程序中的注释。这个程序source是一个数组,其中source[i]表示第 i 行源码。 这表示每行源码由 '\n' 分隔。

在 C++ 中有两种注释风格,行内注释和块注释。

  • 字符串// 表示行注释,表示//和其右侧的其余字符应该被忽略。
  • 字符串/* 表示一个块注释,它表示直到下一个(非重叠)出现的*/之间的所有字符都应该被忽略。(阅读顺序为从左到右)非重叠是指,字符串/*/并没有结束块注释,因为注释的结尾与开头相重叠。

第一个有效注释优先于其他注释。

  • 如果字符串//出现在块注释中会被忽略。
  • 同样,如果字符串/*出现在行或块注释中也会被忽略。

如果一行在删除注释之后变为空字符串,那么不要输出该行。即,答案列表中的每个字符串都是非空的。

样例中没有控制字符,单引号或双引号字符。

  • 比如,source = "string s = "/* Not a comment. */";" 不会出现在测试样例里。

此外,没有其他内容(如定义或宏)会干扰注释。

我们保证每一个块注释最终都会被闭合, 所以在行或块注释之外的/*总是开始新的注释。

最后,隐式换行符可以通过块注释删除。 有关详细信息,请参阅下面的示例。

从源代码中删除注释后,需要以相同的格式返回源代码。

提示:

  • 1 <= source.length <= 100
  • 0 <= source[i].length <= 80
  • source[i] 由可打印的 ASCII 字符组成。
  • 每个块注释都会被闭合。
  • 给定的源码中不会有单引号、双引号或其他控制字符。
#define MAX_LINE_LEN 80
//直接遍历,如果有【/*】阻挡就寻找【*/】并设置block,如果有【//】且没有block则忽略当前行的所有内容

char ** removeComments(char ** source, int sourceSize, int* returnSize) {
   char **res = (char **)calloc(sourceSize, sizeof(char *));   //初始化result
   char new_line[sourceSize * MAX_LINE_LEN + 1];   //初始化当前字符串
   int pos = 0, new_line_pos = 0;  //记录数组位置
   bool in_block = false;  //记录是否阻挡
   for (int j = 0; j < sourceSize; j++) {
       char *line = source[j];     //当前行的源代码字符串
       int line_size = strlen(line);   //源代码字符串长度
       for (int i = 0; i < line_size; i++) {
           if (in_block) {     //如果有阻挡
               if (i + 1 < line_size && line[i] == '*' && line[i + 1] == '/') {    //如果【*/】结尾
                   in_block = false;   //取消block
                   i++;    
               }
           } else {    //如果没有阻挡
               if (i + 1 < line_size && line[i] == '/' && line[i + 1] == '*') {    //如果【/*】开头
                   in_block = true;    //重制为有阻挡
                   i++;
               } else if (i + 1 < line_size && line[i] == '/' && line[i + 1] == '/') {     //如果【//】开头
                   break;      //结束当行,因为肯定覆盖了后面所有字符
               } else {    //如果其他字符开头
                   new_line[new_line_pos++] = line[i];     //当前字符计入该行字符串
               }
           }
       }
       if (!in_block && new_line_pos > 0) {    //如果当前字符串非空 且 没有阻挡
           new_line[new_line_pos] = '\0';  //字符串append 【\0】结尾符号
           res[pos] = (char *)calloc(new_line_pos + 1, sizeof(char));  //更新下一行的处理过的代码
           strcpy(res[pos], new_line);     //下一行append 当前字符串
           pos++;  //换行
           new_line_pos = 0;   //初始化当前字符串位置
       }
       *returnSize = pos;  //返回大小为行大小
   }
   return res;
}

980 不同路径Ⅲ(8.4)

在二维网格 grid 上,有 4 种类型的方格:

  • 1 表示起始方格。且只有一个起始方格。
  • 2 表示结束方格,且只有一个结束方格。
  • 0 表示我们可以走过的空方格。
  • -1 表示我们无法跨越的障碍。

返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目**。**

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格

提示:

  • 1 <= grid.length * grid[0].length <= 20
//回溯函数,i和j表示坐标,n表示未访问的【0】坐标数
int dfs(int i,int j,int n,int **grid,int r,int c){
    if(grid[i][j]==2){  //reach termination
        if(n==0) return 1;  //【0】均访问
        else return 0;  //【0】还有剩
    }
    int t=grid[i][j];   //暂存当前坐标的值为t
    int res=0;  //记录是否可能为result
    grid[i][j]=-1;  //当前坐标标记为不可访问
    int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};  //探索当前坐标的上下左右方位情况
    for(int k=0;k<4;k++){
        int ni=i+dir[k][0];     //模拟某一个方位的i坐标
        int nj=j+dir[k][1];     //模拟某一个方位的j坐标
        if(ni>=0 && ni<r && nj>=0 && nj<c && (grid[ni][nj]==0 || grid[ni][nj]==2)){
            res+=dfs(ni,nj,n-1,grid,r,c);   //递归
            //返回某一个方位的新坐标,n-1个剩余【0】坐标,grid坐标
            //r和c有什么用?——r是row numbers,c是column numbers,用于判定边界。
        }
    }
    grid[i][j]=t;   //回复当前坐标的值
    return res;
}

int uniquePathsIII(int** grid, int gridSize, int* gridColSize){
    //每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
    int r=gridSize,c=gridColSize[0];    //initialize row and column numbers
    int si=0,sj=0,n=0;  
        //n用于遍历记录所有【0】坐标个数 + 【1】
        //si是start i,sj是start j,记录起点坐标
    for(int i=0;i<r;i++){
        for(int j=0;j<c;j++){
            if(grid[i][j]==0) n++;
            else if(grid[i][j]==1){
                n++;
                si=i;
                sj=j;
            }
        }
    }   //遍历过程
    return dfs(si,sj,n,grid,r,c);   //回溯法,递归寻找
}

21 合并两个有序链表(8.5)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode*l1=list1,*l2=list2;		//rename
    if(l1==NULL) return l2;		//讨论l1和l2为空的情况
    else if(l2==NULL) return l1;
    else if(l1->val <= l2->val){
        //l1 value smaller
        l1->next=mergeTwoLists(l1->next,l2);
        //递归求解l1的下一个节点,把当前下一个节点和l2合并即可
        return l1;
    }
    else{
        //l2 value smaller
        l2->next=mergeTwoLists(l2->next,l1);
        //递归求解l2的下一个节点,把当前下一个节点和l1合并即可
        return l2;
    }
}/*主要用递归继续合并*/
/*以前的做法*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* tail,* dummyNode;//哨兵
    tail = dummyNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummyNode->next = NULL;
    while(list1 && list2){
        if(list1->val < list2->val){
            tail->next = list1;//链接小的节点
            tail = tail->next;//往后更新尾结点
            list1 = list1->next;//往后更新节点
        }
        else{
            tail->next = list2;
            tail = tail->next;
            list2 = list2->next;
        }
    }
    if(list1) tail->next = list1;
    if(list2) tail->next = list2;
    return dummyNode->next;
}

24 两两交换链表中的节点(8.6)

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* swapPairs(struct ListNode* head){
    if(head==NULL || head->next==NULL){
        //如果头节点是空或者只有一个节点
        return head;
    }
    struct ListNode* newHead=head->next;    //建立新头节点
    head->next=swapPairs(newHead->next);    //对新头节点进行递归swap
    newHead->next=head;     //新头节点的下一个节点连接当前头节点
    return newHead;     //返回新头节点
}

344 反转字符串(8.7)

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符
void reverseString(char* s, int sSize){
    int left=0,right=sSize-1;
    while(left<=right){
        char temp=s[left];
        s[left]=s[right];
        s[right]=temp;
        left++;
        right--;
    }
    return s;
}
//双指针 left and right

1749 任意子数组和的绝对值的最大值(8.8)

给你一个整数数组 nums 。一个子数组 [numsl, numsl+1, ..., numsr-1, numsr]和的绝对值abs(numsl + numsl+1 + ... + numsr-1 + numsr)

请你找出 nums和的绝对值 最大的任意子数组(可能为空),并返回该 最大值

abs(x) 定义如下:

  • 如果 x 是负整数,那么 abs(x) = -x
  • 如果 x 是非负整数,那么 abs(x) = x

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
int maxAbsoluteSum(int* nums, int numsSize){
    int pmax=0,nmin=0,psum=0,nsum=0;
    //pmax-positive max number
    //nmin-negative min number
    //psum-positive sum
    //nsum-negative sum
    //psum和nsum用于动态规划
    for(int i=0;i<numsSize;i++){
        psum+=nums[i];  //psum加上当前数字
        pmax=fmax(pmax,psum);   //pmax和psum取最大值
        psum=fmax(0,psum);  //如果psum仍然为正数,则延用
        nsum+=nums[i];  //nsum加上当前数字
        nmin=fmin(nmin,nsum);   //nmin和nsum取最小值
        nsum=fmin(0,nsum);  //如果nsum仍然为负数,则延用
    }
    return fmax(pmax,-nmin);
}

【53 最大子数组和】的变种

int maxSubArray(int* nums, int numsSize){
    int f=0,max=nums[0];
    for (int i=0;i<numsSize;i++){
        f=fmax(f+nums[i],nums[i]);
        max=fmax(max,f);
    }
    return max;
}
//f用于暂存最大前缀片段和
//max用于暂存最大子数组和

动态转移方程:f(i)=max{ f(i−1)+nums[i] , nums[i]}

前缀片段和的更新:加入nums[i]后,片段和是否变得更大

你可能感兴趣的:(随想录,linux,网络,运维)