这是我参与「第五届青训营 」笔记创作活动的第15天
分治算法
基本思想
将一个难以直接解决的大问题,分解为一些规模较小的相同子问题,各子问题相互独立;递归的解决各子问题,将子问题的解并成原问题的解。
分治法的解题步骤
1.分解------即将问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题;
2.治理------步骤2-1:求解各个子问题(递归)
步骤2-2:各个子问题的解的合并
二分查找
#include<stdio.h>
int BinarySearch(int s[],int x,int low,int high){
if(low>high) return -1;
int middle = (low+high);//分解
if(x == s[middle]) return middle;
else if(x>s[middle]){
return BinarySearch(s,x,middle+1,high);
}
else{
return BinarySearch(s,x,low,middle-1);
}
}
int main(){
int a[10]= {0,1,2,3,4,5,6,7,8,9};
int b;
b = BinarySearch(a,3,a[0],a[9]);
printf("%d",b);
return 0;
}
结果为:
合并排序
算法思想
合并排序是采用分治策略对n个元素进行排序的算法,是分治法的典型应用和完美体现。它是一种平衡的、简单的二分分治策略,其计算过程分为三步:
1.分解:将排序元素分成大小大致相同的两个子序列。
2.求解子问题:用合并排序法分别对两个子序列递归的进行排序。
3.合并:将排好序的有序子序列进行合并,得到符合要求的有序序列。
算法描述
快速排序
快速排序的求解过程
1.分-------选定一个元素作为基准元素,小于基准元素的放左边,大于基准元素的放右边
2.治-------递归求解子问题
快速排序的算法描述
首先根据基准元素进行排序,然后再左边右边分别排序。
快速排序算法的实现
def partition(arr,p,q):
#lo、hi分别表示指向首个元素和倒数第 2 个元素的索引
lo = p
hi = q-1
#pivot 表示选中的中间值
pivot = arr[q]
while True:
#lo从左往右遍历,直至找到一个不小于 pivot 的元素
while arr[lo] < pivot:
lo = lo + 1
#hi从右往左遍历,直至找到一个不大于 pivot 的元素
while hi > 0 and arr[hi] > pivot:
hi = hi - 1
#如果 lo≥hi,退出循环
if lo >= hi:
break
else:
#交换 arr[lo] 和 arr[hi] 的值
arr[lo],arr[hi] = arr[hi],arr[lo]
#lo 和 hi 都向前移动一个位置,准备继续遍历
lo = lo + 1
hi = hi - 1
#交换 arr[lo] 和 arr[q] 的值
arr[lo],arr[q] = arr[q],arr[lo]
#返回中间值所在序列中的位置
return lo
def quick_sort(arr,p,q):
#如果待排序序列不存在,或者仅包含 1 个元素,则不再进行分割
if q - p <= 0:
return
#调用 partition() 函数,分割 [p,q] 区域
par = partition(arr,p,q)
#以 [p,par-1]作为新的待排序序列,继续分割
quick_sort(arr,p,par-1)
#以[par+1,q]作为新的待排序序列,继续分割
quick_sort(arr,par+1,q)
arr=[35,33,42,10,14,19,27,44,26,31]
#对于 arr 列表中所有元素进行快速排序
quick_sort(arr,0,9)
print(arr)
汉诺塔问题
汉诺塔问题源自印度一个古老的传说,印度教的“创造之神”梵天创造世界时做了 3 根金刚石柱,其中的一根柱子上按照从小到大的顺序摞着 64 个黄金圆盘。梵天命令一个叫婆罗门的门徒将所有的圆盘移动到另一个柱子上,移动过程中必须遵守以下规则:
-
每次只能移动柱子最顶端的一个圆盘;
-
每个柱子上,小圆盘永远要位于大圆盘之上;
图 1 给您展示了包含 3 个圆盘的汉诺塔问题:
图 1 汉诺塔问题
一根柱子上摞着 3 个不同大小的圆盘,那么在不违反规则的前提下,如何将它们移动到另一个柱子上呢?图 2 给大家提供了一种实现方案:
图 2 汉诺塔问题的解决方案
汉诺塔问题中,3 个圆盘至少需要移动 7 次,移动 n 的圆盘至少需要操作 2n-1 次。
在汉诺塔问题中,当圆盘个数不大于 3 时,多数人都可以轻松想到移动方案,随着圆盘数量的增多,汉诺塔问题会越来越难。也就是说,圆盘的个数直接决定了汉诺塔问题的难度,解决这样的问题可以尝试用分治算法,将移动多个圆盘的问题分解成多个移动少量圆盘的小问题,这些小问题很容易解决,从而可以找到整个问题的解决方案。
汉诺塔问题的代码实现
#include <stdio.h>
void hanoi(int num, char sou, char tar,char aux) {
//统计移动次数
static int i = 1;
//如果圆盘数量仅有 1 个,则直接从起始柱移动到目标柱
if (num == 1) {
printf("第%d次:从 %c 移动至 %c\n", i, sou, tar);
i++;
}
else {
//递归调用 hanoi() 函数,将 num-1 个圆盘从起始柱移动到辅助柱上
hanoi(num - 1, sou, aux, tar);
//将起始柱上剩余的最后一个大圆盘移动到目标柱上
printf("第%d次:从 %c 移动至 %c\n", i, sou, tar);
i++;
//递归调用 hanoi() 函数,将辅助柱上的 num-1 圆盘移动到目标柱上
hanoi(num - 1, aux, tar, sou);
}
}
int main()
{
//以移动 3 个圆盘为例,起始柱、目标柱、辅助柱分别用 A、B、C 表示
hanoi(3, 'A', 'B', 'C');
return 0;
}