一、静态链表
例题
(1)静态链表既有顺序存储的优点,又有动态链表的优点。所以,它存取表中第i个元素的时间与i无关。
(2)静态链表中能容纳的元素个数的最大数在表定义时就确定了,以后不能增加.
(3)静态链表与动态链表在元素的插入、删除上类似,不需做元素的移动。
以上错误的是()
B。
(1)错,(2)(3)对。
静态链表是用数组存储节点数据,模拟链表的实现,但是没有用到指针。每个数组节点包括两部分:data域和cursor(游标)域。data存储数据,cursor指明下个元素在数组中的下标。
(1)存取第i个元素时,需要从头遍历到i-1和元素,由第i-1个节点的cursor,才能知道第i个元素存储的位置,因此和i是相关的。
(2)使用数组对元素进行存储,在定义时大小已经确定。
(3)插入和删除操作无需移动元素,只需要修改cursor游标的值即可,就像修改动态链表中的指针一样。
二、归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
算法思想图解:
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
![](https://i-blog.csdnimg.cn/blog_migrate/dac804c018662debf167b73d85833173.gif)
一道例题
将两个各有 N 个元素的有序表归并成一个有序表,其最少的比较次数是( )。
A.N
B.2N -1
C.2N
D.N -1
【答案】 A
【解析】 最简单的归并是直接将两个有序的子表合并成一个有序的表,归并排序最好情况下,一个有序表所有元素都比另外一个有序表小,为了得出这一结论需要我们扫描N个表结点,即比较N次
举例:默认由小到大排序,若有序表a的所有元素都比表b的要小,那么就只需比较N次,然后a中所有元素就加入了新的表,然后再把b中所有元素加入新的表即可
时间复杂度为 O(n)。
延伸:
将两个各有 N 个元素的有序表归并成一个有序表,其最多的比较次数是( )。
A.N
B.2N -1
C.2N
D.N -1
【答案】 B
【解析】最多的比较次数是当两个有序表的数据刚好是插空顺序的时候,那么就相当于要扫描2N-1个表结点,也就是比较2N-1次。
比如:第一个序列是1,3,5,第二个序列是2,4,6,把第二个序列插入到第一个序列中,先把第二个序列中的第一个元素2和第一个序列依次比较,需要比较2次(和1,3比较),第二个元素4需要比较2次(和3,5比较,因为4比2大,2之前的元素都不用比较了),第三个元素6需要比较1次(只和5比较),所以最多需要比较5次。即2n-1次。
实现代码
一、将两个独立的有序表归并到新的有序表(直观理解)
//归并两个有序表,升序排列(由小到大)
void Merge(list& a, list& b,list& c)
{
//定义四个指针分别指向线性表a和b的头尾
int *pa=a.elem;
int* qa=a.elem+a.length-1;
int *pb=b.elem;
int* qb=b.elem+b.length-1;
int i=1;
//当a和b都未遍历结束时
while(pa<qa&&pb<qb)
{
//只要a表元素比b表元素小,就继续扫下去
if(*pa<=*pb)
{
Insert(c,i,*pa);//在第i个位置插入元素,返回插入(是否成功)的状态
i++;
pa++;
}
else if(*pb<=*pa)
{
Insert(c,i,*pb);
i++;
pb++;
print(c);
}
}
//若是表a指针指向了表尾跳出了上层循环
if(pa==qa)
{
Insert(c,i,*pa);
i++;
while(pb<=qb)
{
Insert(c,i,*pb);
i++;
pb++;
}
}
else if(pb==qb)
{
Insert(c,i,*pb);
i++;
while(pa<=qa)
{
Insert(c,i,*pa);
pa++;
i++;
}
}
}
二、将一个表的两个部分r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]
void print(int a[], int n){
for(int j= 0; j<n; j++){
cout<<a[j] <<" ";
}
cout<<endl;
}
//将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]
void Merge(ElemType *r,ElemType *rf, int i, int m, int n)
{
int j,k;
for(j=m+1,k=i; i<=m && j <=n ; ++k){
if(r[j] < r[i]) rf[k] = r[j++];
else rf[k] = r[i++];
}
while(i <= m) rf[k++] = r[i++];
while(j <= n) rf[k++] = r[j++];
print(rf,n+1);
}
void MergeSort(ElemType *r, ElemType *rf, int lenght)
{
int len = 1;
ElemType *q = r ;
ElemType *tmp ;
while(len < lenght) {
int s = len;
len = 2 * s ;
int i = 0;
while(i+ len <lenght){
Merge(q, rf, i, i+ s-1, i+ len-1 ); //对等长的两个子表合并
i = i+ len;
}
if(i + s < lenght){
Merge(q, rf, i, i+ s -1, lenght -1); //对不等长的两个子表合并
}
tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf
}
}
int main(){
int a[10] = {2,3,4,5,15,19,26,27,36,38,44,46,47,48,50};
int b[10];
MergeSort(a, b, 15);
print(b,15);
cout<<"结果:";
print(a,10);
}
三、双向链表插入、删除(前提:不能断链,画图很直观)
💡小技巧:赋值号‘=’可以翻译成“指向”,‘=’左边是要改变的指针(地址),右边是暂时不改变的地址,比如:
p->prior->next = q 就可翻译成:让“p->prior->next”这个指针指向q的地址
![](https://i-blog.csdnimg.cn/blog_migrate/bfd45cc41ff5a77d89d16165bf20f549.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5d863f5e6c2d63e904668e1aa34dd4b9.png)
例题
在一个双向链表中,在p结点之前插入q结点的操作是https://blog.csdn.net/m0_60341379/article/details/120779153
四、循环队列rear、front指针的值
循环队列的出队入队
!!循环队列依然是FIFO结构!!所以只能在队尾添加元素、从队头挤出元素,
出队(⭐⭐⭐)
【算法思想】①判断队列是否为空,若空则返回ERROR。
②保存队头元素。
③队头指针加1 //相当于队头往后移,把要出队的元素“挤出”
【算法描述】
Status DeQueue (SqQueue &Q,QElemType &e){
if(Q.front==Q.rear) return ERROR; //判断是否队空
e=Q.base[Q.front]; //保存队头元素
Q.front=(Q.front+1)%MAXQSIZE; //队头指针+1
return OK;
}
入队(⭐⭐⭐)
【算法思想】①判断队列是否满,若满则返回ERROR。
②将新元素插人队尾。
③队尾指针加1
【算法描述】
Status EnQueue(SqQueue &Q,QElemType e){
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR; //判断队满
Q.base[Q.rear]=e; //新元素插入队尾
Q.rear=(Q.rear+1)%MAXQSIZE; //队尾指针+1
return OK;
}
队头(front)、队尾(rear)指针的进1操作
▪ 队头、队尾指针加1,可用取模(余数)运算实现。(从7->0就要取余)
▪ 队头指针进1: front = (front+1) %maxsize;
▪ 队尾指针进1: rear = (rear+1) % maxsize;
▪ 队列初始化:front = rear = 0;
▪ 队空条件:front == rear;
▪ 队满条件:(rear+1) % maxsize == front;//注意区分队空、队满判断条件!!!
![](https://i-blog.csdnimg.cn/blog_migrate/23ae2f28c6026f0ee0f0e84171ea0ed6.png)
总结:正常(非队空/队满)情况下,
入队->队尾指针+1->rear = (rear+1) % maxsize;
出队->队头指针+1->front = (front+1) %maxsize;
一道例题
五、中缀后缀表达式转换
栈实现法则
若为‘(’,入栈
若为‘)’,则依次将栈中的运算符加入后缀表达式,直到出现‘(’,并从栈中删除‘(’
若为‘+’,‘-’,‘*’,‘/’
栈顶元素为‘(’,入栈
高于栈顶元素优先级,入栈
否则(优先级低于栈顶元素),依次弹出栈顶运算符,直到一个优先级比它低的运算符或‘(’为止
遍历完成,若栈非空,依次弹出栈中所有元素
快速求法:
根据运算法则,先求的表达式先转换成后缀式,然后把其当成一个整体;
如:[(A+B)*C]-[E-F]
先计算A+B,转化为AB+
AB+作为整体与C运算,转化为CAB+*(若写作 AB+C* 容易引起误解!!)
运算E-F,转化为EF-
将AB+C*与EF-作为整体执行-运算,转化为CAB+*EF--
保险做法:借助二叉树
中缀:中序遍历(LVR)
后缀:后序遍历(LRV)
前缀:前序遍历(VLR)
中缀式的建树法则:运算符号一定做树根(V),变量一定作为左右孩子
例题
将下列中缀表达式转换成后缀表达式
➢ a-(b+c)*(d-e/f)的后缀表达式是?
➢ a*(b+c)-d的后缀表达式?
abc+def/-*-
abc+*d-