数据结构(1)链接https://blog.csdn.net/Z_timer/article/details/105595519(多图预警)
数据结构(3)链接https://blog.csdn.net/Z_timer/article/details/113867898
目录
栈、队列
栈(stack)
先进后出
栈是一种只能在一端进行插入或删除操作的线性表。
存储结构:顺序栈、链栈。
顺序栈: int stack[maxSize]; int top = -1; stack[++top] = 值; 元素入栈 。 x = stack[top--]; 元素出栈(top在哪里,栈就在哪里虽然没有删除该数值,但top已经后移一位,在栈里删除了该值,实际存储空间里还有后续只要入栈就会覆盖当前值)
判断栈的情况
链栈:
队列(queue)
先进先出FIFO
存储结构:顺序队、链队
顺序队(循环队):(规定:入队 先移动“队尾指针”再入队元素,出队 先移动“对头指针”再出队元素,队空状态 front = rear时为空)
入队:queue[++rear] = x; 出队:x = queue[++front];
(会造成假溢出,即rear在最后一位,但是前面的front已经离开了第0位,rear再往前走会造成数据溢出,需要弄成一个“环”。)
(%取余数)队空:front == rear 为 True
链队:
结构体里存指针,为空就是front为空的时候
考点总结
输出序列
由下可知,出栈从3开始,因此入栈到1、2、3结束;然后出3,入4,出4,出2,出1,至此栈内无元素,入栈5、6,后发现栈的容量为3(注意画图和栈的特性先进后出)
408 09年2题。 注意队列是先进先出,所以出队序列就是出栈序列,不会影响。由出栈序列b可知,入a,b,栈容量2,出b,留a;序列d可知,入c、d,栈容量3,出d,留a、c;。。。可知答案为3
此考点多做题就好,参考https://blog.csdn.net/Z_timer/article/details/109599781---队列的3、5、7题。
表达式转换
中缀表达式:a+b、前缀表达式(波兰式):+ab、后缀表达式(逆波兰式):ab+
中缀转前后缀
方法:加括号和花操作树,注意运算次序,先括号,后乘除最后加减。此处加括号(参考https://blog.csdn.net/Z_timer/article/details/109599781---队列的2、4、6)里体会,操作树跟加括号差不多,只是更清楚明白。
由中缀画出操作树后根据前后缀,分别用前序遍历或者后序遍历即可。
后缀转中缀
注:非运算符算一个表达式,括号括起来的内容也算表达式。两个表达式加运算符把运算符移到表达式中间即可。
1. 由ab+可知,ab为表达式,+为运算符,由此得:(a+b)
2. 由(a+b)c*可知,(a+b)c为表达式,*为运算符,由此得:((a+b)*c)
3. 省略到 (((a+b)*c)+d)eg+h*-
4. 由(((a+b)*c)+d)eg+可知,选择距离运算符最近的两个表达式,得eg+转化为e+g
5. 由(((a+b)*c)+d)(e+g)h*可知(e+g)h为表达式,*为运算符,由此得:(e+g)*h
6. (((a+b)*c)+d)(e+g)*h-可知,(((a+b)*c)+d)为表达式(e+g)*h为表达式,-为运算符,最后去掉不必要的括号得((a+b)*c)+d-(e+g)*h
后缀转前缀
同中缀,只不过把运算符放到最前面
中缀转后缀---栈实现
- 将栈初始化为空栈;
- 从左到右扫描表达式的每一个字符,执行下面操作:
2.1 遇到操作数:直接输出(添加到后缀表达式中)
2.2 栈为空时,遇到运算符,直接入栈
2.3 遇到左括号:将其入栈
2.4 遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。
2.5 遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
2.6 最终将栈中的元素依次出栈,输出。
https://www.cnblogs.com/wkfvawl/p/12864789.html
例题:A
中缀转前缀---栈实现
https://cloud.tencent.com/developer/article/1596127
基本同中转后相同,不同点:
1、上面的从左到右扫描表达式,改成从右向左扫描每一个字符。
2、左括号和右括号的判断和上面相反。
后缀转前缀---栈实现
结束
每次遇到一个运算符,把相邻的两个表达式放在运算符后面
中缀转前、后缀代码:
https://blog.csdn.net/Z_timer/article/details/111316815
求中缀值代码:
计算3+4*5*(2+3)
1. 栈1:3 4 5 top1(栈顶指针)
栈2:+ *['*' > '+', 所以入栈,必须大于]
2. 栈1:3 20(4*5 注意顺序) top2(栈顶指针)
栈2:+ * ['*' == '*' 所以第一个*出栈 ]
3. 栈1:3 20 2 3
栈2:+ * ( +
4. 栈1:3 20 5(2+3 注意顺序)
栈2:* +
5. 栈1:3 100(20*5)
栈2:+
6. 103
int getPriority(char op) {
if (op=='+' || op=='-') return 0;
else return 1;
}
int calSub(float opand1, char op, float opand2, float &result) {
if (op == '+') result = opand1 + opand2;
if (op == '-') result = opand1 - opand2;
if (op == '*') result = opand1 * opand2;
if (op == '/') {
if (fabs (opand2) < Min) return 0; // 判断是否为0,不能用==0 因为会存在bug具体百度,
//Min代表一个趋近于0的值
else result = opand1 / opand2;
}
return 1; // 不返回result是因为判断数据是否合法 0否1是
}
float calInfix(char exp[]) {
float s1[maxSize];
int top1 = -1;
float s2[maxSize];
int top2 = -1;
int i=0;
while (exp[i] != '\0') {
if ('0' <= exp[i] && exp[i] <= '9') { //数字进操作数栈
s1[++top1] = exp[i] - '0'; // 转化为数字
++i;
} else if (exp[i] == '(') { //左括号直接放入运算栈,待下次判断右括号
s2[++top2] = '(';
++i;
} else if (exp[i]=='+' ||
exp[i]=='-' ||
exp[i]=='*' ||
exp[i]=='/') { //判断运算符
if (top2==-1||
s2[top2]=='('||
getPriority(exp[i]) > getPriority(s2[top2])) {
s2[++top2] = exp[i]; //运算符栈为空、左括号、优先级比栈内元素大 都可直接入运算栈
++i;
} else {
float opnd1, opnd2, result;
char op;
int flag;
opnd2 = s1[top1--]; // 注意操作数赋值顺序
opnd1 = s1[top1--];
op = s2[top2--];
flag = calSub(opnd1, op, opnd2, result);
if (flag == 0) {
std::cout << "ERROR" << std::endl;
return 0;
}
s1[++top1] = result;
}
} else if (exp[i] == ')') { //左括号直接全部放入输出栈
while (s2[top2] != '(') {
float opnd1, opnd2, result;
char op;
int flag;
opnd2 = s1[top1--]; // 注意操作数赋值顺序
opnd1 = s1[top1--];
op = s2[top2--];
flag = calSub(opnd1, op, opnd2, result);
if (flag == 0) {
std::cout << "ERROR" << std::endl;
return 0;
}
s1[++top1] = result;
}
--top2; //弹出'('
++i;
}
}
while (top2 != -1) {
float opnd1, opnd2, result;
char op;
int flag;
opnd2 = s1[top1--]; // 注意操作数赋值顺序
opnd1 = s1[top1--];
op = s2[top2--];
flag = calSub(opnd1, op, opnd2, result);
if (flag == 0) {
std::cout << "ERROR" << std::endl;
return 0;
}
s1[++top1] = result;
}
return s1[top1];
}
// 此处三处代码一模一样,有需求的直接整合成函数,然后调用函数即可 ps考试不会这么长代码
求前后缀的值就不多加赘述了,只用把操作数(表达式)入栈,遇到运算符出两个操作数,运算结果放入栈中即可。
判断空满的问题
正常情况下:队空:front == rear;队满:front == (rear+1) %maxSIze
在该情况下元素个数计算
非正常情况下:
1. 入队出队的代码顺序相反
但是队空、队满、计算元素个数同上
2.
例题:
双端队列
什么是双端队列:https://www.bilibili.com/s/video/BV1754y1d7yh
题:https://my.oschina.net/gouqizi12/blog/4414498
栈的扩展
共享栈
栈1为空top[0] == -1,栈2为空top[1] == maxSize;
S1入栈:stack[++top[0]] = x;S2入栈:stack[--top[1]] = x;
栈满:top[0] + 1 == top[1]
用栈模拟队列
括号匹配
https://leetcode-cn.com/problems/valid-parentheses/solution/
计算问题
串
串是特殊的线性表,每个单元都是一个字符。
// 不定长存储结构 便于动态分配内存
Str S;
S.length = L;
S.ch = (char*)malloc((L+1)*sizeof(char)); // 此处动态分配
S.ch[length范围内的下标] = 某字符/变量;
某字符/变量 = S.ch[length内的下标];
free(S.ch); //直接清空数据
串的基本操作
赋值操作
int StrAssign(Str &str, char *ch){
if (str.ch) free(str.ch); // 清空原数据
int len = 0;
char *c = ch;
while (*c){ // 获取该字符串的个数,到'\0'结束 len不包括'\0'
++len;
++c;
}
if (len == 0){
str.ch = NULL;
str.length = 0;
return 1;
}
str.ch = (char*)malloc(sizeof(char)*(len+1));
if (str.ch == NULL) return 0; // 判断分配内存是否失败
else {
c = ch;
for (int i=0; i<=len; ++i,++c)
str.ch[i] = *c;
str.length = len;
return 1;
}
}
// string.h里的strcpy
取串长度
int strLength (Str str){ return str.length; }
串比较
int strCompare (Str s1, Str s2){
for (int i=0; i<s1.lenght && i<s2.length; ++i){
if (s1.ch[i] != s2.ch[i])
return s1.ch[i] - s2.ch[i]; // 两个字符串不相等则返回相减值 判断谁大谁小
return s1.length - s2.length; // 判断字符串长度,谁大谁小
}
}
串连接
int concat(Str &str, Str str1, Str str2){
if (str.ch){
free(str.ch);
str.ch = NULL;
}
str.ch = (char*)malloc(sizeof(char)*(str1.length + str2.length+ 1));
if (!str.ch) return 0;
int i = 0;
while (i < str1.length){
str.ch[i] = str1.ch[i];
++i;
}
int j = 0;
while (j < str2.length){
str.ch[i+j] = str2.ch[j]; // i+j从str1末尾继续赋值
++j;
}
str.length = str1.length + str2.length;
return 1;
}
求子串
关键操作:
清空字符串
int clearString(Str &str) {
if (str.ch){
free(str,ch);
str.ch = NULL;
}
str.length = 0;
return 1;
}
KMP算法
建议看这里 https://blog.csdn.net/Z_timer/article/details/109599781#t13的题目和解析,应该有两三个视频。
代码:
数组、矩阵、广义表
408不常考,非统考基本考下标
数组
注意下标与实际差一即可。以及列优先、行优先。推荐参考这里的题目https://blog.csdn.net/Z_timer/article/details/109599781#t17
特殊矩阵和稀疏矩阵
特殊矩阵
对称矩阵、三角矩阵、对角矩阵
稀疏矩阵
三元组表示法第一行存储5个数字,4行4列
邻接表表示法
十字链表表示法
广义表
逻辑结构
广义表的长度:为表中最上层元素的个数。如广义表C长度为2,注意不是3.
广义表的深度:为表中括号的最大层数。求深度时可将子表展开,如D应为 ((d, e), (b, (c, d))), 由绿色可知有三层,所以深度为3
表头(Head)表尾(Tail):当广义表非空时,第一个元素为表头,其余元素组成的表是广义表的表尾。
考试一般采用这种形式:getHead(B) = d; getTail(B) = (e) // 其中函数不用具体实现,知道getHead取表头,getTail取表尾即可,注意表尾有括号,因为取出来为广义表
getHead(D) = B; getTail(D) = (C) // B、D为上表中的数据
getHead((a)) = (a); getTail((a)) = () // 其中(a) 指一个广义表,我也不清楚为什么getHead的结果要加括号
存储结构
(1代表表节点,0代表原子节点,下面的ABCDE都是上面的图中的数据)
扩展线性表存储结构