【leetcode 和 c++】 合并两个有序链表, 以及其升级题型合并K个有序链表(和数组并找中位数) 24.03.01

一、合并两个有序链表

1、题型描述:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
提示
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
题目链接

2、思路:

1、建立一个虚拟头节点,省去了头节点预判和结构变化的麻烦
2、while循环,保证两个输入链表的不为空进入
3、比较链表存储值,哪个小,给新建立的链表next赋值,反复迭代,形成合并
4、对其中一种为空的情况做处理(都为空,返回新建链表也没问题,所以不用处理)
5、返回的时候要记得跳过虚拟头节点。

3、代码:

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    // 虚拟头结点,注意,这里ListNode在new后面是没有*的,new本身就是要申请内存地址的意思
    ListNode* dummy = new ListNode(-1), *p = dummy;
    ListNode* p1 = l1, *p2 = l2;
    
    while (p1 != nullptr && p2 !=nullptr) {
        // 比较 p1 和 p2 两个指针
        // 将值较小的的节点接到 p 指针
        if (p1->val > p2->val) {
            p->next = p2;
            p2 = p2->next;
        } else {
            p->next = p1;
            p1 = p1->next;
        }
        // p 指针不断前进
        p = p->next;
    }
    if (p1 != nullptr) {
        p->next = p1;
    }
    if (p2 != nullptr) {
        p->next = p2;
    }
    return dummy->next;
}

4、细节、注意和总结:

  1. listnode 要挂 #include 头文件
  2. while循环,保证两个输入链表的不为空进入
  3. 对其中一种为空的情况做处理(都为空,返回新建链表也没问题,所以不用处理)
  4. 返回的时候要记得跳过虚拟头节点。
  5. NULL最好都改成nullptr
  6. ListNode* p1 = l1, *p2 = l2; 这里 p2前面一定要有 星号,否则它就不是指针了
    ===================================================================

二、合并K个有序链表

1、升级题型描述

给定一个链表数组,每个链表都已经按升序排列。
请将所有链表合并到一个升序链表中,返回合并后的链表。
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
题目链接

2、解题思路

因为多了很多链表,这是糟糕的部分
单每个链表都是有序的,这是可以利用的部分

因此我们用一个数据结构去自动排序输入每个链表的头节点,并输出此时链表头节点中最小的,
就可以玩上面一摸一样的游戏了。

就是其实加入的只有链表头,然后链表头被虚拟结点接纳以后,其后面部分又成为了新的链表头,参与优先队列的最小堆排序

3、代码

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.empty())
        {
            return nullptr;
        }
        ListNode * dummy = new ListNode(-1);
        ListNode *p = dummy;
        
        priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> pq([](ListNode* l1,ListNode* l2){return l1->val > l2->val;});

        for(auto head:lists)
        {
            if(head != nullptr)
            {
                pq.push(head);
            }
        }

        while(!pq.empty())
        {
            ListNode* tempN = pq.top();
            pq.pop();
            p->next = tempN;
            if(tempN->next != nullptr)
            {
                pq.push(tempN->next);
            }
            p = p->next;
        }
        return dummy->next;
    }
};

4、细节、注意和总结

1、用lists.empty() 的返回值就可以判断lists=[] 和 lists= [[]] ,这两种情况了(是的,我最开始用的是sizeof判断非零,甚至还单独写了一个函数判断空,苦笑)
2、c++模板编程基础
3、用优先级队列要挂#include
4、c++中优先级队列的用法
5、vector用法
6、
报错:
heap-buffer-overflow:
(1)数组越界
(2)malloc、new造成的内存泄漏或覆盖
7、
vector nums(2001);
声明一个2001个数的vector
报错:
expected parameter declarator
意思,编译器不知道你这是成员函数还是成员变量
解决方案:
vector nums = vectro(2001);
8、 priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> pq([](ListNode* l1,ListNode* l2){return l1->val > l2->val;});
首先是priority的三个模板参数,分别表示存取的类型,容器的类型,以及比较的方式,其次其实例化了一个priority_queue 叫pq,pq在实例化的时候,调用了priority_queue中的一个有参构造函数,用于描述比较的具体过程,这个函数是一个lambda函数,[]表示获取参数,()形参描述,{}具体函数实现,记得{}内加分号,最后面也要加分号。
因为记录的是链表,所以其模板参数都是链表指针类型。

5、拓展知识:二叉堆 与 优先级队列

(1) 二叉堆:

主要操作: sink下沉 swim上浮
二叉堆是一种完全二叉树的结构,单与链表二叉树不同,其底层存储数据结构是数组,因此要把指针变为数组里的数组索引

最大堆和最小堆: 每个结点都大于(小于)它的两个子节点

(2) 优先级队列:

功能:插入 or 删除元素后,元素会自动排序(利用二叉堆的上浮和下沉功能)

三、合并两个有序数组

题目描述

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

代码(思路就不说了,类似的)

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i  = m-1, j = n-1;
        int p = nums1.size()-1;
		
		这里的i和j设的十分巧妙,i,j,p都要和具体论述少1位,因此
		写以0且等于0为界限
        while(i >= 0 && j >=0)
        {
            if(nums1[i]>nums2[j])
            {
                nums1[p] = nums1[i];
                i--;
            }
            else
            {
                nums1[p] = nums2[j];
                j--;
            }
            p--;
        }
        while(j>=0)
        {
            nums1[p] = nums2[j];
            j--;
            p--;
        }
      
    }
};

四、寻找两个有序数组的中位数

题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

代码(思路类似)

class Solution {
   
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {   
        int m_and_n =  (nums1.size()+nums2.size());
        double res;
        vector<int> nums = vector<int>(m_and_n);
   
        vector<int> num = Get2numSort(nums1,nums2,nums);
        if((m_and_n )%2 == 0)
        {
        	这里的-1是debug后才发现的,还是序号和数字顺序错位问题
           res = ((double)(num[m_and_n/2-1 ]+ num[m_and_n/2 +1-1]))/2;
        }
        else
        {	
            res = num[(m_and_n+1)/2-1];
        }

        return res;
    }
    vector<int> Get2numSort(vector<int>& nums1, vector<int>& nums2,vector<int>& nums)
    {
        int i = 0 ,j = 0 ,k = 0;
       while(i+1 <= nums1.size() && j+1 <= nums2.size() )
       {
           if(nums1[i] < nums2[j])
           {
               nums[k] = nums1[i];
               i++;
           }
           else
           {
               nums[k] = nums2[j];
                j++;
           }
           k++;
       }
       这两个while是为了解决nums1和nums2长短不一致的问题,若短的
       做完了,那么会跳出循环,长的需要注意设置好两个while的进入
       条件,i+1,那么就和正常的计数器一样了,就可以加等号,因为,相等
       的时候,我们也可以读一下。
        while(i+1<=nums1.size())
        {
            
            nums[k] = nums1[i];
            i++;
            k++;
        }

         while(j+1<=nums2.size())
        {
            
            nums[k] = nums2[j];
            j++;
            k++;
        }

       if(nums1.size() == 0)
       {
           nums = nums2;
       }
       if(nums2.size() == 0)
       {
           nums = nums1;
       }

       return nums;
    }
};
  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本题要求合并两个有序链表。对于链表中的每一个节点,由于链表有序的,所以可以将两个链表中的节点按照大小顺序进行比较,然后逐个将较小的节点链接上去,最终得到一个新的有序链表。 使用Rust语言实现这个问题,需要首先定义一个链表节点的结构体,包含node值以及next指针。然后定义一个函数来合并两个有序链表,输入为两个链表的头节点指针,输出为新的有序链表的头节点指针。 在合并过程中,首先需要判断两个链表的头节点哪一个较小,将较小的节点作为新链表的头节点,并将该节点的next指针指向递归调用合并函数的结果。递归结束条件为其中一个链表为空,则将另一个链表直接链接到新链表上。 完整代码如下: ```rust // 定义链表节点结构体 #[derive(Debug)] struct ListNode { val: i32, next: Option<Box<ListNode>>, } impl ListNode { fn new(val: i32) -> Self { ListNode { val, next: None } } } // 合并两个有序链表 fn merge_two_lists(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> { match (l1, l2) { (None, None) => None, // 两个链表均为空 (l1, None) => l1, // 其中一个链表为空,直接返回另一个链表 (None, l2) => l2, (Some(mut l1), Some(mut l2)) => { if l1.val < l2.val { l1.next = merge_two_lists(l1.next, Some(l2)); Some(l1) } else { l2.next = merge_two_lists(Some(l1), l2.next); Some(l2) } } } } // 测试代码 fn main() { let l1 = Some(Box::new(ListNode { val: 1, next: Some(Box::new(ListNode { val: 2, next: Some(Box::new(ListNode { val: 4, next: None, })), })), })); let l2 = Some(Box::new(ListNode { val: 1, next: Some(Box::new(ListNode { val: 3, next: Some(Box::new(ListNode { val: 4, next: None, })), })), })); let merged = merge_two_lists(l1, l2); println!("{:?}", merged); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值