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 输出:[]
方法一:使用虚拟头结点
先为原始链表创建一个虚拟头结点,然后在下一个节点存在的情况下,判断下一个节点是否 == val,如果等于则进行删除操作.否则继续遍历.
参考代码1
//使用带虚拟头结点的方法. ListNode* removeElements(ListNode* l, int val) { ListNode* L = new ListNode(-1,l); ListNode* p = L,*q;//注意这里的指针定义. while(p->next!=NULL){//如果当前节点的下一个节点不为空. if(p->next->val==val){ q = p->next; p->next = p->next->next;//指针方向进行变化. delete q;//删除该节点. }else{ p = p->next;//更新p } } p = L->next; delete L; return p; }
方法二:无虚拟头结点
因为头结点前面没有节点,所以如果头结点是我们的删除目标,删除的办法就和删除非头结点的不同,需要特殊处理.
所以基本思路是:
先判断头结点是否 == val,如果等于则直接指针后移,并进行删除该节点.
头结点处理完毕后,继续后序的处理,依次遍历节点,判断当前节点的下一个节点是否 == val,如果等于则进行删除下一个节点.不相等,则指针后移,继续遍历.
参考代码2
ListNode* removeElements(ListNode* l, int val) { //删除头结点. ListNode* temp; while(l!=NULL&&l->val==val){ temp = l; l = l->next; delete temp; } if(l==NULL){//判断头结点是否为null了,如果是,则直接返回NULL. return NULL; } //删除非头结点 (和第一种方法一样) ListNode* cur = l; while(cur->next!=NULL){ if(cur->next->val==val){ temp = cur->next; cur->next = cur->next->next; delete temp; }else{ cur = cur->next; } } return l; }
206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
方法一:双指针
基本思路:从前往后将指针的方向改变,返回最后一个节点
使用pre指向前一个节点,cur代表当前节点,从第一个节点开始进行遍历,每次让cur指向pre,之后cur继续遍历下一个节点,直至链表遍历完毕.
参考代码1
//双指针法 ListNode* reverseList(ListNode* head) { ListNode* cur = head; ListNode* pre = NULL; ListNode* temp;//用于保存当前节点的下一个节点. while(cur){ temp = cur->next; cur->next = pre;//改变当前节点的指针方向. pre = cur;//pre指针后移 cur = temp;//当前节点后移. } return pre; }
方法二:递归1(从前往后改变指针方向)
思路和方法一基本一致,递归实际上就是自己调用自己,每次的操作一致.
- 每次我们需要改变指针指向,需要操作两个节点,当前节点cur和上一个节点pre,所以这就是递归函数的参数.
- 递归的结束条件就是当前节点为NULL,则不再需要改变方向了.递归结束.
参考代码2
//递归本质上就是在做相同的操作,自己不断的调用自己. ListNode* reverse(ListNode* pre,ListNode* cur){ if(cur==NULL){ return pre; } ListNode* temp = cur->next; cur->next = pre; return reverse(cur,temp);//继续递归. } ListNode* reverseList(ListNode* head) { return reverse(NULL,head); }
方法三:递归2(从后往前改变指针方向)
后期进行完善…
24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4] 输出:[2,1,4,3]
示例 2:
输入:head = [] 输出:[]
示例 3:
输入:head = [1] 输出:[1]
思路分析
题目的要求是交换相邻的节点,题目也说了是这两个节点的顺序发生改变,而不是仅仅交换下值.而且如果当前节点只有一个,则肯定不需要交换啦.
两个数进行交换我们需要一个中间值.而两个节点 1, 2 进行交换涉及到当前节点 0 的指向,A,B这两个节点的指针指向.
接下来我们用图解来表示:
根据图中,我们可以使用三个辅助变量便可完成操作.
参考代码
ListNode* swapPairs(ListNode* l) { ListNode* head = new ListNode(-1,l); ListNode* cur = head; ListNode* temp1,temp2,temp3;//分别存放,当前节点后面的三个节点,便于操作. while(cur->next!=NULL&&cur->next->next!=NULL){ temp1 = cur->next; temp2 = cur->next->next; temp3 = cur->next->next->next; cur->next = temp2;//执行第一步:当前0节点指向2号节点 temp2->next = temp1;//执行第二步:当前2节点指向1号节点 temp1->next = temp3;//执行第三步:当前1节点指向3号节点. cur = cur->next->next;//cur后移两位. } return head->next; }
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]
思路分析
快慢指针
先搞一个虚拟头结点,方便我们接下来的操作.
第一步slow和fast都指向虚拟头结点.
第二步fast向前移动n+1个节点
slow和fast同时向前移动,直至fast为空,则slow对应的便是目标节点的前一个节点.
为啥fast要先移动n+1个节点呢?why?
因为这样的话,之后当fast和slow同时移动,当fast为null时,slow对应的刚好是target前一个节点. 而如果是n,则slow对应的是目标节点.
参考代码
ListNode* removeNthFromEnd(ListNode* l, int n) { ListNode* head = new ListNode(-1,l); ListNode* fast = head,*slow= head; while(n--&&fast){ fast = fast->next; } fast = fast->next;//fast再向前走一步,因为需要让slow指向删除节点的上一个节点 while(fast){ fast=fast->next; slow = slow->next; } slow->next = slow->next->next; return head->next; }