使用栈思想算法相关练习

184 阅读12分钟

1、数制转换,将十进制的数值转换成十六进制及以下的进制

#include <stdlib.h>
/**
 思路:利用栈的先进后出特性
 */
void NumSwitch(int num, int targeNum){
    if (targeNum>16) {
        printf("⚠️该算法仅支持十六进制及以下的转换\n");
        return;
    }
    int maxSize = 20;
    //开辟动态数组
    char *result = (char *)malloc(sizeof(char)*maxSize);
    //栈顶
    int top = -1;
    while (num) {
        if (top == 20 - 1) {//当数组空间满了再扩容
            result = realloc(result, sizeof(char)*(maxSize += 10));
        }
        top++;
        int r = num % targeNum;
        switch (r) {
            case 10:
                result[top] = 'a';
                break;
            case 11:
                result[top] = 'b';
                break;
            case 12:
                result[top] = 'c';
                break;
            case 13:
                result[top] = 'd';
                break;
            case 14:
                result[top] = 'e';
                break;
            case 15:
                result[top] = 'f';
                break;
                
            default:
                result[top] = r + '0';
                break;
        }
        num = num / targeNum;
    }
    printf("转换后的值为:");
    while (top>-1) {
        printf("%c",result[top]);
        top--;
    }
    printf("\n");
}


2、括号匹配检测

假设表达式中允许包含两种括号:圆括号与⽅括号,其嵌套顺序随意,即([]()) 或者[([][])]都是正
确的.⽽这[(]或者(()])或者([()) 都是不正确的格式. 检验括号是否匹配的⽅法可⽤期待的急迫程度"这个概念来描述.例如,考虑以下括号的判断: [ ( [ ] [ ] ) ] 

/**
 思路:
        1、将字符串第一个字符入栈
        2、从第二个字符开始遍历字符串,定义为e = data[i],每次循环取出栈顶元素
        3、当栈顶元素为[、{ 、( 左括号时分别判断,以  [  为例子:
        ①若果e  != ]时,则e入栈,否则则出栈
        ②其余括号如 [
        4、当栈顶元素不为这三种左括号时(也就是栈顶元素不存在)则直接入栈
        5、当循环结束时,如果栈为空栈,则括号匹配成功,否则失败
 */
Status MatchStack(SqStack stack, char *data){
    PushStack(&stack, data[0]);
    int i = 1;
    while (i<strlen(data)) {
        //栈顶元素
        char top = GetStackElemData(stack);
        char e = data[i];
        switch (top) {
            case '[':
            {
                if (e != ']') {//入栈
                    PushStack(&stack, e);
                }else{
                    PopStack(&stack);
                }
            }
                break;
            case '{':
            {
                if (e != '}') {//入栈
                    PushStack(&stack, e);
                }else{
                    PopStack(&stack);
                }
            }
                break;
            case '(':
            {
                if (e != ')') {//入栈
                    PushStack(&stack, e);
                }else{
                    PopStack(&stack);
                }
            }
                break;
            case '0':
            {
                PushStack(&stack, e);
            }
                break;
                
            default:
                break;
        }
        i++;
    }
    if (EmptyStack(stack)) {
        printf("括号匹配正确\n");
        return ERROR;
    }else{
        printf("括号匹配错误\n");
        return OK;
    }
}

3、杨辉三角的实现

#include <stdio.h>
#include <stdlib.h>
/**
 思路:使用二维数组处理 result[i][j]
        1、杨辉三角的第一列都是1,即result[0][j] = 1;
        2、当i == j时,result[i][j] = 1;
        3、根据杨辉三角的性质我们发现 result[i][j] = result[i-1][j-1] + result[i-1][j];
 */
int **YangTrigonometry(int rowNum){
    int **result = (int **)malloc(sizeof(int *)*rowNum);
    for (int i = 0; i<rowNum; i++) {
        //第i行开辟i+1个空间
        result[i] = (int *)malloc(sizeof(int)*(i+1));
        result[i][0] = 1;
        result[i][i] = 1;
        for (int j = 1; j<i; j++) {
            result[i][j] = result[i-1][j-1] + result[i-1][j];
        }
    }
    return result;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    int **result = YangTrigonometry(6);
    for (int i = 0; i<6; i++) {
        for (int j = 0; j<i; j++) {
            printf("%4d",result[i][j]);
        }
        printf("\n");
    }
    return 0;
}

4、爬楼梯问题

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不
同的⽅法可以爬到楼顶呢?注意:给定 n 是⼀个正整数

/**
 递归算法:
 1、要计算n有多少个方法就要先计算n-1和n-2
 2、当n<=2时结束递归, n == 1 return 1; n == 2 return 2;
 */
long long GetNum(int n){
    if (n <= 2) {
        if (n == 1) {
            return 1;
        }
        return 2;
    }else{
        return GetNum(n-2) + GetNum(n - 1);
    }
}

/**
 动态规划算法
 1、n == 1时,有1种,n == 2时,有2种
 2、n == 3时,有 1 + 2 = 3种,即f(3) = f(1) + f(2)
 3、n == 4时,有 2 + 3 = 5种,即f(4) = f(2) + f(3),因此可推算出f(n) = f(n - 1) + f(n - 2),其中n>2
 */
long long GetNum2(int n){
    long long sum = 0;
    if (n == 1) {
        sum = 1;
    }else if (n == 2){
        sum = 2;
    }else{
        int *result = (int*)malloc(sizeof(int)*n);
        result[0] = 1;
        result[1] = 2;
        for (int i = 2; i<n; i++) {
            result[i] = result[i - 1] + result[i - 2];
        }
        sum = result[n-1];
        free(result);
    }
    return sum;
}

5、气温问题

根据每⽇⽓温列表,请重新⽣成⼀个列表,对应位置的输⼊是你需要再等待多久温度才
会升⾼超过该⽇的天数。如果之后都不会升⾼,请在该位置0来代替。例如,给定⼀个列
表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1,
1, 0, 0]。提示:⽓温 列表⻓度的范围是 [1, 30000]。每个⽓温的值的均为华⽒度,都是
在 [30, 100] 范围内的整数

/**
思路:
       1、第一层循环历气温数组,从i= 0开始,到length-1结束,因为最后一个气温不会出现比它高的
       2、第二层循环 j 从 j = i + 1开始,直到 j = length 结束
       4、如果list[j]>list[i],则找到了比list[i]高的气温,此时reslut[i] = j  - i,跳出二层循环; 否则result[i] = 0;
*/

void RankTemperatures1(int *list, int length){
    int *reslut = (int *)malloc(sizeof(int)*length);
    reslut[length - 1] = 0;
    for (int i = 0; i<length - 1; i++) {
        if (i > 0 && list[i] == list[i - 1]) {
            reslut[i] = reslut[i - 1] == 0 ? 0 : reslut[i - 1] - 1;
        }else{
            for (int j = i + 1; j<length; j++) {
                if (list[i] < list[j]) {
                    reslut[i] = j - i;
                    break;
                }else{
                    if (j == length - 1) {
                        reslut[i] = 0;
                        break;
                    }
                    //或者直接reslut[i] = 0;
                }
            }
        }
    }
    printf("计算结果是:");
       for (int i = 0; i<length; i++) {
           printf("%2d",reslut[i]);
       }
       printf("\n");
}
/**
    跳跃法
 思路:
        1、第一层循环从右到左遍历气温数组,最后一天气温不会升高,result[length] = 0;
        2、第一层循环从倒数第二个数据开始遍历,即 i = length - 2,直到 i== 0结束
        3、第二层循环 j 从 j = i + 1开始,直到 j = length - 1结束
        4、如果list[j]>list[i],则找到了比list[i]高的气温,此时reslut[i] = j  - i,跳出二层循环; 否则result[i] = 0;
 */
void RankTemperatures2(int *list, int length) {
    int *reslut = (int *)malloc(sizeof(int)*length);
    reslut[length - 1] = 0;
    for (int i = length - 2; i>=0; i--) {
        for (int j = i + 1; j<length; j++) {
            if (list[i] < list[j]) {
                reslut[i] = j - i;
                break;
            }else{
                if (reslut[j] == 0) {//表明没有找到比list[i]高的气温
                    reslut[i] = 0 ;
                    break;
                }
                //或者
//                reslut[i] = 0;
            }
        }
    }
    printf("计算结果是:");
    for (int i = 0; i<length; i++) {
        printf("%2d",reslut[i]);
    }
    printf("\n");
}
/**
 当前算法逻辑参考,有具体讲解 https://leetcode-cn.com/problems/daily-temperatures/solution/leetcode-tu-jie-739mei-ri-wen-du-by-misterbooo/
 思路: 栈中需要记录气温以及该气温在气温列表中的位置
        1、开辟reslut数组记录相差天数
        1、遍历气温数组,将reslut[i]先设置成0,记录当前气温为temp = list[i]
        2、判断栈是否为空栈,如果不是空栈:
            ①、取出栈顶top,当栈顶top的气温小于temp时,计算当前 i 与栈顶top气温位置的差值,此差值即栈顶气温下一个超过它的天数
            ②、满足①,出栈
            ③、循环①②,直到栈为空
        3、每个气温元素都要入栈
 */
void RankTemperatures3(int *list, int length){
    SqStack stack;
    InitStack(&stack);
    //给结果先赋值为0
    int *result = (int *)malloc(sizeof(int)*length);
    //遍历数组
    for (int i = 0; i<sizeof(list); i++) {
        printf("当前值为:%d\n",i);
        result[i] = 0;
        int temp = list[i];
        while (!EmpthStack(stack)) {//如果栈不为空,取出栈顶元素
            ListSNode top = GetTopNodeStack(&stack);
            if (!top) {
                continue;
            }
            if (temp > top->data) {//如果当前数组元素比栈顶元素的值大,则表示找到了气温高的
                //计算天数差别
                result[top->index] = i - top->index;
                //找到后出栈
                PopStck(&stack);
            }else{
                break;
            }
        }
        //将数组中气温入栈,栈中记录元素的位置i
        PushStack(&stack, temp, i);
    }
    printf("结果是:");
    for (int i = 0; i<sizeof(&result); i++) {
        printf("%2d",result[i]);
    }
    printf("\n");
}

6、字符串编码问题

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

例如:

s = "3[a]2[bc]", 返回 "aaabcbc".

s = "3[a2[c]]", 返回 "accaccacc".

s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".

/**
 方法一
 思路:
        1、遍历字符串list,获得复制的次数count
        2、使用result保存出栈之前保存的字符串
        3、遇到 ‘[’则入栈
        4、遇到 ‘]’ 则出栈,出栈时要复制字符串,并将复制后的字符串拼接到原来result后面
        5、若字符不是 ‘[’ 也不是 ']' ,则直接拼接到result后面
 */
Status DecodeString(ModelStack *stack, char *list){
    SElemType result = (SElemType)malloc(sizeof(char)*(MAXSIZE));
    *result = '\0';
    int count = 0;
    for (int i = 0; i<strlen(list); i++) {
        char data = list[i];
        if (data >= '0' && data <= '9') {
            count = 10 * count + data - '0';
        }else if (data == '['){//入栈
            ModelNode node = (ModelNode)malloc(sizeof(StrackNode));
            node->next = NULL;
            //将解码后(也可能是空)的字符串入栈保存起来
            node->data = result;
            node->count = count;
            PushStack(stack, node);
            //重置count、result
            count = 0;
            //每次入栈后需要给result重新开辟空间,否则下次入栈的信息会把上次的覆盖掉
            result = (SElemType)malloc(sizeof(char)*(MAXSIZE));
            *result = '\0';
        }else if (data == ']'){//出栈
            ModelNode node = PopStack(stack);
            if (node) {
                //这是当前最深的或[]里的字符串,比如3[a]2[bc]里面的bc或者3[a2[c]]中c
                SElemType cur = result;
                //取出栈中保存的解码后字符串(这个字符串需要放在前面的)
                result = (node->data);
                SElemType temp = (SElemType)malloc(sizeof(char)*MAXSIZE);
                *temp = '\0';
                if (node->count>0) {
                    for (int i = 0; i<node->count; i++) {//获得字符串拼接
                        strcat(temp, cur);
                    }
                }else{
                    strcat(temp, cur);
                }
                //将出栈后拼接完成的字符串拼接到result上
                strcat(result, temp);
                //释放结点node
                free(node);
            }
        }else{
            //将[]里面的字符串拼接成一个字符串result,比如3[a]2[bc]中的bc拼接成result
            strncat(result, &data, 1);
        }
    }
    printf("结果的值为:");
    for (int i = 0; i<strlen(result); i++) {
        printf("%c",result[i]);
    }
    printf("\n");
    return OK;
}

/**
 方法二
 思路:以字符串list = "13[abc]"为例
        1、遍历字符串list中的字符,当没有遇到 ']' 时,字符均入栈stack
        2、当遇到字符 ‘[’ 时取出stack栈中的字符串
        ①当遇到字符 ‘[’ 时先停止出栈,stack栈中元素从栈顶到栈底依次为c、b、a、[、3、1
        ②将c、b、a存储到新的栈中temp,temp栈中元素从栈顶到栈底依次为a、b、c
        ③取出stack栈中需要复制字符串的个数n,比如n=13,此时stack栈已经为空了
        ④将temp栈中的元素取出并复制n遍后入栈stack
        ⑤释放临时栈temp存储空间
        3、stack中追加结束字符 ‘\0’,此时stack栈中的内容就是编码后的字符串
 */
char *GetDecodeString(char *list){
    if (!list) {
        return list;
    }
    int stackSize = 20;
    char *stack = (char *)malloc(sizeof(char)*stackSize);
    int top = -1;
    //字符串长度
    int length = (int)strlen(list);
    for (int i = 0; i<length; i++) {
        char s = list[i];
        if (s != ']') {//入栈
            if (top == stackSize - 1) {//存储空间不够,扩容
                stack = realloc(stack, sizeof(char)*(stackSize += stackSize));
            }
            stack[++top] = s;
        }else{//出栈
            /*            取[]之间的字符串,并保存到临时栈temp中*/
            int tempSize = 10;
            char *temp = (char *)malloc(sizeof(char)*tempSize);
            int tempTop = -1;
            while (stack[top] != '[') {//只取【之前的字符,不要数字
                if (tempTop == tempSize - 1) {//临时栈temp存储空间不够,则扩容
                    temp = realloc(temp, sizeof(char)*(tempSize += tempSize));
                }
                temp[++tempTop] = stack[top];
                top--;
            }
            /*      取[之前的数字    */
            char num[11];
            //记录数字的上限
            int stopNumTop = top;
            //此时top指向是'[',所以top--
            top--;
            //找到数字的下限
            while (top != -1 && stack[top] >= '0' && stack[top] <= '9') {
                top--;
            }
            //此时top多减了1
            for (int j = top + 1; j<stopNumTop; j++) {
                num[j - (top + 1)] = stack[j];
            }
            //给数字添加结束符
            num[stopNumTop - (top + 1)] = '\0';
            //将字符串数据转成int
            int copyNum = atoi(num);
            if (copyNum>0) {
                for (int k = 0; k < copyNum; k++) {
                
                    int topTemp = tempTop;
                    while (topTemp != -1) {
                        stack[++top] = temp[topTemp];
                        topTemp--;
                    }
                }
            }else{
                if (tempTop > -1) {
                    int topTemp = tempTop;
                    while (topTemp != -1) {
                        stack[++top] = temp[topTemp];
                        topTemp--;
                    }
                }
            }
            //释放temp栈内存
            free(temp);
            temp = NULL;
        }
    }
    //调整动态内存空间,减少浪费
    char *reslut = realloc(stack, sizeof(char)*(top+1));
    top++;
    reslut[top] = '\0';
    return reslut;
    
}



7、字符串去重并且字典序最小问题

给你⼀个仅包含⼩写字⺟的字符串,请你去除字符串中重复的字⺟,使得每个字⺟只出现⼀
次。需保证返回结果的字典序最⼩(要求不能打乱其他字符的相对位置)

例如:''cbacdcbc’',返回''acdb'';

例如:"bcabc",返回"abc"

/**
 思路
        1、给字符串list中所有的字符添加出现次数
        2、每次遍历字符时判断是否已经出现了,如果已经出现则给出现的次数减一
        3、如果没有出现过
         ①判断栈是否不为空,如果不为空,且栈顶元素大于list[i]字符,且该栈顶元素出现的次数大于1,则表示后面还有栈顶元素出现,那么久出栈,并且栈顶元素出现的次数减一
         ②如果①没有满足,则入栈
        4、给栈顶添加结束符
        5、栈中元素即去重后的字典序最小的字符串
 */
char *GetMinString(char *list){
    if (strlen(list) == 0) {
        return "";
    }else if (strlen(list) == 1){
        return list;
    }
    int length = (int)strlen(list);
    char *resultStack = (char*)malloc(sizeof(char)*(length + 2));
    //showCount用来记录每个字符出现的次数
    int showCount[26] = {0};
    for (int i = 0; i<length; i++) {
        //list[i] - 'a'可以计算出唯一的位置值,可以称为n,当这个n同的时候表示出现了,所以要++
        showCount[list[i] - 'a']++;
    }
    //栈顶位置
    int top = -1;
    char temp;
    for (int i = 0; i<length; i++) {
        temp = list[i];
        bool isExist = false;
        for (int j = 0; j<length; j++) {
            if (temp == resultStack[j]) {//当栈中出现了该元素
                isExist = true;
                break;
            }
        }
        if (isExist) {//如果出现过,记录出现的次数要减一
            showCount[temp - 'a']--;
        }else{
            while (top>-1 && //栈非空
                   resultStack[top]>temp && //栈顶的元素大于temp
                   showCount[resultStack[top] - 'a']>1) { //数组list后面还有栈顶元素
                //栈顶元素出栈
                top--;
                //跳过该元素,记录栈顶元素出现的次数减一
                showCount[resultStack[top] - 'a']--;
            }
            //栈顶++,入栈
            top++;
            resultStack[top] = temp;
        }
    }
    //栈顶位置添加结束符
    resultStack[++top] = '\0';
    return resultStack;
}