线性表-链表算法题

234 阅读5分钟

C语言链表定义及基本方法:

#include <stdio.h>

#define OK 1
#define ERROR 0

typedef int ElemType;
typedef int Status;

typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

typedef struct Node * LinkList;

// initialize
Status InitilizeList(LinkList *L){
    *L = (LinkList)malloc(sizeof(Node)); // L is tou jie dian
    if (*L == NULL) return ERROR; // why we need detect whether L is null???
    (*L)->next = NULL;
    return OK;
}

// print all element in linkedlist
Status ListTraverse(LinkList L){
    LinkList p = L->next;
    if (!p){
        printf("the list is empty\n");
        return ERROR;
    }
    while(p) {
        printf(" %d ", p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

// insert node into a linkedlist
Status ListInsert(LinkList *L, int i, ElemType e) {
    int j;
    LinkList p, s;
    p = *L;
    j = 1;
    while(p && j<i) {
        p = p->next;
        ++j;
    }

    if (!p || j>i) return ERROR; // why j>i
    s = (LinkList)malloc(sizeof( Node));
    s->data = e;

    s->next = p->next;
    p->next = s;

    return 0;
}
  1. 将两个递增的有序链表合并为一个有序链表;要求结果链表仍然使用两个链表的存储空间,不另外占用其他的存储空间。表中不允许有重复的数据。(输入{1,2,3},{3,6,9}。 输出{1,2,3,6,9})
void mergeList(LinkList *La, LinkList *Lb, LinkList *Lc){
    LinkList pa, pb, pc, temp;
    pa = (*La)->next;
    pb = (*Lb)->next;

    *Lc = *La;
    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 {
            pc->next = pa;
            pc = pa;
            pa = pa->next;

            temp = pb->next;
            free(pb);
            pb = temp;
        }

    }

    pc->next = pa ? pa : pb;
    free(*Lb);

}
  1. 已知两个链表A和B分别表示两个集合,其元素递增排列,设计一个算法,用于求出A与B的交集,并存储在A链表中
void Intersection(LinkList *La, LinkList *Lb, LinkList *Lc){
    LinkList 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;
            temp = pb;
            pb = pb->next;
            free(temp);
        } else if(pa->data < pb->data){
            temp = pa;
            pa = pa->next;
            free(temp);
        } else {
            temp = pb;
            pb = pb->next;
            free(temp);
        }
    }

    pc->next = NULL;

    while(pa){
        temp = pa;
        pa = pa->next;
        free(temp);
    }

    while(pb){
        temp = pb;
        pb = pb->next;
        free(temp);
    }
}

3.设计一个算法,将链表中的所有节点的链接方向“原地旋转”, 要求仅仅利用原表的存储空间, 换句话说,要求算法空间复杂度为O(1)。

void reverseLinkList(LinkList *L){

    LinkList p, q;
    p = (*L)->next;
    (*L)->next = NULL;

    while(p){
        q = p->next;
        p->next = (*L)->next;
        (*L)->next = p;
        p = q;
    }
}
  1. 设计一个算法,删除递增有序链表中值大于等于mink且小于等于maxk(mink, maxk是给定的两个参数, 其值可以和表中的元素相同,也可以不同)的所有元素。
void DeleteMinMax(LinkList *L, int mink, int maxk){
    LinkList p, q, pre, temp;
    pre = *L;

    p = (*L)->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;
    }
}
  1. 设将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) n个数据原地逆置;{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, (2) 拆解; {9, 8, 7, 6, 5, 4, 3}{2, 1, 0} (3) n-p数据/p个数据 再一次逆置 {3 4 5 6 7 8 9}{0 1 2}
void Reverse(int *pre, int left, int right){

    int i = left, j = right;
    int temp;
    while(i<j){
        temp = pre[i];
        pre[i] = pre[j];
        pre[j] = temp;

        i++;
        j--;
    }

}

void LefrShift(int *pre, int n, int p){

    if(p>0 && p<n){
        Reverse(pre, 0, n-1);
        Reverse(pre, 0, n-p-1);
        Reverse(pre, n-p, n-1);
    }

}
  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。 思路: (1)选择候选主元素,循环计数方式找到链表哪个元素出现次数多!认为候选人 (2)判断到底是不是候选,候选人count > n/2
int MainElement(int *A, int n){
    int count = 1;
    int key = A[0];

    for(int i=1; i<n; i++){
        if(A[i] == key){
            count++;
        } else {
            if(count>0){
                count--;
            }else {
                key = A[i];
                count = 1;
            }
        }
    }

    int total = 0;
    for(int i=0; i<n; i++){
        total = (A[i] == key) ? total+1 : total;
    }

    return (total > n/2) ? key : -1;
}

7.用单链表保存m个整数,结点的结构为(data,link),且|data|<=n(n为正整数)。 现在要去设计一个时间复杂度尽可能高效的算法。对于链表中的data绝对值相等的结点,仅保留第一次出现的结点,而删除其余绝对值相等的结点。例如:链表A={21,-15,15,-7,15},删除后的链表A={21,-15,-7}; 算法思路: (1)申请n+1辅助空间数组,初值设为零; (2)首元结点开始遍历,t[|data|],保留这个结点,t[|data|] = 1; 若t[|data|]=1, 删除;

void DeleteEqualNode(LinkList *L, int n){

    int *p = alloca(sizeof(int)*(n+1));
    LinkList r = *L;
    for(int i=0; i<n+1; i++){
        *(p+i) = 0;
    }

    LinkList temp = r->next;
    while(temp != NULL){
        if(p[abs(temp->data)] == 0){
            p[abs(temp->data)] = 1;
            r = temp;
            temp = temp->next;
        } else {
            r->next = temp->next;
            free(temp);
            temp = r->next;
        }
    }

}