首页 > 其他分享 >随机链表的复制 && 排序链表

随机链表的复制 && 排序链表

时间:2024-06-22 09:27:28浏览次数:3  
标签:NULL ListNode struct next 链表 && 排序 cur

随机链表的复制

题目

. - 力扣(LeetCode)

思路:

思路:

        ①一个结点一个节点去拷贝,当拷贝了第一个节点的时候,把原节点与拷贝节点连接起来,直接到所有的节点拷贝完毕,这样做的目的是为下一步处理 random 指针做准备

        ② 处理random

        ③处理整个链表,还原原链表,连接复制的链表,返回头结点

详解步骤 :

①一个结点一个节点去拷贝,当拷贝了第一个节点的时候,把原节点与拷贝节点连接起来,直接到所有的节点拷贝完毕,这样做的目的是为下一步处理 random 指针做准备

 ② 处理random,cur需要不断跌代,cur->next 则为 copyLsit(复制出来的结点),观察可以发现,当cur->random->next 即为 copyList->random

③处理整个链表,还原原链表,连接复制的链表,返回头结点

完整代码:

struct Node* copyRandomList(struct Node* head) 
{
    if(head == NULL) 
    {
        return NULL;
    }
    // 1,复制节点
    struct Node* cur = head;
    while(cur) 
    {
        struct Node* copyNode = (struct Node*)malloc(sizeof(struct Node));
        if(copyNode == NULL) 
        {
            return NULL;
        }
        copyNode->next = NULL;
        copyNode->random = NULL;
        //连接复制的节点
        struct Node* next = cur->next;
        copyNode->val = cur->val;
        cur->next = copyNode;
        copyNode->next = next;
        cur = next;
    }
    //处理random
    cur = head;
    while(cur) 
    {
        struct Node* copy = cur->next;
        struct Node* next = cur->next->next;
        if(cur->random == NULL) 
        {
            copy->random = NULL;
        }
        else 
        {
            copy->random = cur->random->next;
        }
        cur = next;
    }
    //处理整个链表,还原链表,返回复制链表的头节点
    cur = head;
    struct Node* copyHead = cur->next; //最后需要返回复杂链表的头
    while(cur) 
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        cur->next = next;
        if(next) 
            copy->next = next->next;
        cur = next;
    }
    return copyHead;
}

排序链表

题目

. - 力扣(LeetCode)

思路

需用到这个题目:合并两个有序链表

在有归并排序的基础写这个题目就会轻松一点,因为这个题目和归并排序的非递归写法有点类似

先回忆一下归并排序的思想,归并排序是把一段无序的数据一个一个递归出去,然后把一个一个数据当成有序的,再合并,合成2个数后有序了,接下来2个合并,合并成4个数有序,再4个4个合并,以此类推,不断合并

①计算链表的个数,用于控制每一层合并的间距

②创建一个虚拟头节点(带哨兵位的头节点),用于记录每组已经完成归并的链表

③分割链表,找每组的左右子链表进行合并有序链表

详细步骤:

①计算链表的个数,用于控制每一层合并的间距

②创建一个虚拟头节点(带哨兵位的头节点),用于记录每组已经完成归并的链表

   当 gap == 1 的时候,表示数据个数是一个,

   我们需要合并 4 2 为一组合并,14    5 、10   15、 3    7    各为一组合并

   创建 prev 指向虚拟头结点,cur 指向真正链表的头

    prev用于记录每组链表中完成了合并有序链表的操作,cur用于找需要合并的子链表

   head1表示左子链表,head2表示右子链表,在找左子链表的过程中,我们知道gap是会变化的,也就是说子链表的节点个数就是gap的值 (最后末尾可能会存在多一个或者少一个的情况,或者右子链表根本没有)

这只是gap == 1的情况下,gap第一次是 1,第二次是 2,第三次是 4,所以控制间距是 gap * = 2

③分割链表,找每组的左右子链表进行合并有序链表 

如图所示:这只是gap == 1的情形,当 gap 不断变化的时候,原理都是一样的(建议自己画图动手理解)

边界的控制

完整代码

struct ListNode* MergerTwoLists(struct ListNode* list1,struct ListNode* list2) 
 {
    struct ListNode* l1 = list1;
    struct ListNode* l2 = list2;
    if(l1 == NULL) 
        return l2;
    if(l2 == NULL) 
        return l1;
    struct ListNode* head = NULL, *tail = NULL;
    head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
    while(l1 && l2) 
    {
        if(l1->val < l2->val) 
        {
            tail->next = l1;
            l1 = l1->next;
        }else 
        {
            tail->next = l2;
            l2 = l2->next;
        }
        tail = tail->next;
    }
    if(l1) 
    {
        tail->next = l1;
    }
    if(l2) 
    {
        tail->next = l2;
    }
    struct ListNode* reallyHead = head->next;
    free(head);
    return reallyHead;
 }
struct ListNode* sortList(struct ListNode* head) 
{
    //计算排序链表的个数
    int Length = 0;
    struct ListNode* node = head;
    while(node) 
    {
        ++Length;
        node = node->next;
    }    
    //创建虚拟头结点(带哨兵位的头结点)
    //每组归并都是从头开始,最后用于返回头结点
    struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(dummyHead == NULL) 
        return  NULL;
    dummyHead->next = head;
    //分割区间
    for(int gap = 1; gap < Length; gap *= 2) 
    {
        struct ListNode* prev = dummyHead;
        struct ListNode* cur = prev->next;
         //找两个链表的头
        while(cur) 
        {
            //找了头还需要确定左子链表的末尾,断开联系,挪动cur的位置,先确定头再确定右子链表的末尾断开联系,保存下一个,让cur不断往后走,归并,
            //归并完之后让 prev 不断走,合并完不需要合并,cur不断向后走
            struct ListNode* head1 = cur;
            for(int i = 1; i < gap && cur->next; ++i)  //左子链表的确定,如果下一个结点为NULL说明到了末尾,没有右子链表
            {
                cur = cur->next;
            }
            struct ListNode* head2 = cur->next;   // 如果head2 == NULL,不进人下面的for循环
            cur->next = NULL;  //确定了第一个左子链表
            //确定右子链表
            cur = head2;   // cur 跟上
            for(int i = 1; i < gap && cur && cur->next; ++i )  
            {
                cur = cur->next;
            }   
            //gap为 1的时候里面的一小部分,断开右子链表的联系
            struct ListNode* next = NULL;
            if(cur) 
            {
                next = cur->next;
                cur->next = NULL;
            }
            //这样两个左右子链表寻找完毕,接下来就需要合并有序链表了
            struct ListNode* Mergerd = MergerTwoLists(head1,head2);
            prev->next = Mergerd;
            while(prev->next)  // prev 找合并之后链表的尾
            {
                prev = prev->next;
            }
            cur = next; // cur继续更新
        }
    }
    struct ListNode* reallyHead = dummyHead->next;
    free(dummyHead);
    return reallyHead;
}

标签:NULL,ListNode,struct,next,链表,&&,排序,cur
From: https://blog.csdn.net/m0_63207201/article/details/139869244

相关文章

  • LeetCode刷题day3——链表part1
    203.移除链表元素这个题是二刷了一开始这个思路就是利用虚拟头结点,但是中间很多小细节都考虑不到,例如初始化一个新的链表,循环条件的写法等classSolution{public:structListNode{intval;ListNode*next;ListNode(intx):val(x),next(NULL){};//定义构......
  • LeetCode刷题day4——链表part2
    24.两两交换链表中的节点用pre代表第1个节点,cur代表它后面的相邻节点,tmp存储cur->next。但是问题找不到怎么返回新的节点。想着就循环一次,在第一次交换的时候就确认新的头结点。但还存在很多问题,具体如下:classSolution{public:ListNode*swapPairs(ListNode*he......
  • 手撕排序2--选择排序(直接选择+堆排序
    目录:1.直接选择排序  的实现及分析2.堆排序的实现及分析1.直接选择排序1.1基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。1.2一次排序-->将最大值放在第一个,最小值放在最后一个代码实现:#incl......
  • 【数据结构与算法 刷题系列】判断链表是否有环(图文详解)
                   ......
  • 校招常见七大排序C++版(适合新人,通俗易懂)
    作者:求一个demo版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处内容通俗易懂,没有废话,文章最后是面试常问内容是否稳定最优、最坏、平均时间复杂度最优、最坏、平均空间复杂度冒泡排序是O(n)、O(n^2)、O(n^2)0、O(n)、O(1)选择排序否O(n^2)、O(n^2)......
  • 一千题,No.0089(链表元素分类)
    给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而[0,K]区间内的元素都排在大于K的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为18→7→-4→0→5→-6→10→11→-2,K为10,则输出应该为-4→-6→-2→7→0→5→10......
  • P8500 [NOI2022] 冒泡排序 题解
    考虑特殊性质B。限制相当于钦定一些位置的值,其他位置无限制。可以发现性质:无限制的位置上填的值是单调不减的。证明:设得到的最优序列为\(A\),对于无限制的位置\(i,j\),若\(A_i>A_j\),交换\(i,j\)后逆序对个数必然减小。根据改性质,只需考虑每个位置对已经确定位置的位置的贡......
  • c语音实现单链表初始化的四种方式
    typedefstructmyLink{ intdata; structmyLink*next;}myLink,*myLLink;1、对于上面的简单结构,用函数赋值需要传递引用,需要用到指针的指针。对指针使用不是很清楚的童鞋很是头痛。voidinitlink(myLink**head){ *head=(myLink*)malloc(sizeof(myLink)); if(......
  • 链表插入的小技巧
    一个带有优先级的链表:structlist{structlist*next;u32priority;} 如果要按照优先级插入某个新节点node,算法一般会写成:intlist_insert(list**head,list*new){if(head==null||*head==null||new==null)return1;list*prev......
  • 4.插入排序
    插入排序插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。排序思路:假设按照升序排序1.从索引为1的元素开始向前比较,一旦前面一个元素大于自己就让前面的元素......