分治与递归【实战篇】

目录

一、二分搜索技术:

1.算法核心:

2.代码实现:

二、合并排序:

1.算法核心:

2.代码实现:

三、快速排序:

1.算法核心:

2.代码实现:

四、线性时间选择:

1.算法核心:

2.代码实现:

五、循环赛:

1.算法思想:

2.算法步骤:

3.代码实现:


一、二分搜索技术:

1.算法核心:

(1)将数组分为大致相同的两半;

(2)比较 a[n/2] 与 x 之间的大小;

(3)重复步骤  (1)、(2)  至找到所需数据。

2.代码实现:

#include <stdio.h>
#include <stdlib.h>
 
//二分查找算法,找不到就返回-1,找到了就返回值
int binary_search(int * list,int len,int target)
{
    int l = 0;
    int r = len-1;
    int middle;
    while(l <= r)
	{
        middle = (l + r)/2;
        if(list[middle] == target)
        {
            printf("已找到该值,数组下标为:%d\n",middle);
            return list[middle];
        }
        else if(list[middle] > target)
        {
            r = middle -1;
        }
        else if(list[middle] < target)
        {
            l = middle + 1;
        }
    }
    printf("未找到该值");
    return -1;
}
 
int main()
{
 
    int a[] = {2,4,5,8,9,44};
    int b = binary_search(a,sizeof(a)/4,5);
    printf("b=%d\n",b);
    return 0;
}

二、合并排序:

1.算法核心:

(1)将数组分为大小大致相同的两个子集合;

(2)分别对这两个子集合排序;

(3)合并已经排好序的子集合。

2.代码实现:

#include <stdio.h>
#include <stdlib.h>
#define N 7

void merge(int arr[], int l, int mid, int r)
{
    int i, k;
    int *tmp = (int *)malloc((r-l+1)*sizeof(int));
	
	//申请空间,使其大小为两个
    int left_low = l;
    int left_high = mid;
    int right_low = mid + 1;
    int right_high = r;
    
	for(k=0; left_low<=left_high && right_low<=right_high; k++)//比较两个指针所指向的元素,取较小的元素赋值给tmp[k] 
	{  
        if(arr[left_low]<=arr[right_low])
		{
            tmp[k] = arr[left_low];
            left_low++;
        }
		else
		{
            tmp[k] = arr[right_low];
            right_low++;
        }
    }
    
    if(left_low <= left_high)//若第一个序列有剩余,直接复制出来粘到合并序列尾
	{ 
		for(i=left_low;i<=left_high;i++)
        	tmp[k++] = arr[i];
    }
    if(right_low <= right_high) //若第二个序列有剩余,直接复制出来粘到合并序列尾
	{
        for(i=right_low; i<=right_high; i++)
        	tmp[k++] = arr[i];
    }
    for(i=0; i<r-l+1; i++)
        arr[l+i] = tmp[i];
    free(tmp);
    return;
}
void merge_sort(int arr[], unsigned int l, unsigned int r)
{
    int mid = 0;
    if(l<r)
	{
        mid = (l+r)/2;
        merge_sort(arr, l, mid);
        merge_sort(arr, mid+1,r);
        merge(arr,l,mid,r);
    }
    return;
}
int main()
{
    int i;
    int a[N]={32,12,56,78,76,45,36};
   
    merge_sort(a,0,N-1);  // 排序
   
    for(i=0;i<N;i++)
        printf("%d ",a[i]); printf("\n");
    system("pause");
    return 0;
}

三、快速排序:

1.算法核心:

(1)分解:先从数列中取出一个数作为基准数,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。将数组   a[p:r]  分解为两个数组:a[p:k]  ,  a[k+1:r];

(2)递归:对这两个数组分别排序,重复(1)至只有一个元素;

(3)合并:合并两个数组。

2.代码实现:

#include <stdio.h>
#include <stdlib.h>
#define BUF_SIZE 10
 
void display(int array[], int maxlen)
{
    int i;
    for(i = 0; i < maxlen; i++)
    {
        printf("%-3d", array[i]);
    }
    printf("\n");
    return ;
}

int partition(int *arr,int p, int r)
{
	int x;
	int key = arr[p];		// 记下最前面的数
	while (p < r)			// 把小于key的放前面, 大于key的放后面
	{
		while (arr[r] >= key && p < r)
			r--;
		if (p < r)
			arr[p] = arr[r];
		while (arr[p] <= key && p < r)
			p++;
		if (p < r)
			arr[r] = arr[p];
	}
	// 分别从两头开始, 找到中间时, 把key存回
	arr[p] = key;
	return p;
}

void QuickSort(int *arr, int l, int r)
{
    if (l < r)
    {
    	int q=partition(arr,l,r);
        // 递归调用
        QuickSort(arr, l, q - 1);     // 排序k左边
        QuickSort(arr, q + 1, r);    // 排序k右边
    }
}
 
// 主函数
int main()
{
    int array[BUF_SIZE] = {12,85,25,16,34,23,49,95,17,61};
    int maxlen = BUF_SIZE;
    
    QuickSort(array, 0, maxlen-1);  // 快速排序

    display(array, maxlen);
    return 0;
}

四、线性时间选择:

1.算法核心:

线性时间的选择的精髓就在Select算法之中,其算法复杂度最坏情况为 O(n) ,具体步骤如下:

(1)将 n 个元素,分成\left \lceil n/5 \right \rceil组,取出每组的中位数(第三小的元素);
(2)取出\left \lceil n/5 \right \rceil个中位数的中位数(Select函数可以取中位数);
(3)利用快速排序中的分解函数 partition(),以所求中位数为基准,划分 a[p : r] 为两段;
(4)取其中一段进行递归。

2.代码实现:

#include <cstdio>
#include <cstdlib>

int num[2000000];

int select(int p, int r, int k);
int partition(int p, int r, int median);
void selectSort(int p, int r);
void swap(int &a, int &b);

int main()
{
	int n, m, i;
	while (~scanf("%d%d", &n, &m))
	{
		for (i = 0; i < n; i++)
		{
			scanf("%d", &num[i]);
		}						
		printf("%d\n", select(0, n - 1, m - 1));
	}
	return 0;
}
// 中位数法线性时间选择
int select(int p, int r, int k)
{
	// 小于75个数据
	if (r - p < 74)
	{
		selectSort(p, r);				// 选择排序
		return num[p + k];				
	}

	for (int i = 0; i <= (r-p-4)/5; i++)
	{
		int start = p + 5 * i;			
		int end = start + 4;		
		for (int j = 0; j < 3; j++)				//
			for (int m = start; m < end - j; m++)
				if (num[m] > num[m + 1])
					swap(num[m], num[m+1]);
		swap(num[p + i], num[start + 2]);		// 每组的中位数交换到前面第p + i的位置
	}
	
	int median = select(p, p + (r-p-4)/5, (r-p+6) / 10);	// 找中位数的中位数
	int i = partition(p, r, median);	//将数组分为两段, 左边的小于中位数的中位数, 右边的大于中位数的中位数
	int j = i - p + 1;		

	if (k == j)					//a[k]等于中位数 
		return num[i];			
	if (k < j)					//a[k]小于中位数 
		return select(p, i - 1, k);				
	if (k > j)					//a[k]大于中位数 
		return select(i + 1, r, k - j);	
}

int partition(int p, int r, int median)		// 以中位数进行分割, 分成两半
{
	int x;
	for (int i = p; i <= r; i++)
		if (num[i] == median)
		{
			x = i;			//记录中位数下标 
			break;
		}

	swap(num[x], num[p]);	// 将中位数交换到最前面
	int key = num[p];		// 记下最前面的数

	while (p < r)			// 把小于key的放前面, 大于key的放后面
	{
		while (num[r] >= key && p < r)
			r--;
		if (p < r)
			num[p] = num[r];
		while (num[p] <= key && p < r)
			p++;
		if (p < r)
			num[r] = num[p];
	}
	// 分别从两头开始, 找到中间时, 把key存回
	num[p] = key;
	return p;
}
// 选择排序
void selectSort(int p, int r)
{
	for (int i = p; i <= r; i++)//将第i大的值放在第i位 
	{
		int MIN = i;
		for (int j = i + 1; j <= r; j++)//找到最大值 
			if (num[MIN] > num[j])
				MIN = j;
		swap(num[i], num[MIN]);
	}
}
// 交换两个元素
void swap(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}

五、循环赛:

1.算法思想:

        将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。

2.算法步骤:

(1)初始化第一行:

(2)定义一个m值,作为填充矩阵的起始位置标记

(3)将问题分成k部分(n=2 ^{k})。以k=3为例,将问题分成3大部分:

        第一部分为,根据已经初始化的第一行,填写第二行;

        第二部分为,根据已经填充好的第一部分,填写第三四行;

        第三部分为,根据已经填充好的前四行,填写最后四行。

(4)将第 i 部分都划分为 t=n^{\frac{k}{i+1}} 即:第一部分有四个小单元,第二部分有两个小单元,第三部分有一个小单元。

(5)对单元内的矩阵进行赋值(对角线填充)。

3.代码实现:

#include<iostream>
#include <string>
using namespace std;

void order(int k,int n)
{
	int** a;
	int x=n;
	a=new int*[n];
	for(int i=1;i<=n;i++)
		a[i]=new int[n];
	for(int i=1;i<=n;i++)//初始化第一行 
		a[1][i]=i;

	int m=1;//m代表着分组的长度 
	for(int s=1;s<=k;s++)
	{
		n=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++)
				{
					a[i][j+(t-1)*(2*m)]=a[i-m][j+(t-1)*(2*m)-m];//右下角==左上角 
					a[i][j+(t-1)*(2*m)-m]=a[i-m][j+(t-1)*(2*m)];//左下角==右上角 
				}
			}
		}
		m=m*2;
	}
	for(int i=1;i<=x;i++)
	{
		for(int j=1;j<=x;j++)
		{
			cout<<a[i][j]<<' ';
		}
		cout<<endl;
	}
}

int main()
{
	int k=0,n=1;
	cout<<"请输入一个整数:"<<endl;
	cin>>k;
	for(int i=0;i<k;i++)
	{
		n=2*n;
	}
	order(k,n);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/aimat2020/article/details/124160287