遇到的错误都写在前一篇了,链表主要就是不要空指针->next就好
第一题
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
刚开始接触这种题不会写,直接看答案了
方法一
使用栈解决,不过没想过借助别的东西,也没思考过这个,贴一下代码当作学习吧
public ListNode reverseList(ListNode head) {
Stack<ListNode> stack = new Stack<>();
//把链表节点全部摘掉放到栈中
while (head != null) {
stack.push(head);
head = head.next;
}
if (stack.isEmpty())
return null;
ListNode node = stack.pop();
ListNode dummy = node;
//栈中的结点全部出栈,然后重新连成一个新的链表
while (!stack.isEmpty()) {
ListNode tempNode = stack.pop();
node.next = tempNode;
node = node.next;
}
//最后一个结点就是反转前的头结点,一定要让他的next
//等于空,否则会构成环
node.next = null;
return dummy;
}
方法二
双链表求解
![](https://img-blog.csdnimg.cn/img_convert/5540b6313e241c5647cc4667b9f3d624.png)
这个方法和迭代差不多,迭代是我看了答案后自己写的,等会贴一起
方法三
递归解决
我对递归太不熟练了,以前看的第一个递归都看了半天,这个也是看了半天看不懂,第二天再看评论区里别人的解析终于看懂了,自己也试着自主写了一遍
/**
* 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) {}
* };
*/
// if(head==NULL||head->next==NULL){ //这是迭代
// return head; //head是头一个 pre是当前 nex是下一个
// } //当前p指向头一个h之后 头一个h找不到当前p,就直接赋值h变成当前p
// ListNode *pre=head->next; //当前p找不到下一个n,就直接赋值p变下一个n,然后先用n找到下一个n记录下来
// ListNode *nex=head->next;
// head->next=NULL;
// while(nex!=NULL){
// nex=nex->next;
// pre->next=head;
// head=pre;
// pre=nex;
// }
// return head;
class Solution {
public:
ListNode* reverseList(ListNode* head) { //比如1 2 3 4 5的链表
if(head==NULL||head->next==NULL){ //一直往后,找到最后一个结点5,直接返回
return head;
}
ListNode *newList=reverseList(head->next); //最后一个结点5不做操作直接返回,那么先在倒数第二个结点4进行操作,此时head=4
head->next->next=head; //4的下一个是5,5指向4
head->next=NULL; //4原本指向5.另它指向空
return newList;
}
};
题目二
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
/**
* 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 *newhead=head;
while(head!=NULL&&head->val==val){ //找到第一个与val不一样的结点
newhead=newhead->next;
head->next=NULL;
head=newhead;
}
if(newhead==NULL){ //如果本来为空或者链表全被删了,返回head或者newhead都行,都指向空
return head;
} //如果有新的链表,head是新链表的头,等会拿来输出,不要动了
ListNode *donull=newhead; //创建一个指针用来遇到下一个是要删除的结点的时候,让它指向下下个结点
newhead=newhead->next; //newhead不是空 值也不是val,直接往后走一步
while(newhead!=NULL){ //拿newhead遍历,遇到要删的用donull操作,donull是正确链表的位置
if(newhead->val==val){
newhead=newhead->next; //遇到要删的结点newhead直接往后走
donull->next=newhead; //donull进行操作,因为donull指向要删的结点,让它指向要删的下一个就好
}
else{
newhead=newhead->next;//如果不用操作,直接往前走
donull=donull->next; //这个结点不要删,也就是要保留,donull往后走
}
}//这些代码没有让要删的结点指向空,而是让要删的上一个结点指向要删的下一个结点,要删的结点仍指向要删的下一个结点
return head;
}
};
这道题是自己自主写的,写的比较多,这道题主要还是指针判断问题,确定好一些特殊情况,不要访问空指针
题目三
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
/**
* 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* oddEvenList(ListNode* head) {
if(head==NULL||head->next==NULL||head->next->next==NULL){
return head;
}
ListNode *odd=head; //单数链表指向头
ListNode *oddnn=head; //用oddnn遍历链表找出奇数结点
head=head->next;
ListNode *eve=head; //偶数链表的头eve
ListNode *evenn=head; //用evenn遍历链表找出偶数结点
head=head->next;
for(;head!=NULL;){
oddnn->next=head;
oddnn=head;
head=head->next;
if(head==NULL){
evenn->next=NULL;
oddnn->next=eve;
return odd;
}
else{
evenn->next=head;
evenn=head;
head=head->next;
}
}
oddnn->next=eve;
return odd;
}
};
自己思考了一下就按照自己思路写了,结果一个=写成==让我检查半天,还是问人别人帮我看出来的,检查的时候一直画图感觉自己思路对结果错在这。
题目四
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
/**
* 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* reverlist(ListNode *headnn){ //反转函数代码
if(headnn==NULL||headnn->next==NULL){
return headnn;
}
ListNode *newList=reverlist(headnn->next);
headnn->next->next=headnn;
headnn->next=NULL;
return newList;
}
bool isPalindrome(ListNode* head) { //题目函数
if(head==NULL||head->next==NULL){
return true;
}
ListNode *f=head;
ListNode *s=head;
ListNode *snn=head;
while(f!=NULL&&f->next!=NULL){ //快指针走两步,慢指针走一步,快指针走完,慢指针刚好在中间
s=s->next;
f=f->next->next;
}
if(f!=NULL){
s=s->next;
}
//如果快指针不为空,链表结点为单数,慢指针在最中间
//比如12345在3,下一个4才是回文判定开始
snn=reverlist(s); //s是后面回文判定的开始,把s放进去反转
//1->2->3->4->5 反转后变 1->2->3<-4<-5 此时snn指向5 3的next指向空
//1->2->3->4->5->6 反转后变 1->2->3->4<-5<-6 此时head在1 s在4 4指向空 snn在6
//错误思考的两种情况
// while(head!=snn){ //所以这个遍历判断是错的 3的下一个是4 4的下一个是空 无法判定相等
// if(head->val!=snn->val){ //而且snn变4 next指向空 再next就出错
// return false;
// }
// head=head->next;
// snn=snn->next;
// }
// while(head->next!=snn||head!=snn){ //1->2->3->4->5奇数情况判定完了
// if(head->val!=snn->val){ //1->2->3->4<-5<-6 偶数情况 4指向空
// return false;
// } //错误,奇数情况没有判定成功
// head=head->next; //1->2->3->4->5<-6<-7 5指向空 snn为5的时候,head为3
// snn=snn->next; // 再下一步 snn为空 head为4出问题
// }
// if(head->next!=snn){ //偶数情况判断3和4
// if(head->val==snn->val){
// return true;
// }
// return false;
// }
//正确思考
while(snn!=NULL){ //1->2->3->4->5<-6<-7 5指向空 回文只需要判断到5
if(snn->val!=head->val){ //5指向空,当snn从7指向5的时候判断最后一个 指向空的时候判断完成
return false;
}
snn=snn->next;
head=head->next;
}
return true;
}
};
这道题自己写的,想了很久,主要就是调用了空指针->next,现在深刻理解了这个错误,也理解了画图时的失误。