203.移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。
思路
- 注意虚拟头结点的设计(使头部元素变为普通元素,不需要特殊处理,从而降低编码复杂度)
- 需要学会链表的定义方式,最好自己编写一遍。
- 返回头结点时需要返回新的头结点:virtualhead->next
代码
/**
* 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* virtualhead = new ListNode(0);
virtualhead->next=head;
ListNode* cur=virtualhead;
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 virtualhead->next;
}
};
复杂度
时间复杂度:O(n)
707.设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:
val
和next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果是双向链表,则还需要属性
prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。实现
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
的节点。
思路
设计链表题,难度不低,但对链表理解很有帮助。
代码
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(NULL){}
};
MyLinkedList(){
virtualhead = new LinkedNode(0);
size =0; //长度
}
int get(int index){
if(index<0||index>size-1) //下标无效
return -1;
LinkedNode* cur=virtualhead;
while(index--){
cur=cur->next;
}
return cur->next->val;
}
void addAtHead(int val){
LinkedNode* newNode=new LinkedNode(val); //初始化新结点
newNode->next=virtualhead->next;
virtualhead->next=newNode;
size++; //插入结点需要注意增加长度
}
void addAtTail(int val){
LinkedNode* newNode=new LinkedNode(val);
LinkedNode* cur=virtualhead;
while(cur->next!=NULL){
cur=cur->next; //cur移动到最后一个元素
}
cur->next=newNode; //添加尾部一个元素
size++;
}
void addAtIndex(int index, int val) {
if (index<0) index=0;
if(index>size) return; //index比长度更大,故不会插入
LinkedNode* cur=virtualhead;
LinkedNode* newnode=new LinkedNode(val);
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= virtualhead;
while(index--)
cur=cur->next;
LinkedNode* tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
size--; //删除数据长度减少
}
private:
int size;
LinkedNode* virtualhead;
};
/**
* 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);
*/
复杂度
时间复杂度:涉及index,需要从头开始遍历,所以时间复杂度O(n),其余情况为O(1)。
206.反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
思想
定义一个pre指针(前驱指针)来指示链表的前驱结点,然后将pre指针变为next指针,pre指针初始值为空指针。具体步骤:
- 首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
- 然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
- 为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
- 接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
- 最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
代码
/**
* 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=NULL; //前驱指针,初始为空
ListNode* cur=head;
while(cur){
ListNode* tmp=cur->next; //暂存下一个元素
cur->next= pre;
pre=cur;
cur=tmp;
}
return pre;
}
};
复杂度
时间复杂度:O(n)