leetcode链表的相关题目

C友们,如果觉得光看文字不好理解,我在b站上发了关于链表题目的相关视频,欢迎各位前来:链表题目视频

2022/7/6

141. 环形链表

题目:
给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。

注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

解题思路为:使用双指针法

本方法需要读者对「Floyd 判圈算法」(又称龟兔赛跑算法)有所了解。
假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。

我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
当块指针等于null或fast->next等于null时,跳出循环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==nullptr||head->next==nullptr){
            return false;
        }
        ListNode *slow=head;
        ListNode *fast=head->next;
        while(slow!=fast){
            if(fast==nullptr||fast->next==nullptr)
            {return false; }
            slow=slow->next;
            fast=fast->next->next;
           
        }
        return true;

    }
};

2022/7/8

142. 环形链表 II

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。

如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

在这里插入图片描述

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

解题思路为:
方法一:哈希表

一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。
代码

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode *> visited;
        while (head != nullptr) {
            if (visited.count(head)) {
                return head;
            }
            visited.insert(head);
            head = head->next;
        }
        return nullptr;
    }
};

时间复杂度为:O(n),其中n 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。
空间复杂度为:O(n),其中 n 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。

方法二:快慢指针

思路与算法

我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与slow 指针在环中相遇。

因此,当发现 slow 与 fast 相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和slow 每次向后移动一个位置。最终,它们会在入环点相遇。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        while (fast != nullptr) {
            slow = slow->next;
            if (fast->next == nullptr) {
                return nullptr;
            }
            fast = fast->next->next;
            if (fast == slow) {
                ListNode *ptr = head;
                while (ptr != slow) {
                    ptr = ptr->next;
                    slow = slow->next;
                }
                return ptr;
            }
        }
        return nullptr;
    }
};

时间复杂度:O(n),其中 n 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)。

空间复杂度:O(1),我们只使用了slow,fast,ptr 三个指针

2022/7/10

160. 相交链表

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

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

在这里插入图片描述

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

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

自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:

在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

在这里插入图片描述

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

在这里插入图片描述

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

解题思路:

首先,创建一个哈希集合,将链表A的所有元素都放入到该哈希集合中,然后判断该哈希集合中是否存在链表B的元素值,若含有,则返回该节点,若把整个链表B都遍历完后都不存在与哈希集合中相等的节点,则返回false。
代码:

/**
 * 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) {
        unordered_set<ListNode*> visited;
        ListNode * temp=headA;
        while(temp!=nullptr){
            visited.insert(temp);
            temp=temp->next;
        }
        temp=headB;
        while(temp!=nullptr){
            if(visited.count(temp)){
                return temp;
            }
            else
            {
                temp=temp->next;
            }
        }
        return nullptr;
    }
};

时间复杂度为:O(m+n),m、n为链表A,链表B的长度,分别遍历链表A和链表B.
空间复杂度:m为链表A的全部节点,需要用哈希集合存储链表A的全部节点。

方法二:

首先创建2个指针pA,pB分别指向两个链表,pa指向链表A的头结点,pb指向链表B的头结点,当指针pA遍历完链表A后,让pA指向链表B的头结点继续遍历链表B.同理,当指针pB遍历完链表B后,让pB指向链表A的头结点继续遍历链表A.当pA,pB相遇时指向的节点即为两个链表的交点。
代码如下:

/**
 * 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) {
        if(headA==nullptr||headB==nullptr)
        return nullptr;
        ListNode * pa;
        ListNode * pb;
        pa=headA;
        pb=headB;
        while(pa!=pb){
            if(pa==nullptr)
            {
                pa=headB;
            }
            else 
            pa=pa->next;
            if(pb==nullptr){
                pb=headA;
            }
            else
            pb=pb->next;
        }
        return pa;
    }
};

时间复杂度为:O(m+n),m、n为链表A,链表B的长度,分别遍历链表A和链表B.每个指针遍历两个链表各一次。
空间复杂度为:O(1)

2022/7/12

19. 删除链表的倒数第 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]

解题思路为:双指针法

哑结点其实就是放在第一个存放数据结点之前、头结点之后的结点。加入哑结点之后就可以使所有数据结点都有前驱结点,这样就会方便执行链表的一些操作。

首先,创建一个哑结点链接在头结点的前面,让哑结点成为第一个结点,创建2个指针分别为快指针和慢指针,让快指针指向头结点,让慢指针指向哑结点。因为要找到倒数第n个结点,所以让快指针先移动n个单位,当快指针所指向的位置与慢指针所指向位置相差n,让快指针与慢指针同时移动,当快指针指向空时,慢指针即指向了将要删除的节点的前一个结点,让其next指针指向下下一个节点,即删除了倒数第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* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dull=new ListNode(0,head);
        ListNode *slow=dull;
        ListNode *fast=head;
        
        int count=0;
          for(int i=0;i<n;i++){
            fast=fast->next;
        }    
        while(fast!=nullptr){
      fast=fast->next;
      slow=slow->next;
      
        }
        slow->next=slow->next->next;
       
        ListNode *ptr=dull->next;
        delete dull;
        return ptr;
    }
};

时间复杂度:O(L),L是链表的长度。
空间复杂度:O(1)

方法二:计算链表长度

我们首先从头节点开始对链表进行一次遍历,得到链表的长度 L。随后我们再从头节点开始对链表进行一次遍历,当遍历到第 L-n+1 个节点时,它就是我们需要删除的节点。

为了方便删除操作,我们可以从哑节点开始遍历 L-n+1个节点。当遍历到第 L-n+1 个节点时,它的下一个节点就是我们需要删除的节点,这样我们只需要修改一次指针,就能完成删除操作。

class Solution {
public:
    int getLength(ListNode* head) {
        int length = 0;
        while (head) {
            ++length;
            head = head->next;
        }
        return length;
    }

    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        int length = getLength(head);
        ListNode* cur = dummy;
        for (int i = 1; i < length - n + 1; ++i) {
            cur = cur->next;
        }
        cur->next = cur->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};

时间复杂度:O(L),L是链表的长度。

空间复杂度:O(1)

方法三:栈

我们也可以在遍历链表的同时将所有节点依次入栈。根据栈「先进后出」的原则,我们弹出栈的第 nn 个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点。这样一来,删除操作就变得十分方便了。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        stack<ListNode*> stk;
        ListNode* cur = dummy;
        while (cur) {
            stk.push(cur);
            cur = cur->next;
        }
        for (int i = 0; i < n; ++i) {
            stk.pop();
        }
        ListNode* prev = stk.top();
        prev->next = prev->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};

时间复杂度:O(L),其中 L是链表的长度。

空间复杂度:O(L),其中 L 是链表的长度。主要为栈的开销。

2022/7/14

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == 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
输出:[]

解题思路:

首先设置一个哑结点指向头结点的,哑结点是为了防止当头结点被删除后,链表为空。创建一个临时指针指向哑结点,让临时指针遍历整个链表,我们找到符合条件的节点的前驱节点,让这个前驱节点指向满足条件节点的后一个节点,即把满足条件的结点删除了。当遍历完这个链表后,也完成了相应的操作。

时间复杂度为:O(n),n为链表的长度,需要遍历链表一次。

空间复杂度为:O(1)
代码:

/**
 * 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 * dull=new ListNode(0,head);
        ListNode *pa=dull;
        
        while(pa->next!=nullptr)
        {
      if(pa->next->val==val)
            pa->next=pa->next->next;
            else
            pa=pa->next;
        }
        ListNode *ptr=dull->next;
        delete dull;
        return ptr;
    }
};

法2:使用递归算法:c语言:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val){
if(head==NULL)
{
    return head;
}
head->next=removeElements(head->next,val);
if(head->val==val)
{
   struct ListNode* tmp = head->next;
    free(head);
    return tmp;
}
return head;

}

时间复杂度:O(n),其中 n 是链表的长度。递归过程中需要遍历链表一次。

空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用栈,最多不会超过 n 层。

2022/7/17

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

在这里插入图片描述

输入:head = [1,2,2,1]
输出:true

示例 2:

在这里插入图片描述

输入:head = [1,2]
输出:false

解题思路:

首先遍历一边链表当中的元素,将链表中的数据放入到新创建的数组当中,设置双指针,用来判断是否为回文串。用双指针来判断数组是否为回文串的方法为

设置左右指针分别指向字符串的首端和末端,依次比较所指向的数字是否相同?若相同则left++,right–,直至left==right时,则遍历结束。

注意:用到的API为isalnum()函数用来检测一个字符是否是字母或十进制数字。

返回值非0表示字符为字母或十进制数字,返回值为0表示字符为非字母或非十进制数字。
时间复杂度:O(n),n为链表的长度,也就是链表中元素的个数。第一步: 遍历链表并将值复制到数组中。

空间复杂度:O(n),n为链表的元素个数,我们使用了一个数组对链表元素进行了存放。
代码:

class Solution {
public:
    bool isPalindrome(string s) {
        int left=0;
        
        string tea;
        for(char c:s){
            if(isalnum(c)){
                tea+=tolower(c);
            }

        }
int right=tea.size()-1;
        while(left<right){
            if(tea[left]==tea[right]){
                left++;
                right--;
            }
            else
            {
                return false;
                break;

            }
        }
        return true;
    }
};

2022/7/18

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

解题思路:

将两个有序链表进行连接,将两个链表进行连接起来,不占用新的空间,常见一个新的头结点prevhead,定义一个新的指针prev指向prevhead,当两个链表均不为空时,对节点进行遍历,当list1->val<list2->val时,让prev指向list1,list1右移,当list2->val<list1->val时,让prev指向list2,list2右移,让prev=prev->next让prev向右移动,最后prev->next=list1==nullptr?list2:list1;list1是否为空,若是,则prev->next=list2,若不是空,则prev->next=list1.

/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode * prevhead=new ListNode(-1);
        ListNode * prev=prevhead;
        while(list1!=nullptr&&list2!=nullptr)
        {
            if(list1->val<list2->val)
            {
               prev->next=list1;
               list1=list1->next;
            }
            else
            {
                prev->next=list2;
                list2=list2->next;
            }
prev=prev->next;
        }
        prev->next=list1==nullptr?list2:list1;
        return prevhead->next;
    }
};

2022/7/20

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

在这里插入图片描述

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

解题思路为:

这个题是将两个链表的节点的val值分别相加得到数放到一个新生成的链表当中。当2个链表的长度相同时,分别将2个链表的节点依次相加放入到新生成的链表的节点当中,当最后尾节点相加时,若有进位则创建新节点将进位放入到其中。

当2个链表长度不同时,即一长一短时,可以将先遍历完的那个链表的值赋值为0,在后续的遍历过程中,其值设为了0,它的遍历也结束了。

在同时遍历2个链表的过程中,当其中一个链表为空时,不在继续遍历,遍历另一个链表即可,直到另一个链表为空时,跳出循环结束。

时间复杂度为:O(max(m,n)),m、n分别为2个链表的长度,我们需要遍历2个链表的全部位置,处理每个位置需要O(1)的时间。

空间复杂度为:O(1)

代码:

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *p1;
        ListNode *p2;
         ListNode * ptr=nullptr;
         ListNode * head=nullptr;
        p1=l1;
        p2=l2;
        int temp;
        int carry=0;
        int sum;
       int n1,n2;
        while(p1!=nullptr||p2!=nullptr){
          if(p1==nullptr)
         n1=0;
         if(p1!=nullptr)
         n1=p1->val;
          if(p2==nullptr)
          n2=0;
          if(p2!=nullptr)
          n2=p2->val;
           temp= (n1+n2+carry)%10;
            carry=(n1+n2+carry)/10;
            if(head==nullptr)
            {
                    ptr=head=new ListNode(temp);
            }
            else
            {
                ptr->next=new ListNode(temp);
          ptr=ptr->next;
            }
            if(p1!=nullptr)
           p1=p1->next;

           if(p2!=nullptr)
           p2=p2->next;
        }
  if(carry>0)
  ptr->next=new ListNode(carry);
        return head;
    }
};

2022/7/21

61. 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

在这里插入图片描述

输入:head = [0,1,2], k = 4
输出:[2,0,1]

解题思路为

首先,我们可以通过遍历链表来获得链表的长度,将链表的最后一个节点与第一个节点相连接,形成一个有环的链表,旋转链表的实质是将2个节点之间的连接将其断开,形成一个新的头结点和一个尾节点。

例如,当旋转2次后新链表的头结点为4,尾节点为3,其实就是将3和4之间的连接断开了。将头结点设为val值为4的节点。我们从头结点遍历,找到值为3的节点后,将它与值为4的节点断开。

时间复杂度为:O(n),最坏的情况下,遍历链表2次。
空间复杂度为:O(1)

代码:

/**
 * 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* rotateRight(ListNode* head, int k) {
      if(head==nullptr||k==0||head->next==nullptr)
      return head;
      
        ListNode *ptr =head;
        int length=1;
     
while(ptr->next!=nullptr){
            ptr=ptr->next;  
             length++;      
        }
        ptr->next=head;

        int m=k%length;
        int count=length-m;
       ListNode * ptr1=head;
        int i=1;
        while(i<count)
        {
            ptr1=ptr1->next;
           i++;
                   }
         ListNode *newhead =ptr1->next;
        ptr1->next=nullptr;
        return newhead;
       
    }
};

2022/8/23

剑指 Offer 06. 从尾到头打印链表

栈的特点是后进先出,即最后压入栈的元素最先弹出。考虑到栈的这一特点,使用栈将链表元素顺序倒置。从链表的头节点开始,依次将每个节点压入栈内,然后依次弹出栈内的元素并存储到数组中。

创建一个栈,用于存储链表的节点
创建一个指针,初始时指向链表的头节点
当指针指向的元素非空时,重复下列操作:
将指针指向的节点压入栈内
将指针移到当前节点的下一个节点
获得栈的大小 size,创建一个数组 print,其大小为 size
创建下标并初始化 index = 0
重复 size 次下列操作:
从栈内弹出一个节点,将该节点的值存到 print[index]
将 index 的值加 1
返回 print

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
stack<ListNode*> stack;
vector<int> arr;
ListNode* ptr=head;
while(ptr!=nullptr)
{
    stack.push(ptr);
    ptr=ptr->next;
}
while(stack.empty()==false)
{
    ListNode *node=stack.top();
    arr.push_back(node->val);
    stack.pop();
}
return arr;
    }
};

206. 反转链表

创建三个指针分别为pre,temp,和after,temp指向当前节点,pre指向temp的上一个节点,after指向temp的下一个节点,首先让temp指向头结点,pre值为空,让after指针指向temp节点的下一个节点,让temp->next=pre;pre=temp;temp=after;最后返回节点pre.
c:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
struct ListNode* pre;
struct ListNode* temp;
struct ListNode* after;
pre=NULL;
temp=head;
while(temp!=NULL)
{
    after=temp->next;
temp->next=pre;
pre=temp;
temp=after;
}
return pre;
}

时间复杂度:O(n),其中n是链表的长度。需要遍历链表一次。

空间复杂度:O(1)。

2022/11/5

147. 对链表进行插入排序

c代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* insertionSortList(struct ListNode* head){
if(head==NULL)
return head;
struct ListNode* dummyHead=malloc(sizeof(struct ListNode));
dummyHead->val=0;
dummyHead->next=head;
struct ListNode* lastSorted=head;
struct ListNode* cur=head->next;
while(cur!=NULL)
{
    if(lastSorted->val<=cur->val)
    lastSorted=lastSorted->next;
    else
    {
        struct ListNode* prev=dummyHead;
        while(prev->next->val<cur->val)
        prev=prev->next;
lastSorted->next=cur->next;
cur->next=prev->next;
prev->next=cur;
    }
    cur=lastSorted->next;
}
return dummyHead->next;

}

83. 删除排序链表中的重复元素

c:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* deleteDuplicates(struct ListNode* head){
if(head==NULL)
return head;
struct ListNode* ptr=head;
while(ptr->next!=NULL)
{
    if(ptr->val==ptr->next->val)
    ptr->next=ptr->next->next;
    else
    ptr=ptr->next;
}
return head;
}

2022/11/6

奇偶链表

将索引为奇数的元素连接成一个链表,将索引为偶数的元素连接成一个链表,将偶数链表连接到奇数链表的末尾。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* oddEvenList(struct ListNode* head){
    if(head==NULL)
return head;
struct ListNode * odd=head;
struct ListNode * even=head->next;
struct ListNode *evenHead=head->next;

while(even!=NULL&&even->next!=NULL)
{
    odd->next=even->next;
    odd=even->next;
    even->next=odd->next;
    even=even->next;
}
odd->next=evenHead;
return head;
}

剑指 Offer 22. 链表中倒数第k个节点

解题思路:定义2个指针,ptr1和ptr2,让2个指针同时指向head节点,首先让ptr1先走k个位置后,让ptr2指向head节点,然后2个指针同时遍历整个链表,当ptr1遍历完整个链表后,ptr2所指向的位置就是链表的倒数第k个节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* ptr1=head;
struct ListNode* ptr2=head;
while(ptr1!=NULL)
{
   ptr1=ptr1->next;
   k--;
   if(k<0)
   {
ptr2=ptr2->next;
   } 
}
return ptr2;
}

法2:首先遍历整个链表得到链表的长度len,倒数第k个元素就是正数第len-k个元素,从头结点遍历找到正数第len-k个元素即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* ptr=head;
int len=0;
while(ptr!=NULL)
{
    ptr=ptr->next;
    len++;
}
ptr=head;
for(int i=0;i<len-k;i++)
{
ptr=ptr->next;
}
return ptr;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值