III.二分算法

目录

A.程序或算法的时间复杂度

B.二分查找 

C.求方程的根

D.寻找指定和的整数对

E.Aggressive cows 


A.程序或算法的时间复杂度


  • 在无序数列中查找某个数(顺序查找) O(n)
  • 平面上有n个点,要求出任意两点之间的距离 O(n2)
  • 插入排序、选择排序、冒泡排序 O(n2)
  • 快速排序 O( n*log(n))
  • 二分查找O(log(n))

B.二分查找 


题目描述

A心里想一个1-1000之间的数,B来猜,可以问问题,A只能回答是或否。怎么猜才能问的问题次数最少?

题目解答

每次缩小猜测范围到上次的范围的一半,只需要10次

  • 写一个函数BinarySeach,在包含size个元素的、从小到大排序的int数组a里查找元素p,如果找到,则返回元素下标,如果找不到,则返回-1。要求复杂度O(log(n))
int BinarySearch(int a[],int size,int p)
{
    int L = 0;//查找区间的左端点
    int R = size - 1;//查找区间的右端点
    while( L<=R )//如果查找区间不为空就继续查找
    {
        int mid = L + (R-L)/2;//取查找区间正中元素的下标
        if( p == a[mid] )
            return mid;
        else if( p > a[mid] )
            L = mid + 1;//设置新的查找区间的左端点
        else
            R = mid - 1;; //设置新的查找区间的右端点
    }
    return -1;
}//复杂度O(log(n))
  •  写一个函数LowerBound,在包含size个元素的、从小到大排序的int数组a里查找比给定整数p小的,下标最大的元素。找到则返回其下标,找不到则返回-1
int LowerBound(int a[],int size,int p)//复杂度O(log(n))
{
    int L = 0;//查找区间的左端点
    int R = size - 1;//查找区间的右端点
    int lastPos = -1;//到目前为止找到的最优解
    while( L<=R )//如果查找区间不为空就继续查找
    {
        int mid = L + (R-L)/2;//取查找区间正中元素的下标
        if(a[mid]>=p)
            R = mid - 1;
        else
        {
            lastPos = mid;
            L = mid + 1;
        }
    }
    return lastPos;
}

 注意

        int mid = (L+R)/2; //取查找区间正中元素的下标

为了防止 (L+R)过大溢出:

        int mid = L+(R-L)/2;

C.求方程的根

题目描述

求下面方程的一个根:f(x) = x3-5x2+10x-80 = 0若求出的根是a,则要求 |f(a)| <= 10-6

题目解答

对f(x)求导,得f'(x)=3x2-10x+10。由一元二次方程求根公式知方程f'(x)= 0 无解,因此f'(x)恒大于0。故f(x)是单调递增的。易知 f(0) < 0且f(100)>0,所以区间[0,100]内必然有且只有一个根。由于f(x)在[0,100]内是单调的,所以可以用二分的办法在区间[0,100]中寻找根。

#include <stdio.h>
#include <math.h>

double EPS = 1e-6;
double f(double x)
{
    return x*x*x - 5*x*x +10*x - 80;
}

int main()
{
    double root, x1 = 0, x2 = 100;
    root = x1 + (x2-x1)/2;
    int triedTimes = 1;
    double y;
    y = f(root);
    while( fabs(y) > EPS)
    {
        if( y > 0 )
            x2 = root;
        else
            x1 = root;
        root = x1 + (x2 - x1)/2;
        y = f(root);
        triedTimes ++;
    }
    printf("%.8f\n",root);
    printf("%d",triedTimes);
    return 0;
}

D.寻找指定和的整数对

题目描述

输入n ( n<= 100,000)个整数,找出其中的两个数,它们之和等于整数m(假定肯定有解)。题中所有整数都能用 int 表示

解法一:

        用两重循环,枚举所有的取数方法,复杂度是O(n2)的。

for(int i = 0;i < n-1;i++)
    {
        for(int j = 0;j < n;j++)
        {
            if( a[i]+b[j] == m)
                break;
        }
    }

 解法二:

  • 将数组排序,复杂度是O(n×log(n))
  • 对数组中的每个元素a[i],在数组中二分查找m-a[i],看能否找到。复杂度log(n),最坏要查找n-2次,所以查找这部分的复杂度也是O(n×log(n))

解法三:(x)

  • 将数组排序,复杂度是O(n×log(n))
  • 查找的时候,设置两个变量i和j,i初值是0,j初值是n-1.看a[i]+a[j],如果大于m,就让j减1,如果小于m,就让i加1,直至a[i]+a[j]=m。
#include <stdio.h>
#include <stdlib.h>

int int_cmp(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;//升序排序
}

int main()
{
    int n;
    scanf("%d",&n);
    int a[100001];
    for(int i=0;i < n;i++)
    {
        scanf("%d",&a[i]);
    }
    qsort(a, n, sizeof(a[0]), int_cmp);
    int m;
    scanf("%d",&m);
    int i = 0, j = n-1;
    if( a[i] + a[j] > m)
        j--;
    else if( a[i] + a[j] < m)
        i++;
    else
        printf("%d + %d == %d",a[i],a[j],m);
    return 0;
}

E.Aggressive cows 

题目描述

农夫 John 建造了一座很长的畜栏,它包括N (2≤N≤100,000)个隔间,这些小隔间的位置为x0,...,xN-1 (0≤xi≤1,000,000,000,均为整数,各不相同).
John的C (2≤C≤N)头牛每头分到一个隔间。牛都希望互相离得远点省得互相打扰。怎样才能使任意两头牛之间的最小距离尽可能的大,这个最大的最小距离是多少呢?

解法一:

先得到排序后的隔间坐标 x0,...,xN-1
从1,000,000,000/C到1依次尝试这个 “最大的最近距离”D,找到的第一个可行的就是答案。

尝试方法:

1)    第1头牛放在x0
2)    若第k头牛放在xi ,则找到xi+1到xN-1中第一个位于[xi+D, 1,000,000,000]中的Xj ,第k+1头牛放在Xj。找不到这样的Xj,则 D=D-1,转 1)再试若所有牛都能放下,则D即答案

解法二:(x)

先得到排序后的隔间坐标 x0,...,xN-1
在[L,R]内用二分法尝试“最大最近距离”D = (L+R)/2 (L,R初值为[1,  1,000,000,000/C]
若D可行,则记住该D,然后在新[L,R]中继续尝试(L= D+1)
若D不可行,则在新[L,R]中继续尝试(R= D-1)

#include <stdio.h>

bool check(int mid,int a[],int n,int m)
{
	int cnt = 1;
	int cur = a[0];
	for(int i =1;i < n;i++)//从第二头牛开始放
    {
        if(a[i]-cur >= mid)
        {
			cnt++;//放下则数量加一
			cur = a[i];//如果放下,则将当前的牛栏作为初始继续往后找可以放的下的
		}
	}
	if(cnt>=m)//在给定的值下能够放下的牛的个数大于m,则说明给的值小了
		return true;
    else
	    return false;
}

int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	int a[100000];
	for(int i =0;i < n;i++)
    {
		scanf("%d",&a[i]);
	}
	sort(a,a+n);
	int left = 0, right = a[n-1];//二分取值
	int mid = left + (right-left)/2;
	while(left<=right)
	{
		if( check(mid,a,n,m) )
            left = mid + 1;
		else
            right = mid - 1;
		mid = left + (right-left)/2;
	}
	printf("%d",right);
}

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是我为您准备的100道Java算法题: 1. 翻转字符串 2. 在数组中查找重复数字 3. 单例模式 4. 冒泡排序 5. 快速排序 6. 选择排序 7. 插入排序 8. 希尔排序 9. 归并排序 10. 桶排序 11. 堆排序 12. 计数排序 13. 二叉树的遍历 14. 二叉树的深度 15. 二叉树的最大深度 16. 二叉树的最小深度 17. 二叉树的最大宽度 18. 二叉树的节点个数 19. 二叉树的叶子节点个数 20. 二叉树的高度 21. 二叉树的镜像 22. 二叉树的中序遍历 23. 二叉树的前序遍历 24. 二叉树的后序遍历 25. 二叉树的层次遍历 26. 二叉树的层次遍历 II 27. 二叉树的锯齿形层次遍历 28. 二叉树的最近公共祖先 29. 二叉树的路径和 30. 二叉树的路径和 II 31. 二叉树的所有路径 32. 二叉树的所有路径 II 33. 二叉树的最长连续序列 34. 二叉树的最深叶子节点的最近公共祖先 35. 二叉树的最大路径和 36. 二叉树的最小深度 II 37. 二叉树的右视图 38. 二分查找 39. 二分查找的变体 40. 二叉搜索树的插入 41. 二叉搜索树的删除 42. 两数之和 43. 三数之和 44. 四数之和 45. 无重复字符的最长子串 46. 最长回文子串 47. 最长公共前缀 48. 字符串中的第一个唯一字符 49. 罗马数字转整数 50. 整数转罗马数字 51. 最大子序和 52. 最长上升子序列 53. 最长公共子序列 54. 最长回文子序列 55. 编辑距离 56. 最长有效括号 57. 最长公共子串 58. 最长连续递增序列 59. 最长连续递减序列 60. 最长连续重复子串 61. 最长重复子数组 62. 最短无序连续子数组 63. 最长的斐波那契子序列的长度 64. 最长等差数列 65. 最长连续子序列 66. 最长湍流子数组 67. 最长子数组的和 68. 最小覆盖子串 69. 最小路径和 70. 最小栈 71. 最大栈 72. 最小栈和最大栈的实现 73. 最小栈和最大栈的查找 74. 最小栈和最大栈的删除 75. 最小栈和最大栈的修改 76. 最小栈和最大栈的插入 77. 最小栈和最大栈的求和 78. 最小栈和最大栈的求差 79. 最小栈和最大栈的求积 80. 最小栈和最大栈的求商 81. 最小栈和最大栈的求余 82. 最小栈和最大栈的排序 83. 最小栈和最大栈的反转 84. 最小栈和最大栈的合并 85. 最小栈和最大栈的交集 86. 最小栈和最大栈的并集 87. 最小栈和最大栈的差集 88. 最小栈和最大栈的对称差 89. 最小栈和最大栈的求最大值 90. 最小栈和最大栈的求最小值 91. 最小栈和最大栈的求中位数 92. 最小栈和最大栈的求平均值 93. 最小栈和最大栈的求方差 94. 最小栈和最大栈的求标准差 95. 最小栈和最大栈的求众数 96. 最小栈和最大栈的求众数 II 97. 最小栈和最大栈的求众数 III 98. 最小栈和最大栈的求众数 IV 99. 最小栈和最大栈的求众数 V 100. 最小栈和最大栈的求众数 VI 希望这些算法题能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值