链表经典题目

移除链表元素

https://leetcode.cn/problems/remove-linked-list-elements/

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

链表基础题目,没啥好说的,注意生成虚拟头结点,然后进行删除。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead -> next = head;
        ListNode* cur = dummyhead;
        while(cur -> next != NULL){
            
            if(cur -> next -> val == val){
                ListNode* tmp = cur -> next;
                cur -> next = cur -> next -> next;
                delete tmp;
            }else{
                cur = cur -> next;
            }
        }
        return dummyhead -> next;

    }
};

设计链表

https://leetcode.cn/problems/design-linked-list/

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

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。

  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。

  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。

  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。

  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

这道题主要考虑了链表制造的方方面面,注意虚拟头结点不算第0个节点,其他都比较常规(注意链表size的变化!)

class MyLinkedList {
public:
    
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };

    MyLinkedList() {
        size = 0;
        dummyhead = new LinkedNode(0);
        

    }
    
    int get(int index) {
        if(index < 0 || index > (size - 1)) return -1;
        LinkedNode* cur = dummyhead -> next;
        while( index--){
            cur = cur -> next;
        }
        return cur -> val;

    }
    
    void addAtHead(int val) {
        LinkedNode* NewNode = new LinkedNode(val);
        NewNode -> next = dummyhead->next;
        dummyhead -> next = NewNode;
        size++;


    }
    
    void addAtTail(int val) {
        LinkedNode* NewNode = new LinkedNode(val);
        LinkedNode* cur = dummyhead;
        while(cur->next != NULL){
            cur = cur -> next;
        }
        cur -> next = NewNode; 
        size++;

    }
    
    void addAtIndex(int index, int val) {
        LinkedNode* NewNode = new LinkedNode(val);
        if(index < 0 ) index = 0;
        LinkedNode* cur = dummyhead;
        if(index > size) return;
        while(index-- ){
            cur = cur -> next;
        }
        NewNode->next = cur->next;
        cur->next = NewNode;
        size++;

    }
    
    void deleteAtIndex(int index) {
        if(index < 0 || index > size - 1) return;
        LinkedNode* cur = dummyhead;

        while(index-- ){
            cur = cur -> next;
        
        }
        LinkedNode* tmp = cur->next;
        cur -> next = cur -> next -> next;
        delete tmp;
        size--;
    }
    
    private:
    LinkedNode* dummyhead;
    int size;
};

反转链表

题意:反转一个单链表。

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

https://leetcode.cn/problems/reverse-linked-list/

用双指针法,让一个指针指向头结点,一个指向null,通过指针让头结点的指针指向null;这样完成了第一个节点指向null的动作,然后两个指针统一右移,也就是null的节点到头结点,头结点到下一个节点,继续循环。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp; // 保存cur的下一个节点
        ListNode* cur = head;
        ListNode* pre = NULL;
        while(cur) {
            temp = cur->next;  // 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur->next = pre; // 翻转操作
            // 更新pre 和 cur指针
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

两两交换链表中的节点

https://leetcode.cn/problems/swap-nodes-in-pairs/

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

通过画图解决。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* tmp = cur->next; // 记录临时节点
            ListNode* tmp1 = cur->next->next->next; // 记录临时节点

            cur->next = cur->next->next;    // 步骤一
            cur->next->next = tmp;          // 步骤二
            cur->next->next->next = tmp1;   // 步骤三

            cur = cur->next->next; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};

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

https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

思路:这道题考察了双指针的经典用法,也就是使用双指针的快慢指针间距进行删除特定元素。

举个例子说,链表删除倒数第四个节点,也就是链表[12345678]里面的5,这个时候我们使用一个快慢指针,分别指向1和指向6,这样当指向6的指针指向空的时候,指向1的指针指向4,就可以删除第4-> next个数值,也就是删掉5。而1和6之间间隔5,即n+1.

代码如下:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead ->next = head;
        ListNode* FastIndex = dummyhead;
        ListNode* SlowIndex = dummyhead;
        while( n--){
            FastIndex = FastIndex -> next;
        }
        FastIndex = FastIndex -> next;
        while(FastIndex != NULL ){
        SlowIndex = SlowIndex -> next;
        FastIndex = FastIndex -> next;

    }
    ListNode* tmp = SlowIndex -> next;
    SlowIndex -> next = SlowIndex -> next -> next;
    delete tmp;
    return dummyhead -> next;
    }
};

链表相交

https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

思路很简单,目前知道两个链表a和b,先用while求出链表各自长度,然后长的那个肯定要从第index个(也就是index后的长度和短的那个相等的地方)开始遍历。里面有个小难点就是我们默认curA为最长链表的头,lenA为其长度,如果B更长,那么使用swap把a和b的长度和指针换一下。

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;
    }
};

环形链表II

https://leetcode.cn/problems/linked-list-cycle-ii/

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

这道题背过,公式推导后结果是:

定义一个快慢指针,快的走两步,慢的走一步,快的和慢的一定会在环形区域相遇(前提是有环形区域),然后再相间的地方定义快的指针,从头结点定义慢的指针,两个一起遍历,相遇的地方就是我们要找的入环的第一个节点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值