数据结构与算法(线性表习题篇)

798 阅读7分钟

线性表-习题

今天主要分享的是解题的思路,希望对大家有所帮助。 无论是什么样的习题,都应该先想好解题的思路,这样做题才会事半功倍。😝。

第一题:将2个递增的有序链表合并为⼀一个链表的有序链表; 要求结果链表仍然使⽤用两个链表的存储空间,不另外占⽤用其他的存储空间. 表中不不允许有重复的数据

首先我们想一下思路(只是提供一种解题思路,也可能有更好的思路)。

1.假设有2个有序链表La,Lb。(分别是:{1,2,4},{2,3,4},我们的目的是变成{1,2,3,4})
2.可以声明一个头指针Lc(借用La的),pa,pb分别指向La,Lb中的元素.
3.循环遍历,Lc存贮较小者,然后相等的话,存pa,删除pb。
4.达到其中一个元素尾部,剩余元素直接拼接(因为这是一个有序递增的)
5.最后把Lb释放了。

然后直接上代码:

void mergeLinkList(ListNode *La, ListNode *Lb, ListNode *Lc){
   ListNode pa,pb,pc,temp;
   pa = (*La)->next;
   pb = (*Lb)->next;
   
   *Lc = pc = (*La);
   
   while (pa && pb) {
       
       if (pa->data < pb->data) {
           pc->next = pa;
           pc = pa ;
           pa = pa->next;
       }else if (pa->data > pb->data){
           pc->next = pb;
           pc = pb;
           pb = pb->next;
       }else{
           temp = pb;
           
           pc->next = pa;
           pc = pa ;
           pa = pa->next;
           pb = temp->next;
           
           free(temp);
       }
   }
   pc->next = pa?pa:pb;
   free(*Lb);
}

第二题:已知两个链表A和B分别表示两个集合.其元素递增排列列. 设计⼀一个算法,⽤用于求出A与B的交集,并存储在A链表中; 例例如 : La = {2,4,6,8}; Lb = {4,6,8,10}; Lc = {4,6,8}

老规矩,这个时候先自己想下思路:

这个其实和第一题比较相似。
1.同样,La,Lb,Lc.pa,pb,pc,然后Lc借用La的头结点
2.循环遍历,相等的话,取La,
3.pa小于pb的话,pa向后顺移,然后之前结点删掉。
4.pa大于pb的话,pb向后顺移,然后之前结点删掉。
5.达到其中一个的尾部,另一个的尾部也删掉,因为不可能存在伤痛元素了
6.释放Lb

上代码:

void intersectionLinkList(ListNode *La, ListNode *Lb, ListNode *Lc){
    ListNode pa,pb,pc,temp;
    pa = (*La)->next;
    pb = (*Lb)->next;
    *Lc = pc = (*La);
    
    while (pa && pb) {
        if (pa->data < pb->data) {
            temp = pa;
            pa = pa->next;
            free(temp);
        }else if (pa->data>pb->data){
            temp = pb;
            pb = pb->next;
            free(temp);
        }else{
            pc->next = pa;
            pc = pa;
            pa= pa->next;
            temp = pb;
            pb = pb->next;
            free(temp);
        }
    }
    //Lb为空,删除非空表La中的所有元素
    while (pa) {
        temp = pa;
        pa = pa->next;
        free(temp);
    }
    //La为空,删除非空表Lb中的所有元素
    while (pb) {
        temp = pb;
        pb = pb->next;
        free(temp);
    }
    pc->next = NULL;
    free(*Lb);
}

第三题:设计⼀一个算法,将链表中所有节点的链接⽅方向"原地旋转",即要求仅仅利利⽤用原表的存储空间. 换句话说,要求算法空间复杂度为O(1); 例如:L={0,2,4,6,8,10}, 逆转后: L = {10,8,6,4,2,0};

思路:

1.这个马上想到的是头插法。
2.然后从前向后遍历

这个就直接代码了(看完之后大呼简单😄):

void rotateLinkList(ListNode *La){
    
    ListNode p,q;
    p = (*La)->next;
    (*La)->next = NULL;
    
    while (p) {
        q = p->next;
        p->next = (*La)->next;
        (*La)->next = p;
        p = q;
    }
}

第四题:设计⼀一个算法,删除递增有序链表中值⼤大于等于mink且⼩小于等于maxk(mink,maxk是给定的两个参数,其值可以和表中的元素相同,也可以不不同)的所有元素;

兄弟们,思路:

1.首先他会给2个值。
2.我们把  mink~maxk 之间的结点干掉。
3.用mink前面的结点指向maxk之后的结点
4.结束啦

上代码:

void deleteItems(ListNode *La,int mink, int maxk){
    ListNode p,q,pre,temp;
    pre = (*La);
    p= (*La)->next;
    
    //找前面的结点
    while (p && p->data < mink) {
        pre = p;
        p = p->next;
    }
    
    //找后面的结点
    while (p && p->data <= maxk) {
        p = p->next;
    }
    
    // 存一波,然后指过去
    q = pre->next;
    pre->next = p;
    
    //删掉中间的结点
    while (q!=p) {
        temp = q->next;
        free(q);
        q = temp;
    }
}

第五题:设将n(n>1)个整数存放到⼀一维数组R中, 试设计⼀一个在时间和空间两⽅方⾯面都尽可能⾼高效的算法;将R中保存的序列列循环左移p个位置(0<p<n)个位置, 即将R中的数据由(x0,x1,......,xn-1)变换为 (xp,xp+1,...,xn-1,x0,x1,...,xp-1).例如: pre[10] = {0,1,2,3,4,5,6,7,8,9}, n = 10,p = 3; pre[10] = {3,4,5,6,7,8,9,0,1,2}。

大家不要看到题比较长就比较慌,这道题大多数就是举例,然后让你明白他要表达什么。(写了这么多,他想表达的就是左移)

思路:

1.将一维数组中所有元素逆置。
2.找到n-p-1的位置,分成2个数组。
3.在逆置
(当然,大家可能有更好的办法。也可以多多尝试。我写的应该是最简单。。😄)

代码:

//逆置方法
void Reverse(int *arr , int left, int right){
    int temp;
    while (left < right) {
        temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left ++;
        right --;
        
    }
}
//左移方法(主方法)
void leftMove(int *arr, int n, int p){
    //全部逆序
    Reverse(arr, 0, n-1);
    //前面在逆序
    Reverse(arr, 0, n-p-1);
    //后面在逆序
    Reverse(arr, n-p, n-1);
}

第六题:已知⼀一个整数序列列A = (a0,a1,a2,...an-1),其中(0<= ai <=n),(0<= i<=n). 若存在ap1= ap2 = ...=apm = x,且m>n/2(0<=pk<n,1<=k<=m),则称x 为 A的主元素. 例例如,A = (0,5,5,3,5,7,5,5),则5是主 元素; 若B = (0,5,5,3,5,1,5,7),则A 中没有主元素,假设A中的n个元素保存在⼀一个⼀一维数组中,请设 计⼀一个尽可能⾼高效的算法,找出数组元素中的主元素,若存在主元素则输出该元素,否则输出-1.

这个题还是这么长,不慌,慢慢分析。

找到数组中的主元素(个数>数组个数/2)
1.首先临时选取key为一个主元素。遍历数组。找到相同的数,count则加一,否则减一。
2.当count小于0时更换下一个元素为候选主元素。
3.遍历之后并不代表key就是主元素。判断key的count>n/2,成立则是主元素,不成立就没有主元素了。

上代码:

int findMianElement(int *A, int n){
    int key = A[0];
    int count = 1;
    for (int i = 0; i<n; i++) {
        
        if (A[i] == key) {
            count ++;
        }else{
            if (count > 0 ) {
                count --;
            }else{
                key = A[i];
                count = 1;
            }
        }
    }
    
    if (count > 0) {
        for (int i = count = 0; i<n; i++) {
            if (key == A[i]) {
                count ++ ;
            }
        }
    }
    if (count >n/2) {
        return key;
    }
    return -1;
}

第七题:⽤用单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计⼀一个时间复杂度尽可能⾼高效的算法. 对于链表中的data 绝对值相等的结点, 仅保留留第⼀一次出现的结点,⽽而 删除其余绝对值相等的结点.例例如,链表A = {21,-15,15,-7,15}, 删除后的链表A={21,-15,-7};

提示要一个时间复杂度尽可能高效的算法。所以我们可以用空间换时间。

思路:

1.申请一个大于n的辅助数组。初始值为0
2.遍历链表。给数组赋值(当然要去绝对值了),这个值出现赋值1。
3.如果这个值等于1,说明不是第一次出现,,删除。
4.如果这个值等于0,说明第一次出现,留下。

上代码:

void deleteEqualABSValue(ListNode *L,int n){
    int *p = alloca(sizeof(int)*n);
     ListNode r = *L;
    
     //2.数组元素初始值置空
     for (int i = 0; i < n; i++) {
         *(p+i) = 0;
     }
     //3.指针temp 指向首元结点
     ListNode temp = (*L)->next;
     
     //4.遍历链表,直到temp = NULL;
     while (temp!= NULL) {
         
         //5.如果该绝对值已经在结点上出现过,则删除该结点
         if (p[abs(temp->data)] == 1) {
             
             //临时指针指向temp->next
             r->next = temp->next;
             //删除temp指向的结点
             free(temp);
             //temp 指向删除结点下一个结点
             temp = r->next;
         }else
         {
             //6. 未出现过的结点,则将数组中对应位置置为1;
             p[abs(temp->data)] = 1;
             r = temp;
             //继续向后遍历结点
             temp = temp->next;
         }
     }
}

总结

其实解析所有问题都是一样,首先理清思路,然后在根据思路写逻辑。没有人第一眼看下去,啥都不想直接写的(除非他把这道题背下来了。。)。所以想清解题思路超级重要。