LeetCode--Divide and Conquer

215. Kth Largest Element in an Array(数组中第K大的数)

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4] and k = 2, return 5.

Note:

You may assume k is always valid, 1 ? k ? array’s length.

解析:

(1)方法一:堆排序。先对数组[0,length]建立一个大顶堆,将根节点和最后一个节点交换,此时最后一个节点为第1大,然后继续对数组[0,length-i]建堆,直到求得第k大。
(2)方法二:分而治之。对于数组[low,high],以数组中‘第一’个数字[low]为基准,大于他的数放在他前面,小于他的数放在他后面,返回原来第一个数子的最终位置split。如果split==k-1,则说明分割点split就是第K大;如果split >= k,则说明第K大的数在前半部分,因此设置high=split-1,继续上述操作。否则,则说明第K大的数在后半部分,设置low = split+1,继续上述操作。

C++代码实现:
堆排序

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        if(n==1)    return nums[0];
        for(int i=(n-1)/2; i>=0; i--) {
            heapAdjust(nums,i,n-1);
        }
        int result = 0;
        for(int i=1; i<=k; i++) {
            result = nums[0];
            nums[0] = nums[n-i];
            nums[n-i] = result;
            heapAdjust(nums,0,n-i-1);
        }
        result = nums[n-k];
        return result;
    }
private:
    void heapAdjust(vector<int>& nums,int low,int high) {   //建立大顶堆
        int key = nums[low];
        for(int j=2*low; j<=high; j*=2) {
            if(j<high && nums[j] < nums[j+1])   //左节点 < 右节点
                ++j;        //保证num[j]是大的        
            if(key >= nums[j])              //根节点 > 孩子节点
                break;
            nums[low] = nums[j];
            low = j;
        }
        nums[low] = key;
    }
};

分而治之

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        if(n==1)    return nums[0];
        int low = 0, high = n-1, split = 0;
        while(low < high) {
            split = partition(nums,low,high);
            if(split == k-1)    
                return nums[split];
            else if(split >= k)  
                high = split-1;
            else
                low = split+1;
        }
        return nums[low];
    }
private:
    //以第一个元素为基准,将大于他的数放在他前面,小于他的数放在他后面
    int partition(vector<int>& nums, int low, int high){
        if(low==high)   return low;
        int key = nums[low];
        while(low < high) {
            while(low < high && nums[high] <= key)
                --high;
            nums[low] = nums[high];
            while(low < high && nums[low] >= key)
                ++low;
            nums[high] = nums[low];
        }
        nums[low] = key;
        return low;
    }
};

23. Merge k Sorted Lists(合并k个有序链表)

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

解析:

合并多个有序链表的实质和合并两条有序链表是一样的。只不过合并多条有序数组的时候,分成(k+1)/2组,每组两条进行合并。然后不断进行分组及合并,知道只有1条数组。10–>5–>3–>2–>1。

C++代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n = lists.size();
        if(n==0)    return NULL;
        if(n==1)    return lists[0];
        vector<ListNode*> mList((n+1)/2,NULL);
        int k = 0;
        if(n&1 != 0)            //链表数量为奇数
            mList[k++] = lists[0];
        for(int i=k; i<n; i+=2) {
            ListNode* p = mergeList(lists[i], lists[i+1]);    //两两合并
            mList[k++] = p;
        }
        return mergeKLists(mList);
    }
private:
    //合并两条有序数组
    ListNode* mergeList(ListNode* node1, ListNode* node2) {
        if(node1==NULL)   return node2;
        if(node2==NULL)   return node1;

        ListNode head(0);
        ListNode* p = &head;
        ListNode* p1 = node1;
        ListNode* p2 = node2;

        while(p1!=NULL && p2!=NULL){
            if(p1->val < p2->val){
                p->next = p1;
                p1 = p1->next;
            }else {
                p->next = p2;
                p2 = p2->next;
            }
            p = p->next;
        }
        if(p1!=NULL)
            p->next = p1;
        if(p2!=NULL)
            p->next = p2;
        return head.next;
    }
};

493. Reverse Pairs

Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j].
You need to return the number of important reverse pairs in the given array.

Example:

Input: [1,3,2,3,1]
Output: 2
Input: [2,4,3,5,1]
Output: 3

Note:

The length of the given array will not exceed 50,000.
All the numbers in the input array are in the range of 32-bit integer.

解析:

(1)方法一:暴力搜索。对于每个节点i,在子数组[0,i)中搜索大于等于2*nums[i]+1的节点。时间复杂度为O(n*n),超时。
(2)方法二:二叉搜索树。对于每个节点i,在子数组[0,i)中搜索大于等于2*nums[i]+1的节点,因此我们可以将子数组[0,i)构建成二叉搜索树,然后利用二叉搜索提高搜索效率。时间复杂度为O(n*logn),比第一个方法好很多,但是如果数组是有序的,则二叉搜索树将退化成单边树,则最差时间复杂度为O(n*n)。超时。
(3)方法三:类似合并排序。借鉴合并排序的思想,将数组[start,end]分成两部分left[start, mid]和right[mid+1, end],然后分别统计left及right子数组中inverse pair的数量、left和right之间的inverse pair数量。超时。

C++代码实现:
方法二:二叉搜索树,超时

struct Node{
    int val;
    int count;      //表示大于等于该节点val的节点数量
    Node *left,*right;
    Node(int value):val(value),count(1),left(NULL),right(NULL){}
};

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        if(n<2) return 0;
        Node* head = NULL;
        int result = 0;
        for(int i=0; i<n; i++) {
            result += search(head,nums[i]*2LL+1);
            head = insert(head,nums[i]);
        }
        return result;
    }
private:
    //从二叉搜索树中查找
    int search(Node* head, long long target) {
        if(head==NULL)
            return 0;
        if(head->val == target)
            return head->count;
        else if(head->val > target)
            return head->count + search(head->left,target);
        else
            return search(head->right,target);
    }
    //将n插入BST中
    Node* insert(Node* head,int n){
        if(head==NULL)
             return new Node(n);
        if(head->val == n)
            head->count++;
        else if(head->val > n)
            head->left = insert(head->left,n);
        else{
            head->count++;
            head->right = insert(head->right,n);  
        }
        return head;        
    }
};

方法三:类似合并排序

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        if(n<2) return 0;
        return mergeSortAndCount(nums,0,n-1);
    }
private:
    int mergeSortAndCount(vector<int>& nums, int start, int end) {
        if(start < end) {
            int mid = (start+end)/2;
            int count = mergeSortAndCount(nums,start,mid) + mergeSortAndCount(nums,mid+1,end);
            int j = mid+1;
            for(int i=start; i<=mid; i++) {
                while(j<=end && nums[i] > nums[j]*2LL)
                    j++;
                count += j-(mid+1);
            }
            merge(nums,start,mid,end);
            return count;
        }
        return 0;
    }
    void merge(vector<int>& nums, int start, int mid,int end) {
        int m = mid - start + 1;
        int n = end - mid;
        vector<int> left(m,0), right(n,0);
        for(int i=0; i<m; i++)
            left[i] = nums[i+start];
        for(int j=0; j<n; j++)
            right[j] = nums[j+mid+1];
        int i=0, j=0;
        for(int k=start; k<=end; k++) {
            if((i<m && left[i]<right[j]) || j>=n)
                nums[k] = left[i++];
            else
                nums[k] = right[j++];
        }
    }
};

4. Median of Two Sorted Arrays(两个有序数组的中位数)

There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example

nums1 = [1, 3]
nums2 = [2]
The median is 2.0
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5

解析:

(1)方法一:合并排序。将两个数组进行合并排序,然后去中位数即可。时间复杂度为O(m+n)。不满足题目要求。
(2)方法二:分而治之。假设两个有序序列共有n个元素,如果n为奇数,则第(n/2+1)个元素为中位数,如果n为偶数,则第(n/2+1)和(n/2)元素的均值为中位数。那么,我们可以将本题转变成“搜索两个有序序列的第K个元素”。
那么如何搜索两个有序序列的第k个元素呢?
由于两个序列都是有序的,因此,假设序列1的前p个元素和序列2的前q个元素构成了有序序列的前k-1个,即p+q=k-1,且序列1前p个元素和序列2前q个元素都小于等于第k个元素。这样,序列1第p+1个或序列2第q+1个元素就是第K个元素。
通过上述分析,我们可以用二分法将问题进行分解。假设,p=k/2-1,则q = k-p-1,且p+q=k-1。
如果序列1第p个元素小于序列2第q个元素,则序列1前p个元素必然小于第k个元素,因此我们抛弃序列1的前p个元素,形成较短的新序列1,然后再找其中第k-p个元素,依次递归。
同理,如果序列1第p个元素大于序列2第q个元素,则抛弃序列2前q个元素。
递归终止条件如下:
(1)较短的序列所有元素都被抛弃(长度为0),则返回较长序列的第k个元素
(2)序列1第p个元素等于序列2第q个元素,此时总序列第p+q=k-1个元素的后一个元素,即为总序列的第k个元素。
综上,时间复杂度为O(log(m+n))。

C++代码实现:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if(m==0 && n==0)    
            return 0;
        else if(m==0)
            return n%2==0 ? (nums2[n/2 - 1]+nums2[n/2])/2.0 : nums2[n/2];
        else if(n==0)
            return m%2==0 ? (nums1[m/2 - 1]+nums1[m/2])/2.0 : nums1[m/2];

        if(nums1[m-1] <= nums2[0]) {     //[a1,a2,...,am,b1,b2,...,bn]
            if(m==n)    return (nums1[m-1]+nums2[0])/2.0;
            else if(m < n)
                return (m+n)%2==0 ? (nums2[(m+n)/2 - m] + nums2[(m+n)/2 - m-1])/2.0 : nums2[(m+n)/2 - m];
            else
                return (m+n)%2==0 ? (nums1[(m+n)/2] + nums1[(m+n)/2 - 1])/2.0 : nums1[(m+n)/2];
        }

        if(nums1[0] >= nums2[n-1]) {     //[b1,b2,...,bn,a1,a2,...,am]
            if(m==n)    return (nums2[n-1]+nums1[0])/2.0;
            else if(n > m)
                return (m+n)%2==0 ? (nums2[(m+n)/2] + nums2[(m+n)/2 - 1])/2.0 : nums2[(m+n)/2];
            else
                return (m+n)%2==0 ? (nums1[(m+n)/2 - n] + nums1[(m+n)/2 - n - 1])/2.0 : nums1[(m+n)/2-n];
        }
        int k = (m+n)/2;
        return (m+n)%2==0 ? (findKth(nums1,nums2,0,0,m,n,k)+findKth(nums1,nums2,0,0,m,n,k+1))/2.0 : findKth(nums1,nums2,0,0,m,n,k+1);
    }
private:
    //查找第k大的元素
    int findKth(vector<int>& nums1, vector<int>& nums2, int start1, int start2, int len1, int len2,int k) {
        if(len1 > len2) //保证nums1是较短的数组
            return findKth(nums2,nums1,start2,start1,len2,len1,k);
        if(len1==0)
            return nums2[start2 + k -1];
        if(k==1)
            return min(nums1[start1],nums2[start2]);
        int p = min(k/2,len1);
        int q = k - p;
        if(nums1[start1 + p -1] < nums2[start2 + q - 1])        //排除nums1中前p个元素
            return findKth(nums1,nums2,start1 + p,start2,len1-p,len2,k-p);
        else if(nums1[start1 + p -1] > nums2[start2 + q - 1])   //排除nums2中前q个元素
            return findKth(nums1,nums2,start1,start2 + q,len1,len2-q,k-q);
        else
            return nums1[start1 + p -1];
    }
};

327. Count of Range Sum

Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ? j), inclusive.

Note:

A naive algorithm of O(n2) is trivial. You MUST do better than that.

Example:

Given nums = [-2, 5, -1], lower = -2, upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.

解析:

和上题方法类似,分而治之,归并排序。不断将数组进行切分,然后进行归并。
count = count(left) + count(right) + count(right~left)。
下面开始具体讲解:
我们将分治对象设置为数组的和,sums[j]-sums[i] = sum{i,j}。
在归并阶段,我们有两个有序的子数组left和right,
对于left中的每个元素i,在right中找到这样的j和k,使得
sums[j] - sums[i] <=upper,
sums[k] - sums[i] >= lower
这样我们就找到了j-k个满足条件的子数组。
然后将left和right进行合并。继续递归。

C++代码实现:

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int n = nums.size();
        if(n==0)    return 0;
        vector<long long> sums(n+1,0);
        for(int i=0; i<n; i++)
            sums[i+1] = sums[i] + nums[i];
        return merge(sums,0,n,lower,upper);
    }
private:
    int merge(vector<long long>& sums,int start, int end,int lower,int upper) {
        if(start>=end)  return 0;
        int mid = start + (end-start)/2;
        //count(left)+count(right)
        int count = merge(sums,start,mid,lower,upper) + merge(sums,mid+1,end,lower,upper);
        int i = start, j = mid+1, k = mid+1, t = mid+1, r = 0;
        vector<long long> temp(end-start+1,0);
        //对于左边的每个元素i,使得sum[j]-sum[i]<=upper,sum[k]-sum[i]>=lower,则有j-k个
        for(; i<=mid; i++,r++) {
            while(j<=end && sums[j]-sums[i] <= upper)   j++;
            while(k<=end && sums[k]-sums[i] < lower)    k++;
            count += j-k;
            //合并左右子数组
            while(t<=end && sums[t]<=sums[i])   temp[r++] = sums[t++];
            temp[r] = sums[i];
        }
        for(int i=0; i<r; i++){
            sums[start+i] = temp[i];    //将合并后的数组赋给原来的数组
        }
        return count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值