二分查找为什么不能用于链表

二分查找不能用于链表的原因:二分查找是一种高效的查找算法,适用于已排序且静态的数组或列表,而链表不支持随机内存访问,即每一个节点的地址不能在O(1)的时间复杂度内获得,因此二分查找不能使用于链表。

一、二分查找不能用于链表的原因

链表的每一个节点的地址不能在O(1)的时间复杂度内获得,二分查找是一种高效的查找算法,适用于已排序且静态的数组或列表。然而,二分查找不能使用于链表,因为链表不支持随机内存访问。如下图,名列前茅行是地址,第二行是数组(顺序表),第三行是链表。

二分查找为什么不能用于链表

可以看到,在数组(线性表)中,a[i]的地址就是 &a[0]+sizeof(int)*i (a+i),这样你对于一次二分查找 [l,r) 区域,通过简单的计算得到中间值 mid = (l+r)/2 对应的值而对于链表,每个节点对应的坐标是不确定的,比如查找 [0,4) ,需要比较mid = (0+4)/2 = 2 的值,然后电脑就开始找,从0开始往后找到1,找到1再往下找,遍历很多次之后才能找到 a[2] 位于0x0010 这个地址,理论而言,我们每次都需要遍历整个查询区域二分之一长度的数据,即 T(n/2) 。再加上二分查找本身的 T(logn) 总的时间复杂度是 O(nlogn) 甚至比直接遍历链表查询更慢。

二、二分查找的原理及实现

二分查找的实现原理非常简单,首先要有一个有序的列表。但是如果没有,则该怎么办?可以使用排序算法进行排序。以升序数列为例,比较一个元素与数列中的中间位置的元素的大小,如果比中间位置的元素大,则继续在后半部分的数列中进行二分查找;如果比中间位置的元素小,则在数列的前半部分进行比较;如果相等,则找到了元素的位置。每次比较的数列长度都会是之前数列的一半,直到找到相等元素的位置或者最终没有找到要找的元素。

我们先来想象一下,如果数列中有 3 个数,则先与第 2 个数进行比较,如果比第 2 个数大,则与第 2 个数右边的数列进行二分查找,这时这个数列就剩下一个数了,直接比较是否相等即可。所以在 3 个数的时候非常多比较两次。同理,在有 4 个数的时候,我们与中间数进行比较,一般中间数是首加末除以 2 算出来的,这时我们算出来的中间数是 (1+4)/2 等于 2,所以我们把要查找的数与第 2 个数比较,若比第 2 个数小,则直接与第 1 个数比较;否则与后面两个数进行二分查找,这时的中间数是 (3+4)/2 等于 3,也就是后半部分的第 1 个数。再接着进行比较,相等则找到相应的元素,小于则没有这个数(因为左边所有的数都已经判断过了),大于则继续向右查找。所以在 4 个数的时候非常多比较 3 次。以此类推,在 5 个数的时候非常多查找 3 次,在 6 个数的时候也是非常多查找 3 次。

实现代码

public class BinarySearch {
    private int[] array;
    /**
     * 递归实现二分查找
     * @param target
     * @return
     */
    public int searchRecursion(int target) {
        if (array != null) {
            return searchRecursion(target, 0, array.length - 1);
        }
        return -1;
    }

    private int searchRecursion(int target, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + (end - start) / 2;
        if (array[mid] == target) {
            return mid;
        } else if (target < array[mid]) {
            return searchRecursion(target, start, mid - 1);
        } else {
            return searchRecursion(target, mid + 1, end);
        }
    }
}

三、链表简介

1、链表概念

链表是一种常见的基础数据结构,结构体指针在这里得到了充分的利用。链表可以动态的进行存储分配,也就是说,链表是一个功能极为强大的数组,他可以在节点中定义多种数据类型,还可以根据需要随意增添,删除,插入节点。链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的。

2、链表的构成

链表中每个节点都分为两部分,一个数据域,一个是指针域。说到这里你应该就明白了,链表就如同车链子一样,head指向名列前茅个元素:名列前茅个元素又指向第二个元素,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。作为有强大功能的链表,对他的操作当然有许多,比如:链表的创建,修改,删除,插入,输出,排序,反序,清空链表的元素,求链表的长度等等。

3、常见的链表

  • 单链表
  • 循环链表
  • 双向链表

4、链表的增删改查

我们在进行数组的插入、删除操作的时候,为了保持内存数据的连续性,需要进行大量的数据搬移工作,所以时间复杂度为 O(n);而在链表中插入或者删除一个数据我们并不需要为了保持内存的连续性而搬移节点,因为链表本身的存储空间也不是连续的,所以在链表中插入和删除一个数据是非常快的。

  • 插入数据: 我们只需要将要插入位置的前一个数据单元的next指针指向插入数据的内存地址,插入数据的next指针指向下一个数据的内存地址;
  • 删除数据: 将要删除数据的前一个数据单元的next指针指向要删除数据的下一个数据单元的内存地址,然后再删除数据。

5、实现代码

public class MyLinkedList<E> {
10     /**
11      * 私有的 Node
12      */
13     private class Node{
14         public E e;
15         public Node next;
16 
17         public Node(E e, Node next){
18             this.e = e;
19             this.next = next;
20         }
21         public Node(E e){
22             this(e, null);
23         }
24         public Node(){
25             this(null, null);
26         }
27     }
28     private Node head;
29     private int size;
30 
31     public MyLinkedList(){
32         head = null;
33         size = 0;
34     }
35     public int getSize(){
36         return this.size;
37     }
38     public boolean isEmpty(){
39         return size == 0;
40     }
41 }

延伸阅读

循环链表简介

  • 单向循环链表 [Circular Linked List] : 由各个内存结构通过一个指针 Next 链接在一起组成,每一个内存结构都存在后继内存结构,内存结构由数据域和 Next 指针域组成。
  • 双向循环链表 [Double Circular Linked List] : 由各个内存结构通过指针 Next 和指针 Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构,内存结构由数据域、Prev 指针域和 Next 指针域组成。

文章标题:二分查找为什么不能用于链表,发布者:Z, ZLW,转载请注明出处:https://worktile.com/kb/p/49385

(1)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Z, ZLWZ, ZLW认证作者
上一篇 2023年4月15日
下一篇 2023年4月15日

相关推荐

  • 如何进行好项目融资管理

    项目融资管理是确保资金有效利用、风险最小化、收益最大化的综合活动,其核心在于策划合适的融资结构、制定严密的财务策略、对融资过程进行周密的管理。进行良好的项目融资管理首先要对融资需求和来源进行准确评估、选择合适的融资工具、构建风险管理机制,并且持续监控项目的财务状况。 在项目融资管理中,策划合适的融资…

    2024年4月10日
    13200
  • Java抽象类是如何产生的,有哪些特点

    Java抽象类是在面向对象编程中,对共享特征和行为的抽象化表达而产生的。这种类无法被实例化,通常用于构架类的继承体系和提供通用功能。主要特点包括:1、包含抽象方法,这些方法没有具体实现,仅定义方法签名;2、可包含具体成员和成员方法,可以被子类继承和使用;3、不能被实例化,只能作为基类;4、当子类继承…

    2023年11月16日
    20000
  • 如何提高项目规范管理质量

    项目规范管理质量的提高关键在于制定明确的项目管理标准、持续跟踪和评估项目进度、确保团队成员间的有效沟通、运用专业的项目管理工具以及进行定期的项目审计和复盘。首要的是制定明确的项目管理标准,它为项目的各个环节设立了清晰的指导原则和执行细则,有助于确保项目按预定的品质和要求进行,减少偏差和错误。 一、制…

    2024年4月10日
    4400
  • oa上线通知

    2023年OA系统上线通告,系统将于4月12日启动运行,本次升级涉及到新功能增加1、用户体验优化、2、数据安全强化、3、跨平台兼容性提升。突出之处在于数据安全强化环节,通过引入先进的加密技术确保用户信息和公司数据的安全性与隐私。从倡导最新的授权机制到实施端到端的数据加密,系统在数据保护方面达到了行业…

    2024年1月16日
    23500
  • kotlin中apply和with的区别和用法是什么

    Kotlin 中有许多很棒的功能,我们可以利用所有这些功能在 Kotlin 中编写更好的应用程序。在所有这些特性中,apply和with是重要的特性。根据定义,apply 接受一个函数,并将其范围设置为调用 apply 的对象的范围。这意味着不需要对对象的显式引用。 一、什么时候用“apply”,什…

    2023年5月13日
    65500
  • AI 驱动的超分辨技术落地实践

    近年来,随着深度学习技术的快速发展,基于AI的超分辨技术在图像恢复和图像增强领域呈现出广阔的应用前景,受到了学术界和工业界的关注和重视。但是,在RTC视频领域中,很多AI算法并不能满足实际场景下的应用需求。本文将着眼于AI技术从研究到部署的落地问题,分享超分辨技术在RTC领域落地应用所面临的机遇与挑…

    2022年3月17日
    96400
  • 城投公司oa

    标题:城投公司运营分析 城投公司负责资本运作与基础设施建设,是地方经济发展的重要力量。本文讨论城投公司运营的三个核心方面:1、资金筹集与管理、2、项目审查与投资、3、风险控制与优化。详细分析中将突出资金筹集与管理的重要性,并述及城投公司如何通过多渠道获取资金来满足日趋复杂的市场需求。资金来源的多样性…

    2024年1月16日
    23700
  • 考试用的无储存记忆,编程功能的计算器是哪类计算器

    考试用的无储存记忆,编程功能的计算器是是科学型计算器。科学型计算器是电子计算器的一种,可进行乘方、开方、指数、对数、三角函数、统计等方面的运算,又称函数计算器。 科学型计算器是电子计算器的一种,可进行乘方、开方、指数、对数、三角函数、统计等方面的运算,又称函数计算器。 函数计算器(也称“科学计算器”…

    2023年2月13日
    2.7K00
  • 低代码软件有哪些功能?

    近年来,低代码平台在企业和开发人员中获得了极大的欢迎。低代码平台能够以最少的编码实现快速应用程序开发,使企业更容易快速部署应用程序。然而,并不是所有的低码平台都是一样的。为了确保您投资于正确的平台,了解低代码型平台的必要功能是非常重要的。

    2023年8月2日
    34600
  • 收尾项目设备如何管理

    收尾项目设备的管理应采取系统化、有序化的策略,以确保设备在结项时可以迅速、高效地得到处理。关键策略包括:设备盘点、维护和保养、价值评估、合同审查、设备处置与转移、文档整理和知识传承。在这些策略中,设备盘点 是一个至关重要的步骤,它涉及到对现有项目设备进行全面的清点和记录,包括设备的种类、数量、状态、…

    2024年4月10日
    5000

发表回复

登录后才能评论
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部