题目要求
所有主要考察对链表的增删查改的功能
总结
- 对于有些从头遍历到尾的方法,创建一个头结点使得所有的结点能以统一的方式且全部被遍历到,不会出现头结点不被遍历的问题。
- 对于遍历的条件,有的时候curNode != nullptr,有的时候是curNode->next != nullptr,看作用范围。
- 对于增删查改,尤其是查询类涉及到输入index的,注意判断index的越界问题,是否大于0及是否大于链表长度从而进行处理。
移除链表元素-简单
给你一个链表的头节点 head
和一个整数val
请你删除链表中所有满足Node.val == val
的节点,并返回新的头节点 。
示例:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
要点
就是注意删除的话,可以考虑删除当前节点的next的val,这样不会丢失链接。循环结束的标志也是current->next==nullptr。
可能会有的疑惑:
1.循环结束的条件会不会导致最后一个结点没有被遍历?
答:不会的,因为每一次current都判断的是下一个节点是否被删。所以最后一个在上一次遍历已经判断过了。
2.初始的那个结点会不会没有遍历到?
有可能会,所以我们创建一个虚拟的头结点,虚拟的头结点->next=head,这样就能遍历到所有的值
代码
/*** 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* firstNode = new ListNode(0); firstNode -> next = head;ListNode* cur = firstNode;ListNode* temp;while(cur -> next) {if(cur -> next -> val == val) {temp = cur -> next;cur -> next = cur -> next -> next;delete temp;} else {cur = cur -> next;}}return firstNode -> next;}
};
设计链表-中等
你可以选择使用单链表或者双链表,设计并实现自己的链表。
要求:
1.单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
2.如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
3.实现 MyLinkedList 类:
MyLinkedList()
初始化 MyLinkedList 对象。
int get(int index)
获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val)
将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val)
将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val)
将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为 index 的节点。
要点
注意看清题目给的类是一个链表类而非一个结点类。所以需要自己定义一个结点类
同样的最好给这个链表一个虚拟头结点,这样便于统一的做一些操作。
一定要先浏览一遍题目,其实可以不用给链表定义一个length,但看void addAtIndex(int index, int val)
中有提到关于长度的内容,所以还是要定义一个,不要偷懒。
class MyLinkedList {
public:struct myListNode {int val;myListNode* next;myListNode(int val) : val(val), next(nullptr) {}};MyLinkedList() {head = new myListNode(0);length = 0;}int get(int index) {if (index < 0 || index >= length) return -1;myListNode* curNode = head->next;for (int i = 0; i < index; ++i) {curNode = curNode->next;}return curNode->val;}void addAtHead(int val) {myListNode* newNode = new myListNode(val);newNode->next = head->next;head->next = newNode;++length;}void addAtTail(int val) {myListNode* newNode = new myListNode(val);myListNode* curNode = head;while (curNode->next) {curNode = curNode->next;}curNode->next = newNode;++length;}void addAtIndex(int index, int val) {if (index > length) return;if (index <= 0) {addAtHead(val);} else if (index == length) {addAtTail(val);} else {myListNode* newNode = new myListNode(val);myListNode* curNode = head;for (int i = 0; i < index; ++i) {curNode = curNode->next;}newNode->next = curNode->next;curNode->next = newNode;++length;}}void deleteAtIndex(int index) {if (index < 0 || index >= length) return;myListNode* curNode = head;for (int i = 0; i < index; ++i) {curNode = curNode->next;}myListNode* temp = curNode->next;curNode->next = temp->next;delete temp;--length;}private:myListNode* head;int length;
};
翻转链表 - 简单
给你单链表的头节点head
,请你反转链表,并返回反转后的链表。
示例:
要点
利用双指针法,一个pre代表上一个结点的地址,一个cur代表当前节点的地址,我们要做的就是让cur->next = pre。
依据上面的推断,当实现了cur->next = pre时,要让cur继续按照原先的顺序往下走,我们还需要一个保存cur的下一个结点的地址的temp。
因此逻辑就是:每一次遍历,更新cur->next阶段:temp=cur->next,cur->next=pre,更新pre阶段:pre = cur,更新cur阶段:cur = temp;
需要注意的是,每一次遍历改变的是进入循环时刻的pre和cur之间的链表指向关系。
代码
/*** 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* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;ListNode* temp;while(cur) {temp = cur ->next;cur -> next = pre;pre = cur;cur = temp;}//当遍历完之后,此时的cur已经指向链表外了。即最后一个结点(此时是pre)的next故为空,=退出循环return pre;}
};