链表算法练习

1. 移除链表元素

删除链表中等于给定值 val 的所有节点。

示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 
输出:[1,2,3,4,5]

示例 2: 输入:head = [], val = 1 
输出:[]

示例 3: 输入:head = [7,7,7,7], val = 7 
输出:[]

代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct Node
{
    int val;
    Node* next;
    Node(int val) : val(val), next(nullptr) {}
};

Node* removeElement(Node* head, const int val)
{
    Node* dum = new Node(0);
    dum->next = head;
    Node* cur = dum;

    while (cur->next)
    {
        if (cur->next->val == val)
        {
            cur->next = cur->next->next;
        }
        else
        {
            cur = cur->next;
        }
    }

    return dum->next;
}

void showList(Node* head)
{   
    Node* cur = head;
    cout << "[";
    while (cur)
    {
        cout << cur->val << ", ";
        cur = cur->next;
    }
    cout << "]" << endl;
}

int main()
{
    Node* a = new Node(1);
    Node* b = new Node(2);
    Node* c = new Node(6);
    Node* d = new Node(3);
    Node* e = new Node(4);
    Node* f = new Node(5);
    Node* g = new Node(6);

    a->next = b;
    b->next = c;
    c->next = d;
    d->next = e;
    e->next = f;
    f->next = g;
    g->next = nullptr;

    int val = 6;

    showList(removeElement(a, 6));
    return 0;
}

2. 设计链表

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

用的带访问权限和函数的 struct:

bool isValid(int index):判断索引 index 是否有效。
ListNode* findPosition(bool bFront, int index):寻找操作位置;标志 bFront 标识是在 index 节点之前还是自身操作。
get(int index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtIndex(int index,int val):
- 指定位置 index,在其前加值为 val 的节点;
- 如果 index=count,则附加到尾,
- 如果 index>count,则不插入;
- 如果index<0,则插入到头。
addAtHead(int val):在链表的头元素前加值为 val 的节点。
addAtTail(int val):在链表的尾元素后加值为 val 的节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int val) : val(val), next(nullptr) {}
};

struct MyList
{
public:
    MyList()
    {
        this->count = 0;
        this->dum = new ListNode(0);
    }

    bool isValid(int index);
    ListNode* findPosition(bool bFront, int index);
    int get(int index);
    void addAtIndex(int index, int val);
    void addAtHead(int val);
    void addAtTail(int val);
    void deleteAtIndex(int index);
    void showList();
private:
    ListNode* dum;
    int count;  
};

bool MyList::isValid(int index)
{
    // 非法索引
    if (index < 0 || index > count)
    {
        cout << "index invalid" << endl;
        return false;
    }
    return true;
}

ListNode* MyList::findPosition(bool bFront, int index)
{
    ListNode* cur = nullptr;
    // 需要前一个节点就从 dum 开始数,否则从 真实数据 开始
    if (bFront) 
        cur = this->dum;
    else 
        cur = this->dum->next;

    while (index--) 
        cur = cur->next;

    return cur;
}

int MyList::get(int index)
{
    // 非法索引
    if (!isValid(index) || index == count) return -1;
    // 获取自身索引 故为 false
    ListNode* cur = findPosition(false, index);
    return cur->val;
}

void MyList::addAtIndex(int index, int val)
{
    // 非法索引
    if (!isValid(index)) return;
    // 尾节点之后
    if (index == count && count != 0)
    {
        // count = true_idx + 1,所以需要 count - 1 = true_idx,
        // 尾结点索引自身 故为 false
        ListNode* cur = findPosition(false, count - 1);
        cur->next = new ListNode(val);
    }
    // 所有节点之前
    else 
    {
        // 索引前 为 true
        ListNode* cur = findPosition(true, index);
        ListNode* tmp = cur->next;
        cur->next = new ListNode(val);
        cur->next->next = tmp;
    }
    ++count;
}

void MyList::addAtHead(int val)
{
    addAtIndex(0, val);
}

void MyList::addAtTail(int val)
{
    addAtIndex(this->count, val);
}

void MyList::deleteAtIndex(int index)
{
    // 非法索引
    if (!isValid(index) || index == count) return;
    // 获取删除节点索引的前一个 为 true
    ListNode* cur = findPosition(true, index);
    ListNode* tmp = cur->next->next;
    delete cur->next;
    cur->next = tmp;
    --count;
}

void MyList::showList()
 {   
    ListNode* cur = this->dum->next;
    cout << "[";
    while (cur)
    {
        cout << cur->val << ", ";
        cur = cur->next;
    }
    cout << "]" << endl;
}

int main()
{
    MyList* list = new MyList();
    list->addAtHead(7);
    list->addAtHead(2);
    list->addAtHead(1);
    list->addAtIndex(3, 0);
    list->deleteAtIndex(2);
    list->addAtHead(6);
    list->addAtTail(4);
    list->showList();

    cout << list->get(4) << endl;

    list->addAtHead(4);
    list->addAtIndex(5, 0);
    list->addAtHead(6);
    list->showList();
    return 0;
}

3. 反转链表

反转一个单链表

示例: 
输入: 1->2->3->4->5->NULL 
输出: 5->4->3->2->1->NULL

代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int val) : val(val), next(nullptr) {}
};

ListNode* reverseList(ListNode* head)
{
    ListNode* pre = nullptr;
    ListNode* cur = head;
    while (cur)
    {
        ListNode* tmp = cur->next;
        cur->next = pre;
        pre = cur;
        cur = tmp;
    }
    return pre;
}

void showList(ListNode* head)
 {   
    ListNode* cur = head;
    while (cur)
    {
        cout << cur->val << "->";
        cur = cur->next;
    }
    cout << "NULL" << endl;
}

int main()
{
    ListNode* a0 = new ListNode(1);
    ListNode* a1 = new ListNode(2);
    ListNode* a2 = new ListNode(3);
    ListNode* a3 = new ListNode(4);
    ListNode* a4 = new ListNode(5);

    a0->next = a1;
    a1->next = a2;
    a2->next = a3;
    a3->next = a4;

    showList(a0);
    a0 = reverseList(a0);
    showList(a0);

    return 0;
}

4. 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换


image

示例 1:
输入:head =[1,2,3,4]
输出:[2,1,4,3]

示例 2:
输入:head =[]
输出:[]

示例 3:
输入:head =[1]
输出:[1]

代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int val) : val(val), next(nullptr) {}
};

void showList(ListNode* head)
 {   
    ListNode* cur = head;
    cout << "[";
    while (cur)
    {
        cout << cur->val;
        if (cur->next)
            cout << ", ";
        cur = cur->next;
    }
    cout << "]" << endl;
}

ListNode* exchangeDouble(ListNode* head)
{
    ListNode* dum = new ListNode(-1);
    dum->next = head;
    ListNode* cur = dum;

    while (cur->next && cur->next->next)
    {
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        tmp->next = cur->next->next;
        cur->next->next = tmp; 

        cur = tmp;
    }

    return dum->next;
}

int main()
{
    ListNode* a0 = new ListNode(1);
    ListNode* a1 = new ListNode(2);
    ListNode* a2 = new ListNode(3);
    ListNode* a3 = new ListNode(4);

    a0->next = a1;
    a1->next = a2;
    a2->next = a3;

    showList(a0);
    a0 = exchangeDouble(a0);
    showList(a0);

    return 0;
}

5. 删除链表的倒数第N个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
尝试使用一趟扫描实现。

示例 1:
输入:head = [1,2,3,4,5], n = 2 
输出:[1,2,3,5] 

示例 2:
输入:head = [1], n = 1 
输出:[] 

示例 3:
输入:head = [1,2], n = 1 
输出:[1]

代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int val) : val(val), next(nullptr) {}
};

void showList(ListNode* head)
{   
    ListNode* cur = head;
    cout << "[";
    while (cur)
    {
        cout << cur->val;
        if (cur->next)
            cout << ", ";
        cur = cur->next;
    }
    cout << "]" << endl;
}

ListNode* removeIndexElem(ListNode* head, int index)
{
    ListNode* dum = new ListNode(0);
    dum->next = head;
    ListNode* pre = dum;
    ListNode* tail = dum;

    // 先用 index 定位尾巴(因为要删除,要提前一位,所以 index + 1)
    ++index;
    while (index--)
    {
        tail = tail->next;
    }

    while (tail)
    {
        pre = pre->next;
        tail = tail->next;
    }

    ListNode* tmp = pre->next;
    pre->next = tmp->next;
    delete tmp;

    return dum->next;
}

int main()
{
    ListNode* a0 = new ListNode(1);
    ListNode* a1 = new ListNode(2);
    ListNode* a2 = new ListNode(3);
    ListNode* a3 = new ListNode(4);
    ListNode* a4 = new ListNode(5);

    a0->next = a1;
    a1->next = a2;
    a2->next = a3;
    a3->next = a4;

    showList(a0);
    removeIndexElem(a0, 2);
    showList(a0);

    return 0;
}

6. 链表相交

在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,这两个链表可能相交,也可能不相交。
请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null即可,
要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到0(N+M),额外空间复杂度请达到0(1)。

相交分情况:
1. 先判断有无环;
    快慢指针寻找,有环会相遇。
2. 其次判断交点:
(1)均无环:
    1)不相交,返回 null;
    2)相交,长短统一后,寻找相交一个节点。
(2)有环:
    1)一个有,一个无,必不相交,返回 null;
    2)两个都有,看入环点:
        相同:
            去除环后,找寻相交节点:
                有,则返回;
                无,则在环上相交,返回切入点。
        不同:
            选择一个入环点,寻找相交节点:
                有,返回任意一个入环点;
                无,返回 null。

画图示意:
image


代码如下:

点击查看代码
#include<iostream>
using namespace std;

struct ListNode
{
   int val;
    ListNode* next;
    ListNode(int val) : val(val), next(nullptr) {}
};

// 双环的入环点匹配
ListNode* cricleEntryMatch(ListNode* head1, ListNode* head2)
{
    ListNode* idx1 = head1, *idx2 = head2;
    ListNode* stop = head2;
    while (idx1 && idx2)
    {
        if (idx1 == idx2)
            return idx1;
        idx2 = idx2->next;
        if (idx2 == stop)
            return nullptr;
    }
}

ListNode* cricleDetect(ListNode* head)
{
    ListNode* slow = head, *fast = head;
    // ----- 快慢匹配 -----
    while(fast && fast->next)
    {   
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            // ----- 入环点匹配 -----
            ListNode* idx1 = head, *idx2 = fast;
            while (idx1 != idx2)
            {
                idx1 = idx1->next;
                idx2 = idx2->next;
            }
            return idx1;
            // ----- 入环点匹配 -----
        }
    }
    // ----- 快慢匹配 -----
    return nullptr;
}

ListNode* crossDetect(ListNode* head1, ListNode* head2)
{   
    ListNode* cur1 = head1, *cur2 = head2; 
    // ----- 统计长度 -----
    int len1 = 0, len2 = 0;
    while (cur1)
    {
        ++len1;
        cur1 = cur1->next;
    }
    while (cur2)
    {
        ++len2;
        cur2 = cur2->next;
    }
    // ----- 统计长度 -----

    // ----- 长短统一 -----
    bool bShort1 = len1 > len2 ? true : false;
    int gap = bShort1 ? len1 - len2 : len2 - len1;
    cur1 = head1, cur2 = head2;
    while (gap--)
    {
        if (bShort1)
            cur1 = cur1->next; 
        else
            cur2 = cur2->next;          
    }
    // ----- 长短统一 -----

    // ----- 寻找相交一个节点 -----
    while (cur1)
    {   
        if (cur1 == cur2)
        {
            return cur1;
        }

        cur1 = cur1->next; 
        cur2 = cur2->next;
    }
    // ----- 寻找相交一个节点 -----
    return nullptr;
}

ListNode* Detector(ListNode* head1, ListNode* head2)
{
    // 先判断有无环
    int count = 0;
    ListNode* cricleEntry1 = cricleDetect(head1);
    if (cricleEntry1) ++count;
    ListNode* cricleEntry2 = cricleDetect(head2);
    if (cricleEntry2) ++count;
    // 均无环
    if (count == 0)
    {   
        // 1)不相交,返回 null;
        // 2)相交,长短统一后,寻找相交一个节点。
        return crossDetect(head1, head2);
    }
    // 有 1 环 必不相交
    else if (count == 1)
    {
        return nullptr;
    }
    // 有 2 环 看入环点
    else 
    {   
       // 相同
       if (cricleEntry1 == cricleEntry2)
       {    
            /*
                去除环后,找寻相交节点(记得恢复):
                    有,则返回;
                    无,则在环上相交,返回切入点
            */
            ListNode* tmp1 = cricleEntry1->next;
            ListNode* tmp2 = cricleEntry2->next;
            cricleEntry1->next = nullptr;
            cricleEntry2->next = nullptr;
            ListNode* ans = crossDetect(head1, head2);
            cricleEntry1->next = tmp1;
            cricleEntry2->next = tmp2;
            return ans ? ans : cricleEntry1;
       }
       // 不同
       else
       {
            /*
                选择一个入环点,寻找相交节点:
                    有,返回任意一个入环点;
                    无,返回 null。
            */
            ListNode* tmp = cricleEntry1->next;
            cricleEntry1->next = nullptr;
            ListNode* ans = cricleEntryMatch(cricleEntry1, cricleEntry2);
            cricleEntry1->next = tmp;
            return ans;
       }
    }

}

void showResult(ListNode* ans)
{
    if (!ans)
        cout << "不相交" << endl;
    else
    {
        cout << "交点为:" << ans << ", ";
        cout << "交点值为:" << ans->val << endl;
    }
}

int main()
{
    // ----- 无环 -----
    // ~ 不相交 ~
    ListNode* a0 = new ListNode(1);
    ListNode* a1 = new ListNode(2);
    ListNode* a2 = new ListNode(3);
    ListNode* b0 = new ListNode(4);
    ListNode* b1 = new ListNode(5);
    a0->next = a1;
    a1->next = a2;
    b0->next = b1;
    // [1,2,3]; [4,5]
    cout << "无环";
    showResult(Detector(a0, b0));
    // ~ 不相交 ~

    // ~ 相交 ~
    ListNode* ca0 = new ListNode(1);
    ListNode* ca1 = new ListNode(2);
    ListNode* ca2 = new ListNode(3);
    ListNode* ca3 = new ListNode(8);
    ListNode* cb0 = new ListNode(4);
    ListNode* cb1 = new ListNode(5);
    ca0->next = ca1;
    ca1->next = ca2;
    ca2->next = ca3;
    cb0->next = ca3;
    ca3->next = cb1;
    // [1,2,3,8,5]; [4,8,5]; 交点是8
    cout << "无环相交, ";
    showResult(Detector(ca0, cb0));
    // ~ 相交 ~
    // ----- 无环 -----

    // ----- 有环 -----
    // ~ 1 环 ~
    ListNode* rca0 = new ListNode(1);
    ListNode* rca1 = new ListNode(2);
    ListNode* rca2 = new ListNode(3);
    ListNode* rca3 = new ListNode(8);
    ListNode* rcb0 = new ListNode(4);
    ListNode* rcb1 = new ListNode(5);
    rca0->next = rca1;
    rca1->next = rca2;
    rca2->next = rca3;
    rca3->next = rca1;
    rcb0->next = rcb1;
    // [1,2,3,8,2,3,8,.....] 有环点 2 ; [4,5] 
    cout << "有 1 环";
    showResult(Detector(rca0, rcb0));
    // ~ 1 环 ~

    // ~ 2 环 环点不同 ~
    ListNode* rca011 = new ListNode(1);
    ListNode* rca111 = new ListNode(2);
    ListNode* rca211 = new ListNode(3);
    ListNode* rca311 = new ListNode(8);
    ListNode* rcb011 = new ListNode(4);
    ListNode* rcb111 = new ListNode(5);
    rca011->next = rca111;
    rca111->next = rca211;
    rca211->next = rca311;
    rca311->next = rca111;
    rcb011->next = rcb111;
    rcb111->next = rca311;
    // [1,2,3,8,2,3,8,.....]; [4,5,8,2,3,8,...]; 有环点 2,8
    cout << "有 2 环, 相交但环点不同, 任意一";
    showResult(Detector(rca011, rcb011));

    ListNode* da01 = new ListNode(1);
    ListNode* da11 = new ListNode(2);
    ListNode* da21 = new ListNode(3);
    ListNode* da31 = new ListNode(7);
    ListNode* da41 = new ListNode(8);
    da01->next = da11;
    da11->next = da21;
    da21->next = da31;
    da31->next = da41;
    da41->next = da21;
    ListNode* db01 = new ListNode(4);
    ListNode* db11 = new ListNode(5);
    ListNode* db21 = new ListNode(6);
    ListNode* db31 = new ListNode(9);
    db01->next = db11;
    db11->next = db21;
    db21->next = db31;
    db31->next = db11;
    // [1,2,3,7,8,3,7,8,.....]; [4,5,6,9,5,6,9,...]; 有环点 3,5
    cout << "有 2 环, 相交但环点不同, 且是自身有环, ";
    showResult(Detector(da01, db01));
    // ~ 2 环 坏点不同 ~

    // ~ 2 环 环点相同 ~
    ListNode* rca01 = new ListNode(1);
    ListNode* rca11 = new ListNode(2);
    ListNode* rca21 = new ListNode(3);
    ListNode* rca31 = new ListNode(8);
    ListNode* rcb01 = new ListNode(4);
    ListNode* rcb11 = new ListNode(5);
    rca01->next = rca11;
    rca11->next = rca21;
    rca21->next = rca31;
    rca31->next = rca11;
    rcb01->next = rcb11;
    rcb11->next = rca11;
    // [1,2,3,8,2,3,8,.....]; [4,5,2,3,8,2,...]; 有环点 2
    cout << "有 2 环, 相交且环点相同, ";
    showResult(Detector(rca01, rcb01));

    ListNode* da0 = new ListNode(1);
    ListNode* da1 = new ListNode(2);
    ListNode* da2 = new ListNode(3);
    ListNode* da3 = new ListNode(7);
    ListNode* da4 = new ListNode(8);
    ListNode* db0 = new ListNode(4);
    ListNode* db1 = new ListNode(5);
    ListNode* db2 = new ListNode(6);
    da0->next = da1;
    da1->next = da2;
    da2->next = da3;
    da3->next = da4;
    da4->next = da2;
    db0->next = db1;
    db1->next = db2;
    db2->next = da1;
    // [1,2,3,7,8,3,7,8,.....]; [4,5,6,2,3,7,8,3,7,8,...]; 有交点 2 环点 3
    cout << "有 2 环, 相交且环点相同, 但前面有交点, ";
    showResult(Detector(da0, db0));
    // ~ 2 环 环点相同 ~
    // ----- 有环 -----
    return 0;
}

结果截图:
image

posted @ 2024-04-15 10:16  bok_tech  阅读(34)  评论(0)    收藏  举报