初阶算法(3):二分法的讲解与实现(C语言),以及二分不止光在有序数组中的应用

简介: 初阶算法(3):二分法的讲解与实现(C语言),以及二分不止光在有序数组中的应用

前言

      哇,转眼就到了第三章,在前两章内(如果想看前两章的内容,请点击系列文章目录的链接),我们主要讲解了时间复杂度为O(N^2)的排序算法(冒泡排序,选择排序,插入排序),不知道大家看的如何?


      在这一章,我们主要讲述二分法的使用,讲述有关二分法的三道编程题来打破大家对二分法只能用在有序数组的思想。


一、二分法的讲解与实现(C语言)

1.1 为什么在有序数组中用二分法查找?

      一般地,我们在一个有序数组中查找一个数字,如果我们一一查找是要根据数据量的,有时会很快,有时会很慢!这就比较麻烦,所以我们不能用一一查找。


      为什么说有时很快,有时很慢呢?假如,我们现在有一个有序数组(1,2,3,4,5,6,7,8,9,10,11,12,13),如果你要查找1的话, 一一查找只需遍历一位即可;但如果你要查找13的话,则需要将整个数组遍历完。


      此时,这个要查找的数组是一个有序数组,我们就可以利用有序数组的特性去使用二分查找去寻找我们所要查找的数字。


1.2 二分查找的讲解:

      在上面的引子中说在一个有序数组中使用二分查找,那么二分查找是如何实现的呢?


二分查找的基本思路是:先找到中间的数与要查找数进行比较,然后分情况:


      1)如果比要查找数小,那么就舍弃左边一半的数,在右边一半的数再找到中间的数与要查找数进行比较,重复此过程;


      2)如果比要查找数大,那么就舍弃右边一半的数,在左边一半的数再找到中间的数与要查找数进行比较,重复此过程;


      最后,直到查找到最后一个数。


      接下来,进行估算二分查找的时间复杂度,用最坏的数据进行计算,因为一次代码流程要舍弃一半的数,所以时间复杂度为O(logN)。


1.3 二分法的代码实现:

      经过上面的详细分析后,小编觉得各位看官已经充分了解了二分法的思路了,那么接下来,和小编进行二分法的实现吧!


实现二分法的代码如下:

    int left = 0;
    int right = n - 1;
    while (left <= (right - 1)){     //判断条件是左边指向的位置大于右边指向的位置
        int mid = (left + right) / 2;//中点的位置要随时更新,以便求出最新的中点坐标
        if(k > arr[n - 1])      //如果要查找的数大于数组的最后一个数时,则不需要继续查找
            break;
        if (arr[mid] == k){
            right = mid;
            if(arr[mid - 1] < arr[mid])  //与中间数进行比较
                ans = mid;
        }
        if(arr[mid] > k)     //随时更新中点坐标
            right = mid;  
        if(arr[mid] < k)
            left = mid;
    }

二、二分法的应用(不止在有序数组中)

2.1 常规用法

      在上面我们已经进行过在一个有序数组中查找某一个数,接下来,我们将写出一个变式:查找某个位置。


题目:你需要输入一个n,一个数k,然后输入一个长度为n个大小的数组arr,然后你需要在arr上找满足>=K的最左位置,并且输出这个位置,如果不存在这个位置就输出-1。


     看到这道题面,小编我的第一个反应是将这个有序数组进行遍历, 只要找到第一个数字等于k,则是我们所要查找的位置,那么这种方法就和上面小编写的第一道体题的思想一样,就是有时很慢。有时很快。


      这篇文章中,我们学习了二分法,就要利用二分法进行解题。基本思路是:先找到中间的数与要查找数进行比较,然后分情况:


      1)如果比要查找数小,>=K的最左位置应该在右边,那么就舍弃左边一半的数,在右边一半的数再找到中间的数与要查找数进行比较,重复此过程;


      2)如果比要查找数等于或大于,>=K的最左位置应该在右边,那么就舍弃右边一半的数,如果等于k的话进行标记,在左边一半的数再找到中间的数与要查找数进行比较,重复此过程;最后将整个数组进行遍历,找到第一个标记的数字。


      最后,直到查找到最后>=K一个数。


实现题目的代码如下:

int main()
{
  int n = 0, k = 0;
  scanf("%d %d", &n, &k);
  int a[1000000];
  for (int i = 0; i < n; i++)
    scanf("%d", &a[i]);
  int left = 0;
  int right = n - 1;
  int mid = 0;
  int reslut = -1;    //因为如果找不到,就输出-1
  while (left <= right){
    mid = (left + right) / 2;
    if (a[mid] >= k){
      reslut = mid;
      right = mid - 1;
    }
    else
      left = mid + 1;
  }
  printf("%d\n", reslut);
  return 0;
}

2.2 非常规做法(在无需数组中进行)

      二分法真的只能用在有序数组中吗?小编在了解完这个题目后,也是大吃一惊!答案是可以的!


      接下来,跟随小编的步伐进行学习吧!看这道题目:局部最小值问题。


局部最小的概念:1)数组中0位置的数比1位置的数要小;


                            2)数组中N-1位置的数比N-2位置的数要小;


                            3)数组中第i位置的数要比两边位置的数都要小。


题目: 给定无序数组arr,已知arr中任意两个相邻的数都不相等,只需要返回arr中任意一个局部最小出现的位置即可,如果不存在这个位置就输出-1。

      小编进行分析可得:


      先判断0位置的数和1位置的数,再判断N-1位置的数和N-2位置的数。如果这两种情况都不成立,那么一定会出现一开始是下降的,在最后是上扬的,又因为数组中每两个数都不相同,所以在1到N-2中一定有局部最小。


      接下来,我们就可以利用二分进行舍弃数字。找到中间位置的数与两边位置的数进行比较,如果都小于,则返回中间位置的数;如果有一边不小于,则舍弃一半,在另一半继续寻找。


实现题目的代码如下:

int main() {
    int n = 0;
    scanf("%d", &n);
    int a[100000];
    for (int i = 0; i < n; i++) {
        scanf("%d", a + i);
    }
    int left = 0;
    int right = n - 1;
    int mid = -1;
    if (n == 1 || a[0] < a[1]) {
        printf("%d\n", 0);
        return 0;
    }
    else if (a[n - 1] < a[n - 2]) {
        printf("%d\n", n - 1);
        return 0;
    } else {
        while (left <= right) {
            mid = (left + right) / 2;
            if (a[mid] > a[mid - 1]) {
                right = mid - 1;
            }
            else if (a[mid] > a[mid + 1]) {
                left = mid + 1;
            } else {
                //flag = 1;
                printf("%d\n", mid);
                return 0;
            }
        }
    }
    return 0;
}

总结

      以上就是今天要讲的内容,本文介绍了二分法及其在无序数组中的应用,过几天小编会进行编写关于二分法的一些习题,希望大家看完以后,进行点评,谢谢大家!

相关文章
|
4天前
|
存储 机器学习/深度学习 算法
R语言贝叶斯Metropolis-Hastings采样 MCMC算法理解和应用可视化案例
R语言贝叶斯Metropolis-Hastings采样 MCMC算法理解和应用可视化案例
|
4天前
|
机器学习/深度学习 算法 数据挖掘
【C 言专栏】C 语言与机器学习的应用
【5月更文挑战第6天】C语言在机器学习中扮演关键角色,以其高效性、灵活性和可移植性实现底层算法、嵌入式系统和高性能计算。在神经网络、决策树和聚类算法等领域的实现中不可或缺。C语言被用于TensorFlow和OpenCV等知名库的底层,常与C++、Python结合使用。尽管面临开发难度和适应新算法的挑战,但C语言在机器学习领域的价值和潜力将持续展现,为科技进步贡献力量。
【C 言专栏】C 语言与机器学习的应用
|
4天前
|
数据采集 机器学习/深度学习 算法
数据分享|WEKA关联规则挖掘Apriori算法在学生就业数据中的应用
数据分享|WEKA关联规则挖掘Apriori算法在学生就业数据中的应用
|
6天前
|
存储 缓存 算法
【C 言专栏】C 语言中的数据结构应用
【5月更文挑战第4天】本文探讨了C语言中的核心数据结构,包括数组、链表(单链表和双链表)、栈、队列、二叉树(如二叉搜索树和二叉堆)以及图结构。这些数据结构在程序设计中扮演着关键角色,如数组的快速访问、链表的动态管理、栈和队列的处理流程控制、树和图的复杂关系表示。理解并选择适当的数据结构可优化程序性能,而内存管理和算法优化则进一步提升效率。通过案例分析和展望未来发展趋势,本文旨在帮助读者深化对C语言数据结构的理解和应用。
【C 言专栏】C 语言中的数据结构应用
|
8天前
|
机器学习/深度学习 自然语言处理 算法
机器学习算法原理与应用:深入探索与实战
【5月更文挑战第2天】本文深入探讨机器学习算法原理,包括监督学习(如线性回归、SVM、神经网络)、非监督学习(聚类、PCA)和强化学习。通过案例展示了机器学习在图像识别(CNN)、自然语言处理(RNN/LSTM)和推荐系统(协同过滤)的应用。随着技术发展,机器学习正广泛影响各领域,但也带来隐私和算法偏见问题,需关注解决。
|
9天前
|
机器学习/深度学习 算法 C语言
【C言专栏】递归算法在 C 语言中的应用
【4月更文挑战第30天】本文介绍了递归算法在C语言中的应用,包括基本概念(通过调用自身解决子问题)、特点(调用自身、终止条件、栈空间)和实现步骤(定义递归函数、分解问题、设置终止条件、组合解)。文中通过阶乘计算和斐波那契数列两个案例展示了递归的使用,强调了递归可能导致的栈溢出问题及优化需求。学习递归有助于理解和应用“分而治之”策略。
|
10天前
|
存储 算法 程序员
【C言专栏】C 语言结构体的应用与实践
【4月更文挑战第30天】C语言中的结构体是自定义数据类型的关键,它组合不同类型的數據以创建新类型,尤其适合处理复杂对象如学生信息。通过定义结构体如`struct Student`,包含名字、学号和成绩,可以方便地实例化和访问成员。结构体在链表实现、函数参数传递和数组中都有广泛应用,如表示链表节点和处理批量数据。理解并熟练运用结构体对于C语言编程至关重要,能提升代码效率和可读性。
|
10天前
|
机器学习/深度学习 数据可视化 算法
【Python机器学习专栏】t-SNE算法在数据可视化中的应用
【4月更文挑战第30天】t-SNE算法是用于高维数据可视化的非线性降维技术,通过最小化Kullback-Leibler散度在低维空间保持数据点间关系。其特点包括:高维到二维/三维映射、保留局部结构、无需预定义簇数量,但计算成本高。Python中可使用`scikit-learn`的`TSNE`类实现,结合`matplotlib`进行可视化。尽管计算昂贵,t-SNE在揭示复杂数据集结构上极具价值。
|
10天前
|
机器学习/深度学习 算法 数据挖掘
【Python机器学习专栏】层次聚类算法的原理与应用
【4月更文挑战第30天】层次聚类是数据挖掘中的聚类技术,无需预设簇数量,能生成数据的层次结构。分为凝聚(自下而上)和分裂(自上而下)两类,常用凝聚层次聚类有最短/最长距离、群集平均和Ward方法。优点是自动确定簇数、提供层次结构,适合小到中型数据集;缺点是计算成本高、过程不可逆且对异常值敏感。在Python中可使用`scipy.cluster.hierarchy`进行实现。尽管有局限,层次聚类仍是各领域强大的分析工具。
|
10天前
|
机器学习/深度学习 算法 前端开发
【Python机器学习专栏】集成学习算法的原理与应用
【4月更文挑战第30天】集成学习通过组合多个基学习器提升预测准确性,广泛应用于分类、回归等问题。主要步骤包括生成基学习器、训练和结合预测结果。算法类型有Bagging(如随机森林)、Boosting(如AdaBoost)和Stacking。Python中可使用scikit-learn实现,如示例代码展示的随机森林分类。集成学习能降低模型方差,缓解过拟合,提高预测性能。