AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 校园
  • 应用
  • 文章
    • 题解
    • 分享
    • 问答
  • 吐槽
  • 登录/注册

1.考研(链表相关力扣题)

作者: 作者的头像   有点丶 ,  2022-06-24 10:54:12 ,  所有人可见 ,  阅读 25


0


1.从尾到头打印链表
输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。

返回的结果用数组存储。

数据范围
0≤ 链表长度 ≤1000。

样例
输入:[2, 3, 5]
返回:[5, 3, 2]


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> printListReversingly(ListNode* head) {
        vector<int> a;
        while(head!=NULL)
        {
            a.push_back(head->val);
            head=head->next;
        }
        reverse(a.begin(),a.end());
        return a;
    }
};

2.在O(1)时间删除链表结点,删除指定节点
给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。

假设链表一定存在,并且该节点一定不是尾节点。

数据范围
链表长度 [1,500]。

样例
输入:链表 1->4->6->8
删掉节点:第2个节点即6(头节点为第0个节点)

输出:新链表 1->4->8

/**
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        ListNode* newNode=new ListNode(-1);
        newNode=node->next;
        node->val=newNode->val;
        node->next=newNode->next;

    }
};

3.反转链表
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

思考题:

请同时实现迭代版本和递归版本。
数据范围
链表长度 [0,30]。

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

输出:5->4->3->2->1->NULL

迭代法:
空间复杂度分析:遍历时只有3个额外变量,所以额外的空间复杂度是 O(1)。
时间复杂度分析:只遍历一次链表,时间复杂度是 O(n)。
代码

/**
 * 
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode*cur=head;
        ListNode*pre=NULL;
        while(cur)
        {
            ListNode*ne=cur->next;
            cur->next=pre;
            pre=cur;
            cur=ne;
        }
        return pre;

    }
};

递归法:
首先我们先考虑 reverseList 函数能做什么,它可以翻转一个链表,并返回新链表的头节点,也就是原链表的尾节点。
所以我们可以先递归处理 reverseList(head->next),这样我们可以将以head->next为头节点的链表翻转,并得到原链表的尾节点tail,此时head->next是新链表的尾节点,我们令它的next指针指向head,并将head->next指向空即可将整个链表翻转,且新链表的头节点是tail。
空间复杂度分析:总共递归 n 层,系统栈的空间复杂度是 O(n),所以总共需要额外 O(n)的空间。
时间复杂度分析:链表中每个节点只被遍历一次,所以时间复杂度是 O(n)。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode*tail = reverseList(head->next);
        head->next->next=head;
        head->next=NULL;
        return tail;

    }
};

4.合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

数据范围
链表长度 [0,500]。

样例
输入:1->3->5 , 2->4->5

输出:1->2->3->4->5->5

时间复杂度
两个链表各遍历一次,所以时间复杂度为O(n)
代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode*dummy = new ListNode(0);
        ListNode*cur=dummy;

        while(l1!=NULL&l2!=NULL)
        {
            if(l1->val<l2->val)
            {
                cur->next=l1;
                l1=l1->next;
            }
            else
            {
                cur->next=l2;
                l2=l2->next;
            }
            cur=cur->next;
        }
        if(l1!=NULL)
        {
            cur->next=l1;
        }
        else
        {
            cur->next=l2;
        }
        return dummy->next;
    }
};

5.删除排序链表中的重复元素(I)
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
输入:head = [1,1,2]
输出:[1,2]
输入:head = [1,1,2,3,3]
输出:[1,2,3]

时间复杂度
循环了一次链表,时间复杂度为O(n)
代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
       ListNode*duyy = head;
        while(duyy!=NULL)
        {
            if(duyy->next!=NULL&&duyy->val==duyy->next->val)
            {
                duyy->next=duyy->next->next;
            }
            else
            {
                duyy=duyy->next;
            }
        }
        return head;
    }
};

6.删除排序链表中的重复元素(II)
在一个排序的链表中,存在重复的节点,请删除该链表中重复的节点,重复的节点不保留。

数据范围
链表中节点 val 值取值范围 [0,100]。
链表长度 [0,100]。

样例1
输入:1->2->3->3->4->4->5

输出:1->2->5
样例2
输入:1->1->1->2->3

输出:2->3

算法
(线性扫描)
之前做的删除节点的题是有重复元素只保留1个下来,这道题的要求是一个都不保留。(有点狠兄弟)
那么就要求在cur指针还没扫到重复元素区间的第一个元素的时候,就知道它们是重复元素了。
那么针对于2,解决方法是新建一个指针go,go = cur->next。就像cur相当于是上帝视角,是大佬,go是小兵向前冲。一般第一步cur->next->val是一定等于go->val的,接下来是继续判断,如果有重复元素,小兵go会继续向前冲,最后碰到不同元素,跳出while循环。
逻辑是这样:如果cur的后面是有一段相同元素,cur是不会涉足这段元素的,go会遍历这一段的每一个元素。最后,cur是相同元素区间的前一个节点,go是相同元素区间的后一个节点。
接下来的if判断,if(cur->next->next == go)成立意味着cur和go之间没有重复元素,那么cur向前走一步。如果else成立,说明有重复元素,那么cur->next指向go,直接略过相同元素这一段,相当于删除了这一段。
时间复杂度
O(n)

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head) {
        ListNode*dummy = new ListNode(-1);
        dummy->next=head;
        ListNode*cur=dummy;
        while(cur->next)
        {
            ListNode*go=cur->next;
            while(go&&cur->next->val==go->val)go=go->next;
            if(cur->next->next==go)cur=cur->next;
            else cur->next=go;
        }

        return dummy->next;
    }

};

7.链表中倒数第k个节点
输入一个链表,输出该链表中倒数第 k 个结点。

注意:

k >= 1;
如果 k 大于链表长度,则返回 NULL;
数据范围
链表长度 [0,30]。

样例
输入:链表:1->2->3->4->5 ,k=2

输出:4

时间复杂度:
1.先将链表反转,循环链表一次
2.后有循环一次链表查找第k位
时间复杂度为O(n)

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* findKthToTail(ListNode* pListHead, int k) {
        ListNode*pre=NULL;
        ListNode*cur=pListHead;

        while(cur)
        {
            ListNode*ne=cur->next;
            cur->next=pre;
            pre=cur;
            cur=ne;
        }
        int i=1;

        while(pre&&i<k)
        {
            pre=pre->next;
            i++;
        }
        return pre;
    }
};

8.两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。

当不存在公共节点时,返回空节点。

数据范围
链表长度 [1,2000]。

样例
给出两个链表如下所示:
A:
····· a1 → a2
····· · ······ ↘
····················· c1 → c2 → c3
··················· · ↗
······ b1 → b2 → b3
B:

输出第一个公共节点c1

时间复杂度
循环链表 o(n)

注意:
没有公共节点,也会同时走到对方的NULL而相等的
为什么我运行if(p -> next)会错呢?
因为要考虑二者没有相交的情况,这个时候返回要让两个指针都指向尾部的null,要是写成(p->next)会发现自始至终指针没有指向尾部的NULL

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
        ListNode* p=headA;
        ListNode* q=headB;

        while(p!=q)
        {
            if(p)
            {
                p=p->next;
            }
            else
            {
                p=headB;
            }
            if(q)
            {
                q=q->next;
            }
            else
            {
                q=headA;
            }
        }
        return q;
    }
};

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) { // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) { // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) {
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

9.设计链表
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

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

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

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3

提示:

所有val值都在 [1, 1000] 之内。
操作次数将在  [1, 1000] 之内。
请不要使用内置的 LinkedList 库。

代码

class MyLinkedList {
public:
    struct ListNode
    {
        int val;
        ListNode*next;
        ListNode(int val):val(val),next(NULL){}
    };
    // 初始化链表
    MyLinkedList() {
        _dummyHead = new ListNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        _size = 0;
    }
    // index=0;是头节点(第一个节点)
    int get(int index) {
        if(index>_size-1||index<0)
        {
            return -1;
        }
        ListNode*p = _dummyHead->next;
        while(index--)
        {
            p=p->next;
        }
        return p->val;
    }

    void addAtHead(int val) {
        ListNode*node = new ListNode(val);
        node->next=_dummyHead->next;
        _dummyHead->next=node;
        _size++;
    }

    void addAtTail(int val) {
        ListNode*node= new ListNode(val);
        ListNode*cur=_dummyHead;
        while(cur->next)
        {
            cur=cur->next;
        }
        cur->next=node;
        _size++;
    }

    void addAtIndex(int index, int val) {
        ListNode*node=new ListNode(val);
        if(index>_size)return;
        ListNode*cur=_dummyHead;
        while(index--)
        {
             cur=cur->next;
        }
        node->next=cur->next;
        cur->next=node;
        _size++;
    }

    void deleteAtIndex(int index) {
        if(index>=0&&index<_size)
        {
            ListNode*cur=_dummyHead;
            while(index--)
            {
                cur=cur->next;
            }
            ListNode* tmp = cur->next;
            cur->next = cur->next->next;//若只单纯留这一句,这多余的节点还在内存中没删除,消耗多余内存
            delete tmp;                 
            _size--;
        }
    }
      // 打印链表
    void printLinkedList() {
        ListNode* cur = _dummyHead;
        while (cur->next != nullptr) {
            cout << cur->next->val << " ";
            cur = cur->next;
        }
        cout << endl;
    }
    private:
    int _size;
    ListNode* _dummyHead;
};  

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

1 评论


用户头像
有点丶   7天前     回复

持续更新,一天解决至少五道,督促自己


你确定删除吗?

© 2018-2022 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息