203.移除链表元素
手把手带你学会操作链表 | LeetCode:203.移除链表元素
思路
从头节点出发,依次向后遍历,直到遍历到待删除元素的前驱节点时,即使用cur->next->val是否于target相同来对比是否找到待删除节点(为什么不使用cur->val来对比呢?原因是,如果发现当前所指节点为待删除节点的时候没办法删除,单链表要删除一个元素必须要其前驱),一直向后遍历,直到所有待删除元素全部删除为止。
代码实现:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *dummyhead =new ListNode(0);
dummyhead->next=head;
ListNode *p=dummyhead;
while(p->next!=nullptr){
if(p->next->val==val){
ListNode *q=p->next;
p->next=q->next;
delete q;
}
//p=p->next;出错,这样会漏处理节点
else p=p->next;
}
head=dummyhead->next;
delete dummyhead;
return head;
}
};
时间复杂度为o(n) 单纯删除链表上的一个元素该操作的时间复杂度是o(1)但是由于链表没有随机访问的特性,因此对于只带头指针的单链表来说,找到特定的节点必须从头开始一个节点一个节点的遍历,该操作的时间复杂度为o(n) 因此总的时间复杂度为o(n) 未利用到额外的空间开销,空间复杂度为o(1);
此外,需要注意的是,这里所说的头节点和考研中所学的头节点是两个概念,这里的头节点是指链表的第一个节点,对应于考研中不带头节点的链表,这里所插入的虚拟头节点对应于考研中的头节点,此外掌握c++语法,如何新建一个节点,使用关键字new 例如ListNode *head = new ListNode(头节点的值)
707.设计链表
思路:
本题就是实现一遍链表的一些基本操作,比较考验代码能力
代码实现:
class MyLinkedList {
public:
//比较容易 就是比较考验代码能力
struct LinkNode{
int val;
LinkNode *next;
LinkNode(int val):val(val),next(nullptr){}
};
//初始化链表
MyLinkedList() {
_dummyhead=new LinkNode(0);
_size=0;
}
//获取指定节点元素的值
int get(int index) {
//条件判断,判断index是否为合法值
if(index>(_size-1)||index<0) return -1;
LinkNode *p=_dummyhead;
for(int i=0;i<=index;i++){
p=p->next;
}
//while(index--) p=p->next;
return p->val;
}
//在链表头插入节点
void addAtHead(int val) {
if(_size>1000) return;
LinkNode *node=new LinkNode(val);
node->next=_dummyhead->next;
_dummyhead->next=node;
_size++;
}
//在链表末尾插入节点
void addAtTail(int val) {
if(_size>1000)return;
LinkNode *node = new LinkNode(val);
LinkNode *p=_dummyhead;
while(p->next!=nullptr){
p=p->next;
}
node->next=nullptr;
p->next=node;
_size++;
}
//在指定位置插入节点
void addAtIndex(int index, int val) {
if(index>(_size)||index<0)return;
LinkNode *node=new LinkNode(val);
LinkNode *p=_dummyhead;
int i;
for(i=0;i<index;i++){
p=p->next;
}
node->next=p->next;
p->next=node;
_size++;
}
//删除指定节点
void deleteAtIndex(int index) {
if(index>(_size-1)||index<0) return;
LinkNode *p=_dummyhead;
int i;
for(i=0;i<index;i++) p=p->next;
LinkNode *q=p->next;
p->next=q->next;
delete q;
_size--;
}
/* void printLinkList(){
LinkNode *p=_dummyhead;
while()
}*/
private:
int _size;
LinkNode *_dummyhead;
};
206.反转链表
帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法
思路:
方法一:头插法
只需要使用头插法的性质即可,即创建的链表元素顺序是原来的逆序,并且只需要创建一个虚拟头节点来进行头插即可,不需要额外的空间
代码实现:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *dummyhead=new ListNode(0);
dummyhead->next=nullptr;
//头插法
while(head!=nullptr){
ListNode *p=head;
head=head->next;
p->next=dummyhead->next;
dummyhead->next=p;
}
head=dummyhead->next;
delete dummyhead;
return head;
}
};
时间复杂度为o(n)空间复杂度为o(1)
方法二:双指针法
只需要要改变链表next指针的指向,直接将链表反转,而不用重新定义一个新的链表,首先定义一个p指针指向头节点,再定义一个pre指针,初始化为null。然后开始反转,首先要把p->next节点用temp保存一下,否则把p的指向改变之后,后面的链表就找不到了,走丢了。然后把p->next指向pre,p向后走,pre向后走,直到p指向空,说明所有的元素都处理了一遍,整个链表反转完成,pre指向的就是新的头节点
代码实现:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//解法二 双指针法
ListNode *temp;
ListNode *p=head;
ListNode *pre=nullptr;
while(p!=nullptr){
temp=p->next;
p->next=pre;//改变链表指向
pre=p;
p=temp;//两个指针向后移动
}
return pre;
}
};
时间复杂度 o(n)空间复杂度o(1)
方法三:递归
思路和双指针法相同,只不过写成了递归版本,把每次反转过程用一个递归处理。
代码实现:
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur==nullptr) return pre;//对应双指针写法,当cur指向空说明所有节点都处理了一遍 返回pre
ListNode *temp = cur->next;
cur->next=pre;//改变指向
return reverse(cur,temp);//相当cur和pre向后移动一位
//这里要把pre一层一层的返回回去才对
}
ListNode* reverseList(ListNode* head) {
//递归版本
return reverse(nullptr,head);
}
};
时间复杂度o(n) 空间复杂度o(n),因为每一个节点的反转过程用一个递归函数实现,一共有n个节点,所以会压入栈n个函数,空间复杂度为o(n);
总结
今天的算法题算是用代码把理论知识实践了一下,加强了对链表的认知,还不错。