线性表易错知识点简单整理

一、静态链表

例题

(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的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。

一道例题

将两个各有 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的地址

例题

在一个双向链表中,在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;//注意区分队空、队满判断条件!!!

总结:正常(非队空/队满)情况下,

入队->队尾指针+1->rear = (rear+1) % maxsize;

出队->队头指针+1->front = (front+1) %maxsize;

一道例题

五、中缀后缀表达式转换

栈实现法则

  1. 若为‘(’,入栈

  1. 若为‘)’,则依次将栈中的运算符加入后缀表达式,直到出现‘(’,并从栈中删除‘(’

  1. 若为‘+’,‘-’,‘*’,‘/’

  • 栈顶元素为‘(’,入栈

  • 高于栈顶元素优先级,入栈

  • 否则(优先级低于栈顶元素),依次弹出栈顶运算符,直到一个优先级比它低的运算符或‘(’为止

  • 遍历完成,若栈非空,依次弹出栈中所有元素

快速求法:

根据运算法则,先求的表达式先转换成后缀式,然后把其当成一个整体;

如:[(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),变量一定作为左右孩子

例题

将下列中缀表达式转换成后缀表达式

  1. ➢ a-(b+c)*(d-e/f)的后缀表达式是?

  1. ➢ a*(b+c)-d的后缀表达式?

abc+def/-*-

abc+*d-

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值