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
c
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 字符组成。- 每个块注释都会被闭合。
- 给定的源码中不会有单引号、双引号或其他控制字符。
c
#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
c
//回溯函数,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
l1
和l2
均按 非递减顺序 排列
c
/**
* 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;
}
}/*主要用递归继续合并*/
c
/*以前的做法*/
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
c
/**
* 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 码表中的可打印字符
c
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
c
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 最大子数组和】的变种
c
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]后,片段和是否变得更大