考研——线性表算法大题整理(二)

亿点说明:对于要用带头结点或者不带头结点的单链表问题,题目只要不特别指明,默认都是对带头结点的单链表进行操作

一、两个单链表的操作问题

1. A、B递增,求C = A ∪ B (并集,归并)

无题目链接

(1) C递增
// 利用尾插法将两个递增单链表归并
LNode* merge(LNode* A, LNode* B)
{
    auto dummy = new LNode(-1);
    auto cur = dummy;
    A = A->next, B = B->next;
    while (A && B)
    {
        if (A->data <= B->data)
        {
            cur->next = A;
            A = A->next;
            cur = cur->next;
        }
        else
        {
            cur->next =B;
            B = B->next;
            cur = cur->next;
        }
    }
    if (A) cur->next = A;
    else cur->next = B;
    return dummy;
}
(2) C递减
// 利用头插法将两个递增单链表归并
LNode* merge(LNode* A, LNode* B)
{
    auto dummy = new LNode(-1);
    dummy->next = NULL;
    A = A->next, B = B->next;
    while (A && B)
    {
        if (A->data <= B->data)
        {
            auto s = A;
            A = A->next;	// 不可以放下面两句后面
            s->next = dummy->next;
            dummy->next = s;
        }
        else
        {
            auto s = B;
            B = B->next;	// 不可以放下面两句后面
            s->next = dummy->next;
            dummy->next = s;
        }
    }
    while (A)
    {
        auto s = A;
        A = A->next;
        s->next = dummy->next;
        dummy->next = s;
    }
    while (B)
    {
        auto s = B;
        B = B->next;
        s->next = dummy->next;
        dummy->next = s;
    }
    return dummy;
}

2. A、B递增,求A = A - B (差集),A递增

无题目链接

LNode* difference(LNode* A, LNode* B)
{
    auto p = A->next, q = B->next;
    auto pre = A;
    while (p && q)
    {
        if (p->data < q->data)
        {
            pre = p;
            p = p->next;
        }
        else if (p->data > q->data)
            q = q->next;
        else
        {
            auto r = p;
            pre->next = r->next;
            p = p->next;
            free(r);
        }
    }
    return A;
}

3. A、B递增,求A = A ∩ B (交集),A递增

无题目链接

LNode* converge(LNode* A, LNode* B)
{
    auto p = A->next, q = B->next;
    auto pre = A;
    while (p && q)
    {
        if (p->data < q->data)
        {
            auto r = p;
            pre->next = r->next;
            p = p->next;
            free(r);
        }
        else if (p->data > q->data)
            q = q->next;
        else
        {
            pre = p;
            p = p->next;
            q = q->next;
        }
    }
    while (p)
    {
        auto r = p;
        pre->next = r->next;
        p = p->next;
        free(r);
    }
    return A;
}

4. A、B递增有序,公共元素产生单链表C(与上一题求∩相像)

无题目链接

void get_common(LNode* A, LNode* B)
{
    auto C = new LNode(-1);
    auto cur = C;
    auto p = A->next, q = B->next;
    while (p && q)
    {
        if (p->data < q->data) p = p->next;
        else if (p->data > q->data) q = q->next;
        else
        {
            auto s = new LNode(p->data);
            cur->next = s;
            cur = cur->next;
            p = p->next;
            q = q->next;
        }
    }
    cur->next = NULL;
}

5. A = A + B (分解)

(1) A中含有序号为奇数的元素,B中含有序号为偶数的元素

无题目链接

LNode* B = new LNode(-1);
void split(LNode* &A)
{
    if (!A->next) return;
    auto cur = B;
    auto pre = A, p = A->next;
    int cnt = 0;
    while (p)
    {
        cnt ++;
        if (cnt % 2)
        {
            pre = p;
            p = p->next;
        }
        else
        {
            cur->next = p;
            cur = cur->next;
            pre->next = p->next;
            p = p->next;
            // 或者这样写
            // auto r = p;
            // pre->next = p->next;
            // p = p->next;
            // cur->next = r;
            // cur = cur->next;
        }
    }
    cur->next = NULL;
}
(2) A中含有data域为奇数的元素,B中含有data域为偶数的元素,且保持相对顺序

无题目链接

LNode* B = new LNode(-1);
void split(LNode* &A)
{
    if (!A->next) return;
    auto cur = B;
    auto pre = A, p = A->next;
    while (p)
    {
        if (p->data % 2)
        {
            pre = p;
            p = p->next;
        }
        else
        {
            cur->next = p;
            cur = cur->next;
            pre->next = p->next;
            p = p->next;
            // 或者这样写
            // auto r = p;
            // pre->next = p->next;
            // p = p->next;
            // cur->next = r;
            // cur = cur->next;
        }
    }
    cur->next = NULL;
}

6. 找出A和B的公共结点

题目链接

LNode *findFirstCommonNode(LNode* A, LNode* B) {
        auto p = A->next, q = B->next;
        while (p != q)
        {
            if (p) p = p->next;
            else p = B->next;
            
            if (q) q = q->next;
            else q = A->next;
        }
        return p;
    }

二、逆置(反转)问题

1. 将单链表L就地逆置

题目链接

void reverse(LNode* &L)
{
    auto p = L->next;
    L->next = NULL;
    while (p)
    {
        // 头插法建立新的链表
        auto q = p->next;
        p->next = L->next;
        L->next = p;
        p = q;
    }
}

2. 从尾到头反向输出结点值

无题目链接

// 方法1:反转后遍历输出
// 方法2:递归
void print(LNode* L)
{
    if (L)
    {
        print(L->next);
        cout << L->data << ' ';
    }
}

三、去重问题

1. 去掉递增有序的单链表L中数值相同的结点

题目链接

LNode* deleteDuplicates(LNode* L) {
        if (!L->next) return L;
        auto pre = L, p = L->next;
        while (p && p->next)
        {
            if (p->next->val == p->val)
            {
                int tmp = p->val;
                while (p && p->val == tmp) p = p->next;
                pre->next = p;
            }
            else
            {
                pre = p;
                p = p->next;
            }
        }
        return L;
    }

2. 去掉单链表中绝对值相等的结点

题目链接

bool st[10010] = {};
// bool st[10010];
    LNode* filterList(LNode* L)
    {
        // memset(st, false, sizeof st);
        auto pre = L, p = L->next;
        while (p)
        {
            if (st[abs(p->val)])
            {
                auto r = p;
                pre->next = p->next;
                p = p->next;
                free(r);
            }
            else
            {
                st[abs(p->val)] = true;
                pre = p;
                p = p->next;
            }
        }
        return L;
    }

四、删除问题

1. 递归删除不带头结点的单链表L中所有值为x的节点

无题目链接

void delete_x(LNode* &L, ElemType x)
{
    if (!L) return;
    if (L->data == x)
    {
        auto p = L;
        L = L->next;
        free(p);
        delete_x(L, x);
    }
    else delete_x(L, x);
}

2. 删除单链表L中所有值为x的结点并释放空间

无题目链接

void delete_x(LNode* &L, ElemType x)
{
    // if (!L->next) return;
    auto pre = L, p = L->next;
    while (p)
    {
        if (p->data == x)
        {
            auto r = p;
            pre->next = p->next;
            p = p->next;
            free(r);
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
}

3. 删除单链表L中最小值结点(假设唯一)

无题目链接

void delete_min(LNode* &L)
{
    // if (!L->next) return;
    auto pre = L, p = L->next;
    auto minpre = pre, minp = p;
    while (p)
    {
        if (p->data < minp->data)
        {
            minpre = pre;
            minp = p;
        }
        pre = p;
        p = p->next;
    }
    minpre->next = minp->next;
    free(minp);
}

4. 删除单链表L中所有值介于x和y之间的元素的结点

无题目链接

void delete_xtoy(LNode* &L, ElemType x, ElemType y)
{
    if (!L->next) return;
    auto pre = L, p = L->next;
    while (p)
    {
        if (p->data > x && p->data < y)
        {
            auto r = p;
            pre->next = p->next;
            p = p->next;
            free(r);
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
}

5. 递增输出单链表L中各结点的数据元素并释放空间

无题目链接

// 方法1
void dele_print(LNode* &L)
{
    while (L->next)
    {
        auto pre = L, p = L->next;
        auto minpre = pre, minp = p;
        while (p)
        {
            if (p->data < minp->data)
            {
                minpre = pre;
                minp = p;
            }
            pre = p;
            p = p->next;
        }
        cout << minp->data << ' ';
        minpre->next = minp->next;
        free(minp);
    }
    free(L);
}
// 方法2:王道书上给的,个人感觉没方法1好用
void dele_print(LNode* &L)
{
    while (L->next)
    {
        auto minpre = L;
        auto p = L->next;
        while (p->next)
        {
            if (p->next->data < minpre->next->data)
                minpre = p;
            p = p->next;
        }
        cour << minpre->next->data << ' ';
        auto r = minpre->next;
        minpre->next = r->next;
        free(r);
    }
    free(L);
}

五、排序问题

1. 插入排序:使单链表中元素递增有序

无题目链接

void insert_sort(LNode* L)
{
    if (!L->next) return;
    auto p = L->next, r = p->next;
    p->next = NULL;
    p = r;
    while (p)
    {
        r = p->next;
        auto pre = L;
        while (pre->next && pre->next->data < p->data)
            pre = pre->next;
        p->next = pre->next;
        pre->next = p;
        p = r;
    }
}

2. 快速排序:使单链表中元素递增有序

LNode* quickSortList(LNode* head) {
        if (!head->next) return head;
        quick_sort(head->next, NULL);
        return head;
    }
    
    void quick_sort(LNode *head, LNode *tail)
    {
        if (head != tail)
        {
            // 定义参考元素x,永远取头指针指向的元素
            int x = head->data;
            // 双指针操作,p之前(不包括p)都是小于等于x的,
            // p——q(包括p)都是大于等于x的
            // p指针作用: 分段用
            // q指针作用: 遍历用,检索当前元素dataue与x的关系,
            // 若dataue比x小,则扔p前面
            LNode *p = head, *q = head->next;
            while (q != tail)
            {
                if (q->data < x)
                {
                    p = p->next;
                    swap(p->data, q->data);
                }
                q = q->next;
            }
            // 遍历完之后,重置参考元素x的位置,
            // 即依旧在头指针指向的位置,然后开始递归两段
            if (p != head) swap(head->data, p->data);
            quick_sort(head, p);
            quick_sort(p->next, tail);
        }
    }

最后——杂七杂八

1. 判断单链表L是否有环

题目链接

/*
以下文字来自题目链接中牛客官方给的解析:
step 1:设置快慢两个指针,初始都指向链表头。
step 2:遍历链表,快指针每次走两步,慢指针每次走一步。
step 3:如果快指针到了链表末尾,说明没有环,因为它每次走两步,
        所以要验证连续两步是否为NULL。
step 4:如果链表有环,那快慢双指针会在环内循环,因为快指针每次走两步,
        因此快指针会在环内追到慢指针,二者相遇就代表有环
*/
bool has_cycle(LNode *head) {
        auto fast = head, slow = head;
        while (fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) return true;
        }
        return false;
    }

2. 查找单链表L中倒数第k个位置上的结点

题目链接

LNode* FindKthToTail(LNode* head, int k)
{
    if (!head->next) return NULL;
    int len = 0;
    for (auto i = head->next; i; i = i->next) len ++;
    int cnt = len - k + 1;
    if (k <= 0 || cnt <= 0) return NULL;
    auto p = head;
    for (int i = 0; i < cnt; i ++) p = p->next;
    return p;
}

3. 重排单链表L’ = (a1,an,a2,an-1,a3,an-2,···)

题目链接
参考我去年写的博客

// 不带头结点的反转链表
void reverse(LNode* &b)
    {
        auto c = b->next;
        b->next = NULL;
        while (c)
        {
            auto tmp = c->next;
            c->next = b;
            b = c;
            c = tmp;
        }
    }
    // 两个链表的归并
    void merge(LNode* &a, LNode* b)
    {
        while (b)
        {
            auto p = a->next, q = b->next;
            b->next = a->next;
            a->next = b;
            a = p, b = q;
        }
    }
    void rearrangedList(LNode* head) {
        if (!head->next) return;
        // 1. 对链表进行分段
        // 结点1 ~ (n + 1)/2是左半段,结点(n + 1)/2 ~ n是右半段
        int len = 0;
        for (auto i = head; i; i = i->next) len ++;
        int left = len + 1 >> 1;
        // int len = 0;
        // auto i = head;
        // while (i) i = i->next, len ++;
        // int left = len + 1 >> 1;
        
        auto a = head;
        for (int i = 1; i < left; i ++) a = a->next;
        // auto a = head;
        // while (-- left) a = a->next;
        auto b = a->next;
        a->next = NULL;
        // 2. 反转右边那段链表
        reverse(b);
        // 3. 合并链表,即将b归并入主链表中
        merge(head, b);
    }
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值