数据结构与算法——练习题2

282 阅读9分钟

1. 括号匹配检验

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

解题思路:
1.用栈来实现
2.取出第一个字符,入栈
3.遍历字符串,取出元素与栈顶元素比较
4.如果能配对 则出栈, 否则入栈
5.最后比较栈中元素数量

typedef struct StackNode{
    char data;
    struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack{
    LinkStackPtr top;
    int count;
}LinkStack;

int Push(LinkStack *S, char e){
    LinkStackPtr p = (LinkStackPtr)malloc(sizeof(StackNode));
    if (p == NULL) {
        return 0;
    }
    p->next = S->top;
    p->data = e;
    S->top = p;
    S->count++;
    return 1;
}

int Pop(LinkStack *S){
    if (S->top == NULL) {
        return 0;
    }
    LinkStackPtr t = S->top;
    S->top = t->next;
    free(t);
    S->count--;
    
    return 1;
}

int isValid(char *s){
    if (strlen(s)==0) {
      return 0;
    }
    LinkStack ls;
    
    ls.top = NULL;
    ls.count = 0;
    Push(&ls, s[0]);
    for (int i=1; i<strlen(s); i++) {
        char c = s[i];
        if (ls.count == 0) {//栈为空时 直接入栈
            Push(&ls, c);
            continue;
        }
        char top = ls.top->data;
        
        if ((top == '{' && c == '}') ||
            (top == '[' && c == ']') ||
            (top == '(' && c == ')')) {//匹配上则出栈
            Pop(&ls);
        }
        else {//未匹配上则入栈
            Push(&ls, c);
        }
    }
    
    if (ls.count == 0) {
        return 1;
    } else {
        return 0;
    }
    
}

2. 每日天气

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

解法一: 遍历法 O(n^2) 耗时太长 leetcode不能通过 1.从前向后遍历取出温度T[i]; 2.从j开始依次向后取出温度T[j]; 3.如果T[i]<T[j],根据j-i,求出天数

int* dailyTemperatures(int* T, int TSize, int* returnSize){
    int count;
    int *result = (int *)malloc(sizeof(int)*TSize);
    *returnSize = TSize;
    for (int i=0; i<TSize; i++) {
        count = 0;
        for (int j=i+1; j<TSize; j++) {
            if (T[i]<T[j]) {
                count = j-i;
                break;
            }
        }
        result[i] = count;
    }
    return result;
}

解法二:
利用栈
1.top为栈顶指针 stack为用来模拟栈的数组;
2.result存储返回结果,初始化全部为0;
3.遍历温度数组,取出当前温度值 与 栈顶元素的值(数组下标) 作比较;
4.如果大于 则找到栈顶元素的所需要的天数 出栈 结果存入result;
5.继续与栈顶元素作比较;
6.如果不大于 则将当前温度的下标入栈;
这里需要注意一点 从栈底元素到栈顶元素 元素所对应的温度值是依次减小的 否则就是出栈了

int* dailyTemperatures2(int* T, int TSize, int* returnSize){
    *returnSize = TSize;
    int top = -1; //栈顶下标
    int stack[TSize];//创建TSize大小的数据 模拟栈 存储值为T的下标
    int *result = (int *)malloc(sizeof(T[0])*TSize);
    for (int i = 0;i < TSize;i++) {
        result[i] = 0;
    }
    for (int i = 0; i<TSize; i++) {
        int t = T[i];
        while (top>=0) {
            int index = stack[top];
            int data = T[index];
            if (t>data) {
                top--;
                result[index] = i - index;
            } else {
                break;
            }
        }
        top++;
        stack[top] = i;
    }
    
    return result;
}

解法三: 跳跃法可以 减少循环次数 从右向左遍历 可以利用后面温度的天数结果 来减少循环次数 1.声明同等容量值数组 赋初始值都为0 2.从倒数第二个温度开始向前遍历 倒数第一个天数肯定是0 3.遍历到第i个温度时,与第i+1个温度值作比较 4.如果T[i]<T[j],则拿j-i的值作为结果 5.如果T[i]不小于T[j],则向后跳result[j]个温度再去向后查找 因为这中间不可能有大于T[i]的温度了 6.如果T[i]不小于T[j] 且 result[j] 为0, 则直接result[i]=0。因为后面大于T[j]的都没,更不可能有大于T[i]的了

int* dailyTemperatures3(int* T, int TSize, int* returnSize){
   
    int *result = (int *)malloc(sizeof(int)*TSize);
    *returnSize = TSize;
    result[TSize-1] = 0;
    
    for (int i=TSize-2; i>=0; i--) {
        for (int j=i+1; j<TSize; j += result[j]) {
            
            if (T[i]<T[j]) {
                result[i] = j-i;
                break;
            } else if (result[j]==0) {
                result[i] = 0;
                break;
            }
        }
    }
    return result;
}

3. 爬楼梯问题

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
解法一:
采用递归 采用自顶而下的分治法
走到n阶时 可以是从n-2走了2步 也可以是从n-1走了1步
fn = f(n-1)+f(n-2)
当n = 1 时返回1
当n = 2 时返回2
当n <= 0是 返回0
缺点: 耗时较多 leetcode 不能通过

int climbStairs(int n){
    if ( n <= 0) return 0;
    if ( n <= 2) return n;
    return climbStairs(n-2) + climbStairs(n-1);
}

解法二: 采用动态规划
采用自底而上开始累加 刚好与 递归相反
当n = 1 时返回1
当n = 2 时返回2
当n = 3 时 = 1 + 2
n递增 得到
fn = f(n-1)+f(n-2)

int climbStairs2(int n){
    if (n == 1) {
        return 1;
    }
    if (n == 2) {
        return 2;
    }
    int t1, t2, t3;
    t1 = 1;
    t2 = 2;
    t3 = t1 + t2;
    for (int i = 3; i<=n; i++) {
        t3 = t2 + t1;
        t1 = t2;
        t2 = t3;
    }
    return t3;
}

4.字符串解码

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

例如: s = "3[a]2[bc]", 返回 "aaabcbc".z s = "3[a2[c]]", 返回 "accaccacc". s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".

解题思路:

  1. 利用栈来解题
  2. 循环遍历字符串,如果字符不是’]‘ 就入栈
  3. 如果遇到’]‘,开始解码
  4. 出栈 直到遇到'[' 得到要复制的字符串
  5. 将'['出栈
  6. 继续出栈,直到遇到不是数字的字符,得到要复制的数目
  7. 通过两层循环 将复制的字符串入栈
  8. 继续遍历
  9. 得到解码字符串

代码实现:

char * decodeString(char * s){
    
    int len = (int)strlen(s);
    int stackSize = 50;
    char* stack = (char*)malloc(stackSize * sizeof(char));
    int top = -1;
    
    for (int i=0; i<len; i++) {
        char c = s[i];
        if (c != ']') {//如果字符不是]则一直入栈
            if (top==stackSize-1) {//如果此时栈满了 就扩容
                stack = realloc(stack, (stackSize += 50) * sizeof(char));
            }
            stack[++top] = c;
        } else {
            //创建临时数组 存放要复制的字符
            int tempSize = 10;
            char* tempStack = (char*)malloc(tempSize * sizeof(char));
            int tempTop = -1;
            //出栈 得到到要复制的字符串
            while (stack[top] != '[') {
                if (tempTop==tempSize-1) {
                    tempStack = realloc(tempStack, (tempSize += 10)*sizeof(char));
                }
                //栈顶元素存入临时数组
                tempStack[++tempTop] = stack[top];
                //stack 出栈
                top--;
            }
            //得到要复制的字符串
            char *cpyStr = (char*)malloc((tempTop+2)*sizeof(char));
            for (int i=tempTop; i>=0; i--) {
                cpyStr[tempTop-i] = tempStack[i];
            }
            cpyStr[++tempTop] = '\0';
            
            //'['出栈
            top--;
            
            //取出需要复制的数量
            int cpyNum = 0;
            int j = 0;
            while (top>=0 && stack[top]>='0' && stack[top]<='9') {
                cpyNum += (stack[top]-'0')*pow(10, j);
                top--;
                j++;
            }
            
            //复制的字符重新入栈
            for (int i=0; i<cpyNum; i++) {
                for (int j=0; j<strlen(cpyStr); j++) {
                    if (top==stackSize-1) {//如果此时栈满了 就扩容
                        stack = realloc(stack, (stackSize += 50) * sizeof(char));
                    }
                    stack[++top] = cpyStr[j];
                }
            }
            
            free(tempStack);
        }
    }
    
    char* result = realloc(stack, (top + 2) * sizeof(char));
    result[++top] = '\0';
   
    return result;
}

5.杨辉三角

思路:
用一个二维数组来表示
1.第一层循环控制行数i : 默认[i][0] = 1,[i][i] = 1
2.第二层循环控制列数j : triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]

 int** generate(int numRows, int* returnSize){
    *returnSize = numRows;
    int **result = malloc(sizeof(int*)*numRows);
    for (int i=0; i<numRows; i++) {
        //二维数组的每一个元素 都是一个一维数组的指针
        result[i] = malloc(sizeof(int)*(i+1));
        
        //每行第一个元素和最后一个元素,固定赋值为1;
        result[i][0] = 1;
        result[i][i] = 1;
        
        for (int j=1; j<i; j++) {
            result[i][j] = result[i-1][j] + result[i-1][j-1];
        }
    }
    return result;
}

6.进制转换

十进制转换成8进制
1.初始化一个空栈S
2.当十进制N非零时,循环执行以下操作
* 把N与8求余得到的八进制数压入栈S;
* N更新为N与8的商;
3.当栈S非空时,循环执行以下操作
* 弹出栈顶元素e;
* 输出e;

void conversion(int N){
  int stackSize = 20;
  int *stack = malloc(sizeof(int)*stackSize);
  int top = -1;
  
  while (N) {
      if (top==stackSize-1) {
          stack = realloc(stack, stackSize+=20);
      }
      stack[++top] = N%8;
      N=N/8;
  }
  
  printf("\n");
  while (top>=0) {
      printf("%d", stack[top--]);
  }
  printf("\n");
}

7.字符串去重

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

示例 1:
输入: "bcabc"
输出: "abc"

示例 2:
输入: "cbacdcbc"
输出: "acdb"

解题思路:
用栈来存储最终返回的字符串,并维持字符串的最小字典序。
1.先统计一下每个字母在字符串中出现的次数 2.遍历字符串
3.如果当前字符已在栈中 则继续向后遍历
4.如果当前字符不在栈中
4.1 当前字符大于栈顶元素,先入栈,栈中元素保持字典序递增
4.2 当前字符小于栈顶元素,从栈顶向下遍历栈元素,看是否可以使得当前栈中元素的字典序 变得更小:
4.2.1如果字符串后面还有同样的栈顶元素,就先出栈,等到后面再存进来
4.2.2如果字符串后面没有同样的栈顶元素了,那字典序也不能再小了
4.3当前字符入栈

char * removeDuplicateLetters(char * s){
    if (s == NULL || strlen(s) == 0) {
        return "";
    }
    if (strlen(s) == 1) {
        return s;
    }
    
    char record[26] = {0};
    int len = (int)strlen(s);
    int top = -1;
    char *stack = malloc(sizeof(char)*(len+1));
    for (int i=0; i<len; i++) {
        record[s[i]-'a']++;
    }
    
    for (int i=0; i<len; i++) {
        
        int isExist = 0;
        for (int j=0; j<=top; j++) {
            if (stack[j]==s[i]) {
                isExist = 1;
                record[s[i]-'a']--;
                break;
            }
        }
        if (isExist==1) {
            continue;
        } else {
            if (top==-1 || s[i]>stack[top]) {
                stack[++top] = s[i];
                record[s[i]-'a']--;
            } else {
                while (top>=0 && record[stack[top]-'a']>0 && s[i]<stack[top]) {
                    top--;
                }
                stack[++top] = s[i];
                record[s[i]-'a']--;
            }
        }
    }
    
    stack[++top] = '\0';
    return stack;;
}