力扣链表小结

力扣小结一共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的时候是不用连成环的,看了别人的代码之后想着用双指针遍历一次就行,实际上不行,还是要提前把链表的长度遍历出来,因为旋转次数太多了,要对实际长度取模才能之后才能得到实际旋转次数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值