前言
每天记录一点,坚持下去,相信遇见不一样的自己。实在没得什么东西写了,有时间在写点小日记吧!
目录
正文
数组
3月27日
今天晚上外面的风很大,不过房间里面既安静又温暖,把所有事情干完,似乎很适合小憩一会?
1491.去掉最低工资和最高工资后的工资平均值(easy)
给你一个整数数组 salary ,数组里每个数都是 唯一 的,其中 salary[i] 是第 i 个员工的工资。
请你返回去掉最低工资和最高工资以后,剩下员工工资的平均值。
示例 1:
输入:salary = [4000,3000,1000,2000] 输出:2500.00000 解释:最低工资和最高工资分别是 1000 和
4000 。 去掉最低工资和最高工资以后的平均工资是 (2000+3000)/2= 2500 示例 2:输入:salary = [1000,2000,3000] 输出:2000.00000 解释:最低工资和最高工资分别是 1000 和 3000
。 去掉最低工资和最高工资以后的平均工资是 (2000)/1= 2000
自写版本:
int compare(const void*a,const void*b){
return (*(int*)a-*(int*)b);
}
double average(int* salary, int salarySize) {
qsort(salary,salarySize,sizeof(int),compare);
int sum=0;
for(int i=1;i<salarySize-1;i++){
sum+=salary[i];
}
return (double)sum/(salarySize-2);//记得把它转换为double类型
}
解题思路
- compare 函数:用于qsort函数的调用
- 排序操作:利用qsort函数将数组从小到大排序
- 计算剩余薪资总和:利用for循环从第二元素开始,一直到倒数第二个元素,将它们累加到sum中
- 计算并返回平均值:将总和 sum 转换为 double 类型,再再除以剩余元素的数量(salarySize - 2)并返回
代码实现
自写版本已经正确
3月31日
还有一天就到4月了,不知道4月份会过得怎么样呢?这个月还没回过家,希望那天假放长一点好让我回去一趟……最近睡得都比较晚,以后争取12点前可以睡觉。
1672.最富有的客户资产总量(easy)
给你一个 m x n 的整数网格 accounts ,其中 accounts[i][j] 是第 i 位客户在第 j 家银行托管的资产数量。返回最富有客户所拥有的 资产总量 。
客户的 资产总量 就是他们在各家银行托管的资产数量之和。最富有客户就是 资产总量 最大的客户。
示例 1:
输入:accounts = [[1,2,3],[3,2,1]] 输出:6 解释: 第 1 位客户的资产总量 = 1 + 2 + 3 = 6
第 2 位客户的资产总量 = 3 + 2 + 1 = 6 两位客户都是最富有的,资产总量都是 6 ,所以返回 6 。 示例 2:输入:accounts = [[1,5],[7,3],[3,5]] 输出:10 解释: 第 1 位客户的资产总量 = 6 第 2
位客户的资产总量 = 10 第 3 位客户的资产总量 = 8 第 2 位客户是最富有的,资产总量是 10 示例 3:输入:accounts = [[2,8,7],[7,1,3],[1,9,5]] 输出:17
自写版本:
int maximumWealth(int** accounts, int accountsSize, int* accountsColSize) {
int max=0;
int sum=0;
for(int i=0;i<accountsSize;i++){ //这里应该是accountsSize-1
sum=0;
for(int j=0;j<accountsColSize[i];j++){ //改为accountsColSize-1
sum+=accounts[i][j];
}
if(max<sum){
max=sum;
}
}
return max;
}
解题思路
-
明确目标:
题目要求是找到所有客户中拥有最大资产总和的那个值。也就是要对二维数组的每一行分别求和,然后找出这些和中的最大值。 -
初始化变量
-
遍历客户(二维数组的行):
使用一个for循环来遍历二维数组的每一行 -
计算当前客户的资产总量:
在遍历每一行(每个客户)时,再使用一个内层循环来遍历该行的每一个元素(每个客户的每个账户存款),并将它们累加到sum中 -
更新最大资产总量:
如果当前sum值大于max,那么更新max的值为当前sum的值 -
返回结果
代码实现
自写版本已经正确
4月3日
最近几天好累,因该是休息少了,要对自己好一点。
1572.矩阵对角线元素和(easy)
给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。
请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。
示例 1:
输入:mat = [[1,2,3],
[4,5,6],
[7,8,9]] 输出:25 解释:对角线的和为:1 + 5 + 9 + 3 + 7 = 25 请注意,元素 mat[1][1] = 5 只会被计算一次。 示例 2:输入:mat = [[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]] 输出:8 示例 3:输入:mat = [[5]] 输出:5
自写版本:
int diagonalSum(int** mat, int matSize, int* matColSize) {
int result=0;
for(int i=0;i<matSize;i++){
result+=mat[i][i];
result+=mat[i][matSize-i-1];
}
if(matSize%2!=0){
result-=mat[matSize/2][matSize/2];
}
return result;
}
解题思路
- 初始化结果:
设立一个变量用于存放最终的总和,初始值设为 0 - 遍历矩阵:
对于每一行,依据主、副对角线元素的索引规律获取对应元素,将每次获取到的主、副对角线元素累加到结果变量中。 - 处理重复元素:
判断矩阵的行数是否为奇数。若为奇数,主、副对角线存在一个公共元素,在之前的累加过程中该元素被加了两次,所以要从结果中减去这个元素一次。 - 返回结果
代码实现
自写版本已经正确
4月6日
明天就是周一了,又要开始忙忙忙了,今天晚上要早点睡。
1275.找出井字棋的获胜者(easy)
井字棋 是由两个玩家 A 和 B 在 3 x 3 的棋盘上进行的游戏。井字棋游戏的规则如下:
玩家轮流将棋子放在空方格 (’ ') 上。 第一个玩家 A 总是用 ‘X’ 作为棋子,而第二个玩家 B 总是用 ‘O’ 作为棋子。 ‘X’
和 ‘O’ 只能放在空方格中,而不能放在已经被占用的方格上。 只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
如果所有方块都放满棋子(不为空),游戏也会结束。 游戏结束后,棋子无法再进行任何移动。 给你一个数组 moves,其中 moves[i] =
[rowi, coli] 表示第 i 次移动在 grid[rowi][coli]。如果游戏存在获胜者(A 或
B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。你可以假设 moves 都 有效(遵循 井字棋 规则),网格最初是空的,A 将先行动。
示例 1:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]] 输出:“A” 解释:“A” 获胜,他总是先走。
自写版本:
#define SIZE 3
int checkWin(char board[SIZE][SIZE], char player){
for(int i=0;i<SIZE;i++){
if(board[i][0]==player&&board[i][1]==player&&board[i][2]==player){
return 1;
}
}
for(int j=0;j<SIZE;j++){
if(board[0][j]==player&&board[1][j]==player&&board[2][j]==player){
return 1;
}
}
if(board[0][0]==player&&board[1][1]==player&&board[2][2]==player){
return 1;
}
if(board[0][2]==player&&board[1][1]==player&&board[2][0]==player){
return 1;
}
return 0;
}
int isBoardFull(char board[SIZE][SIZE]){
for(int i=0;i<SIZE;i++){
for(int j=0;j<SIZE;j++){
if(board[i][j]==' '){
return 0;
}
}
}
return 1;
}
char* tictactoe(int** moves, int movesSize, int* movesColSize) {
char board[SIZE][SIZE];
for(int i=0;i<SIZE;i++){
for(int j=0;j<SIZE;j++){
board[i][j]=' ';
}
}
for(int i=0;i<movesSize;i++){
int row=moves[i][0];
int col=moves[i][1];
if(i%2==0){
board[row][col]='X';
}else{
board[row][col]='O';
}
if(i>=4){
if(checkWin(board,'X')){
return "A";
}
if(checkWin(board,'O')){
return "B";
}
}
}
if(isBoardFull(board)){
return "Draw";
}
return "Pending";
}
解题思路
整体思路:
游戏结果可能是玩家 A 获胜、玩家 B 获胜、平局或者游戏尚未结束。代码借助三个关键函数来达成这一目标:checkWin 用于检查某个玩家是否获胜,isBoardFull 用于检查棋盘是否已满,tictactoe 作为主函数,负责模拟整个游戏过程并给出最终结果。
- checkWin 函数
- 行检查
- 列检查
- 对角线检查
- isBoardFull 函数
- 遍历棋盘
- 判断空位:若发现某个位置为空(用空格表示),就返回 0 表示棋盘未满。
- 棋盘已满
- tictactoe 函数
- 初始化棋盘:
创建一个 SIZE x SIZE 的二维字符数组 board 来表示棋盘,并且将所有位置初始化为空格。 - 模拟落子:
借助循环遍历玩家的每一次落子,依据落子的顺序来判断是玩家 A 还是玩家 B 的落子。偶数次落子为玩家 A(用 ‘X’ 表示),奇数次落子为玩家 B(用 ‘O’ 表示)。 - 检查获胜条件:
从第 5 步开始,每次落子后都检查玩家 A 和玩家 B 是否获胜。若有玩家获胜,就返回相应的结果(“A” 或 “B”)。 - 检查平局:
若所有落子都完成后仍无玩家获胜,就检查棋盘是否已满。若棋盘已满,就返回 “Draw” 表示平局。 - 游戏未结束
代码实现
本代码比较长,所以自写版本是一段一段敲出来的,已经是正确的了。
贪心
3月30日
很普通的一天,呆在宿舍里面做作业、背单词,希望晚上之前把任务全部干完。
860.柠檬水找零(easy)
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5
美元。注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回
false 。示例 1:
输入:bills = [5,5,5,10,20] 输出:true 解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 第
4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5
美元的钞票。 由于所有客户都得到了正确的找零,所以我们输出 true。 示例 2:输入:bills = [5,5,10,10,20] 输出:false 解释: 前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 对于最后一位顾客,我们无法退回 15
美元,因为我们现在只有两张 10 美元的钞票。 由于不是每位顾客都得到了正确的找零,所以答案是 false。
自写版本:
bool lemonadeChange(int* bills, int billsSize) {
int five=0;
int ten=0;
for(int i=0;i<billsSize;i++){
if(bills[i]==5){
five++;
}else if(bills[i]==10){
if(five>0){
five--;
ten++;
}else{
return 0;
}
}else{
if(five>0&&ten>0){
five--;
ten--;
}else if(five>=3){
five-=3;
}else{
return 0;
}
}
}
return 1;
}
解题思路
- 数据统计:
要记录手中 5 美元和 10 美元钞票的数量,因为 20 美元无法用于找零,所以无需记录。 - 遍历支付序列
- 不同面额支付的处理:
-
5 美元支付:
顾客支付 5 美元时,无需找零,只需增加手中 5 美元的数量。 -
10 美元支付:
若手中有 5 美元,就用一张 5 美元找零,并增加手中 10 美元的数量。若手中没有 5 美元,就无法完成找零,判断失败。
-
20 美元支付:
优先选择一张 10 美元和一张 5 美元进行找零,因为这样能保留更多的 5 美元,以应对后续可能的 10 美元找零。若没有 10 美元和 5 美元的组合,再考虑用三张 5 美元找零。
若既没有 10 美元和 5 美元的组合,也没有三张 5 美元,就无法完成找零,判断失败。
代码实现
自写版本已经正确。本题实际上很简单,只要数学到位,还是很容易写出来的
栈
4月1
今天过得老快了,晚自习弄智能体,老实说,这比赛其实没什么含金量吧。还是多吧注意力放在提升自己编程的能力上更好。
232.用栈实现队列(easy)
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek()
返回队列开头的元素 boolean empty() 如果队列为空,返回 true ;否则,返回 false 说明:你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is
empty 操作是合法的。 你所使用的语言也许不支持栈。你可以使用 list 或者
deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。示例 1:
输入: [“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”] [[], [1], [2],
[], [], []] 输出: [null, null, null, 1, 1, false]
自写版本:
typedef struct {
int stackInTop,stackOutTop;
int stackIn[100],stackOut[100];
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* queue=(MyQueue*)malloc(sizeof(MyQueue));
queue->stackInTop=0;
queue->stackOutTop=0;
return queue;
}
void myQueuePush(MyQueue* obj, int x) {
obj->stackIn[(obj->stackInTop)++]=x;
}
int myQueuePop(MyQueue* obj) {
int stackInTop=obj->stackInTop;
int stackOutTop=obj->stackOutTop;
if(stackOutTop==0){
while(stackInTop>0){
obj->stackOut[stackOutTop++]=obj->stackIn[--stackInTop];
}
}
int top=obj->stackOut[--stackOutTop];
obj->stackInTop=stackInTop;
obj->stackOutTop=stackOutTop;
return top;
}
int myQueuePeek(MyQueue* obj) {
int stackInTop=obj->stackInTop;
int stackOutTop=obj->stackOutTop;
if(stackOutTop==0){
while(stackInTop>0){
obj->stackOut[stackOutTop++]=obj->stackIn[--stackInTop];
}
}
return obj->stackOut[stackOutTop-1];
}
bool myQueueEmpty(MyQueue* obj) {
return obj->stackInTop==0&&obj->stackOutTop==0;
}
void myQueueFree(MyQueue* obj) {
free(obj);
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
解题思路
- 数据结构设计:
我们需要定义两个栈,一个栈 s1 用于入队操作,另一个栈 s2 用于出队操作。 - 入队操作:
入队操作很简单,直接将元素压入 s1 栈即可。 - 出队操作:
检查 s2 是否为空:如果 s2 为空,需要将 s1 中的元素全部转移到 s2 中。
- 转移元素:将 s1 中的元素依次弹出并压入 s2,这样 s2 中的元素顺序就与 s1 相反,即最早入队的元素位于 s2 的栈顶。
- 弹出元素:从 s2 中弹出栈顶元素,该元素就是最早入队的元素,符合队列的先进先出原则。
- 获取队首元素:
获取队首元素的操作与出队操作类似,只是不弹出元素。同样先检查 s2 是否为空,如果为空则将 s1 中的元素转移到 s2 中,然后返回 s2 的栈顶元素。 - 检查队列是否为空:
要判断队列是否为空,只需检查 s1 和 s2 是否都为空。如果两个栈都为空,说明队列中没有元素;否则,队列不为空。
代码实现
本代码比较长,一次性敲出来显然不太可能额,所以自写版本是一段一段敲出来的,已经是正确的了。
4月5日
心情有些乱糟糟的,不过并无大碍,美好的明天在等着我。
682.棒球比赛(easy)
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x - 表示本回合新获得分数 x “+” -
表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。 “D” -
表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。 “C” -
表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。 请你返回记录中所有得分的总和。示例 1:
输入:ops = [“5”,“2”,“C”,“D”,“+”] 输出:30 解释: “5” - 记录加 5 ,记录现在是 [5] “2” -
记录加 2 ,记录现在是 [5, 2] “C” - 使前一次得分的记录无效并将其移除,记录现在是 [5]. “D” - 记录加 2 * 5
= 10 ,记录现在是 [5, 10]. “+” - 记录加 5 + 10 = 15 ,记录现在是 [5, 10, 15]. 所有得分的总和 5 + 10 + 15 = 30
自写版本:
int calPoints(char** operations, int operationsSize) {
int* scores=(int*)malloc(operationsSize*sizeof(int));
int top=-1;
for(int i=0;i<operationsSize;i++){
if(strcmp(operations[i],"+")==0){
scores[++top]=scores[top-1]+scores[top-2]; //正确写法在这里为top++; scores[top]=scores[top-1]+scores[top-2]; scores[++top]容易越界
}else if(strcmp(operations[i],"D")==0){
scores[++top]=scores[top-1]*2;//正确写法在这里为top++; scores[top]=scores[top-1]*2;正确原因同上
}else if(strcmp(operations[i],"C")==0){
top--;
}else{
scores[++top]=atoi(operations[i]);
}
}
int sum=0;
for(int i=0;i<top;i++){ //i<=top,否则漏掉了最后一个
sum+=scores[i];
}
free(scores);
return sum;
}
解题思路
- 用「栈」来记录每一轮的有效分数
- 遍历操作数组,对每种操作做对应处理
- 最后数组中的结果累加起来
代码实现
int calPoints(char** operations, int operationsSize) {
int *scores = (int*)malloc(operationsSize * sizeof(int));
int top = -1;
for (int i = 0; i < operationsSize; i++) {
if (strcmp(operations[i], "+") == 0) {
if (top >= 1) {
top++;
scores[top] = scores[top - 1] + scores[top - 2];
}
} else if (strcmp(operations[i], "D") == 0) {
if (top >= 0) {
top++;
scores[top] = 2 * scores[top - 1];
}
} else if (strcmp(operations[i], "C") == 0) {
if (top >= 0) {
top--;
}
} else {
top++;
scores[top] = atoi(operations[i]);
}
}
int sum = 0;
for (int i = 0; i <= top; i++) {
sum += scores[i];
}
free(scores);
return sum;
}
4月7日
今天天气不错,但我好像有点感冒,总是流鼻涕,但总的来说还算不错。
225.用队列实现栈(easy)
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。 int pop() 移除并返回栈顶元素。 int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。注意:
你只能使用队列的标准操作 —— 也就是 push to back、peek/pop from front、size 和 is empty
这些操作。 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 ,
只要是标准的队列操作即可。示例:
输入: [“MyStack”, “push”, “push”, “top”, “pop”, “empty”] [[], [1], [2],
[], [], []] 输出: [null, null, null, 2, 2, false]
自写版本:
typedef struct QueueNode{
int val;
struct QueueNode* next;
}QueueNode;
typedef struct{
QueueNode* front;
QueueNode* rear;
int size;
}Queue;
QueueNode* createQueueNode(int val) {
QueueNode*newNode=(QueueNode*)malloc(sizeof(QueueNode));
newNode->val=val;
newNode->next=NULL;
return newNode;
}
Queue* createQueue() {
Queue* queue=(Queue*)malloc(sizeof(Queue));
queue->front=NULL;
queue->rear=NULL;
queue->size=0;
return queue;
}
void enqueue(Queue* queue, int val){
QueueNode* newNode=createQueueNode(val);
if(queue->size==0){
queue->front=newNode;
queue->rear=newNode;
}else{
queue->rear->next=newNode;
queue->rear=newNode;
}
queue->size++;
}
int dequeue(Queue* queue){
if(queue->size==0){
return -1;
}
int val=queue->front->val;
QueueNode* temp=queue->front;
queue->front=queue->front->next;
if(queue->front==NULL){
queue->rear=NULL;
}
free(temp);
queue->size--;
return val;
}
int peek(Queue* queue){
if(queue->size==0){
return -1;
}
return queue->front->val;
}
bool isQueueEmpty(Queue* queue){
return queue->size==0;
}
typedef struct {
Queue*queue;
} MyStack;
MyStack* myStackCreate() {
MyStack* stack=(MyStack*)malloc(sizeof(MyStack));
stack->queue=createQueue();
return stack;
}
void myStackPush(MyStack* obj, int x) {
int size=obj->queue->size;
enqueue(obj->queue,x);
for(int i=0;i<size;i++){
enqueue(obj->queue,dequeue(obj->queue));
}
}
int myStackPop(MyStack* obj) {
return dequeue(obj->queue);
}
int myStackTop(MyStack* obj) {
return peek(obj->queue);
}
bool myStackEmpty(MyStack* obj) {
return isQueueEmpty(obj->queue);
}
void myStackFree(MyStack* obj) {
while(!isQueueEmpty(obj->queue)){
dequeue(obj->queue);
}
free(obj->queue);
free(obj);
}
/**
* Your MyStack struct will be instantiated and called as such:
* MyStack* obj = myStackCreate();
* myStackPush(obj, x);
* int param_2 = myStackPop(obj);
* int param_3 = myStackTop(obj);
* bool param_4 = myStackEmpty(obj);
* myStackFree(obj);
*/
解题思路
- 队列操作:
- 创建队列节点(createQueueNode):为新节点分配内存,初始化节点的值和 next 指针。
- 创建队列(createQueue):为队列分配内存,初始化队列的 front、rear 和 size。
- 入队(enqueue):创建新节点,若队列为空,将新节点设为队列的 front 和 rear;否则,将新节点添加到 rear 之后,并更新 rear。
- 出队(dequeue):若队列为空,返回 -1;否则,保存队首元素的值,更新 front 指针,若队列为空则更新 rear 指针,释放原队首节点的内存,返回保存的值。
- 查看队首元素(peek):若队列为空,返回 -1;否则,返回队首元素的值。
- 判断队列是否为空(isQueueEmpty):通过比较 size 是否为 0 来判断。
2.栈操作:
- 创建栈(myStackCreate):为栈分配内存,为栈内的队列分配内存,返回栈的指针。
- 入栈(myStackPush):先记录队列当前的元素数量 size,将新元素入队,然后将队列中已有的 size 个元素依次出队再入队,这样新元素就位于队列的最前面,模拟了栈的后进先出特性。
- 出栈(myStackPop):直接调用队列的出队操作,返回出队元素的值。
- 查看栈顶元素(myStackTop):直接调用队列的查看队首元素操作,返回队首元素的值。
- 判断栈是否为空(myStackEmpty):调用队列的判断队列是否为空操作,返回判断结果。
- 释放栈(myStackFree):不断调用队列的出队操作,直到队列为空,释放队列的内存,再释放栈的内存。
代码实现
代码较长,一个函数一个函数的敲出来的,自写版本经过测试,已经正确。
小结
两周转瞬即逝,又到了发布的和准备汇报的时候了,接下来的目标可能除了刷题想学点别的东西,比如,做个网站?
、