分治算法

分治算法的思想

分治算法的思想就是分而治之,比如规模为n的原问题的解无法直接求出,进行问题规模的缩减,划分子问题。如果子问题的规模仍然不够小,再进行子问题划分,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止,最后将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原问题的解。

分治算法适用条件

1.原问题的规模缩小到一定的程度就可以容易地解决
⒉原问题可以分解为若干个规模较小的相同问题,即原问题具有最优子结构性质
3.利用原问题分解出的子问题的解可以合并为原问题的解
4.原问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题(这条特征涉及到分治法的效率,如果各个子问题不独立,也就是子问题划分有重合的部分,则分治法要重复的求解公共子问题的解,此时虽然也可用分治法,但采用动态规划更好)

二分查找

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 分治算法思想 - 二分搜索
bool binarySearch(vector<int> &vec, int i, int j, int val) {
	if (i > j) {  // 元素不存在
		return false;
	}

	int mid = (i + j) / 2;
	if (vec[mid] == val) {  // 子问题[i,j] 解是已知的了
		return true;
	} 
	else if (vec[mid] > val) { // 在[i, mid-1]子规模问题中
		return binarySearch(vec, i, mid-1, val);
	}
	else { // 在[mid+1, j]子规模问题中
		return binarySearch(vec, mid + 1, j, val);
	}
}
int main1()
{
	vector<int> vec;
	for (int i = 0; i < 11; ++i) {
		vec.push_back(rand() % 100);
	}
	sort(vec.begin(), vec.end());
	for (int v : vec) {
		cout << v << " ";
	}
	cout << endl;

	bool result = binarySearch(vec, 0, vec.size() - 1, 34);
	cout << "result:" << result << endl;

	return 0;
}

快速排序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 快排划分函数,调整基准数
int partation(vector<int> &vec, int i, int j) {
	int val = vec[i]; // 作为基准数
	int l = i;
	int r = j;
	while (l < r) {
		while (l < r && vec[r] >= val) { // 右 - 左  找第一个比val小的
			r--;
		}
		if (l < r) {
			vec[l++] = vec[r];
		}
		while (l < r && vec[l] < val) { // 左 - 右  找第一个比val大的
			l++;
		}
		if (l < r) {
			vec[r--] = vec[l];
		}
	}
	vec[l] = val;  // 放置基准数
	return l; // 返回基准数的下标
}
void quickSort(vector<int> &vec, int i, int j) {
	if (i >= j) {
		return;
	}
	int pos = partation(vec, i, j);
	quickSort(vec, i, pos - 1);
	quickSort(vec, pos + 1, j);
}
int main()
{
	vector<int> vec;
	for (int i = 0; i < 11; ++i) {
		vec.push_back(rand() % 100);
	}
	
	for (int v : vec) {
		cout << v << " ";
	}
	cout << endl;

	quickSort(vec, 0, vec.size() - 1);
	for (int v : vec) {
		cout << v << " ";
	}

	cout << endl;

	return 0;
}

求大数据的top K问题

十万个数字求前十大的数据:

  1. 大根堆小根堆 (优先级队列)。
  2. 快排的划分函数。

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 快排划分函数,调整基准数
static int partation(vector<int> &vec, int i, int j) {
	int val = vec[i]; // 作为基准数
	int l = i;
	int r = j;
	while (l < r) {
		while (l < r && vec[r] >= val) { // 右 - 左  找第一个比val小的
			r--;
		}
		if (l < r) {
			vec[l++] = vec[r];
		}
		while (l < r && vec[l] < val) { // 左 - 右  找第一个比val大的
			l++;
		}
		if (l < r) {
			vec[r--] = vec[l];
		}
	}
	vec[l] = val;  // 放置基准数
	return l; // 返回基准数的下标
}

// 找第k大的,  vec.size()-k 小的 下标
int max_select_topk(vector<int> &vec, int i, int j, int k) {
	int pos = partation(vec, i, j); // pos表示基准数的位置
	if (pos == vec.size() - k) { // 基准数的位置和top k的k值相等了
		return pos;
	}
	else if (pos < vec.size() - k) { // topk应该在基准数的右边
		return max_select_topk(vec, pos + 1, j, k);
	}
	else { // topk应该落在基准数的左边
		return max_select_topk(vec, i, pos - 1, k);
	}
}
// 找第k小的,  k-1小的 下标   3
int min_select_topk(vector<int> &vec, int i, int j, int k) {
	int pos = partation(vec, i, j); // pos表示基准数的位置
	if (pos == k-1) { // 基准数的位置和top k的k值相等了
		return pos;
	}
	else if (pos < k-1) { // topk应该在基准数的右边
		return min_select_topk(vec, pos + 1, j, k);
	}
	else { // topk应该落在基准数的左边
		return min_select_topk(vec, i, pos - 1, k);
	}
}
int main3()
{
	vector<int> vec;
	for (int i = 0; i < 20; ++i) {
		vec.push_back(rand() % 100);
	}

	// 求第top 10大的元素
	int pos = max_select_topk(vec, 0, vec.size() - 1, 4);
	cout << "第topk大的:" << vec[pos] << endl;
	cout << "前topk大的:";
	for (int i = pos; i < vec.size(); ++i) {
		cout << vec[i] << " ";
	}
	cout << endl;

	// 找topl小的
	pos = min_select_topk(vec, 0, vec.size() - 1, 4);
	cout << "第topk小的:" << vec[pos] << endl;
	cout << "前topk小的:";
	for (int i = 0; i <= pos; ++i) {
		cout << vec[i] << " ";
	}
	cout << endl;

	sort(vec.begin(), vec.end());
	for (int v : vec) {
		cout << v << " ";
	}

	cout << endl;

	return 0;
}

归并排序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void merge(vector<int> &vec, int low, int high, int mid) {
	// 定义额外的辅助空间,存储合并的子问题的有序数组
	vector<int> tmp;
	tmp.reserve(high - low + 1);  // reserve resize
	int i = low;  // [low, mid]
	int j = mid + 1; // [mid+1, high]

	while (i <= mid && j <= high) {
		if (vec[i] > vec[j]) {
			tmp.push_back(vec[j++]);
		}
		else {
			tmp.push_back(vec[i++]);
		}
	}

	while (i <= mid) {
		tmp.push_back(vec[i++]);
	}

	while (j <= high) {
		tmp.push_back(vec[j++]);
	}

	// tmp里面的元素 =》 数组vec当中
	for (int k = low; k <= high; ++k) {
		vec[k] = tmp[k - low];
	}
}
// 分治算法应用 - 归并排序
void mergeSort(vector<int> &vec, int i, int j) {
	if (i >= j) { // 子问题划分到一个元素的时候,就代表子问题的解是已知的了
		return;
	}
	int mid = (i + j) / 2;
	// 先划分子问题,降低问题规模
	mergeSort(vec, i, mid);
	mergeSort(vec, mid + 1, j);

	// 向上回溯,回溯的过程中,合并子问题的解
	merge(vec, i, j, mid);  // [i,mid] [mid+i, j]
}
int main4()
{
	vector<int> vec;
	for (int i = 0; i < 11; ++i) {
		vec.push_back(rand() % 100);
	}

	for (int v : vec) {
		cout << v << " ";
	}
	cout << endl;

	mergeSort(vec, 0, vec.size() - 1);
	for (int v : vec) {
		cout << v << " ";
	}
	cout << endl;

	return 0;
}

合并K个有序单链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:

    ListNode* merge(ListNode* a,ListNode * b)
    {
        ListNode *head = nullptr;
        if(a == nullptr){
            return b;
        }
        if(b == nullptr){
            return a;
        }

        if(a->val > b->val){
            head = b;
            b = b->next;
        }
        else{
            head = a;
            a = a->next;
        }
        ListNode *p = head;
        while(a!=nullptr && b!=nullptr)
        {
            if(a->val > b->val){
                p->next = b;
                p = b;
                b = b->next;
            }
            else{
                p->next = a;
                p = a;
                a = a->next;
            }
        }

        if(a!=nullptr){
            p->next = a;
        }
        if(b!=nullptr){
            p->next = b;
        }
        return head;
    }


    ListNode* mergeLink(vector <ListNode*> &lists, int l, int r) {
        if (l == r) return lists[l];
        if (l > r) return nullptr;
        int mid = (l + r) >> 1;
        ListNode *left = mergeLink(lists, l, mid);
	    ListNode *right = mergeLink(lists, mid + 1, r);
        return merge(left,right);
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        int i = 0;
        int j = lists.size()-1;
        int mid = (i+j)/2;
        return mergeLink(lists,i,j);
    }
};

对数时间求解中位数

double middleValue(vector<int> &nums1, int length1, vector<int> &nums2, int length2) {
	if (length1 > length2) { // 在短的数组中求解合适的i和j值
		return middleValue(nums2, length2, nums1, length1);
	}

	if (length1 == 0) {
		// 0 0 0 0 0 0 0    (6-1)/2=3   
		int k = (length2 - 1) / 2;
		if (length2 % 2 == 0) {
			return (nums2[k] + nums2[k + 1]) * 1.0 / 2;
		}
		else {
			return nums2[k];
		}
	}

	int i = 0;
	int j = 0;
	int begin = 0;
	int end = length1;
	int k = (length1 + length2 + 1) / 2;    // 7 / 2 = 3   0 0 0 0 0 0 0
	while (begin <= end) {  // 二分搜索的算法思想,对数时间找到i+j = k
		i = (begin + end) / 2; 
		j = k - i;
		if (i > 0 && j < length2 && nums1[i - 1] > nums2[j]) {
			end = i - 1;
		}
		else if (j > 0 && i < length1 && nums2[j - 1] > nums1[i]) {
			begin = i + 1;
		}
		else {
			break; // arr[i-1] < brr[j] && brr[j-1] < arr[i]
		}
	}
	// nums1特别短,而且nums1数组的元素的值都特别大
	int left = 0;
	if (i == 0) {  // 中位数肯定都在num2这个数组当中
		left = nums2[j - 1];
	} 
	else if (j == 0) { // nums2这个数组太短了  中位数肯定都在num1这个数组当中
		left = nums1[i - 1];
	}
	else {
		left = std::max(nums1[i - 1], nums2[j - 1]);
	}
	int right = 0;
	if (i == length1) { // nums1数组元素太少,而且值都特别的小   中位数肯定都在num2这个数组当中
		right = nums2[j];
	}
	else if (j == length2) { //中位数肯定都在num1这个数组当中
		right = nums1[i];
	}
	else {
		right = std::min(nums1[i], nums2[j]);
	}
	// 找到了合适的i和j的值
	if ((length1 + length2) % 2 == 0) { // 偶数长度
		return (left + right)*1.0 / 2;
	}
	else { // 奇数长度
		return left;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值