题目
20.有效的括号
155.最小栈
225.用队列实现栈
232.用栈实现队列
234.回文链表
844.比较含退格的字符
682.棒球比赛
1047. 删除字符串中的所有相邻重复项
1441.用栈构建数组
1475.商品折扣后的最终价格
1544.整理字符串
1598. 文件夹操作日志搜集器
1614. 括号的最大嵌套深度
150. 逆波兰表达式求值
735. 行星碰撞
1.两数之和
167. 两数之和 II - 输入有序数组
806. 写字符串需要的行数
821. 字符的最短距离
832. 翻转图像
解题代码与分析
20.有效的括号
很简单的栈的运用题,通过左右括号的限制栈的入和出。
bool checkL(char x){
return x=='('||x=='{'||x=='[';
}
bool checkR(char x){
return x==')'||x=='}'||x==']';
}
bool isMatch(char a,char b){
switch(a){
case '(':
return b==')';
case '{':
return b=='}';
case '[':
return b==']';
}
return false;
}
bool isValid(char * s){
int n = strlen(s);
int top = 0;//这里top指向stack的长度
char stack[10005];
for(int i=0;i<n;i++){
if(checkL(s[i])){
stack[top++] = s[i];
}else if(checkR(s[i])){
if(top==0)
return false;
if(isMatch(stack[top-1],s[i]))
top--;
else return false;
}
}
return top==0;
}
155.最小栈
我的做法很朴素,保持最小元素在栈顶即可,只需要对每次入栈操作特殊处理,当栈为空的时候只需要入一个元素,如果栈不为空,则对当前要入的元素进行判断,得出更小的一个元素,然后多入一个元素到栈顶,也就是这个更小的元素,这样也就相当于除了最开始情况下是入一个,其余情况都是两个两个的入栈,所以每次出栈除了最开始的一个情况外都需要
-= 2
。
#define SIZE 15000
typedef struct {
int nums[SIZE];
int top;
} MinStack;
/** initialize your data structure here. */
MinStack* minStackCreate() {
MinStack* obj = malloc(sizeof(MinStack));
obj->top = -1;
return obj;
}
//重点就在于push和pop的处理
void minStackPush(MinStack* obj, int val) {
//当栈空时只入一个,其他情况将栈顶与入栈元素进行比较,保持最小的元素在栈顶,入栈元素在栈顶的下一个。
if(obj->top==-1)
obj->nums[++(obj->top)] = val;
else{
int min = obj->nums[obj->top]>val?val:obj->nums[obj->top];
obj->nums[++(obj->top)] = val;
obj->nums[++(obj->top)] = min;
}
}
void minStackPop(MinStack* obj) {
//若到了最后一个元素,则底下没有其他东西垫底需要特殊处理。
//当栈为空则top不动。
if(obj->top==-1){
return;
}
//确保top指向最小值
if(obj->top==0){
obj->top=-1;
return;
}
obj->top-=2;
}
//关键就在于能改变top指针的操作的设计,而下面两个不能影响top指针,所以很是简单。。
int minStackTop(MinStack* obj) {
if(obj->top==0){
return obj->nums[obj->top];
}
return obj->nums[obj->top-1];
}
int minStackGetMin(MinStack* obj) {
return obj->nums[obj->top];
}
void minStackFree(MinStack* obj) {
free(obj);
}
225.用队列实现栈
用C语言的话,只要对队列的实现较为熟悉应该都好做,我们需要两个队列实现一个栈的话,需要能够熟练的运用好这两个队列,其中一个队列我们作为中转站,也就是,每次如果要执行栈的pop得到栈顶,则我们需要不断的对队列进行pop,然后转移到另一个中转站,直到只剩一个元素,则这个就是栈顶元素,返回这个元素即可。上面这些描述是对栈的pop和top操作中都需要做的操作,以下就是有一些不一样了:
对于pop操作:进行上述操作后,我们需要再把中转站变成要操作的队列(指针交换),注意还需要把这剩下的最后一个元素pop。
对于top操作:进行上述操作后,需要再执行一次push操作,然后再指针交换。
- 结合代码进行观看体验更佳。
#define MAXLEN 20
typedef struct{
int* date;
int front;
int rear;
}queue;
typedef struct {
queue* Q1;
queue* Q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* res = (MyStack*)malloc(sizeof(MyStack));
res->Q1 = (queue*) malloc(sizeof(queue));
res->Q2 = (queue*) malloc(sizeof(queue));
res->Q1->date = (int*)malloc(sizeof(int)*MAXLEN);
res->Q2->date = (int*)malloc(sizeof(int)*MAXLEN);
res->Q1->front = res-> Q1->rear = res->Q2->front = res->Q2->rear = 0;
return res;
}
void myStackPush(MyStack* obj, int x) {
obj->Q1->date[obj->Q1->rear%MAXLEN] = x;//一个栈负责入
obj->Q1->rear++;
}
int myStackPop(MyStack* obj) {
while((obj->Q1->front+1)%MAXLEN != (obj->Q1->rear)%MAXLEN){//把队列push到另外一个队列,只剩下一个元素为止
obj->Q2->date[obj->Q2->rear%MAXLEN] = obj->Q1->date[obj->Q1->front%MAXLEN];
obj->Q2->rear++,obj->Q1->front++;
}
//交换指针内容
queue* t = obj->Q1;
obj->Q1 = obj->Q2;
obj->Q2 = t;
return obj->Q2->date[obj->Q2->front++];
}
int myStackTop(MyStack* obj) {
while((obj->Q1->front+1)%MAXLEN != (obj->Q1->rear)%MAXLEN){//把队列push到另外一个队列,只剩下一个元素为止
obj->Q2->date[obj->Q2->rear%MAXLEN] = obj->Q1->date[obj->Q1->front%MAXLEN];
obj->Q2->rear++,obj->Q1->front++;
}
int res = obj->Q1->date[obj->Q1->front];
obj->Q2->date[obj->Q2->rear%MAXLEN] = obj->Q1->date[obj->Q1->front%MAXLEN];
obj->Q2->rear++,obj->Q1->front++;
//交换指针内容
queue* t = obj->Q1;
obj->Q1 = obj->Q2;
obj->Q2 = t;
return res;
}
bool myStackEmpty(MyStack* obj) {
return obj->Q1->front == obj->Q1->rear;
}
void myStackFree(MyStack* obj) {
free(obj->Q1->date);
obj->Q1->date = NULL;
free(obj->Q2->date);
obj->Q2->date = NULL;
free(obj->Q1);
obj->Q1 = NULL;
free(obj->Q2);
obj->Q2 = NULL;
free(obj);
obj = NULL;
}
232.用栈实现队列
用栈实现队列,单纯的是利用另外一个栈来实现倒序的功能,使得末端变首端,需要注意的是我们只需要一个队列用于push,另一个队列用于 pop 和 front 即可,完全不需要中转站这种操作,需要注意只有当用于 pop 和 front的栈空了,才需要重新补给!
/* 两个栈实现队列, S1,S2 */
/* 入队操作: push元素进入S1 */
/* 出队操作: S2非空时, 从S2弹出元素, S2为空, S1数据导入S2再弹出 */
#define MAXLEN 20
typedef struct{
int* date;
int top;
}stack;
typedef struct {
stack* S1;
stack* S2;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* Q = (MyQueue*)malloc(sizeof(MyQueue));
Q->S1 = (stack*)malloc(sizeof(stack));Q->S2 = (stack*)malloc(sizeof(stack));
Q->S1->date = (int*)malloc(sizeof(int)*MAXLEN);
Q->S2->date = (int*)malloc(sizeof(int)*MAXLEN);
Q->S1->top = Q->S2->top = 0;
return Q;
}
/* push元素时, 只往S1中push */
void myQueuePush(MyQueue* obj, int x) {
obj->S1->date[obj->S1->top++] = x;
}
/* pop元素时, S2非空时, 弹出; S2为空, S1数据导入S2中, 继续从S2弹出数据 */
int myQueuePop(MyQueue* obj) {
if((obj->S2->top)>0)
return obj->S2->date[--(obj->S2->top)];
while(obj->S1->top > 0){
obj->S2->date[obj->S2->top] = obj->S1->date[--(obj->S1->top)];
obj->S2->top++;
}
return obj->S2->date[--(obj->S2->top)];
}
int myQueuePeek(MyQueue* obj) {
if((obj->S2->top)>0)
return obj->S2->date[(obj->S2->top)-1];
while(obj->S1->top > 0){
obj->S2->date[obj->S2->top] = obj->S1->date[--(obj->S1->top)];
obj->S2->top++;
}
return obj->S2->date[obj->S2->top-1];
}
bool myQueueEmpty(MyQueue* obj) {
return obj->S1->top == 0&&obj->S2->top==0;
}
/* 释放申请的空间 */
void myQueueFree(MyQueue* obj) {
free(obj->S1->date);
obj->S1->date = NULL;
free(obj->S2->date);
obj->S2->date = NULL;
free(obj->S1);
obj->S1 = NULL;
free(obj->S2);
obj->S2 = NULL;
free(obj);
obj = NULL;
}
234.回文链表
链表题乱入😀
typedef struct ListNode ListNode;
ListNode* getHalf(ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast->next != NULL && fast->next->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
ListNode* reverseList(ListNode* head) {
ListNode* prev = NULL;
ListNode* curr = head;
while (curr != NULL) { //不断的切断再重组的方式不断迭代形成反转
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev; //返回逆序后的头指针
}
bool isPalindrome(struct ListNode* head) {
if (head == NULL) {
return true;
}
// 找到前半部分链表的尾节点并反转后半部分链表
ListNode* half = getHalf(head);
ListNode* secondHalfStart = reverseList(half->next);
// 判断是否回文
ListNode* p1 = head;
ListNode* p2 = secondHalfStart;
bool result = true;
while (result && p2 != NULL) {
if (p1->val != p2->val) {
result = false;
}
p1 = p1->next;
p2 = p2->next;
}
// 还原链表并返回结果
half->next = reverseList(secondHalfStart);
return result;
}
844.比较含退格的字符
没啥可说的,就简单的运用栈的题目。
#define MAXLEN 205
int solve(char* target,char* stack){
int n = strlen(target);
int top = 0;
for(int i=0;i<n;i++){
if(target[i]!='#')
stack[top++] = target[i];
else{
if(top>0)
top--;
}
}
stack[top] = '\0';
return top;
}
bool backspaceCompare(char * s, char * t){//简单的栈的运用
char stack1[MAXLEN];
char stack2[MAXLEN];
int len1 = solve(s,stack1);
int len2 = solve(t,stack2);
if(len1!=len2)
return false;
return strcmp(stack1,stack2)==0;
}
682.棒球比赛
就从左到右的栈模拟
int calPoints(char ** ops, int opsSize){
int stack[opsSize],top = 0;
for(int i=0;i<opsSize;i++){
if(ops[i][0]=='+'){
int x = stack[top-1]+stack[top-2];
stack[top++] = x;
}
else if(ops[i][0]=='C'){
top--;
}else if(ops[i][0]=='D'){
int x = stack[top-1]*2;
stack[top++] = x;
}else{//为数字的情况
int x = atoi(ops[i]);
stack[top++] = x;
}
}
int res = 0;
for(int i=0;i<top;i++)
res += stack[i];
return res;
}
1047. 删除字符串中的所有相邻重复项
同栈模拟
char * removeDuplicates(char * s){
int n = strlen(s);
char* stack = (char*)malloc(sizeof(char)*n);
int top = 0;
for(int i=0;i<n;i++){
if(top!=0&&stack[top-1]==s[i]){
top--;
}
else
stack[top++] = s[i];
}
stack[top] = '\0';
return stack;
}
1441. 用栈操作构建数组
这个也是通过栈模拟的题,我发现C是可以把直接const char* 的值用char* 来存下的。也就是直接用char** 申请连续的char* 内存便可以用来存再常量区的字符常量地址。当然我还是用的malloc重新申请的堆内存存的。
#define MAXLEN 200
void solve(char** t,const char* date){
char* q = (char*)malloc(sizeof(char)*10);
sprintf(q,"%s",date);
*t = q;
}
char ** buildArray(int* target, int targetSize, int n, int* returnSize){
char** res = (char**)malloc(sizeof(char*)*MAXLEN);
int top = 0;//top记录char**的实时长度
int cnt = 0;//cnt记录已经得到的target中的数字情况
for(int i=1;i<=n;i++){//由于单调递增所以不可能出现每个数不做任何操作的过程
if(target[cnt]==i){
solve(res+top,"Push");
top++;
cnt++;
}else{
solve(res+top,"Push");
top++;
solve(res+top,"Pop");
top++;
}
if(cnt==targetSize)
break;
}
*returnSize = top;
return res;
}
1475.商品折扣后的最终价格
这个根据题目的性质,从右往左更新,然后入栈,只要比当前数字大就pop。
#define MAX_LEN 501
//@栈的基本操作--咋又是单调栈的说。。这次需要从后往前维护一个单调递增的栈
//因为是在 nums[i] 右边的并且距离他最近的小于nums[i]的数,我们只需要从右往左,维护一个比nums[i]小的序列即可--对应的正是单调递增的单调栈!
typedef struct {
int* nums;
int length;
int capacity;
} Stack;
void init(Stack* S) {
S->nums = (int*)malloc(sizeof(int) * MAX_LEN);
S->length = 0;
S->capacity = MAX_LEN;
}
void Push(Stack* S, int val) {
S->nums[S->length++] = val;
}
bool isEmpty(Stack* S) {
return S->length == 0;
}
int Top(Stack* S) {
if (isEmpty(S))
return -1;
return S->nums[S->length - 1];
}
void Pop(Stack* S) {
if (!isEmpty(S)) {
S->length--;
}
}
//@测试接口
int* finalPrices(int* prices, int pricesSize, int* returnSize){
*returnSize = pricesSize;
Stack St;
init(&St);
int* res = (int*)malloc(sizeof(int) * pricesSize);
for (int i = pricesSize - 1; i >= 0; i--) {
while (!isEmpty(&St) && prices[Top(&St)] > prices[i])
Pop(&St);
res[i] = isEmpty(&St) ? prices[i] : prices[i] - prices[Top(&St)];
Push(&St, i);
}
return res;
}
1544.整理字符串
利用栈进行模拟
char * makeGood(char * s){
int n = strlen(s);
char* res = (char*)malloc(sizeof(char)*n+1);
int top = 0;
for(int i=0;i<n;i++){
if(top!=0){//栈不为空的情况
if((res[top-1]^32^s[i])==0)//第一个异或是大小写反转,第二个异或是判断是否相等
top--;
else res[top++] = s[i];
}else{
res[top++] = s[i];
}
}
res[top] = '\0';
return res;
}
1598. 文件夹操作日志搜集器
//分三个情况讨论,是前往下一级,还是返回上一级,还是原地不动
int minOperations(char ** logs, int logsSize){
int cnt = 0;
for(int i=0;i<logsSize;i++){
if(strcmp(logs[i],"../")==0){
if(cnt>0)
cnt--;
}else if(strcmp(logs[i],"./")==0){
continue;
}else{
cnt++;
}
}
return cnt;
}
1614. 括号的最大嵌套深度
int maxDepth(char * s){//由于都是有效括号,那这就很简单了
int n = strlen(s);
char* stack[n+1];
int res = 0,top = 0;
for(int i=0;i<n;i++){
if(s[i]=='('){
stack[top++] = '(';
}else if(s[i]==')'){
top--;
}
res = top>res?top:res;
}
return res;
}
150. 逆波兰表达式求值
int evalRPN(char ** tokens, int tokensSize){//这题典中之典了属于是
int stack[tokensSize+1];
int top = 0;
for(int i=0;i<tokensSize;i++){
if(!strcmp(tokens[i],"+")){
int x = stack[top-2] + stack[top-1];
top -= 2;
stack[top++] = x;
}else if(!strcmp(tokens[i],"-")){
int x = stack[top-2] - stack[top-1];
top -= 2;
stack[top++] = x;
}else if(!strcmp(tokens[i],"*")){
int x = stack[top-2] * stack[top-1];
top -= 2;
stack[top++] = x;
}else if(!strcmp(tokens[i],"/")){
int x = stack[top-2] / stack[top-1];
top -= 2;
stack[top++] = x;
}else{
stack[top++] = atoi(tokens[i]);
}
}
return stack[0];
}
735. 行星碰撞
开始写的是真的冗余。。。
bool isDash(int x,int y){
return x>0&&y<0;
}
int* asteroidCollision(int* asteroids, int asteroidsSize, int* returnSize){
int* stack = (int*) malloc(sizeof(int)*asteroidsSize);
int top = 0;
for(int i=0;i<asteroidsSize;i++){
int t = asteroids[i];
if(top>0&&isDash(stack[top-1],t)){
bool f = false;
while(top>0&&isDash(stack[top-1],t)){
if(abs(stack[top-1])<abs(t)){
top--;
f = true;
}else if(abs(stack[top-1])>abs(t)){
f = false;
break;
}else{
top--;
f = false;
break;
}
}
if(f)
stack[top++] = t;
}
else stack[top++] = t;
}
*returnSize = top;
return stack;
}
后面稍作休整可读性好了很多
int* asteroidCollision(int* asteroids, int asteroidsSize, int* returnSize){
int* stk = (int*)malloc(sizeof(int) * asteroidsSize);
int top = 0;
for (int i = 0; i < asteroidsSize; i++) {
stk[top++] = asteroids[i]; // 入栈
// 处理行星碰撞
while (top >= 2 && stk[top - 1] < 0 && stk[top - 2] > 0) { // 此时才会发生碰撞
int tmp = stk[top - 1] + stk[top - 2];
if (tmp == 0){
top -= 2;
} else if (tmp > 0) {
top--;
} else {
stk[top - 2] = stk[top - 1];
top--;
}
}
}
*returnSize = top;
return stk;
}
1.两数之和
朴素暴力法
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {//两数之和万恶之源
for (int i = 0; i < numsSize; ++i) {
for (int j = i + 1; j < numsSize; ++j) {
if (nums[i] + nums[j] == target) {
int* ret = malloc(sizeof(int) * 2);
ret[0] = i, ret[1] = j;
*returnSize = 2;
return ret;
}
}
}
*returnSize = 0;
return NULL;
}
自己手写简单哈希表解题
#define MAXLEN 49927
#define T 727
/*拉链结点*/
typedef struct Node{
int index;
int val;
struct Node* next;
}node;
/*哈希实体*/
typedef struct{
node* nums;
}Set;
/*初始化*/
Set* init(){
Set* res = (Set*)malloc(sizeof(Set));
res->nums = (node*)malloc(sizeof(node)*MAXLEN);
for(int i=0;i<MAXLEN;i++){
res->nums[i].next = NULL;
}
return res;
}
/*简单的哈希函数。。。*/
int getPos(int val){
int pos = val%MAXLEN;
while(pos<0){
pos = (pos + T)%MAXLEN;
}
return pos;
}
/*查找*/
int find(Set* root,int val){
int pos = getPos(val);
node* t = root->nums[pos].next;
while(t!=NULL){
if(t->val==val)
return t->index;
t = t->next;
}
return -1;
}
/*插入*/
void insert(Set* root,int val,int index){
//根据val找链表插入
int pos = getPos(val);
node* x = (node*)malloc(sizeof(node));
x->val = val;
x->index = index;
x->next = root->nums[pos].next;
root->nums[pos].next = x;
}
/*题目测试接口*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {//两数之和万恶之源
Set* set = init();
for(int i=0;i<numsSize;i++){
int t = find(set,target-nums[i]);
if(t!=-1){
int *ret = (int*)malloc(sizeof(int)*2);
*returnSize = 2;
ret[0] = t;
ret[1] = i;
return ret;
}
insert(set,nums[i],i);
}
*returnSize = 0;
return NULL;
}
167. 两数之和 II - 输入有序数组
枚举+二分
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
for(int i=0;i<numbersSize;i++){
int l = i+1,r = numbersSize-1;
while(l<=r){
int mid = (l+r)/2;
if(numbers[mid]==target-numbers[i]){
int* ret = (int*)malloc(sizeof(int)*2);
*returnSize = 2;
ret[0] = i+1;
ret[1] = mid+1;
return ret;
}
if(numbers[mid]>target-numbers[i]){
r = mid-1;
}else{
l = mid+1;
}
}
}
*returnSize = 0;
return NULL;
}
双指针
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
int l = 0,r = numbersSize-1;
while(l<r){
if(numbers[l]+numbers[r]>target){
r--;
}else if(numbers[l]+numbers[r]<target){
l++;
}else{
int* ret = (int*)malloc(sizeof(int)*2);
*returnSize = 2;
ret[0] = l+1;
ret[1] = r+1;
return ret;
}
}
*returnSize = 0;
return NULL;
}
806. 写字符串需要的行数
简单遍历
int* numberOfLines(int* widths, int widthsSize, char * s, int* returnSize){
int* ret = (int*)malloc(sizeof(int)*2);
*returnSize = 2;
int n = strlen(s);
int sum = 0;
int line = 0;
for(int i = 0;i<n;i++){
if(sum+widths[s[i]-'a']>=100){
line++;
sum = sum+widths[s[i]-'a']>100?widths[s[i]-'a']:0;
continue;
}
sum += widths[s[i]-'a'];
}
ret[0] = sum==0?line:line+1;
ret[1] = sum==0?100:sum;
return ret;
}
821. 字符的最短距离
一次遍历暴力法O(n^2)
直接同时找两头
int find(char* s,char c,int index){
int l = 0,r = 0;
while(index+l>=0||index+r<strlen(s)){
char cmp1 = index+l>=0?s[index+l]:0;
char cmp2 = index+r<strlen(s)?s[index+r]:0;
if(cmp1==c){
return -l;
}
if(cmp2==c){
return r;
}
if(cmp1)l--;
if(cmp2)r++;
}
return -1;
}
int* shortestToChar(char * s, char c, int* returnSize){
int n = strlen(s);
*returnSize = n;
int* res = (int*)malloc(sizeof(int)*n);
int top = 0;
for(int i=0;i<n;i++){
res[i] = find(s,c,i);
}
return res;
}
两次遍历法O(n)
#define INF 0x3f3f3f3f
int* shortestToChar(char * s, char c, int* returnSize){
int n = strlen(s);
*returnSize = n;
int* res = (int*)malloc(sizeof(int)*n);
memset(res,0x3f,sizeof(int)*n);
int prev = INF;
for(int i=0;i<n;i++){
if(s[i]==c){
prev = i;
}
res[i] = res[i]>abs(prev-i)?abs(prev-i):res[i];
}
prev = INF;
for(int i=n-1;i>=0;i--){
if(s[i]==c){
prev = i;
}
res[i] = res[i]>abs(prev-i)?abs(prev-i):res[i];
}
return res;
}
832.翻转图像
按照它的规则来,该怎么做就怎么做,但C语言有一坑:这个int** returnColumnSizes 是外面传过来的数组地址,也就是说我们需要通过这个地址修改数组使得returnColumnSizes[0][i]代表第i行的长度。
void reverse(int* t,int st,int ed){
while(st<ed){
t[st] ^= t[ed];
t[ed] ^= t[st];
t[st] ^= t[ed];
st++,ed--;
}
}
void flip(int* t,int length){
for(int i=0;i<length;i++){
t[i] ^= 1;
}
}
int** flipAndInvertImage(int** image, int imageSize, int* imageColSize, int* returnSize, int** returnColumnSizes){
*returnSize = imageSize;
*returnColumnSizes = imageColSize;
for(int i=0;i<imageSize;i++){
reverse(image[i],0,imageColSize[i]-1);
flip(image[i],imageColSize[i]);
}
return image;
}