线性表相关算法题

一.两个递增有序链表合并成一个有序链表,要求新的链表使用这两个链表的内存,不占用新的内存空间,并且没有重复数据

分析:设两个链表A{1,3,5,6,7,9},B{2,3,6}。合成后:C{1,2,3,5,6,7,9}。不使用新的内存空间,那么C的头结点需要使用A或B的头结点,进入循环:
A:11
A:3>B:2------head->1->2
A:3=B:3------head->1->2->A:3 (B:3没用了,释放掉)
A:51->2->A:3->5
A:6=B:6------head->1->2->A:3->5->A:6 (B:6没用了,释放掉)
B循环结束----head->1->2->A:3->5->A:6->7->9
循环结束,B的头指针没有用了,释放掉。

为了记住A和B循环到哪里了,需要声明两个临时变量来记录:tempAtempB;
为了记住C的最后一个结点,需要声明一个临时变量来记录:tempC;
为了释放重复结点,需要声明一个临时变量记录:temp

int mergeList(LinkList *A,LinkList *B, LinkList *C){
    
    LinkList tempA, tempB, tempC, temp;
    
    tempC = *C = *A;//使用A的头结点
    tempA = (*A)->next;
    tempB = (*B)->next;
    
    while (tempA && tempB) {
        if (tempA->data == tempB->data) {
            tempC->next = tempA;
            tempA = tempA->next;

            temp = tempB;
            tempB = tempB->next;
            tempC = tempC->next;
            
            free(temp);
        }else if (tempA->data < tempB->data){
            tempC->next = tempA;
            tempA = tempA->next;
            
            tempC = tempC->next;
        }else {//>
            tempC->next = tempB;
            tempB = tempB->next;
            
            tempC = tempC->next;
        }
    }
    printf("循环结束");
    if (tempA == NULL) {
        tempC->next = tempB;
    }else {
        tempC->next = tempA;
    }
    
    free(*B);
    
    return OK;
}

以上算法时间复杂度:O(n)n是较短的链表的长度。

二. 两个递增有序链表A和B,求出A和B的交集存储在A链表中

分析:假设A{1,3,5,6,7,9},B{2,3,6}。交集为A{3,6}。
进入循环:作比较,释放较小的,相等,释放B的。
A:1 A:3>B:2------释放2
A:3=B:3------head->B:3 (B:3没用了,释放掉)
A:5 A:6=B:6------head->3->A:6 (B:6没用了,释放掉)
B循环结束----A剩余的结点一定不存在和B的交集,释放
循环结束,B的头指针没有用了,释放掉。

为了记住A和B循环到哪里了,需要声明两个临时变量来记录:tempAtempB;
为了记录新的链表,需要声明一个临时变量:sameList
为了释放不重复的结点,需要声明一个临时变量记录:temp

Status GetSame(LinkList *A,LinkList *B) {
    
    LinkList tempA,tempB,sameList,temp;
    
    tempA = (*A)->next;
    tempB = (*B)->next;
    sameList = (*A);
    
    while (tempA && tempB) {
        if (tempA->data == tempB->data) {
            sameList->next = tempA;
            sameList = sameList->next;
            
            tempA = tempA->next;
            temp = tempB;
            tempB = tempB->next;
            
        }else if (tempA->data < tempB->data){
            temp = tempA;
            tempA = tempA->next;
            
            free(temp);
        }else{//>
            temp = tempB;
            tempB = tempB->next;
            
            free(temp);
        }
    }
    sameList->next = NULL;
    
    if (tempA) {
        while (tempA) {
            temp = tempA;
            tempA = tempA->next;
            
            free(temp);
        }
    }
    
    if (tempB) {
        while (tempB) {
            temp = tempB;
            tempB = tempB->next;
            
            free(temp);
        }
    }
    
    free(*B);
    
    return OK;
}

以上算法时间复杂度:O(n)n是较长的链表的长度。

三.设计一个算法将链表的所有结点的连接方向原地旋转,要求不开辟额外的内存空间

分析:链表A{1,2,3,4,5,6,7}。旋转后A{7,6,5,4,3,2,1}。
使用头插法创建链表链表时,链表顺序是逆序的,可以使用头插法的思路。不能开辟新的空间,所以依然使用链表A的头指针。

临时变量q记录旧的链表,p记录从旧链表中摘下的结点,用头插法将p插入到新的链表A中。过程如图:

Status translateList (LinkList *A) {
    
    LinkList p,q;
    
    p = (*A)->next;
    (*A)->next = NULL;
    
    while (p) {
        q = p->next;
        
        p->next = (*A)->next;
        
        (*A)->next = p;
        p = q;
    }
    
    return OK;
}

四. 设计算法,删除递增有序链表中值大于等于minK且小于等于maxK的所有元素。(minK和maxK是给定的两个参数,可以和表中元素相同,也可以不同)

分析:链表A{1,2,3,4,5,6,8}删除>=3且<=7的结点之后为A{1,2,8}。
循环找到要删除的第一个结点之前的结点q,并记录要删除的第一个结点deletList,继续循环找到要删除的最后一个结点p,让q指向p的下一个结点完成新的链表。释放删除的结点。

Status DeletNodes(LinkList *A, int minK, int maxK){
    
    LinkList deletList = NULL, p, q;
    
    p = q = (*A)->next;
    
    while (p->next) {
        if (p->next->data >= minK && p->next->data < maxK) {
            deletList = p->next;
            q = p;
            break;
        }
        p = p->next;
    }
    
    while (p->next) {
        if (p->next->data > maxK) {
            q->next = p->next;
            p->next = NULL;
            
            break;
        }else if (p->next->data == maxK) {
            p = p->next;
            q->next = p->next;
            p->next = NULL;
            
            break;
        }
        p = p->next;
    }
    
    if (deletList) {
        LinkList temp;
        p  = deletList->next;
        while (p) {
            temp = p;
            p = p->next;
            
            free(temp);
        }
        
        free(deletList);
    }else {
        return -1;
    }
    
    return OK;
}

五. 设将n(n>1)个整数存放到一维数组R中, 将R中保存的序列循环左移p个位置(0

分析:假设R=[1,2,3,4,5,6,7,8,9]。p=3,移动之后为:R= [4,5,6,7,8,9,1,2,3]。
第一步:[1,2,3,4,5,6,7,8,9]
第二步:[9,8,7,6,5,4] [3,2,1]
第二步:[4,5,6,7,8,9] [1,2,3]

void listChange(int *R, int left, int right) {
    
    int i = left, j = right;
    int temp;
    
    while (i0) {
        listChange(R, 0, n-1);
        listChange(R, 0, n-p-1);
        listChange(R, n-p, n-1);
    }
    
    return OK;
}

以上算法时间复杂度:O(n),空间复杂度O(1)

六.⽤单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计一个时间复杂度尽可能高效的算法. 对于链表中的data绝对值相等的结点, 仅保留第一次出现的结点,而删除其余绝对值相等的结点.

分析:A{2,-3,4,3,15,-15,3}处理后:A{2,-3,4,15}
---------R:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
A:2----- R:[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]
A:-3---- R:[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0]
A:4----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]
A:3----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]---R[3]位置已经是1,这个节点删除
A:15---- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,15,0]
A:-15--- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,15,0]---R[15]位置已经是1,这个节点删除
A:3----- R:[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0]---R[3]位置已经是1,这个节点删除

Status deletSameNode(LinkList *A, int n) {
    
    int *R = malloc(sizeof(int)*(n+1));
    
    for (int i = 0; inext;
    int absValue;
    
    while (temp) {
        absValue = abs((temp->data));
        
        if (R[absValue] == 0) {
            R[absValue] = 1;
            p = p->next;
            temp = temp->next;
        }else {
            //删除这个结点
            p->next = temp->next;
            free(temp);
            temp = p->next;
        }
    }
    return OK;
}

以上算法时间复杂度:O(m)m是链表的长度。空间复杂度:O(n)n是链表中最大的值。

七.A中的n个元素保存在⼀个⼀维数组中,请设计一个尽可能高效的算法,找出数组元素中的主元素,若存在主元素则输出该元素,否则输出-1.

  • 概念解释:主元素-数组中出现次数大于元素总数一半的元素。例如:A[2,1,2,3,2,4,2,5,2,6,2]中2是A的主元素,A[1,2,3,2,4,2,5,2,6,2]中没有主元素。

分析:如果A中一个11个元素,元素X出现了6次,那么就有5个其他元素,让1个X和一个其他元素结为一对。那么最后会剩下一个单独的元素,而且一定是X。再判断X出现的次数是否大于总元素个数的一半,我们就知道了X是否是主元素。

int findMainElement(int *A, int n){
    
    int count = 1;
    int key = A[0];
    
    for (int i = 1; i0) {
                count--;
            }else {
                key = A[i];
                count = 1;
            }
        }
        printf("count=%d,key=%d\n",count,key);
    }
    
    int num = 0;
    if (count > 0) {
        for (int i = 0; i n/2) {
            printf("主元素是:%d\n",key);
            return key;
        }
    }
    printf("没有主元素\n");
    return -1;
}

以上算法时间复杂度:O(n)。空间复杂度:O(1)

你可能感兴趣的:(线性表相关算法题)