代码随想录-链表

本次记录代码随想录的链表部分的学习

一.移除链表元素

力扣题目链接(opens new window)

题意:删除链表中等于给定值 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 输出:[]

思路

这里要思考两种情况

一种是要删除的元素位于头结点

另一种是不在头结点

如果是在链表头,那么删除起来不太方便,所以我们考虑加入一个头指针,方便我们操作,头结点不存储任何数据,它的作用只是指向链表的第一个元素

这样我们操作起来就会比较方便,在返回的之后只需要返回头指针指向的节点即可

如果有头指针,我们就可以不断遍历每个元素的值,如果检测到值为val,就删除这个节点

/**
 * 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* removeElements(ListNode* head, int val) {
        ListNode* hp = new ListNode(0);
        hp->next = head;
        ListNode* cur = hp;
        while (cur->next != NULL && cur != NULL)
        {
            if (cur->next->val == val)
            {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } 
            else
            {
                cur = cur->next;
            }
        }
        head = hp->next;
        delete hp;
        return head;
    }
};

上面是有头指针的情况,如果没有人为设置的头指针,那么应该怎么处理

 那就要分两种情况,一种是删除头结点,另一种是删除其他节点

删除头结点

//消除头结点
//不断检测头结点,如果val域一直相等,就一直删除
while (head != NULL && head->val == val)
{   ListNode
* tmp = head;
  head
= head->next;   delete tmp;
}

注意这里使用的是while而不是if,因为可能从头结点开始连续几个值都是val,因此要使用while

删除其他节点,这部分代码就和上面带头指针的删除一样了

//删除非头结点
       ListNode* p = head;
       while (p != NULL && p->next != NULL)
       {
           //删除节点
           if (p->next->val == val)
           {
                ListNode* tmp = p->next;
                p->next = p->next->next;
                delete tmp;
           } 
           else
           {
               p = p->next;
           }
       }

LeetCode代码(不带头指针版)

/**
 * 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* removeElements(ListNode* head, int val) {
       //消除头结点
       //不断检测头结点,如果val域一直相等,就一直删除
       while (head != NULL && head->val == val)
       {
           ListNode* tmp = head;
           head = head->next;
           delete tmp;
       } 
       //删除非头结点
       ListNode* p = head;
       while (p != NULL && p->next != NULL)
       {
           //删除节点
           if (p->next->val == val)
           {
                ListNode* tmp = p->next;
                p->next = p->next->next;
                delete tmp;
           } 
           else
           {
               p = p->next;
           }
       }
       return head;
    }
};

二.设计链表

力扣题目链接(opens new window)

题意:

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

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

这一题主要就是考验对链表的基本操作,需要自己设定一个链表的类

对于本题,我们给定两个成员变量

private:
    int size;
    ListNode* head;

get(index)函数

int get(int index) {
    if (index < 0 || index > (size - 1))
    {
        return -1;
    }
    ListNode* tmp = head->next;
    while (index--)
    {
        tmp = tmp->next;
    }
    return tmp->val;
}

addAtHead(val)

void addAtHead(int val) {
        ListNode* p = new ListNode(val);
        p->next = head->next;
        head->next = p;
        size++;
    }

addAtTail

void addAtTail(int val) {
        ListNode* p = new ListNode(val);
        ListNode* cur = head;
        while (cur->next != NULL)
        {
            cur = cur->next;
        }
        cur->next = p;
        size++;
    }

addAtIndex(index,val)

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

deleteAtIndex(index)

void deleteAtIndex(int index) {
        if (index < 0 || index >= size)
        {
            return ;
        }
        ListNode* cur = head;
        while (index--)
        {
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        size--;
    }

完整LeetCode代码

class MyLinkedList {
public:
/**
 * 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);
 */
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int val) : val(val),next(NULL){}
    };
    MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }
    
    int get(int index) {
        if (index < 0 || index > (size - 1))
        {
            return -1;
        }
        ListNode* tmp = head->next;
        while (index--)
        {
            tmp = tmp->next;
        }
        return tmp->val;
    }
    
    void addAtHead(int val) {
        ListNode* p = new ListNode(val);
        p->next = head->next;
        head->next = p;
        size++;
    }
    
    void addAtTail(int val) {
        ListNode* p = new ListNode(val);
        ListNode* cur = head;
        while (cur->next != NULL)
        {
            cur = cur->next;
        }
        cur->next = p;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > size)
        {
            return ;
        }
        if (index < 0)
        {
            index = 0;
        }
        ListNode* cur = head;
        while (index--)
        {
            cur = cur->next;
        }
        ListNode* p = new ListNode(val);
        p->next = cur->next;
        cur->next = p;
        size++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size)
        {
            return ;
        }
        ListNode* cur = head;
        while (index--)
        {
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        size--;
    }
private:
    int size;
    ListNode* head;
};

三.反转链表

力扣题目链接(opens new window)

题意:反转一个单链表。

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

思路

一个很简单的思路,迭代法,不断修改next指针,让指针翻转,即指向后项的节点改为之指向前项

设置两个指针,pre指向前一个节点,cur指向当前节点的

每次先存储next指针指向的下一个节点,然后将cur指向pre,实现翻转指针,然后将当前指针赋值

LeetCode代码

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = NULL;
        ListNode* cur = head;
        while (cur != NULL)
        {
            ListNode* tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

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

力扣题目链接(opens new window)

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

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

 

 

 思路

对于每两个节点,如果我们能找到这两个点前面的一个点,操作就会很方便

这样的操作为,大箭头为原来的指向,曲线箭头为修改后的指向

 

 

 

 对应的代码操作为

ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;


cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;

这样就完成了两两交换的操作

LeetCode代码

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
     //增加虚拟头结点,方便交换操作 ListNode
* hp = new ListNode(0); hp->next = head; ListNode* cur = hp; while (cur->next != NULL && cur->next->next != NULL) { ListNode* tmp = cur->next; ListNode* tmp1 = cur->next->next->next; //1. cur->next = cur->next->next;
       //2. cur
->next->next = tmp; //3.
       cur
->next->next->next = tmp1; cur = cur->next->next; } return hp->next; } };

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

力扣题目链接(opens new window)

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

进阶:你能尝试使用一趟扫描实现吗?

 思路

普通的解决思路

先遍历一遍链表,得到链表的长度,用长度减去n,得到要删除节点的位置,然后在遍历一遍链表,删除节点

朴素代码实现

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        int size = 0;
     //创建一个虚拟头结点 ListNode
* hp = new ListNode(0); hp->next = head; ListNode* cur = hp;      //得到链表的长度
while (cur->next != NULL) { cur = cur->next; size++; }
     //得到要删除的点的位置
int pos = size - n; cur = hp; while (pos--) { cur = cur->next; }
     //直接跳过这个节点,完成删除 cur
->next = cur->next->next; return hp->next; } };

第二种思路

采用双指针算法

开始时,两个指针都位于头部,让一个指针先走n步,然后两个指针同时走,当走的早的指针走到尾部时,走的慢的指针就到了要删除节点的位置

 

 在上面的图中,红色的点使我们要删除的点,它位于倒数第二个位置处,n为2,则我们让红色指针先走两步,然后两个指针一起走,当红色的指针走到最后一个元素时,橙色指针指向的下一个元素就是我们要删除的元素

LeetCode代码

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* hp = new ListNode(0);
        hp->next = head;
        ListNode* fast = hp;
        ListNode* cur = hp;
        while (n-- && fast != NULL)
        {
            fast = fast->next;
        }
        //ast再提前走一步,因为需要让slow指向删除节点的上一个节点
        fast = fast->next;

        while (fast != NULL)
        {
            fast = fast->next;
            cur = cur->next;
        }
        cur->next= cur->next->next;
        return hp->next;
    }
};

 

六. 链表相交

力扣题目链接(opens new window)

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

图示两个链表在节点 c1 开始相交:

 

 

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

 

 示例 2:

 

示例 3:

 

 

 思路

这题的题目描述不是非常的清晰,这里说的是两个单链表相交的起始节点,而不是两个单链表值相等的起始节点,也就是说我们要比较的是指针,相交的指针在内存中是同一块区域,而这两个链表都指向了这块内存,要求的是这块内存的起始节点

 明白了要求什么,就可以继续向下思考

经过观察,发现要找的节点一定在长度较短的链表的子链里,而不可能在长链的前面的部分,因此我们需要让长链和短链的指针对齐,然后依次比较指针

为了实现对齐,可以先求出两个链表的长度,然后用长的减去短的,计算出长链指针需要移动的次数,然后进行对齐

 

 

如果没有找到,直接返回空即可

LeetCode代码

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

        int sizeA = 0,sizeB = 0;
        while (curA != NULL)
        {
            curA = curA->next;
            sizeA++;
        }
        while (curB != NULL)
        {
            curB = curB->next;
            sizeB++;
        }
        curA = headA;
        curB = headB;
        if (sizeA < sizeB)
        {
            swap(sizeA,sizeB);
            swap(curA,curB);
        }
        int pos = sizeA - sizeB;
        while (pos--)
        {
            curA = curA->next;
        }
        while (curA != NULL)
        {
            if (curA == curB)
            {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;   
    }
};

七.环形链表II

力扣题目链接(opens new window)

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

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

说明:不允许修改给定的链表。

 

思路

主要考察两知识点:

  • 判断链表是否环
  • 如果有环,如何找到这个环的入口

1.如何判断链表有环

设置两个指针,fast和slow,每次fast指针移动两次,而slow指针只移动一次,如果有环,则两个指针一定会相遇

如何证明这一点呢?其实无论指针怎么移动,最后一定会转换为一种情况,就是下面两个指针差一位的情况,那上面的情况举例

上面的情况个移动一次后,就转换为了下面的情况,其他情况也是如此,所以如果有环,两个指针一定会相遇

 2.确定有环之后,如何确定环的入口

当两个指针相遇时,slow指针走过的节点数为x+y,而fast指针走过的节点数为x+y+n*(y+z)

而fast走过的节点数又等于slow走过的节点的两倍,因此x+y+n*(y+z) = 2 * (x+y)

我们要求的值是x,即环形节点的如果,解方程得到x = n (y + z) - y,整理得到x = (n - 1) (y + z) + z

这里n-1是大于0的,所以fast指针至少要多走一圈

我们让n=1,解得x=z

这就意味着,一个指针从头结点开始走,另一个从相遇节点开始走,当两个指针相遇时,就是环的入口

那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

LeetCode代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
     //先判断是否有环
        while (fast != NULL && fast->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
       //寻找环的入口点
            if (fast == slow)
            {
                ListNode* index1 = head;
                ListNode* index2 = fast;
                while (index1 != index2)
                {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2;
            }
        }
        return NULL;
    }
};

 

 

 

posted @ 2023-03-13 21:55  写在风中的信  阅读(17)  评论(0编辑  收藏  举报