【算法学习】减治 · 分治 · 变治(二)

简介: 【算法学习】减治 · 分治 · 变治

2.减去一个常数因子(decrease by a constant factor)

 

减去常数因子实际上就是把规模除以一个常数。(在多数情况下,这个常数因子是二)

微信图片_20220422145135.png

继续以计算f(n)=a^n为栗:

微信图片_20220422145137.jpg这里的时间复杂度为O (log n),就比蛮力法优化多了。


对分查找,假币问题都可以用这种思想。我们以假币问题为例:

有n枚银币,其中有1枚是假的,假币重量偏重、偏轻未知,输入银币的重量,求假币的位置。


我们先取出一枚银币作为对照,再将银币分为两半称重,每次称重对比,再从有问题的一堆中继续称重查找。这样,每次我们都减去了常量因子2。

当然,银币总数是奇偶时需要不同的处理方式,在银币数量为偶数时,由于不知道假币偏轻或偏重,我们需要选择一个compare进行对照。

 

同时,我们还可以把银币分为三堆考虑,进一步优化时间复杂度。(作为代价,代码要考虑总数mode3余1、2、0的情况,同时要比较三堆,会稍稍复杂一些)

 

Codding time:


//三分法求八枚银币问题
#include <iostream>
using namespace std;
int coin[105],num;
int sum(int left, int right)
{
    int sum = 0;
    for (int i = left; i <= right; i++)
        sum += coin[i];
    return sum;
}
int findfake(int left, int right)
{
  int n=right-left+1;
    int p1=(right+left)/3;
    int p2=p1*2;
    int sign = n % 3;
    if (sign == 0)
    {
        int fake = 0;
        int A, B, C;
        A = sum(left,p1);
        B = sum(p1+1,p2);
        C = sum(p2+1,right);
        if (n == 3)
        {
            if (A == B)
            {
                fake = right;
            }
            else if (A == C)
            {
                fake = right - 1;
            }
            else
            {
                fake = left;
            }
            return fake;
        }
        else
        {
            if (A == B)
            {
                fake = findfake(p2+1,right);
                return fake;
            }
            else if (A == C)
            {
                fake = findfake(p1+1,p2);
                return fake;
            }
            else
            {
                fake = findfake(left,p1);
                return fake;
            }
        }
    }
    if (sign == 1)
    {
        int fake = right;
        int A, B, C;
        A = sum(left,p1);
        B = sum(p1+1,p2);
        C = sum(p2+1,right-1);
        if (n == 1)
        {
            return fake;
        }
        else
        {
            if (A == B)
            {
                if (A == C)
                    return fake;
                if (A !=C)
                {
                    fake = findfake(p2+1,right-1);
                    return fake;
                }
            }
            else if (A == C)
            {
                fake = findfake(p1+1,p2);
                return fake;
            }
            else
            {
                fake = findfake(left,p1);
                return fake;
            }
        }
    }
    if (sign == 2)
    {
        int fake = 0;
        int x1 = right - 1;
        int x2=right;
        int A, B, C;
        A = sum(left,p1);
        B = sum(p1+1,p2);
        C = sum(p2+1,right-2);
        if (n == 2)
        {
            int campare; //参照物
            if (right != num)
                campare = coin[num];
            else
                campare = coin[1];
            if (coin[x2] == campare)
                fake = x1;
            else
                fake = x2;
            return fake;
        }
        else
        {
            if (A == B && B == C)
            {
                int campare = coin[0];
                if (coin[x1] == campare)
                    fake = x2;
                else
                    fake = x1;
                return fake;
            }
            else if ((A == B) && (A!=C))
            {
                fake=findfake(p2+1,x1-1);
                return fake;
            }
            else if (A == C)
            {
                fake=findfake(p1+1,p2);
                return fake;
            }
            else
            {
                fake = findfake(left,p1);
                return fake;
            }
        }
    }
    return 0;
}
int main()
{
  cin >> num;
  for (int i = 1; i <= num; i++)
    cin >> coin[i];
    int fake=findfake(1,num);
  cout << fake;
    return 0;
}

微信图片_20220422145251.png

 

3.减去的规模是可变的(variable size decrease)

 

字面理解,就是指每次减去的规模的模式或数值是不同的

微信图片_20220422145254.png

这里我们拿欧几里德算法为栗子。学过数论的盆友们知道(数学害人不浅啊),欧几里得算法是一种求最大公约数的较为简便的方法,也叫辗转相除法。用一个简单的式子来表示:

 

gcd(a,b) = gcd(b,a mod b) (这里a>b)

(题外话,我记得数论里最大公约数是不需要gcd的。。。)

 

可以大致想象到,这里每次减去的规模是完全未知、可变的。

 

我们再举一个栗子:二叉查找树。

 

百度百科告诉我们,二叉查找树是一颗这样的树:

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

(4)没有键值相等的结点。

 

根据树的性质,我们可以发现对二叉查找树的查找并不困难,只要从顶点开始不断比较就行了。但二叉查找树不一定就是平衡二叉树,如果只有一个分支,那减去的规模就会变化。(为什么我感觉解释的好扯啊。。。)

 

查找的代码并不困难,我们这里只给出实现插入和查找两种功能的代码。

(就当练习数据结构了= =)

 

Coding time:


//二叉排序树 
#include <iostream>
using namespace std;
typedef class BiTNode  //定义结点类,指针类型名 
{
public:
  int data;
  class BiTNode *leftchild, *rightchild;
}*BiTree;
// BST查找,查找成功,则p指向该元素节点,返回true
bool searchBST(BiTree T,int key,BiTree f,BiTree &p)
{
  if (!T)
  {
    p = f;
    return false;
  }
  else if (key == T->data)
  {
    p = T;
    return true;
  }
  else if (key < T->data)
    return searchBST(T->leftchild, key, T, p);
  else
    return searchBST(T->rightchild, key, T, p);
}
//BST插入,用于建立树 
bool insertBST(BiTree &T, int key)
{
  BiTree p, s;
  //因为在searchBST的过程中有进行比较,所以search完T就指向该插入位置的父结点 
  if (!searchBST(T, key,NULL, p))
  {
    s = new BiTNode;
    s->data = key;
    s->leftchild = s->rightchild = NULL;
    if (!p)  T = s;      //p是空的说明树是空的,则插入在根节点
    else if(key < p->data) p->leftchild = s;
    else     p->rightchild = s;
    return true;
  }
  else
    return false;
}
int main()
{
    //二叉树的创建
    int n;
    cout<<"输入点个数"<<endl;
    cin>>n;
    BiTree T = NULL;
    cout<<"输入节点"<<endl;
    for (int i = 0; i < n; i++)
    {
      int a;
      cin>>a;
      insertBST(T, a);
    }
    //二叉树的查找
    BiTree p=NULL;
    BiTree f=NULL;
    cout<<"接下来进行二叉排序树的查找,请输入值key"<<endl;
    int key;
    cin>>key;
    if (searchBST(T, key, f, p))
      cout<<"在二叉排序树中"<<endl;
    else
      cout<<"不在二叉排序树中"<<endl;
        return 0;
}

微信图片_20220422145256.png

emmm,这图好像看不出什么。


编写的过程中有一点小插曲,我看错了树的定义,所以一开始的版本出现了错误(┬_┬),可以放出来给无聊的人看看,找找错。(说明看对题目真的很重要!!!)

 

wrong code:


//二叉查找树的建立(数组实现) 
#include <iostream>
using namespace std; 
int size=0;  //size为树的规模 
int tree[1005]; 
const int INF=0;
void addElement(int element)
{
    int p = 1; //指标p 
    if(size == 0)
    {
        tree[p] = element;
        size++;
        return;
    }
    while(true)
  {
        if(element<tree[p])
        {
            int p1 = 2 * p  ;   //p1指向p的左儿子的位置   
            if(tree[p1] == INF)
      {
        tree[p1] = element;
        size++;
        return;
      }
            else 
          p = p1;
        }        
        else
        {
            int p2 = 2 * p + 1;  //p2指向p的右儿子的位置 
            if(tree[p2] == INF)
      {
              tree[p2] = element;
              size++;
        return;
      }
            else   
          p = p2;
        }        
    }
} 
int main()
{
  int i,j=1;
  for (i=1;i<=size;i++)
      tree[i]=INF;
  int temp;
  while(cin>>temp)
  {
    addElement(temp);
  }
  for (i=1;j<=size;i++)
  {
    if(tree[i]!=INF)  
    {
      cout<<tree[i]<<"   ";
      j++;
    }
  }
  return 0;
}


相关文章
|
5天前
|
算法 JavaScript 前端开发
第一个算法项目 | JS实现并查集迷宫算法Demo学习
本文是关于使用JavaScript实现并查集迷宫算法的中国象棋demo的学习记录,包括项目运行方法、知识点梳理、代码赏析以及相关CSS样式表文件的介绍。
第一个算法项目 | JS实现并查集迷宫算法Demo学习
|
9天前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
15 2
|
2月前
|
机器学习/深度学习 人工智能 资源调度
【博士每天一篇文献-算法】连续学习算法之HAT: Overcoming catastrophic forgetting with hard attention to the task
本文介绍了一种名为Hard Attention to the Task (HAT)的连续学习算法,通过学习几乎二值的注意力向量来克服灾难性遗忘问题,同时不影响当前任务的学习,并通过实验验证了其在减少遗忘方面的有效性。
49 12
|
2月前
|
算法 Java
掌握算法学习之字符串经典用法
文章总结了字符串在算法领域的经典用法,特别是通过双指针法来实现字符串的反转操作,并提供了LeetCode上相关题目的Java代码实现,强调了掌握这些技巧对于提升算法思维的重要性。
|
2月前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks
本文提出了一种基于任务条件超网络(Hypernetworks)的持续学习模型,通过超网络生成目标网络权重并结合正则化技术减少灾难性遗忘,实现有效的任务顺序学习与长期记忆保持。
34 4
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之RWalk:Riemannian Walk for Incremental Learning Understanding
RWalk算法是一种增量学习框架,通过结合EWC++和修改版的Path Integral算法,并采用不同的采样策略存储先前任务的代表性子集,以量化和平衡遗忘和固执,实现在学习新任务的同时保留旧任务的知识。
74 3
|
2月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
1天前
|
传感器 算法 C语言
基于无线传感器网络的节点分簇算法matlab仿真
该程序对传感器网络进行分簇,考虑节点能量状态、拓扑位置及孤立节点等因素。相较于LEACH算法,本程序评估网络持续时间、节点死亡趋势及能量消耗。使用MATLAB 2022a版本运行,展示了节点能量管理优化及网络生命周期延长的效果。通过簇头管理和数据融合,实现了能量高效和网络可扩展性。
|
28天前
|
算法 BI Serverless
基于鱼群算法的散热片形状优化matlab仿真
本研究利用浴盆曲线模拟空隙外形,并通过鱼群算法(FSA)优化浴盆曲线参数,以获得最佳孔隙度值及对应的R值。FSA通过模拟鱼群的聚群、避障和觅食行为,实现高效全局搜索。具体步骤包括初始化鱼群、计算适应度值、更新位置及判断终止条件。最终确定散热片的最佳形状参数。仿真结果显示该方法能显著提高优化效率。相关代码使用MATLAB 2022a实现。
下一篇
无影云桌面