数据结构-线性表-7道算法题研究

学有所用

经过前几天对线性表的学习,用几道算法题对线性表的此次学习进行收尾。每一个人的思惟方式不一样,写的算法代码也不一样。如下的相关代码,是本人感受思路比较清晰简洁的方式。如你有本身的想法,能够本身尝试一下,但愿能够将你的方式分享出来给我,谢谢!算法

1.将2个递增的有序链表合并为一个链表的有序链表;要求结果链表仍然使用两个链表的存储空间,不另外占用其余的存储空间。表中不容许有重复的数据

1.1分析已知条件

  • 两个递增有序链表list一、list2
  • 不使用额外的存储空间
  • list3中不容许有重复数据
  • 至关于求两个链表的并集

1.2思路

  • 同时遍历两个链表
  • 取出两个链表中相对小的结点
  • 若是相等,取list1中的结点,释放list2中的结点
  • 取到的结点插入到list3中(链表后插法)

1.3代码实现

Status mergeOrderedLists(LinkList *list1, LinkList *list2, LinkList *list3) {
    LinkList p1 = (*list1)->next;
    LinkList p2 = (*list2)->next;
    LinkList p3 = *list1;
    *list3 = p3;
    LinkList temp;
    
    //循环遍历两个列表
    while (p1 && p2) {
        if (p1->data < p2->data) {//若是链表1中的结点data小于链表2中结点data,链表1结点插入到链表3中
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
        } else if (p1->data > p2->data) {//若是链表1中的结点data大于链表2中结点data,链表2结点插入到链表3中
            p3->next = p2;
            p3 = p2;
            p2 = p2->next;
        } else {//若是链表1中的结点data等于链表2中结点data,保留链表1的结点,free链表2的结点
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
            
            temp = p2->next;
            free(p2);
            p2 = temp;
        }
    }
    //退出循环的条件是链表1或者链表2同时指向NULL或者其中一个指向NULL,用一个三则运算符,把剩下的结点插入到链表3中。
    p3->next = p1?p1:p2;
    free(*list2);
    
    return OK;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    LinkList list1;
    LinkList list2;
    createList(&list1);
    createList(&list2);
    for (int i = 1; i < 10; i += 2) {
        insertValue(&list1, i);
    }

    insertValue(&list2, 1);
    insertValue(&list2, 2);
    insertValue(&list2, 3);
    insertValue(&list2, 4);
    
    printf("list1:");
    displayList(list1);
    printf("\n");
    printf("list2:");
    displayList(list2);
    printf("\n");
    
    LinkList list3;
    mergeOrderedLists(&list1, &list2, &list3);
    displayList(list3);
    
    return 0;
}
复制代码

1.4运行结果

==================分割线==================

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

2.1分析已知条件

  • 两个递增有序链表list一、list2
  • 不占用额外的存储空间
  • list3中存储两个链表的交集结点
  • 至关于求两个链表的交集

2.2思路

  • p1和p2指向两个链表首元结点,开始遍历。
  • p1->data == p2->data
  • 保存p1结点到list3中,释放p2结点
  • 不相等,删除较小的结点,并向下遍历
  • 若是有一个链表遍历到头了,删除另外一个链表的后续结点

2.3代码实现

Status orderedListIntersection(LinkList *list1, LinkList *list2, LinkList *list3) {
    LinkList p1 = (*list1)->next;
    LinkList p2 = (*list2)->next;
    LinkList p3 = *list1;
    *list3 = p3;
    LinkList temp;
    
    while (p1 && p2) {
        if (p1->data == p2->data) {//相等,把p1加入的list3中,释放p2。p1和p2都往下指向
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
            
            temp = p2->next;
            free(p2);
            p2 = temp;
        } else if (p1->data < p2->data){//p1小,释放p1,p1向下指向
            temp = p1->next;
            free(p1);
            p1 = temp;
        } else {//p2小,释放p2,p2向下指向
            temp = p2->next;
            free(p2);
            p2 = temp;
        }
    }
    
    while (p1) {//p1后面还有结点,循环删除
        temp = p1->next;
        free(p1);
        p1 = temp;
    }
    
    while (p2) {//p2后面还有结点,循环删除
        temp = p2->next;
        free(p2);
        p2 = temp;
    }
    
    //p3链表结尾设为NULL
    p3->next = NULL;
    free(*list2);
    
    return OK;
}
int main(int argc, const char * argv[]) {
    LinkList list1;
    LinkList list2;
    
    createList(&list1);
    insertValue(&list1, 1);
    insertValue(&list1, 2);
    insertValue(&list1, 3);
    insertValue(&list1, 4);
    insertValue(&list1, 5);
    insertValue(&list1, 6);
    insertValue(&list1, 7);
    insertValue(&list1, 8);
    insertValue(&list1, 9);
    printf("list1\n");
    displayList(list1);
    
    createList(&list2);
    insertValue(&list2, 2);
    insertValue(&list2, 4);
    insertValue(&list2, 6);
    insertValue(&list2, 8);
    insertValue(&list2, 10);
    printf("list2\n");
    displayList(list2);
    
    LinkList list3;
    orderedListIntersection(&list1, &list2, &list3);
    printf("list3\n");
    displayList(list3);
    
    return 0;
}
复制代码

2.4运行结果

==================分割线==================

3.设计一个算法,将链表中全部节点的连接方向”原地旋转“,即要求仅仅利用原表的存储空间。换句话说,要求算法空间复杂度为O(1)。例如:L={0,2,4,6,8,10},逆转后:L={10,8,6,4,2,0}

3.1分析已知条件

  • 原地旋转链表
  • 不能使用额外的存储空间,空间复杂度为O(1)

3.2思路

  • 链表头插法
  • p指向链表的首元结点,遍历链表
  • 取出p指向的结点,插入到链表的头结点后面

3.3代码实现

Status listReverseOrder(LinkList *list) {
    LinkList p = (*list)->next;
    LinkList q;
    (*list)->next = NULL;
    while (p) {
        q = p->next;
        p->next = (*list)->next;
        (*list)->next = p;
        p = q;
    }
    
    return OK;
}
int main(int argc, const char * argv[]) {
    LinkList list;
    createList(&list);
    for (int i = 0; i <= 10; i+=2) {
        insertValue(&list, i);
    }
    displayList(list);
    
    listReverseOrder(&list);
    displayList(list);
    
    return 0;
}
复制代码

3.4运行结果

==================分割线==================

4.设计一个算法,删除递增有序链表中值大于等于mink且小于等于maxk(mink,maxk是给定的两个参数,其值能够和表中的元素相同,也能够不一样)的全部元素

4.1分析已知条件

  • 递增有序链表
  • 删除mink-maxk之间的值,包含等于

4.2思路

  • 找最小值的前一个结点
  • 找到最大值的结点
  • 循环删除找到的范围内的结点

4.3代码实现

Status OrderedListDelElemByRange(LinkList *list, ElemType min, ElemType max) {
    if (*list == NULL) {
        return ERROR;
    }
    
    LinkList p = (*list)->next;
    LinkList low = NULL, high = NULL;
    //找到小于min的结点的前一个结点low
    while (p && p->data < min) {
        low = p;
        p = p->next;
    }
    //找到小于等于max的结点high
    while (p && p->data <= max) {
        p = p->next;
    }
    high = p;
    //原链表剔除low->next到high之间的结点
    low->next = high;
    
    //删除low->next到high之间的结点
    LinkList temp;
    while (low != high) {
        temp = low->next;
        free(low);
        low = temp;
    }
    
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkList list;
    createList(&list);
    for (int i = 1; i < 100; i++) {
        insertValue(&list, i);
    }
    displayList(list);
    
    OrderedListDelElemByRange(&list, 8, 24);
    displayList(list);
    
    return 0;
}
复制代码

4.4运行结果

==================分割线==================

5.设将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}

5.1分析已知条件

  • 是数组,不是链表
  • 时间和空间两个方面尽量高效
  • 数组循环左移

5.2思路

  • 倒叙原数组{9,8,7,6,5,4,3,2,1,0}
  • 找到分割点,分割数组{(9,8,7,6,5,4,3),(2,1,0)}
  • 再分别倒叙分割点的前部分和后部分{(3,4,5,6,7,8,9),(0,1,2)}

5.3代码实现

void reverse(int *arr,int left ,int right){
    int low = left;
    int high = right;
    while (low < high) {
        arr[low] ^= arr[high] ^= arr[low] ^= arr[high];
        low ++;
        high --;
    }
}

void leftShift(int *arr, int n, int p){
    if (p > 0 && p < n) {
        //总体翻转
        reverse(arr, 0, n - 1);
        //翻转分割点前面部分
        reverse(arr, 0, n - p - 1);
        //翻转分割点后面部分
        reverse(arr, n-p, n-1);
    }
}
int main(int argc, const char * argv[]) {
    int arr[10] = {0,1,2,3,4,5,6,7,8,9};
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    leftShift(arr, 10, 3);
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}
复制代码

5.4运行结果

==================分割线==================

6.已知一个整数序列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.

6.1分析已知条件

  • 找主元素
  • 元素出现的次数大于数组长度的一半

6.2思路

  • 先找候选主元素
  • key = 第一个元素, count = 1
  • 循环数组
  • 下一个元素与key相等,count++
  • 下一个元素与key不相等,而且count > 0,count
  • 若是count < 0,key = 当前元素,count = 1
  • 若是count > 0
  • 循环找这个元素出现的次数,用count表示
  • count大于数组的length/2 返回key 不然返回-1

6.3代码实现

int getMainElement(int *arr, int n){
    //把第一个元素当作候选主元素
    int key = arr[0];
    int count = 1;
    for (int i = 1; i < n; i++) {
        if (key == arr[i]) {
            count ++;
        } else {
            if (count > 0) {
                count --;
            } else {
                key = arr[i];
                count = 1;
            }
        }
    }
    
    //count > 0 ,统计候选主元素出现的次数,用count表示
    if (count > 0) {
        for (int i = count = 0; i < n; i++) {
            if (arr[i] == key) {
                count ++;
            }
        }
    }
    
    //判断count是否知足大于数组一半的要求
    if (count > n / 2) {
        return key;
    } else {
        return -1;
    }
}
int main(int argc, const char * argv[]) {
    int a[] = {0,5,5,3,5,7,5,5};
    int b[] = {0,5,5,3,5,1,5,7};
    int c[] = {0,1,2,3,4,5,6,7};
    for (int i = 0; i < 8; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
    int value = getMainElement(a, 8);
    printf("value = %d \n", value);
    
    value = getMainElement(b, 8);
    printf("value = %d \n", value);
    
    value = getMainElement(c, 8);
    printf("value = %d \n", value);
    
    return 0;
}
复制代码

6.4运行结果

==================分割线==================

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

7.1分析已知条件

  • 时间复杂度尽量高的算法
  • 空间换时间
  • 申请一个空间大小为n+1(0号单元不使用)的辅助数组
  • 数组中保存链表中出现的值
  • 这样,一次循环就能够达到目的

7.2思路

  • 申请大小为n+1的辅助数组
  • 遍历链表
  • 若数组中 |data| = 0 说明第一次出现 并赋值1
  • 若不等于 0 说明已经出现过,删除结点

7.3代码实现

void listDelRepeatElem(LinkList *list,int n){
    //开辟辅助数组
    int *arr = alloca(sizeof(int)*n);
    for (int i = 0; i < n; i++) {
        *(arr+i) = 0;
    }
    
    LinkList p = (*list)->next;
    LinkList temp = *list;
    
    //遍历链表
    while (p) {
        //若是该绝对值已经在结点上出现过,则删除该结点
        if (arr[abs(p->data)] == 1) {
            temp->next = p->next;
            free(p);
            p = temp->next;
        } else {//未出现过的结点,则将数组中对应位置置为1
            arr[abs(p->data)] = 1;
            temp = p;
            p = p->next;
        }
    }
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    LinkList list;
    createList(&list);
    insertValue(&list, 21);
    insertValue(&list, -15);
    insertValue(&list, 15);
    insertValue(&list, -7);
    insertValue(&list, 15);
    displayList(list);
    listDelRepeatElem(&list, 21);
    displayList(list);
    
    return 0;
}
复制代码

7.4运行结果

==================分割线==================

总结

线性表告一段落,接下来还会继续学习数据结构相关知识。继续享受学习过程的快乐。记住:沿途的风景要比目的地更弯的否!!!数组

相关文章
相关标签/搜索