王道算法题(线性表相关)

254 阅读7分钟

线性表相关


单链表的原地逆置(带头结点)

思想:将头结点摘下,然后从第一个结点开始,依次前插到头结点后面(其实就相当于前插法创建链表)

LinkList Reverse(LinkList L)
{
    LinkList p;
    p = L->next;    //找到首结点
    L->next = NULL; //摘下头结点
    while (p != NULL)
    {
        p->next = L->next;
        L->next = p;
    }
    return L;
}



顺序表的原地逆置

思想:第一个元素和最后一个元素对调,第一二个元素和倒数第二个元素对调,...,依此类推。

void Reverse(SqList &L)
{
    int n =L.length;
    int,temp;
    for(i=0,i<n/2;i++)
    {
        temp=L.data[i];
        L.data[i]=L.data[n-i-1];
        L.data[n-i-1]=temp;
    }
}

应用:将数组R中某序列左移p个位置

思路:先将[0,p-1]逆置,再将[p,n-1]逆置,最后整个数组逆置。




从尾到头反向输出链表元素(带头结点)

方法1:先原地逆置,再输出

方法2:正向读取依次放到栈中,然后输出栈中元素

方法3:利用递归,每当访问一个结点时i,先递归输出它后面的结点,再输出它自己。(如下)

void Reverse(LinkList L)
{
    if (L->next != NULL)
        Reverse(L->next);
    cout << L->data;
}



两个有序顺序表合并成一个新的有序顺序表

思想:首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。

bool Merge(SqList A, SqList B, SqList &C)
{
    int lenA = A.length, lenB = B.length;
    if (lenA + lenB > C.maxSize)
        return false;

    int i = 0, j = 0, k = 0;
    while (i < lenA && j < lenB)
    {
        if (A.data[i] <= B.data[j])
            C.data[k++] = A.data[i++];
        else
            C.data[k++] = B.data[j++];
    }
    while (i < lenA)
        C.data[k++] = A.data[i++];
    while (j < lenB)
        C.data[k++] = A.data[j++];

    C.length = k;
    return true;
}

非常典型。




两个递增的链表合并成一个递减的链表。(不采用第三个链表)

思想:均从第一个结点进行比较,将较小的结点链入链表,同时后移工作指针后移。另外,用头插法来解决总链表递减的问题。

void Merge(LinkList &La, LinkList &Lb)
{
    LinkList r, pa = La->next, pb = Lb->next;
    La->next = NULL;   // 为了头插,将头结点取下

    while (pa && pb)
    {
        if (pa->data <= pb->data)
        {
            r = pa->next;
            pa->next = La->next;
            La->next = pa;

            pa = r;
        }
        else
        {
            r = pb->next;
            pb->next = La->next;
            La->next = pb;

            pb = r;  // 工作指针后移,但不能用pa=pa->next,因为pa->next已被占用
        }
    }
    if (pa)
        pb = pa;
    while (pb)
    {
        r = pb->next;
        pb->next = La->next;
        La->next = pb;

        pb = r;
    }
    free(Lb);
}



两个有序链表的有序合并(合并到第三个链表中)

LinkList Merge(LinkList L1, LinkList L2)
{
    LinkList L = NULL;
    if (L1 == NULL)
        return L2;
    else if (L2 == NULL)
        return L1;
    else
    {
        if (L1->data < L2->data)
        {
            L = L1;
            L->next = Merge(L1->next, L2);
        }
        else
        {
            L = L2;
            L->next = Merge(L1, L2->next);
        }
        return L;
    }
}




两个链表的拼接

void Merge2(LinkList L1, LinkList L2)
{
    LinkList p;
    for (p = L1; p->next != NULL; p = p->next);

    p->next = L2->next;  // 与第二个链表的首结点连上,而不是头结点
    free(L2);  // 释放第二个链表的头结点
}




将循环单链表L2连接到循环单链表L1后面,使仍保持循环

思想:先找到两个链表的尾指针,将第一个链表的尾指针与第二个链表的头结点连接起来,再使之成为循环的。

LinkList Link(LinkList &L1, LinkList &L2)
{

    LinkList p, q;
    p = L1;
    while (p->next != L1)
        p = p->next;
    q = L2;
    while (q->next != L2)
        q = q->next;
    p->next = L2;
    q->next = L1;
    return L1
}



判单循环双链表是否对称

思想:从两头往中间扫描

bool Symmetry(LinkList L)
{
    LinkList p = L->next, q = L->prior;
    while (p != q && p->next != q) //总长度为奇或偶两种情况
    {
        if (p->data == q->data)
        {
            p = p->next;
            q = q->prior;
        }
        else
            return false;
    }
    return true;
}



删除顺序表中所有值为x的元素。要求时空复杂度为O(n)、O(1)

void Del_x(SqList &L, int x)
{
    int i, k = 0;
    for (i = 0; i < L.length; i++)
    {
        if (L.data[i] != x)  // 如果换成【L.data[i]<S || L.data[i]>T】,就是【删除值介于S与T之间的元素】
        {
            L.data[k] = L.data[i];
            k++;
        }
    }
    L.length = k;  // 这一句很重要
}




递归删除单链表中所有值为x的结点(不带头结点)

void Del(LinkList &L, int x)
{
    LinkList q; // q指向待删除结点,当替罪羊
    if (L == NULL)
        return;
    if (L->data == x)
    {
        q = L;
        L = L->next;
        free(q);
        Del(L, x);
    }
    else
        Del(L->next, x);
}




删除单链表中所有值为x的结点(带头结点)

思想:用p从头到尾扫描, pre是其前前驱。若p所指向的值为x, 则删除,并让p移向下一个节点;否则让p和pre同步后移一个节点。

void DelX(LinkList &L, int x)
{
    LinkList pre = L, p = L->next, q; //想要删除p结点,就要知道它的前驱,故pre结点不能省略 。q当替罪羊
    while (p != NULL)
    {
        if (p->data == x) //如果修改为【if(p->data>low && p->data<high)】,则该函数的功能就是【删除值介于low和high之间的所有节点】
        {
            q = p; // q指向待删除结点(替罪羊)
            p = p->next;
            pre->next = p;
            free(q);
        }
        // 也能这样写,因为待删除结点和其前驱都是已知的前驱
        // if(p->data==x)
        // {
        //     pre->next=p->next;
        //     free(p);
        //     p=pre->next;
        // }

        else
        {
            pre = p;
            p = p->next;
        }
    }
}



删除单链表中的唯一最小值结点(带头结点)

void DelMin(LinkList &L)
{
    LinkList pre = L, p = L->next;
    LinkList minpre = pre, minp = p;
    while (p != NULL)
    {
        if (p->data < minp->data)
        {
            minp = p;
            minpre = pre;
        }
        pre = p;
        p = p->next;
    }
    minpre->next = minp->next;
    free(minp);
}




删除单链表绝对值相同的数

思想:设置一个数组,用于判断是否出现过绝对值相同的元素,将出现过的元素在数组对应位置中标记为1,如果之后的元素在数组中等于1,说明之前出现过相等的绝对值,将他删除

void del_same(LinkList &L, int n)
{
    int m;
    int *q = new int[n + 1];
    LNode *p = L, *r;
    for (int i = 0; i < n + 1; i++)
        q[i] = 0;
    while (p->next != NULL)
    {
        m = p->next->data > 0 ? p->next->data : -p->next->data;
        if (q[m] == 0)
        {
            q[m] = 1;
            p = p->next;
        }
        else
        {
            r = p->next;
            p->next = r->next;
            free(r);
        }

        delete q;
    }
}



有序表中删除所有值重复的元素,要求时空复杂度为O(n),O(1)

void Del_Same(SqList L)
{
    int len = L.length;
    int i, k = 1;
    for (i = 1; i < len; i++)
    {
        if (L.data[i] != L.data[i - 1])
            L.data[k++] = L.data[i];
    }
    L.length = k;  // 不能省略,因为k之后还有其他元素,但不是想要的
}

(如果改成无序表,则要用散列表才能达到时间复杂度O(n))




按递增序列输出单链表中各节点数据元素,并删除此元素(带头结点)

思想:遍历一次找到一个最小结点,输出并删除,以此类推遍历n次,每次都找到最小结点输出并删除。

void del_min(LinkList L)
{
    LinkList minpre = L, p = L->next, q;
    while (L->next != NULL)
    {
        while (p->next != NULL)
        {
            if (p->next->data < minpre->next->data)
                minpre = p;
            p = p->next;
        }
        cout << minpre->next->data;
        q = minpre->next;
        minpre->next = q->next;
        free(q);
        minpre = L;
        p = L->next;
    }
    free(L);
}



用高效算法输出单链表中倒数第k个值

思想:设置两个指针pq,均指向第一个节点。p向后移动,当移动到第k个结点时,两个指针开始同时后移,当p到末尾时,q恰好在第k个结点上。

int Search_k(LinkList &L, int k)
{
    LNode *p = L->next, *q = L->next;
    int i = 1;
    while (i < k)
    {
        p = p->next;
        i++;
    }
    if (p == NULL)
        return 0;
    while (p->next != NULL)
    {
        p = p->next;
        q = q->next;
    }
    cout << q->data << endl;
    return 1;
}



将链表A分成链表A和B,分别含有奇位元素和偶位元素

思想:为保持相对顺序不变,采用尾插法。

void Split(LinkList &A)
{
    int i = 0;
    LinkList B, p;
    B = (LinkList)malloc(sizeof(LNode));
    B->next = NULL;
    LinkList ra = A, rb = B;

    p = A->next;
    A->next = NULL;
    while (p != NULL)
    {
        i++;
        if (i % 2 == 0)
        {
            rb->next = p;
            rb = p;
        }
        else
        {
            ra->next = p;
            ra = p;
        }
        p = p->next;
    }
    ra->next = rb->next = NULL;
}




判断链表B是否为链表A的连续子序列

算法思想:因为两个整数序列已存入两个链表中,操作从两个链表的第一个结点开始,若对应数据相等,则后移指针;若对应数据不相等,则A链表从上次开始比较结点的后继开始,B链表仍从第一个结点开始比较,直到B链表到尾表示匹配成功。A链表到尾而B链表未到尾表示失败。操作中应记住A链表每次的开始节点,以便下次匹配时好从其后继开始。

int Pattern(LinkList A, LinkList B)
{
    LinkList p = A;
    LinkList pre = p;
    LinkList q = B;
    while (p && q)
    {
        if (p->data == q->data)
        {
            p = p->next;
            q = q->next;
        }
        else
        {
            pre = pre->next;  // 主串从上次开始比较的结点的后继开始
            p = pre;
            q = B;     // 子串从头开始
        }
    }
    if (q == NULL)
        return 1;
    else
        return 0;
}




在递增的单链表中,使重复的值只保留一个

思想:既然是递增,那么重复的值一定是相邻的。用p扫描链表L,若 *p的值等于它后继结点的值,则删除它的后继结点,否则p移向下一个结点

void DelSame(LinkList &L)
{
    LinkList p = L->next, q;
    if (p == NULL)
        return;
    while (p->next != NULL)
    {
        q = p->next;
        if (p->data == q->data)
        {
            p->next = q->next;
            free(q);
        }
        else
            p = p->next;
    }
}