数据结构与算法 实例(萌新)

记录一下那些优化时间复杂度和空间复杂度的小题
1.顺序表
相关定义如下:

struct _seqlist{
    ElemType elem[MAXSIZE];
    int last;
};
typedef struct _seqlist SeqList;

(1)设计一个高效的算法,从顺序表L中删除所有值介于x和y之间(包括x和y)的所有元素(假设y>=x),要求时间复杂度为O(n),空间复杂度为O(1)。

void del_x2y(SeqList* L, ElemType x, ElemType y)
{
    int i = 0, j = 0;
    while (i <= L->last) {
        if (!(L->elem[i] >= x && L->elem[i] <= y)) {
            L->elem[j] = L->elem[i];
            i++;
            j++;
        } else
            i++;
    }
    L->last = j - 1;
}

(2)编写算法,在一非递减的顺序表L中,删除所有值相等的多余元素。要求时间复杂度为O(n),空间复杂度为O(1)。

void del_dupnum(SeqList* L)
{
    int i = 0, j = 0;
    while (i <= L->last) {
        if (L->elem[j] == L->elem[j + 1] || (L->elem[j - 1] == L->elem[i]) || (L->elem[j] < L->elem[j - 1])) {
            if (L->elem[j] == L->elem[i] || (L->elem[j - 1] == L->elem[i]))
                i++;
            else if (j == 0 || (L->elem[j] > L->elem[j - 1])) {
                L->elem[++j] = L->elem[i];
            } else
                L->elem[j] = L->elem[i];
        } else {
            L->elem[j] = L->elem[i];
            i++;
            j++;
        }
    }

    if (L->elem[j - 1] >= L->elem[j])
        L->last = j - 1;
    else
        L->last = j;
}

这种删除多余重复的值。其实这也只是在说明元素的特性,但相比与第一例麻烦的是不能由题目直接得出具体化的表达式。这个题的代码我也调试了很久,在诸多情况下未能找到一种通法,只能不断地通过提示增加条件。如果有大佬知道怎么简化,欢迎交流。
(3)已知顺序表L中的数据元素类型为int。设计算法将其调整为左右两部分,左边的元素(即排在前面的)均为奇数,右边所有元素(即排在后面的)均为偶数,并要求算法的时间复杂度为O(n),空间复杂度为O(1)。

void odd_even(SeqList* L)
{
    int i = 0, j = L->last, temp;
    while (i < j) {
        if (L->elem[i] % 2 == 1)
            i++;
        if (L->elem[j] % 2 == 0)
            j--;
        if (i >= j)
            break;
        if (L->elem[i] % 2 == 0 && L->elem[j] % 2 == 1) {
            temp = L->elem[i];
            L->elem[i] = L->elem[j];
            L->elem[j] = temp;
        }
    }
}

以上例子总结:类似于设计一个高效的算法,从顺序表L中删除所有值为x的元素,并要求算法的时间复杂度为O(n),空间复杂度为O(1)。i 记录原表的位置;j 记录新表将要放数据的位置 若L.data[i] != x,则让L.data[j]=L.data[i],然后i++,j++;若L.data[j] == x,则j++,继续向后找,直到找到值非x的元素并将其移入到L.data[i],然后i++,j++;直到表末尾。

void   delx(SeqList  &L, ElemType x) {
    int i, j ;    
    i=0;
    j=0;
    while(i<L.length)
        if(L.data[i]!=x){ 
            L.data[j]=L.data[i]; /* 建立没有 x 的新表 */
            i++;
            j++;
        }
        else i++; /* 过滤掉 x 的数据项 */   
    L.length-=j;
}

这种为了提高效率一般会动用多个变量记录数组下标的变化。因为无论如何至少会遍历一遍数组。其中无论是删除特定的x ,或是在某个范围之间的值,这只是说明了某个元素是否满足题目条件。
2.链表
相关定义如下:

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

(1)已知线性表中的元素(整数)以值递增有序排列,并以单链表作存储结构。试写一高效算法,删除表中所有大于mink且小于maxk的元素(若表中存在这样的元素),分析你的算法的时间复杂度。

void lnk_del_x2y(LinkList L, ElemType mink, ElemType maxk)
{
    Node *p = L->next, *prev = L, *k = L; //尽量不用p=p->next来作为判断条件,容易造成程序运行超时
    for (; p->next;) {
        if (p->data > mink && p->data < maxk) {
            prev = p;
            p = p->next;
            free(prev);
        } else if (p->data <= mink) {
            k = p; //保留k
            p = p->next;
        } else if (p->data >= maxk)
            break;
        else
            continue;
    }
    k->next = p;
}

在这个例子中的五种情况下,我找到了一种通法。
假如mink=3,maxk=7 则有以下五种情况:
1,2,3,4; 1,2,3;4,5,6;1,2,3,4,5,6,7,8;6,7,8

(2)已知一个带有表头结点的单链表, 假设链表只给出了头指针L。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,函数通过指针参数 p_ele 返回该结点 data 域的值,此时函数返回 1;否则,函数返回 0。

int lnk_search(LinkList L, int k, ElemType* p_ele)
{
    Node *p = L, *n = L;
    int count = 0;
    while (p) {
        p = p->next;
        count++;
        if (count >= k) {
            n = n->next;
        }
    }
    *p_ele = n->data;
    if (n == L)
        return 0;
    else
        return 1;
}

总结:
(3)设线性表A=(a1, a2,…,am),B=(b1, b2,…,bn),试写一个按下列规则合并A、B为线性表C的算法,使得:C= (a1, b1,…,am, bm, bm+1, …,bn) 当m≤n时;或者C= (a1, b1,…,an, bn, an+1, …,am) 当m>n时。线性表A、B、C均以单链表作为存储结构,且C表利用A表和B表中的结点空间构成。注意:单链表的长度值m和n均未显式存储。即将A和B合并为C,其中 C 已经被初始化为空单链表

void lnk_merge(LinkList A, LinkList B, LinkList C)
{
    Node* pa = A->next;
    Node* pb = B->next; //pa、pb当前节点
    Node* pc = C; //借用表La的头结点作为表Lc的头结点
    while ((pa != NULL) && (pb != NULL)) {
        pc->next = pa;
        pc = pa;
        pa = pa->next;
        pc->next = pb;
        pc = pb;
        pb = pb->next;
    }

    if (pa != NULL)
        pc->next = pa;
    else
        pc->next = pb;
    free(B);
    free(A); //将Lb的表头释放
}

总结:链表问题尽量不要用p=p->next来作为条件、循环等语句判断条件,容易造成程序运行超时(你永远也不知道哪种情况会导致p=p->next不存在,有可能这种情况满足你的代码,但当你没考虑到的情况可能会使程序崩溃)。

入坑不久,水平有限。还请各路大神多加指教。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值