线性表中顺序表不懂的题目

1、对长度为n的顺序表L,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素。

我的想法:时间复杂度为O(n),那么只能有一层循环。所以直接从0到n-1遍历一遍,有元素的值等于x就删去,将后面的值一直往前挪。后来实施起来,我才发现:后面移动元素的过程也是循环过程,而且若有多个x,就需要多个循环来移动。

王道书籍的想法:从头到尾遍历一遍,如果不等于x,就放到另一个数组里去。这是一个很好的思路,要记住。

void DeleteList(sqList *L, int x)
{
    int k = 0;
    for (int i = 0; i < L->length; i++)
    {
        if (L->data[i] != x)
        {
            L->data[k++] = L->data[i];
        }
        
    }
    L->length = k;
    
}

2、题目:从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

解析:我的:请你看清楚:是有序表!!!不要总是忘记看是顺序表还是有序顺序表!还是采取复制到另一有序顺序表的策略。把不相同的放到另一个有序顺序表里。只是,我选择挨着的左右比较。

只是如果是1 2 2 2 2 2 3 3  3 3 4 4 5怎么办呢?

解决办法:用类似于直接插入排序的思想,初始时将第一个元素视为非重复的有序表。之后依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,若相同,则继续向后判断,若不同,则插入前面的非重复有序表的最后,直至判断到表尾为止。

我的代码:是让进行比较的前一个放入已排序好了的序列,这样会有一个麻烦,最后一个元素如果不和前面的一致,就没有办法放入已排好序的序列。

oid DeletedSm(sqList *L)
{
    int k = 0,i=0;
    for (int i = 0,j = 1; i < L->length; j++)
    {
       
        if (L->data[i] != L->data[j])
        {
            L->data[k++]=L->data[i];
            i = j;
        }
        if (i == L->length-1)
        {
            L->data[k]=L->data[i];
        }   
    }  
    L->length = K + 1;
}

王道的:是在原来待排序的序列上进行操作,从第二个元素开始,看是否与第一个元素一致。用后面的元素与前面的元素进行比较,存放的后面的。秒啊!!!

void DeletedSm(sqList *L)
{
    for (int i = 0,j=1; j < L->length; j++)
    {
       if (L->data[i] != L->data[j])
       {
         L->data[++i] = L->data[j];
       }
    }
}

3.题目:将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。
算法思想:首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。然后,看那个表还有剩余,将剩下的部分加到新的顺序表后面。(本算法的方法非常典型,需要牢固掌握)。

题目本身没有什么新奇的。就是在王道的代码里,我觉得要学一下这个点。

while (i < A->length && j < B -> length)
{
    if (A->data[i] <= B -> data[j])
    {
        C->data[k++] = A->data[i++];
        //直接在里面自加加,免得多写那么多趟代码。
    } 
}

4、线性表(a1,a2,a3,…,an)中的元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为x的元素,若找到,则将其与后续元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。

算法思想:顺序存储的线性表递增有序,可以顺序查找,也可以折半查找。题目要求:“用最少的时间在表中查找数值为x的元素”,那么推荐用折半查找法。

5、

算法的基本设计思想:
可将这个问题视为把数组ab转换成数组ba(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先将a逆置得到a(-1)b,再将b逆置得到a(-1)b(-1),最后将整个a(-1)b(-1)逆置得到ba。 时间复杂度:O(n),空间复杂度O(1)。

我选择这个✅   另解:借助辅助函数来实现。算法思想:创建大小为p的辅助数组S,将R中前p个整数依次暂存在S中,同时将R中的后n-p个整数左移,然后将S中暂存的p个数依次放回到R中的后续单元。时间复杂度为O(n),空间复杂度为O(p)。

6、

1)算法的基本设计思想如下:
分别求两个升序序列A,B的中位数,设为a和b,求序列A,B的中位数过程如下:

若a=b,则a或b即为所求中位数,算法结束。
若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中,重复上述123过程,直到两个序列中只含有一个元素时为止,较小者即为所求的中位数。

int M_Search(int A[], int B[], int n) {
    int s1 = 0, d1 = n - 1, m1, s2 = 0, d2 = n - 1, m2;
    //分别表示序列A和B的首位数,末位数和中位数
    while (s1 != d1 || s2 || d2) {
        m1 = s1 + ((d1 - s1) >> 1);
        m2 = s2 + ((d2 - s2) >> 1);
        if (A[m1] == B[m2])
            return A[m1];   //满足条件1
        if (A[m1] < B[m2]) {//满足条件2
            if ((s1 + d1) % 2 == 0) {//若元素个数为奇数
                s1 = m1;    //舍弃A中间点以前的部分并保留中间点
                d2 = m2;    //舍弃B中间点以后的部分并保留中间点
            }else{       //若元素个数为偶数
                s1 = m1 + 1;//舍弃A中间点及中间点之前部分
                d2 = m2;    //舍弃B中间点以后部分并保留中间点
            }         
        }
        else {              //满足条件3
            if ((s2 + d2) % 2 == 0) {//若元素个数为奇数
                d1 = m1;    //舍弃A中间点以后的部分并保留中间点
                s2 = m2;    //舍弃B中间点以前的部分并保留中间点
            }
            else { //元素个数为偶数
                d1 = m1;    //舍弃A中间点以后部分并保留中间点
                s2 = m2 + 1;//舍弃B中间点及中间点以前的部分
            }
        }
    }return A[s1] < B[s2] ? A[s1] : B[s2];
}

算法的时间复杂度为O(log2n),空间复杂度为O(1)。

7、

 

 算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确认Num是否是逐元素。
算法可分为以下两步:
1.选取候选的逐元素。依次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数为1;若遇到的下一个整数仍等于Num,则计数加1,否则,计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素。
2.判断c中元素是否是真正的主元素。再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则,序列中不存在主元素。
实现的程序的时间复杂度为O(n),空间复杂度为O(1)。
本题如果采用先排好序再统计的方法(时间复杂度可为O(nlog2n)),只要解答正确,最高可拿11分。即便是写出O(n^2)的算法,最高也能拿10分,因此对于统考算法题,花费大量时间去思考最优解法是得不偿失的。

8、

要求:时间上尽可能高效。

我的想法是直接让最小的正整数去和元素比较,如果有这一元素,加一,继续比较。但是,假设是在    0 3 5 4 6 2  4 5 8 98 43 1 334 554 3 4 3 找,从0开始,遇到0,加一,接下来1=? 遇到1 ,继续从这一元素开始往后比较,看是否有 2=?,但是在1的前面有啊。废物,算法不行。

王道思想:

算法的基本设计思想:
要求在时间上尽可能高效,因此采用时间换空间的办法。分配一个用于标记的数组B[n],用来记录A中是否出现了1 -- n中的正整数,B[0]对应正整数1,B[n-1]对应正整数n,初始化B中全部为0。由于A中含有n个整数,因此可能返回的值是1~(n+1)。当数组A中出现了小于等于0或大于n的值时,会导致1 ~n中出现空余位置,返回结果必然在1 ~n中,因此对于A中出现了小于等于0或大于n的值可以不采取任何操作。
经过上述分析可以得出算法流程,从A[0]开始遍历A,若0<A[i]<=n,则令B[A[i]-1]=1;否则不作操作。对A遍历结束后,开始遍历数组B,若能查找到第一个满足B[i]==0的下标i,返回i+1即为结果,此时说明A中未出现的最小整数在1 ~n之间。若B[i]全部不为0,返回i+1(跳出循环时i=n,i+1等于n+1),此时说明A中未出现的最小正整数是n+1.

没看懂。

int findMissMin(int A[], int n) {
    int i, * B;
    B = (int*)malloc(sizeof(int) * n);//分配空间
    memset(B, 0, sizeof(int) * n);  //赋初值为0
    for (i = 0; i < n; i++) {
        if (A[i] > 0 && A[i] <= n) //若A[i]的值介于1~n,则标记数组B
            B[A[i] - 1] = 1;
    }
    for (i = 0; i < n; i++)//扫描数组B,找到目标值
        if (B[i] == 0)break;
    return i + 1;//返回结果
}

时间复杂度,A遍历一次,B遍历一次,两次循环内操作步骤为O(1)量级,因此时间复杂度为O(n)。空间复杂度:额外分配了B[n],空间复杂度为O(n)。

9、

由D=|a-b|+|b-c|+|c-a|>=0有如下结论:
1.当a=b=c时,距离最小。
2.其余情况,不失一般性,假设a<=b<=c,观察下面的数轴:

 

由D的表达式可知,事实上决定D大小的关键是a和c之间的距离,于是问题就可以简化为每次固定c找一个a,使得L3=|c-a|最小。
1)算法的基本设计思想 

#define INT_MAX 0x7fffffff
int abs_(int a) {
    //计算绝对值
    if (a < 0)return -a;
    else return a;
}
bool xls_min(int a, int b, int c) {
    //a是否是三个数中的最小值
    if (a <= b && a <= c)return true;
    return false;
}
int findMinoTrip(int A[], int n, int B[], int m, int C[], int p) {
    //D_min用于记录三元组的最小距离,初值赋为INT_MAX
    int i = 0, j = 0, k = 0, D_min = INT_MAX, D;
    while (i < n && j < m && k < p && D_min>0) {
        D = abs_(A[i] - B[j]) + abs_(B[j] - C[k]) + abs_(C[k] - A[i]);//计算D
        if (D < D_min) D_min = D;//更新D
        if (xls_min(A[i], B[j], C[k]))i++;//更新a
        else if (xls_min(B[j], C[k], A[i]))j++;
        else k++;
    }return D_min;
}

总结一下:

本次考试可以你觉得莫名其妙的题目,是在考对算法的掌握与运用。想办法抓到敏感次,往查找、排序那两章上凑,毕竟大题肯定要在这里面出。

本次新学了的技巧:1、直接i++,不需要另起一行。2、从头到尾遍历一遍,如果不等于x,就放到另一个数组里去。这是一个很好的思路,要记住。3、想办法直接用后面的算法。

猜你喜欢

转载自blog.csdn.net/weixin_48060069/article/details/127243489