• 减治法(引例中位数、查找问题:折半&二叉&选择、排序问题:堆排序、组合问题:淘汰赛冠军问题&假币问题)


    分治法需要对分解的子问题分别求解,再对子问题进行合并,减治法只对一个子问题求解,并且不需要进行解的合并。减治法的效率更好。

    *时间复杂性为log2n

    *思路:如果n=1,返回a的值;n是偶数且n>1,把该问题的规模减半,计算a^n/2的值;n是奇数且n>1,先利用偶指数的规则计算a^(n-1),再把结果乘以a

                a   n=1

    a^n=    (a^n/2)^2        n>1且n是偶数

                (a^n-1/2)^2*a  n>1且n是奇数

    例题

    减治法求两个序列的中位数

    1. #include
    2. using namespace std;
    3. int SearchMid(int A[],int B[],int n){
    4. //初始化两个序列的上下界
    5. int s1=0,e1=n-1,s2=0,e2=n-1;
    6. int mid1,mid2;
    7. while(s1//循环--直到区间只有一个元素
    8. mid1=(s1+e1)/2; //序列A的中位数下标
    9. mid2=(s2+e2)/2; //序列B的中位数下标
    10. if(A[mid1]==B[mid2]){ //当a=b时 返回a 算法结束
    11. return A[mid1];
    12. }
    13. if(A[mid1]//a
    14. if((s1+e1)%2==0) s1=mid1; //头指针变成mid开始
    15. else s1=mid1+1;
    16. e2=mid2; //尾指针变成中间部分
    17. }
    18. if(A[mid1]>B[mid2]){ //a>b 舍弃a之后的元素舍弃b之前的元素(b,a)之间
    19. if((s2+e2)%2==0) s2=mid2;
    20. else s2=mid2+1;
    21. e1=mid1;
    22. }
    23. }
    24. //返回较小者
    25. if(A[s1]
    26. return A[s1];
    27. }
    28. else{
    29. return B[s2];
    30. }
    31. }
    32. int main(){
    33. int n; //长度
    34. cout<<"请输入数组长度:"<
    35. cin>>n;
    36. int A[n],B[n];
    37. cout<<"请输入A数组:"<
    38. for(int i=0;i
    39. cin>>A[i];
    40. }
    41. cout<<"请输入B数组:"<
    42. for(int i=0;i
    43. cin>>B[i];
    44. }
    45. int res=0;
    46. res=SearchMid(A,B,n);
    47. cout<<"中位数是:"<
    48. return 0;
    49. }

    查找问题 

     折半查找

            折半查找与待查值每比较依次,根据比较结果是的查找的区间减半

            *前提:序列有序,若序列无序,需要先进行排序

    1. #include
    2. using namespace std;
    3. int BinSearch(int r[],int n,int k){
    4. int low=0,high=n-1;
    5. int mid;
    6. while(low<=high){
    7. mid=(low+high)/2;
    8. if(k
    9. high=mid-1;
    10. }
    11. else if(k>r[mid]){
    12. high=mid+1;
    13. }
    14. else{
    15. return mid;
    16. }
    17. }
    18. return -1; //查找失败,返回-1
    19. }
    20. int main(){
    21. int n;
    22. cout<<"请输入序列长度:"<
    23. cin>>n;
    24. int a[n];
    25. cout<<"请输入序列:"<
    26. for(int i=0;i
    27. cin>>a[i];
    28. }
    29. int k;
    30. cout<<"请输入待查找的值:"<
    31. cin>>k;
    32. int res;
    33. res=BinSearch(a,n,k);
    34. cout<<"您所查找的值在"<"下标位置"<
    35. return 0;
    36. }

    二叉查找树(有bug)

            若左子树不为空,左子树上所有结点小于根结点;右子树不为空,右子树上所有结点大于根节点;左右子树都是二叉排序树。

            #在一个无序序列中执行查找操作,可以将无序序列建立一个二叉查找树,然后再二叉查找树中查找值为k的记录。若成功,返回记录k的存储地址;失败,返回空。

    1. #include
    2. using namespace std;
    3. //用二叉链表存储二叉查找树,设root为指向二叉链表的跟指针
    4. struct BiNode{
    5. int data;
    6. BiNode *lchild,*rchild;
    7. };
    8. //二叉树存在以后,二叉树的查找算法
    9. BiNode *searchBST(BiNode *root,int k){
    10. if(root==NULL) //如果二查找树为空,查找失败
    11. {
    12. return NULL;
    13. }
    14. if(root->data==k){ //如果相等 查找成功
    15. return root;
    16. }
    17. else if(root->data//向左边递归查找 查找左子树
    18. return searchBST(root->lchild,k);
    19. }
    20. else if(root->data>k){ //向右递归查找 查找右子树
    21. return searchBST(root->rchild,k);
    22. }
    23. }
    24. //在执行查找操作之前首先需要建立无序二叉查找树
    25. BiNode *InsertBST(BiNode *root,int data){
    26. if(root==NULL){
    27. //首先 为data申请一个新的结点
    28. root=new BiNode;
    29. root->data=data;
    30. //其次,让该结点成为叶子结点
    31. root->lchild=root->rchild=NULL;
    32. return root;
    33. }
    34. if(data<=root->data){//插入在左子树
    35. root->lchild=InsertBST(root->lchild,data);
    36. }
    37. else if(data>root->data){//插入在右子树
    38. root->rchild=InsertBST(root->rchild,data);
    39. }
    40. return root;
    41. }
    42. //将无序序列建立一个二叉查找树
    43. BiNode *CreateBST(int a[],int n){
    44. BiNode *T=NULL;
    45. for(int i=0;i
    46. T=InsertBST(T,a[i]); //在二叉查找树中插入元素a[i]
    47. }
    48. return T;
    49. }
    50. int main(){
    51. int n;
    52. cout<<"请输入待排序的结点个数:"<
    53. cin>>n;
    54. int a[n];
    55. BiNode *T=new BiNode();
    56. cout<<"请输入每个结点:"<
    57. for(int i=0;i
    58. cin>>a[i];
    59. T=CreateBST(a,n);
    60. }
    61. int k;
    62. cout<<"请输入待查找的值"<
    63. cin>>k;
    64. cout<<"查找值就在";
    65. cout<<searchBST(T,k)<<"位置";
    66. }

     选择问题:寻找T的第k小元素的问题

    1. #include
    2. using namespace std;
    3. int Partition(int r[],int low,int high){
    4. //对数据进行划分
    5. //无序数组 左边 右边 左小右大
    6. int i=low,j=high;
    7. while(i
    8. //扫描右侧
    9. while(i
    10. if(i
    11. //如果左边比右边的大 就交换 (左小右大)
    12. int temp=r[i];
    13. r[i]=r[j];
    14. r[j]=temp;
    15. i++; //移动左边的指针
    16. }
    17. //扫描左侧
    18. while(i
    19. if(i
    20. //如果右边比左边的小 就交换 保证左边是小的 右边是大的
    21. int temp=r[i];
    22. r[i]=r[j];
    23. r[j]=temp;
    24. j--; //右边的指针向左移动
    25. }
    26. }
    27. //循环结束i=j时候
    28. //返回i的坐标就是中间值
    29. return i;
    30. }
    31. int SelectMink(int r[],int k,int low,int high)
    32. {
    33. //选择问题的实现 k是待查找的数
    34. int s;//s是要找的轴值 (位置坐标)
    35. s= Partition(r,low,high);
    36. //如果在中间,就直接返回找到了 !查找成功
    37. if(s==k){
    38. return r[s];
    39. }
    40. //如果k
    41. else if(k
    42. return SelectMink(r,k,low,s-1);
    43. }
    44. //如果k>r[i] 在右边递归查找
    45. else{
    46. return SelectMink(r,k,s+1,high);
    47. }
    48. }
    49. int main(){
    50. cout<<"请输入数据总数:"<
    51. int n;
    52. cin>>n;
    53. int r[n];
    54. cout<<"请输入数据:"<
    55. for(int i=0;i
    56. cin>>r[i];
    57. }
    58. int k; //传入的是数据的位置坐标
    59. cout<<"请输入想查找的第k个元素:"<
    60. cin>>k;
    61. k=k-1;
    62. int low=0,high=n-1;
    63. int res=SelectMink(r,k,low,high); //要找的是排行老四位置的数是多少
    64. cout<
    65. }

    排序问题

    插入排序:用插入排序的方法对一个记录序列进行升序排列。

    *依次将待排序序列中的每一个记录插入到一个已经排好序的序列中,直到所有记录都排好序

    1. #include
    2. using namespace std;
    3. /*
    4. void InsertSort(int r[],int n){
    5. int i,j=0; //待排序记录序列存储在r[1]~r[n]中
    6. for(i=2;i<=n;i++){ //从第二个记录开始执行插入操作
    7. if(r[i]
    8. r[0]=r[i]; //暂存待插记录,设置哨兵
    9. for(j=i-1;r[j]>r[0];j--){ //寻找插入位置
    10. r[j+1]=r[j]; //记录后移
    11. }
    12. }
    13. r[j+1]=r[0];
    14. }
    15. }*/
    16. void InsertSort(int* arr, int n)
    17. {
    18. for (int i = 0; i < n - 1; ++i)
    19. {
    20. //记录有序序列最后一个元素的下标
    21. int end = i;
    22. //待插入的元素
    23. int tem = arr[end + 1];
    24. //单趟排
    25. while (end >= 0)
    26. {
    27. //比插入的数大就向后移
    28. if (tem < arr[end])
    29. {
    30. arr[end + 1] = arr[end];
    31. end--;
    32. }
    33. //比插入的数小,跳出循环
    34. else
    35. {
    36. break;
    37. }
    38. }
    39. //tem放到比插入的数小的数的后面
    40. arr[end + 1] = tem;
    41. //代码执行到此位置有两种情况:
    42. //1.待插入元素找到应插入位置(break跳出循环到此)
    43. //2.待插入元素比当前有序序列中的所有元素都小(while循环结束后到此)
    44. }
    45. }
    46. int main(){
    47. int n;
    48. cout<<"请输入个数:"<
    49. cin>>n;
    50. int r[n];
    51. cout<<"请输入数组:"<
    52. for(int i=0;i
    53. cin>>r[i];
    54. }
    55. InsertSort(r,n);
    56. cout<<"排序后的结果是:"<
    57. for(int i=0;i
    58. cout<" ";
    59. }
    60. return 0;
    61. }

    堆排序 

    *1)筛选法:整成大(小)顶堆

    2)将堆顶元素和堆的最后一个元素进行交换,重复步骤1

    1. #include
    2. using namespace std;
    3. //首先将无序序列调整成堆,由于叶子结点均可以看作是堆,因此,可以从编号最大的分支节点直至根节点反复调用筛选法
    4. void SiftHeap(int r[],int k,int n){
    5. //筛选法 输入的数组 数组规模
    6. int i,j,temp;//i:要筛选的结点 j:i的左孩子
    7. i=k;
    8. j=2*i+1;
    9. while(j
    10. if(j-1&&r[j]1]) j++; //比较i的左右孩子 j
    11. if(r[i]>r[j]) break; //结束比较 根节点已经大于左右孩子中的较大者
    12. else{
    13. temp=r[i]; //交换
    14. r[i]=r[j];
    15. r[j]=temp;
    16. i=j; //被筛选结点位于原来结点j的位置
    17. j=2*i+1; //j得继续找做孩子
    18. }
    19. }
    20. }
    21. void HeapSort(int r[],int n){
    22. //堆排序 变成从小到大的输出顺序了
    23. int i,temp;
    24. for(i=(n-1)/2;i>=0;i--){ //从最后一个非叶结点开始
    25. SiftHeap(r,i,n); //调整成堆
    26. }
    27. for(i=1;i<=n-1;i++){
    28. //重复执行移走对顶及重建堆的操作
    29. temp=r[0];
    30. r[0]=r[n-i];
    31. r[n-i]=temp;
    32. SiftHeap(r,0,n-i);//只需要调整根节点
    33. }
    34. }
    35. //堆排序
    36. int main(){
    37. int n;
    38. cout<<"请输入数据总数:";
    39. cin>>n;
    40. int a[n];
    41. cout<<"请输入数组:";
    42. for(int i=0;i
    43. cin>>a[i];
    44. }
    45. HeapSort(a,n);
    46. for(int i=0;i
    47. cout<
    48. }
    49. return 0;
    50. }

    组合问题

    淘汰赛冠军问题

    1. char Game(int n,char r[]){
    2. int i=n;
    3. while(i>1){ //比赛直到剩余1人即为冠军
    4. i=i/2;
    5. for(int j=0;j
    6. if(Comp(r[j+1],r[j])) //模拟两位选手比赛 若mem1获胜返回1
    7. //若mem2获胜返回0
    8. r[j]=r[j+1]; //将胜者存入r[j]中
    9. }
    10. }
    11. return r[0]; //返回冠军
    12. }

     实例:洛谷

    1. #include
    2. using namespace std;
    3. struct node
    4. {
    5. int num,grade;//编号,能力值
    6. } a[130];//最多为2^7,即128,故设数组130
    7. int main()
    8. {
    9. int n,m = 1;
    10. cin>>n;
    11. for(int i = 1;i <= n;i++) m *= 2;//共有z^n支队伍
    12. for(int i = 1;i <= m;i++)
    13. {
    14. a[i].num = i;//记录编号
    15. cin>>a[i].grade;
    16. }
    17. while(m > 2)//循环条件
    18. {
    19. int q = 0;//初始化
    20. for(int i = 1;i <= m;i += 2)//每次+2,两两比较的缘故
    21. {
    22. if(a[i].grade > a[i + 1].grade) a[++q] = a[i]; //q为计数用
    23. else a[++q] = a[i + 1];
    24. }
    25. m /= 2;//每次减少一半,a数组的前一半便是晋级的队伍
    26. }
    27. //最后只剩下两支队伍,比较后输出亚军的编号,即能力值较小的队伍
    28. if(a[1].grade > a[2].grade) cout<2].num<
    29. else cout<1].num<
    30. return 0;
    31. }

     假币问题

    add1存储第一组硬币的重量和

    add2存储第二组硬币的重量和

    add3存储第三组硬币的重量和

    add1

    add2

    add1=add2 在第三组中

    1. #include
    2. using namespace std;
    3. const int N=8;
    4. int a[N]={2,2,1,2,2,2,2,2}; //假币的重量是1
    5. int Coin(int low,int high,int n){
    6. int i,num1,num2,num3; //分别存储三个硬币的个数
    7. int add1=0,add2=0;//存储前两组硬币的重量和
    8. if(n==1){
    9. return low+1; //递归结束条件
    10. }
    11. if(n%3==0){//如果恰好可以被整除
    12. num1=n/3;
    13. num2=n/3;
    14. }
    15. else{
    16. num1=n/3+1; //前两组向上取整
    17. num2=n/3+1;
    18. }
    19. num3=n-num1-num2; //不管是那种情况,第三种的个数都是这也
    20. //计算重量
    21. for(i=0;i
    22. add1=add1+a[low+i];
    23. }
    24. for(i=num1;i
    25. add2=add2+a[low+i];
    26. }
    27. //两组比较大小
    28. if(add1//在第一组中
    29. return Coin(low,low+num1-1,num1);
    30. }
    31. else if(add1>add2){//在第二组中
    32. return Coin(low+num1,low+num1+num2-1,num2);
    33. }
    34. else{//在第三组中
    35. return Coin(low+num1+num2,high,num3);
    36. }
    37. }
    38. int main()
    39. {
    40. int n;
    41. n=Coin(0,7,8);
    42. cout<
    43. }

     

  • 相关阅读:
    ElasticSearch 分布式搜索引擎
    一、C#冒泡排序算法
    【电源专题】案例:电源芯片规格书大标题写5A那是能输出5A吗?
    基于AD Event日志检测NTDS凭据转储攻击
    【软考:系统集成项目管理】之 项目质量管理
    【ARM 嵌入式 编译系列 11.1 -- GCC __attribute__((aligned(x)))详细介绍】
    湖仓一体电商项目(十四):实时任务执行流程
    Java SPI 机制源码级深度理解
    mac下终端命令提示补全
    当10年程序员是什么体验?存款几位数?
  • 原文地址:https://blog.csdn.net/weixin_53043309/article/details/127452406