本篇主要对栈与队列的运用进行了一些练习,其中涉及到了一些常见的算法题目。
代码在这,大家可以参照代码来阅读会更加容易理解。
进制转换
将一个十进制数转为 N 进制,输出结果。
进制转换的思路可以概括为:辗转相除,余数倒序。
其中倒序这个点可以使用栈来实现。
void conversion(int num){
ListStack s;
ItemType e;
int num_temp = num;
stackInit(&s);
// 将辗转相除的余数入栈
while (num_temp) {
stackPush(&s, num_temp % SYSTEMNUM);
num_temp = num_temp / SYSTEMNUM;
}
printf("十进制下 %d 转为 %d 进制编码为:", num, SYSTEMNUM);
while (!isStackEmpty(s)) {
stackPop(&s, &e);
printf("%d", e);
}
printf(" .\n");
}
括号匹配检验
假设字符串中仅包含两种括号:圆括号与方括号,其中的嵌套顺序随意,即 () 或者 [([][])] 都是正确的,而 [(] 或者 ([()) 都不是正确的格式。设计算法检验括号是否匹配可用。
匹配的思路便是先记下等待与之相对的元素出现,而且在最内层的括号一定是直接成对的(左括号下一个一定就是右括号)。所以这个“先记下”和"配对"暗合和
入栈和出栈的思想。
思路:
1.首字符入栈;
2.查看栈顶,如果栈顶是 ( 恰好当前元素就是 ),那么栈顶出栈(这一组就匹配好了,不用管了)。如果不是,那么入栈当前元素(他需要别人和它匹配);
3.使用相同的逻辑判断 [;
4.多组判断中,以 # 间隔,那么一定要以 ( 或 [ 开始,否则即为不合法字符串;
5.最后遍历处理完毕之后若栈中没有待匹配的字符,那么即为合法。
int execute(ListStack stack, char* str){
stackPush(&stack, str[0]);
ItemType e;
for (int i = 1; i < strlen(str); i++) {
char top;
stackGetTop(stack, &top);
stackPrint(stack);
switch (top) {
case '(':
if (str[i] == ')') {
stackPop(&stack, &e);
} else {
stackPush(&stack, str[i]);
}
break;
case '[':
if (str[i] == ']') {
stackPop(&stack, &e);
} else {
stackPush(&stack, str[i]);
}
break;
case '#':
if (str[i] == '(' || str[i] == '[') {
stackPush(&stack, str[i]);
}
break;
default:
return -1;
break;
}
}
if (isStackEmpty(stack)) {
return 0;
}
return -1;
}
每日温度问题
题目: 根据每日气温列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置0来代替。例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
这个和上一题有一些类似。 思路:
1.遍历所有温度,标记当前温度;
2.查看栈顶,如果当前温度比栈顶温度高,那么栈顶温度就找到匹配的温度了,计算差值后出栈。重复出栈符合条件的温度。
3.当前温度没有比栈顶高,那么说明当前温度是一个较低的温度,入栈,等待后续升温进行匹配;
void dailyTemperatures(int *data, int size, int *result, int *resSize){
*result = (int)malloc(sizeof(int) * size);
// 注意此处是使用了栈思想,不需要真的实现一个堆栈
// 使用堆栈记录 data 的下标
int *stack = malloc(sizeof(int) * size);
*resSize = size;
int top = 0;
int tIndex;
// 初始化结果搜赋值为 0 其中最后一位到最后也是为 0。
for (int i = 0; i < size; i++) {
result[i] = 0;
}
for (int i = 0; i < size; i++) {
// 若某一天温度大于栈顶元素了,那么即为温度升高了,所求天数则为下标之差。
// 然后还需要看一下当前温度是不是比新的栈顶高。
while (top > 0 && data[i] > data[stack[top - 1]]) {
printf("栈顶 %2d \n", top);
tIndex = stack[top - 1];
result[tIndex] = i - tIndex;
top--;
}
// 直到栈为空或者说当前温度没有栈顶高,
// 那么这个温度就是一个较低的温度他需要入栈等下一次升温再出栈
stack[top] = i;
top++;
}
}
杨辉三角
打印杨辉三角:
1
1 1
1 2 1
1 3 3 1
思路:
1.默认 [i][0] = 1; [i][i] = 1;
2.在 1. 完成的基础上,[i][j] = [i-1][j-1] + [i-1][j].
int** yanghuiTriangle(int row, int *resSize){
int **result = (int **)malloc(sizeof(int*) * row);
*resSize = row;
for (int i = 0; i < row; i++) {
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;
}
斐波那契数列---爬楼梯
在你面前有一个n阶的楼梯,你一步只能上1阶或2阶。
请问计算出你可以采用多少种不同的方式爬完这个楼梯。
这就是一个菲波那切数列的应用。可以使用递归或者动态规划的方法解决。
//递归算法
int fibonacci_1(int n){
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return fibonacci_1(n-1) + fibonacci_1(n-2);
}
//动态规划算法
int fibonacci_2(int n){
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
int *sum = (int *)malloc(sizeof(int) * (n+1));
sum[0] = 0;
sum[1] = 1;
sum[2] = 2;
for (int i = 3; i <= n; i++) {
sum[i] = sum[i-1] + sum[i-2];
}
return sum[n];
}
字符串编码
题目: 字符串编码LeetCode-中等
编码规则为: 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".
思路详见代码注释。
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) {
if (s[i] != ']') {
// 如果超容了,就扩容。
if (top == stackSize - 1) {
stack = realloc(stack, (stackSize+=50) * sizeof(char));
}
stack[++top] = s[i];
}else{
int tempSize = 10;
// 这是一个新的栈用来保存需要重复的目标字符串
char *temp = (char *)malloc(sizeof(char) * tempSize);
// 目标字符串暂存栈的栈顶
int top_temp = -1;
// 相当于截取了'['和']'之间的字符(也就是需要重复的目标字符)全部存放在了 temp 这个栈里面。
while (stack[top] != '[') {
temp[++top_temp] = stack[top--];
}
char str_int[11];
// 这是记录了数字字符结束的下标
int curTop = top;
top--;
// 把是数字的字符的 下标起始 找到,其实这个时候字符里应该只有数字字符了。
while (top != -1 && stack[top] >= '0' && stack[top] <= '9') {
top--;
}
// 把数字提出来
for (int j = top + 1; j < curTop; ++j){
str_int[j - (top + 1)] = stack[j];
}
//为字符串strOfInt数组加一个字符结束后缀'\0',因为这个数组有 11 位,所以你要标志它的尾。
str_int[curTop - (top + 1)] = '\0';
// 转成数字
int curNum = atoi(str_int);
// 这个地方把原来的 stack 内容重置了,最后的结果就放在这里
for (int k = 0; k < curNum; ++k) {
int kk = top_temp;
while (kk != -1) {
++top;
stack[top] = temp[kk];
kk--;
}
}
free(temp);
temp = NULL;
}
}
//realloc 动态内存调整;
//void *realloc(void *mem_address, unsigned int newsize);
//构成字符串stack后, 在stack的空间扩容.
char *res = realloc(stack, (top + 1) * sizeof(char));
res[++top] = '\0';
free(stack);
return res;
}
字典序去除重复字符
题目:• 去除重复字母(LeetCode-困难)
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)
示例1:
输入:"bcabc"
输出:"abc"
示例2:
输入:"cbacdcbc"
输出:"acdb"
/*
解题关键:
字典序: 字符串之间比较和数字比较不一样; 字符串比较是从头往后挨个字符比较,那个字符串大取决于两个字符串中 【第一个对应不相等】 的字符; 例如 任意一个a开头的字符串都大于任意一个b开头的字符串;例如字典中apple 大于 book;
题目的意思,你去除重复字母后,需要按最小的字典序返回.并且不能打乱其他字母的相对位置;
例如 bcabc 你应该返回abc, 而不是bca,cab;
例如 cbacdcbc 应该返回acdb,而不是cbad,bacd,adcb
例如 zab,应该返回zab,而不是abz;
思路:
1. s 为空或者长度为 1,需要特殊判断。
2. record 数组记录字母出现的次数。
3. stack 堆栈,用以存储结果和虚招次序。
4. 遍历字符串 s; 当前字符记为 keyChar ,
5. 判断 0 ~ top 中是否有有 keyChar,
6. 若已经存在了;record 中该字符的次数若是 > 1 那么需要将次数-1,若次数 = 1,那么说明keyChar 是最后一次了,那么不能出栈了。
7. 若不存在;那么说明这是这个字符最后一次出现了,那么将所有字典序比 keyChar 大的并且后面还会出现的字符全部出栈,再将 kayChar 入栈,这时的位置就是 keyChar 的正确位置。
条件为:top > -1 && 字典序比 keyChar 大 && 后面还会出现的字符(次数>1)
8. top++ keyChar 入栈。
*/
char * removeLetters(char *s){
int len = (int)strlen(s);
// 先处理特殊情况
if (s == NULL || len == 0) {
return "";
}
if (len == 1) {
return s;
}
// 标记数组统计所有字符是否出现了。
char record[26] = {0};
// 为了完成字典序的限制,需要使用堆栈。多一个位置以便最后放结尾标志
char *stack = (char*)malloc((len + 1) *sizeof(char));
// memset 将 stack 中 规定长度内的空间都填充为 0。
memset(stack, 0, (len + 1) * sizeof(char));
int top = -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 (s[i] == stack[j]) {
isExist = 1;
break;
}
}
// 如果存在的话,那么说明该字符已经入栈了,这一次就不入了,同时 标记次数 要减一
if (isExist == 1) {
record[s[i] - 'a']--;
} else {
// 如果不存在,那么当前字符就要入栈了
// 入栈的时候,如果 栈顶有字符 而且 栈顶字符的字典序比当前字符还大 而且 栈顶字符后面还会出现,
// 那么就让栈顶字符把位置让出来给当前字符,因为这样可以让整个字符的字典序更小。
// 所以将符合上述条件的字符全部出栈,同时这些字符的 标记次数 要减一。
while (top > -1 && stack[top] > s[i] && record[stack[top] - 'a'] > 1) {
record[stack[top] - 'a']--;
// 弹栈
top--;
}
top++;
// 压栈,这个位置就是当前字符的正确位置
stack[top] = s[i];
}
}
// 在结尾补上一个结束标记
stack[++top] = '\0';
return stack;
}