Datawhale|LeetCodeTencent Task05(lc23,26,33)

023 Merge k Sorted Lists(H)

Description

在这里插入图片描述

题意为将给定的k个有序单链表合并为一个有序链表。

Analysis & Solution

本题目可以从两个角度思考:
(1)逐个合并
将每个链表看作一个整体,从而将原问题拆分为更小的子问题,即每两个链表进行合并,就可以用到昨天的题目21. Merge Two Sorted Lists
朴素的想法是从第一个到第K个进行遍历,先合并L1和L2,将合并结果与L3合并,以此类推,直到第K个链表完成合并。时间负责度为 O ( n K ) O(nK) O(nK)
进一步可以用到分治的思想,类比归并排序算法,对K个链表进行二分,依次合并,时间复杂度就可以降低到 O ( n l o g K ) O(nlogK) O(nlogK)
(2)同时合并
用K个指针分别指向K个链表,所有链表同时进行合并。每次所有指针中指向值最小的作为新节点,其指针后移。可以很自然地联想到用优先队列解决。K个链表指向的节点构成优先队列,值最小的出队,出队节点后相邻节点入队。

Code

//依次合并
ListNode* mergeKLists(vector<ListNode*>& lists)
{
    int N = lists.size();
    if(N == 0) return nullptr;
    ListNode* res = lists[0];
    for(int i = 1; i < N; i++)
    {
        res = mergeTwoLists(res, lists[i]);
    }
    return res;
}
//分治:归并
ListNode* mergeKLists(vector<ListNode*>& lists)
{
    int N = lists.size();
    if(N == 0) return nullptr;
    if(N == 1) return lists[0];
    if(N == 2) return mergeTwoLists(lists[0], lists[1]);
    int mid = N / 2;
    vector<ListNode*> leftLists(lists.begin(), lists.begin() + mid);
    vector<ListNode*> rightLists(lists.begin() + mid, lists.end());
    return mergeTwoLists(mergeKLists(leftLists), mergeKLists(rightLists));    
}
//优先队列
struct cmp
{
    bool operator()(ListNode* a, ListNode* b)
    {
        return a->val > b->val;
    }
};
ListNode* mergeKLists(vector<ListNode*>& lists)
{
    priority_queue<ListNode*, vector<ListNode*>, cmp> p_q;
    for(auto elem: lists)
        if(elem) p_q.push(elem);
    ListNode dummy(-1);
    ListNode *p = &dummy;

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

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
    if(l1 == nullptr && l2 == nullptr) return nullptr;
    ListNode *l3 = new ListNode(-1);
    ListNode *l3_head = l3;
    while(l1 != nullptr && l2 != nullptr)
    {
        if(l1->val < l2->val)
        {
            l3->next = l1; 
            l3 = l3->next;
            l1 = l1->next;
        }
        else
        {
            l3->next = l2;
            l3 = l3->next;
            l2 = l2->next;
        }
    }
    l3->next = (l1 == nullptr ? l2 : l1);
    return l3_head->next;
}

时间复杂度:依次合并的复杂度为 O ( n K ) O(nK) O(nK),分治和优先队列均为 O ( n l o g K ) O(nlogK) O(nlogK)

026 Remove Duplicates from Sorted Array(E)

Description

在这里插入图片描述

题意为用in-place的方法(不创建新数组)去除数组中的重复元素(仅保留一个),返回处理后的数组长度。

Analysis & Solution

本题思路较简单,对数组遍历一次,发现相同元素删除即可。也可以用两个快慢指针来解决。

Code

int removeDuplicates(vector<int>& nums)
{
    int N = nums.size();
    if(N < 2) return N;
    int j = 0;
    for(int i = 0; i < N - 1; i++)
    {
        if(nums[i] == nums[i+1])
        {
            nums.erase(nums.begin() + i);
            i--;
            N--;
        }
    }
    return nums.size();
}

时间复杂度:仅对数组扫描一次,因此复杂度为 O ( n ) O(n) O(n)

033 Search in Rotated Sorted Array(M)

Description

在这里插入图片描述

题意为在一个旋转有序数组中搜索给定target的位置。

Analysis & Solution

很自然可以想到用分治(二分法)解决。但是与有序数组不同,旋转有序数组像是被分为了两部分,要增加一些条件判断,从而确定搜索范围如何更新。
设当前搜索范围为[left, right],令mid=(left+right)/2。若nums[mid]<nums[0],说明mid位置右侧数组有序,否则说明mid位置左侧数组有序。进而根据target和nums[mid],nums[0]的大小关系确定新的搜索范围,具体参考代码。

Code

int search(vector<int>& nums, int target)
{
    int N = nums.size();
    if(N == 1) return nums[0] == target ? 0 : -1;
    int left = 0, right = N - 1;
    while(left < right)
    {
        int mid = (left + right) / 2;
        if(nums[mid] == target) return mid;
        if(nums[mid] < nums[0])
        {
            if(nums[mid] < target && target < nums[0]) left = mid + 1;
            else right = mid - 1;
        }
        else
        {
            if(nums[mid] > target && target >= nums[0]) right = mid - 1;
            else left = mid + 1;
        }  
    }
    return nums[left] == target ? left : -1;
}

时间复杂度:使用二分法,时间复杂度为 O ( l o g n ) O(logn) O(logn)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值