分治

目录

一,分治

二,经典分治问题和算法

1,二分、三分

2,快速乘法、快速幂、矩阵快速幂

3,汉诺塔

4,线性时间选择算法

5,归并排序、快速排序、桶排序

6,循环赛日程表

7,二叉树遍历

三,OJ实战

力扣 95. 不同的二叉搜索树 II

力扣 剑指 Offer 40. 最小的k个数

力扣 932. 漂亮数组

力扣 42. 接雨水

力扣 2936. 包含相等值数字块的数量


一,分治

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,

如果子问题的答案都求出来了,就可以求出原问题的答案。

二,经典分治问题和算法

1,二分、三分

二分法_两个单峰函数的和是单峰函数吗_csuzhucong的博客-CSDN博客

2,快速乘法、快速幂、矩阵快速幂

快速乘法、快速幂、矩阵快速幂_给定x,求fx对1000007取余的结果_csuzhucong的博客-CSDN博客

3,汉诺塔

汉诺塔_csuzhucong的博客-CSDN博客

4,线性时间选择算法

线性时间选择算法_csuzhucong的博客-CSDN博客

其中,平均运行时间为O(n)的算法,和最坏运行时间为O(n)的算法,都是分治算法。

5,归并排序、快速排序、桶排序

常见排序算法_计算排序_csuzhucong的博客-CSDN博客

大学算法课实验:

编写一个简单的程序,实现归并排序。

#include<iostream>
using namespace std;
 
void merge(int list[],int left,int m,int right)
{
	int b[10];
	int i=left,j=m+1,k=left;
	while(i<=m && j<=right)
	{
		if(list[i]<list[j])
			b[k++]=list[i++];
		else b[k++]=list[j++];
	}
	if(i>m)
		for(int q=j;q<=right;q++)b[k++]=list[q];
	else for (int q=i;q<=m;q++)b[k++]=list[q];
	for(int ii=left;ii<=right;ii++)
	{
		list[ii]=b[ii];
	}
}
 
void mergeSort(int list[],int left,int right)
{
	if(left<right)
	{
		int i=(left+right)/2;
		mergeSort(list,left,i);
		mergeSort(list,i+1,right);
		merge(list,left,i,right);
	}
}
 
int main()
{
	int list[10];
	for(int i=0;i<10;i++)
	{
		cin>>list[i];
	}
	mergeSort(list,0,9);
	for(int i=0;i<10;i++)
	{
		cout<<list[i]<<"  ";
	}
	system("pause>nul");
	return 0;
}

编写一段程序,实现快速排序。

#include<iostream>
using namespace std;
 
int par(int a[],int p,int r)
{
	int i=p,j=r+1;
	int x=a[p];
	while(1)
	{
		while(a[++i]<x && i<r);
		while(a[--j]>x);
		if(i>=j)break;
		int te=a[i];
		a[i]=a[j];
		a[j]=te;
	}
	a[p]=a[j];
	a[j]=x;
	return j;
}
 
void qSort(int list[],int p,int r)
{
	if(p<r)
	{
		int q=par(list,p,r);
		qSort(list,p,q-1);
		qSort(list,q+1,r);
	}
}
 
 
int main()
{
	int list[10];
	for(int i=0;i<10;i++)
	{
		cin>>list[i];
	}
	qSort(list,0,9);
	for(int i=0;i<10;i++)
	{
		cout<<list[i]<<"  ";
	}
	system("pause>nul");
	return 0;
}

6,循环赛日程表

大学算法课实验:

设有n=2k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:(1)每个选手必须与其它n-1个选手各赛一次(2)每个选手一天只能赛一场(3)循环赛进行n-1天。

#include<iostream>
#include<iomanip>
using namespace std;
 
int main()
{
	int k;
	while (cin >> k)
	{
		int n = 1;
		for (int i = 1; i <= k; i++)n *= 2;
		int **p;
		p = new int*[n + 1];
		for (int i = 0; i <= n; i++)
		{
			p[i] = new int[n + 1];
		}
		for (int i = 1; i <= n; i++)p[1][i] = i;
		int  m = 1;
		for (int s = 1; s <= k; s++)
		{
			n /= 2;
			for (int t = 1; t <= n; t++)
			for (int i = m + 1; i <= 2 * m; i++)
			for (int j = m + 1; j <= 2 * m; j++)
			{
				int temp = j + (t - 1)*m * 2;
				p[i][temp] = p[i - m][temp - m];
				p[i][temp - m] = p[i - m][temp];
			}
			m *= 2;
		}
		n = 1;
		for (int i = 1; i <= k; i++)n *= 2;
		for (int i = 1; i <= n; i++)
		{
			cout << endl << setw(2) << p[i][1] << "   ";
			for (int j = 2; j <= n; j++)
			{
				cout << setw(4) << p[i][j] << " ";
			}
		}
	}
	system("pause>nul");
	return 0;
}

7,二叉树遍历

二叉树_csuzhucong的博客-CSDN博客

三,OJ实战

力扣 95. 不同的二叉搜索树 II

二叉搜索树(BST)_拆分二叉搜索树_csuzhucong的博客-CSDN博客

力扣 剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]
 

限制:

0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

分析:

类似线性时间选择算法 线性时间选择算法_csuzhucong的博客-CSDN博客

template<typename T>
bool cmp(T a, T b)
{
    return a < b;
}
template<typename T>
int Partition(vector<T>& arr, int start, int end) //[start,end]闭区间
{
    T x = arr[end];
    int i = start - 1,j;
    for (j = start; j < end; j++) {
        if (cmp(arr[j], x)) {
            ++i;
            T tmp=arr[j];
            arr[j]=arr[i],arr[i]=tmp;
        }
    }
    ++i;
    T tmp=arr[j];
    arr[j]=arr[i],arr[i]=tmp;
    return i;
}

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int start, int end, int k) {
        vector<int>ans;
        if(start>end || k<=0 || k>end-start+1)return ans;
        int part = Partition(arr, start, end);
        if (k < part - start + 1)return getLeastNumbers(arr, start, part-1, k);
        ans=getLeastNumbers(arr, part+1, end,k-part+start-1);
        ans.resize(k);
        for(int i=start;i<=part;i++)ans[i+k-part-1]=arr[i];
        return ans;
    }
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        return getLeastNumbers(arr,0,arr.size()-1,k);
    }
};

力扣 932. 漂亮数组

对于某些固定的 N,如果数组 A 是整数 1, 2, ..., N 组成的排列,使得:

对于每个 i < j,都不存在 k 满足 i < k < j 使得 A[k] * 2 = A[i] + A[j]。

那么数组 A 是漂亮数组。

给定 N,返回任意漂亮数组 A(保证存在一个)。

示例 1:

输入:4
输出:[2,1,4,3]
示例 2:

输入:5
输出:[3,1,2,5,4]
 

提示:

1 <= N <= 1000

思路:

把奇数和偶数各放一边,然后两边都可以归结为同样的问题。


class Solution {
public:
    vector<int> beautifulArray(int N) {
        if(N<=1){
            vector<int>ans(1);
            ans[0]=1;
            return ans;
        }
        vector<int>a=beautifulArray(N/2);
        vector<int>b=beautifulArray(N-N/2);
        for(int i=0;i<a.size();i++)a[i]*=2;
        for(int i=0;i<b.size();i++)b[i]=b[i]*2-1;
        return FvecJoin(a,b);
    }
};

力扣 42. 接雨水

题目:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

思路一:分治

代码:

class Solution {
public:
	int getMax(vector<int>& height, int i, int j)
	{
		int ansMax = -1, ans = -1;
		for (int k = i; k <= j; k++){
			if (ansMax < height[k]){
				ans = k, ansMax = height[k];
			}
		}
		return ans;
	}
	int getSum(vector<int>& height, int i,int j)
	{
		int ans = 0;
		for (int k = i; k <= j; k++)ans += height[k];
		return ans;
	}
	int trap(vector<int>& height,int i,int j) {
		if (j - i <= 1)return 0;
		int k = getMax(height, i + 1, j - 1);
		if (height[k] <= height[i] && height[k] <= height[j]){
			return min(height[i], height[j])*(j - i) + max(height[i], height[j]) - getSum(height, i, j);
		}
		return trap(height, i, k) + trap(height, k, j);
	}
	int trap(vector<int>& height) {
		if (height.size() < 2)return 0;
		return trap(height, 0, height.size() - 1);
	}
};

思路二:

其实可以直接算出每个位置最终的接水量。

每个位置最终的高度,等于两边的最高高度的较小值。

力扣 2936. 包含相等值数字块的数量

给定一个整数数组 nums,其 下标从 0 开始。对于 nums,有以下性质:

  • 所有相同值的元素都是相邻的。换句话说,如果存在两个下标 i < j,使得 nums[i] == nums[j],那么对于所有下标 k,满足 i < k < j,都有 nums[k] == nums[i]

由于 nums 是一个非常大的数组,这里提供了一个 BigArray 类的实例,该实例具有以下函数:

  • int at(long long index): 返回 nums[i] 的值。
  • long long size(): 返回 nums.length

让我们把数组分成 最大 的块,使得每个块包含 相等的值。返回这些块的数量。

请注意,如果要使用自定义测试测试解决方案,对于 nums.length > 10 的测试行为是未定义的。

示例 1:

输入:nums = [3,3,3,3,3]
输出:1
解释:这里只有一个块,也就是整个数组(因为所有数字都相等),即:[3,3,3,3,3]。因此答案是 1。 

示例 2:

输入:nums = [1,1,1,3,9,9,9,2,10,10]
输出:5
解释:这里有 5 个块:
块号 1: [1,1,1,3,9,9,9,2,10,10]
块号 2: [1,1,1,3,9,9,9,2,10,10]
块号 3: [1,1,1,3,9,9,9,2,10,10]
块号 4: [1,1,1,3,9,9,9,2,10,10]
块号 5: [1,1,1,3,9,9,9,2,10,10]
因此答案是 5。

示例 3:

输入:nums = [1,2,3,4,5,6,7]
输出:7
解释:由于所有数字都是不同的,这里有 7 个块,每个元素代表一个块。因此答案是 7。 

提示:

  • 1 <= nums.length <= 1015
  • 1 <= nums[i] <= 109
  • 在生成的输入中所有相同值的元素是相邻的。
  • nums 的所有元素之和最多为 1015
/**
 * Definition for BigArray.
 * class BigArray {
 * public:
 *     BigArray(vector<int> elements);
 *     int at(long long index);
 *     long long size();
 * };
 */
class Solution {
public:
	int countBlocks(BigArray* nums) {
		long long len = nums->size();
		return countBlocks(nums, 0, len - 1, nums->at(0), nums->at(len - 1));
	}
	int countBlocks(BigArray* nums, long long low, long long high, int x, int y) {
		if (x == y)return 1;
		long long mid = (low + high) / 2;
		int a = nums->at(mid), b = nums->at(mid + 1);
		int ans = countBlocks(nums, low, mid, x, a) + countBlocks(nums, mid + 1, high, b, y);
		if (a == b)ans--;
		return ans;
	}
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值