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

随机链表的复制

题目

. - 力扣(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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值