力扣链表算法(c++)(代码随想录链表部分)

您所在的位置:网站首页 c链表初始化 力扣链表算法(c++)(代码随想录链表部分)

力扣链表算法(c++)(代码随想录链表部分)

2024-07-11 17:43| 来源: 网络整理| 查看: 265

文章一部分内容和图片引用代码随想录和力扣官方题解。

链表算法(C++) 链表

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链表的入口节点称为链表的头结点也就是head。

定义链表节点方式 // 单链表 struct ListNode { int val; // 节点上存储的元素 ListNode *next; // 指向下一个节点的指针 ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数 };

如果不定义链表的构造函数,C++会自动生成一个无参的构成函数,在初始化的时候就不能直接给变量赋值。

性能分析

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

203.移除链表元素

力扣题目链接(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 输出:[]

链表操作的两种方式:

直接使用原来的链表来进行删除操作。设置一个虚拟头结点在进行删除操作。

使用原来的链表来进行删除操作,只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。

以一种统一的逻辑来移除 链表的节点可以通过设置虚拟头结点的方式。

设置虚拟头结点 class Solution { public: ListNode* removeElements(ListNode* head, int val) { ListNode* dHead = new ListNode(0); // 设置一个虚拟头结点 dHead->next = head; ListNode* cur = dHead; while(cur->next != NULL){ if(cur->next->val == val){ ListNode* temp = cur->next; cur->next = cur->next->next; delete temp; }else{ cur = cur->next; } } head=dHead->next; delete dHead; //删除虚拟头结点 return head; } }; 707.设计链表

力扣题目链接(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 个节点。 class MyLinkedList { public: // 定义链表节点结构体 struct LinkedNode { int val; LinkedNode* next; LinkedNode(int val):val(val), next(nullptr){} }; // 初始化链表 MyLinkedList() { _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点 _size = 0; } // 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点 int get(int index) { if (index > (_size - 1) || index < 0) { return -1; } LinkedNode* cur = _dummyHead->next; while(index--){ // 如果--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 != nullptr){ cur = cur->next; } cur->next = newNode; _size++; } // 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。 // 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点 // 如果index大于链表的长度,则返回空 // 如果index小于0,则置为0,作为链表的新头节点。 void addAtIndex(int index, int val) { if (index > _size) { return; } if (index < 0) { index = 0; } LinkedNode* newNode = new LinkedNode(val); LinkedNode* cur = _dummyHead; while(index--) { cur = cur->next; } newNode->next = cur->next; cur->next = newNode; _size++; } // 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的 void deleteAtIndex(int index) { if (index >= _size || index < 0) { return; } LinkedNode* cur = _dummyHead; while(index--) { cur = cur ->next; } LinkedNode* tmp = cur->next; cur->next = cur->next->next; delete tmp; _size--; } // 打印链表 void printLinkedList() { LinkedNode* cur = _dummyHead; while (cur->next != nullptr) { cout next->val next; } cout 2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

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

首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。

然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下。

最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。

此时我们return pre指针就可以了,pre指针就指向了新的头结点。

总结

双指针,注意空的情况,循环的时候要用一个空结点保存。

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

力扣题目链接

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

img

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

示例 2:

输入:head = [] 输出:[]

示例 3:

输入:head = [1] 输出:[1] 迭代法

使用虚拟头结点,使逻辑相似。

在这里插入图片描述

class Solution { public: ListNode* swapPairs(ListNode* head) { ListNode* dHead = new ListNode(0); dHead->next = head; ListNode* pre = dHead; while(pre->next !=nullptr && pre->next->next != nullptr){ //非空结点 只有一个节点 ListNode* temp1 = pre->next; ListNode* temp2 = pre->next->next->next; pre->next = temp1->next; pre->next->next = temp1; pre->next->next->next = temp2; pre=pre->next->next; } return dHead->next; } }; 递归法 class Solution { public: ListNode* swapPairs(ListNode* head) { if (head == nullptr || head->next == nullptr) { return head; } ListNode* newHead = head->next; head->next = swapPairs(newHead->next); newHead->next = head; return newHead; } };

返回值:交换完成的子链表 调用单元:设需要交换的两个点为 head 和 next,head 连接后面交换完成的子链表,next 连接 head,完成交换 终止条件:head 为空指针或者 next 为空指针,也就是当前无节点或者只有一个节点,无法进行交换

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

力扣题目链接(opens new window)

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

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

示例 1:

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

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

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

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

双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。

class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* dHead = new ListNode(0); dHead->next = head; ListNode* fast = dHead; ListNode* slow = dHead; while(n-- && fast !=nullptr){ fast = fast->next; } while(fast->next !=nullptr){ slow = slow->next; fast = fast->next; } slow->next = slow->next->next; return dHead->next; } }; 面试题 02.07. 链表相交

力扣题目链接

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

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

img

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

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

求两个链表交点节点的指针。因为题目表述的是交点不是数值相等,而是指针相等,因此不用在意数值上的相等,只用比较指针是否相同(curA == curB)

求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置.(本质上是假定存在交点)

比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode* curA=headA; ListNode* curB=headB; int lengthA =0; int lengthB = 0; while(curA!=NULL){ lengthA++; curA=curA->next; } while(curB!=NULL){ lengthB++; curB=curB->next; } curA =headA; curB = headB; if(lengthA next; } while(curA!=NULL){ if(curA == curB){ return curA; } curA=curA->next; curB=curB->next; } return NULL; } }; 总结

哈希好用,题目有些绕。

142.环形链表II

力扣题目链接(opens new window)

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

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

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

循环链表

1.判断链表是否环

快慢指针,如果相遇,则必定有环。(一个快,一个慢,相遇了肯定是快的又回去了)

2.如果有环,如何找到这个环的入口

从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:

img

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z,

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode* fast = head; ListNode* slow = head; while(fast!=NULL && fast->next!=NULL){ //fast!=NULL 在前, slow = slow->next; fast = fast->next->next; if(fast == slow){ ListNode* i = head; ListNode* j = fast; while(i !=j){ i=i->next; j=j->next; } return i; } } return NULL; } };


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3