力扣小结一共5道题,其中有一道以前写过,有一道复制指针因为自己对深拷贝了解不深做不出,其他三题自己都出来了,对链表了解加深了很多,顺便从指针中明白循环队列为什么要空一个位置判空。做小结的时候遇到的问题主要也是上一篇中遇到的访问越界问题,就不再阐述。
第一题 合并两个升序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
/**
* 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1==NULL){ //1空输出2
return list2;
}
if(list2==NULL){ //2空输出1
return list1;
}
ListNode *right=NULL; //创建一个指针,正确链表的指针
if(list1->val>list2->val){ //找出正确指针right的开始位置
right=list2;
list2=list2->next;
}
else{
right=list1;
list1=list1->next;
}
ListNode *head=right; //记录正确指针的头,等会输出
while(list1!=NULL&&list2!=NULL){
if(list1->val<list2->val){ //由小到大排列,先连接最小的
right->next=list1;
right=right->next;
list1=list1->next;
}
else{
right->next=list2;
right=right->next;
list2=list2->next;
}
}
if(list1==NULL){
right->next=list2;
return head;
}
else{
right->next=list1;
return head;
}
}
};
自己再次把这道题写出来,对链表指针基础合格了,不过自己对递归了解太少了,没能写出递归方式
第二题 两数相加
你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/**
* 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) {}
* };
*/
//想算出结果再创建链表,发现数值太大无法创建
// int num1=0;
// int num2=0;
// int sum=0;
// int rval=0;
// // 错误写法 链表2 4 3会得342 但链表长度是100以内,10的100次方早已超过int
// // for(int i=0,m=0;l1!=NULL;i++){
// // m=pow(10,i); //m为10的i次方,第一次i为0,10的0次方是1,表明val在个位
// // num1=num1+l1->val*m;
// // }
// // 错误写法 链表2 4 3会得243
// // while(l1!=NULL){ //遍历l1得到l1的值,传给nums1
// // num1=num1*10+l1->val;
// // l1=l1->next;
// // }
// // 错误写法 链表2 4 3会得342 但链表长度是100以内,10的100次方早已超过int
// // for(int j=0,n=0;l2!=NULL;j++){
// // n=pow(10,j);
// // num2=num2+l2->val*n;
// // }
// // 错误写法 链表2 4 3会得243
// // while(l2!=NULL){ //遍历l2得到l2的值,传给nums2
// // num2=num2*10+l2->val;
// // l2=l2->next;
// // }
// sum=num1+num2;
// ListNode *right=new ListNode(0); //正确链表的指针
// ListNode *head=right;
// //如果num为0,则不走下面的while循环,那么就没有后续链表,输出head->next的时候输出空
// if(sum==0){
// return head;
// }
// while(sum!=0){ //sum为807 链表为708
// rval=sum%10;
// ListNode *temp=new ListNode(rval);
// right->next=temp;
// right=temp;
// sum=sum/10;
// }
// return head->next;
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *right=new ListNode(0); //创建正确链表right
ListNode *head=right; //创建一个结点指向正确链表的头结点,等会输出
int sum=0; //两个链表数值相加的和
int addbit=0; //如果两个链表的值相加大于10,则要进位
while(l1!=NULL&&l2!=NULL){ //l1和l2不为空的时候,进行加运算
sum=l1->val+l2->val+addbit;
if(sum>9){
sum=sum-10;
addbit=1;
}else{
addbit=0;
}
ListNode *temp=new ListNode(sum);
right->next=temp;
right=right->next;
l1=l1->next;
l2=l2->next;
}
if(l1==NULL){ //l1空了 l2是有或空
while(l2!=NULL&&addbit==1){
sum=l2->val+addbit;
if(sum>9){
sum=sum-10;
addbit=1;
ListNode *temp=new ListNode(sum);
right->next=temp;
right=right->next;
l2=l2->next;
}else{
ListNode *temp=new ListNode(sum);
addbit=0;
right->next=temp; //第一次测试的时候想当然了,这些没加上
right=right->next;
l2=l2->next;
}
}
if(addbit==0){ //进位数为0,l2为空或者还有的情况
right->next=l2;
return head->next;
}else{ //进位还有,l2一定为空,新建一个1结点
ListNode *temp=new ListNode(1);
right->next=temp;
return head->next;
}
}
else{ //这是l2空 l1有或空 其他同上
while(l1!=NULL&&addbit==1){
sum=l1->val+addbit;
if(sum>9){
sum=sum-10;
addbit=1;
ListNode *temp=new ListNode(sum);
right->next=temp;
right=right->next;
l1=l1->next;
}else{
ListNode *temp=new ListNode(sum);
addbit=0;
right->next=temp;
right=right->next;
l1=l1->next;
}
}
if(addbit==0){
right->next=l1;
return head->next;
}else{
ListNode *temp=new ListNode(1);
right->next=temp;
return head->next;
}
}
}
};
前面都是些错误想法,保留了下来,初学者容易掉到这个坑里,自己写的代码比较长。
第三题 扁平化多级双项列表
你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的 多层数据结构 。
给定链表的头节点 head ,将链表 扁平化 ,以便所有节点都出现在单层双链表中。让 curr 是一个带有子列表的节点。子列表中的节点应该出现在扁平化列表中的 curr 之后 和 curr.next 之前 。
返回 扁平列表的 head 。列表中的节点必须将其 所有 子指针设置为 null 。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* prev;
Node* next;
Node* child;
};
*/
class Solution {
public:
Node* flatten(Node* head) {
if(head==NULL){
return head;
}
Node *right=head; //记录链表头,用于输出
Node *ergo1=head; //创建一个指针,用于第一次从头到尾遍历
Node *ergo2=NULL; //创建一个指针,因为第一次遍历要遍历所有结点,一次遍历结果指向尾结点的next也就是空
//不能用空->prev找到尾结点,那么要用一个结点来记录尾结点
Node *temp=NULL; //创建一个临时指针,用于从头到尾遍历遇到有孩子指针的结点时,
//记录结点的next结点,交换结点的孩子指针和next指针
//也用于从尾往头遍历时,记录有孩子指针结点的孩子,用于拼接
while(ergo1!=NULL){ //从第一层的头走到最后一层的尾
if(ergo1->next==NULL){
ergo2=ergo1;
}
if(ergo1->child!=NULL){ //遇到有孩子指针的结点时
temp=ergo1->next; //记录有孩子结点的下一个结点
ergo1->next=ergo1->child; //结点的next指针指向孩子
ergo1->next->prev=ergo1; //孩子的perv指针指向结点
ergo1->child=NULL; //先将有孩子指针的结点 的孩子指针置空,以防结点的next,也就是temp为空的情况
if(temp!=NULL){ //如果结点的next结点不为空
ergo1->child=temp; //把结点的孩子指针指向原本的next结点
}
} //最终所有有孩子的结点都这样
ergo1=ergo1->next; //ergo遍历指针也遍历到了最下层链表的尾结点
}
Node *mix=NULL; //记录从尾往头遍历时,开始遍历层的尾结点,用于和上层拼接
mix=ergo2; //ergo1已经指向尾结点的next也就是空,ergo2才是尾结点
while(ergo2!=NULL){ //从尾往头遍历
if(ergo2->child!=NULL){ //遇到有孩子指针结点的时候,也就是要拼接了
temp=ergo2->child;
mix->next=temp;
temp->prev=mix; //从尾到头遍历中,mix为遍历层的尾,temp为遍历层的上一层,名为拼接层
while(temp->next!=NULL){ //寻找拼接层的尾
temp=temp->next;
}
mix=temp; //遍历层解决,拼接层改为遍历层,开始遍历上一层,记录遍历层的尾
ergo2->child=NULL; //孩子指针置空
}
ergo2=ergo2->prev;
}
return right;
}
};
最开心的就是自主写这道题了,毕竟第一眼看到的时候觉得很难,想了一天,想着用递归,但是写不出来,中间还找到了几次错误的原因,第二天的时候恍然大悟,一步步地往后遍历再往前遍历,注意哪些指针变化了,变化之后的情况根据后续需要进行修改。
第四题 复制带随机指针的链表
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL){
return head;
}
Node *right=NULL; //创建一个正确链表的指针,用于输出
Node *ergo=head; //创建遍历指针
while(ergo!=NULL){ //第一次遍历,在每个结点后面创建一个复制结点,结点next是复制,复制next是下一个结点
Node *temp=new Node(ergo->val);
temp->next=ergo->next;
ergo->next=temp;
ergo=ergo->next->next;
}
ergo=head;
while(ergo!=NULL){ //二次遍历,确定复制指针的随机指针,如果当前结点的随机指针不为空
if(ergo->random!=NULL){ //则当前结点的复制结点的随机指针,等于当前结点的随机指针的next指针
ergo->next->random=ergo->random->next;
}
ergo=ergo->next->next;
}
ergo=head;
right=head->next;
Node *temp=NULL; //创建临时指针,存储断开结点的下一个结点,以免断开后找不到
while(ergo!=NULL){
temp=ergo->next->next; //先记录下次要断掉的结点
if(temp!=NULL){
ergo->next->next=temp->next;
ergo->next=temp;
ergo=temp;
}else{
ergo->next=temp; //原链表的尾结点指向了复制链表的尾结点,按道理来说我们只输出复制链表,
ergo=temp; //不处理/这个也可以,但是不处理的话提交不给过
}
}
return right;
}
};
这道题没写出来主要是看不懂要做什么,看了答案代码之后马上懂了,自主地写了出来。
第五题 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
/**
* 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){
// return head;
// }
// int len=1;
// int ronum=0; //记录实际旋转次数
// ListNode *right=NULL; //记录正确链表头,拿来输出
// ListNode *rehead=head; //找到要断开的结点
// for(;head->next!=NULL;len++){ //拿到链表长度len
// head=head->next; //head停留在尾部最后一个节点
// }
// ronum=k%len; //ronum值范围为0-len-1 每旋转len次变回原来
// if(ronum==0){ //旋转次数为len的倍数,实际旋转次数为0,链表不变直接输出
// return rehead;
// }
// head->next=rehead; //最后一个结点连接头部,形成一个圈 旋转次数不为0时才要形成环
// for(int i=len-ronum;i>1;i--){ //找到要断开的位置
// rehead=rehead->next; // 1 2 5 4 5 长度len为5 本题重点找对关系
// } //(4)(3)(2)(1)(0) 括号为旋转次数断开点,0已在上面验算
// right=rehead->next; //正确的新的链表头
// rehead->next=NULL; //断点断开
// return right;
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
}
};
这道题和删除倒数第几个结点有些相似性,这是自主写代码,,写的时候为了好看提前把最后一个结点接上了变成环,可是旋转次数实际为0的时候是不用连成环的,看了别人的代码之后想着用双指针遍历一次就行,实际上不行,还是要提前把链表的长度遍历出来,因为旋转次数太多了,要对实际长度取模才能之后才能得到实际旋转次数。