【数据结构的魅力】001.认识复杂度&二分法&异或运算

本文探讨了常数时间操作的概念,包括算术运算、位运算和数组寻址等,并介绍了如何确定算法的时间复杂度。通过选择排序、冒泡排序和插入排序的例子,阐述了算法流程的常数项和时间复杂度的计算方法。同时,文章详细解析了二分法的应用,如在有序数组中查找特定元素及其位置。此外,还介绍了异或运算的特性及其在数组中找出现奇数次数字的实用技巧。通过对这些基础知识的掌握,可以更好地理解和优化算法的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常数时间操作

如果一个操作的执行时间不以具体样本量为转移,每次执行时间都是固定时间。称这样的操作为常数时间的操作。

  • 常见的算术运算(+、-、*、/、%等)
  • 常见的位运算(>>、>>>、<<、|、&、^等)
  • 赋值、比较、自增、自减操作等
  • 数组寻址操作

如何确定算法流程的总操作数量与样本数量之间的表达式关系?

1,想象该算法流程所处理的数据状况,要按照最差情况来。

2,把整个流程彻底拆分为一个个基本动作,保证每个动作都是常数时间的操作。

3,如果数据量为N,看看基本动作的数量和N是什么关系。

如何确定算法流程的时间复杂度?

当完成了表达式的建立,只要把最高阶项留下即可。低阶项都去掉,高阶项的系数也去掉。

记为:O(忽略掉系数的高阶项)

三个例子

选择排序

冒泡排序

插入排序

评估算法优劣的核心指标

时间复杂度的意义

额外空间复杂度

算法流程的常数项

认识二分法

1)在一个有序数组中,找某个数是否存在

2)在一个有序数组中,找>=某个数最左侧的位置

3)同理2)

4)找局部最小

认识异或运算

例如:

​ 6 ^ 7

​ 6的二进制:110

​ 7的二进制:111

​ 110

^ 111 = 001

异或运算的性质

1)不使用额外变量交换ab

public class ExchangeAB {

    public static void main(String[] args) {
        int a = 1;
        int b= 2;

        a = a ^ b;
        //a为a^b,b为b
        b = a ^ b;
        //b此时为a,a为a^b
        a = a ^ b;
        //a为b,b为a

        System.out.println(a+","+b);
    }
}

2)一个数组中有一种数出现了奇数次,其他数出现了偶数次,怎么找到并打印这种数

public class FindAOdd {
    //寻找那个奇数
    public static void findThisNum(int[] arr) {
        int eor = 0;

        for (int i = 0; i < arr.length; i++) {
            eor = eor ^ arr[i];
        }

        System.out.println(eor);
    }

    public static void main(String[] args) {
        /*
            4个1
                3个2
            4个3
            2个4
         */
        int[] arr = {1, 4, 2, 3, 1, 2, 2, 1, 3, 3, 1, 3, 4};
        findThisNum(arr);//2
    }
}

3)怎么把一个int类型的数,提取出最右侧的1

例如:

​ n = 0…011010001000…0

经过某种变换

​ n = 0…000000001000…0

思路:n&((~n)+1)

有什么用呢???请看4)

4)一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两种数

思路:

  1. 用一个变量eor异或数组中所有的数,最后eor一定为a^b
  2. 由已知条件a!=b,说明eor一定在某一位上有1。此时数组中的所有数被分为两类(假设第x位为0)
    • ​ 第x位为0但总个数为偶数的数、a(假设a第x位为0)
    • ​ 第x位为1但总个数为偶数的数、b
  3. 用另一个变量eor‘ 来异或某一类(假设为第x位为0的那一组),那些个数为偶数的数仍不会干扰,最后的eor’ 一定是a,另一组亦然。
public class FindDoubleOdd {

    public static void findDoubleOdd(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor = eor ^ arr[i];
            //此时eor为a^b
        }
        int rightOne = eor & (~eor + 1);//提取最右一位为1的数
        int seor = 0;
        for (int i = 0; i < arr.length; i++) {
            //(arr[i] & rightOne) != 0 说明i的x位上和rightOne一样为1
            if ((arr[i] & rightOne) != 0) {
                seor = seor ^ arr[i];
            }
        }
        System.out.println(seor + " " + (eor ^ seor));
    }

    public static void main(String[] args) {
        /*
            3个1
            2个2
            2个3
            3个4
         */
        int[] arr = {1, 1, 1, 2, 2, 3, 3, 4, 4, 4};
        findDoubleOdd(arr);//1,4
    }
}

5)数出一个数的二进制有几位是1

  1. 先找出这个数二进制最右侧的1
  2. 抹掉这位的1
  3. 周而复始直到这个数为0
  4. 统计抹了几次即为有多少位为1
public class FindBit1Counts {

    public static int findBit1Counts(int i){
        int count = 0;

        while (i !=0){
            int rightOne = i&((~i)+1);
            count++;
            i ^= rightOne;
        }
        return count;
    }

    public static void main(String[] args) {
        int i = 6;//0110  2个1
        System.out.println(findBit1Counts(i));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值