认识复杂度和简单排序算法

认识时间复杂度

常数时间的操作

一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。
时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。具体
来说,先要对一个算法流程非常熟悉,然后去写出这个算法流程中,发生了多少常数操作,
进而总结出常数操作数量的表达式。

在表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果为f(N),那
么时间复杂度为O(f(N))。

已选择排排序为例子
      [4, 5, 2, 3, 6, ..... , m] 有n个数
        数据量         比较次数       交换次数
第一次:[0]~[n-1]       n-2           1
第二次:[0]~[n-2]       n-3           1
第三次:[0]~[n-3]       n-4           1
...     ...            ...           1
第n-1次 [0]~[1]        1             1

常数操作为:1+...+(n-3)+(n-2)+(n-1)+ 1+(n-4) +(n-3)+(n-2) + 1 + 1 + 1
最后表达式可表示为:f(N) = aN² + b N + c
去最高阶项,且忽略其系数,时间复杂度为O(N²)

评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行
时间,也就是“常数项时间”。

    public static void prrocess1(){ // 时间复杂度 O(n)
        int N = 1000;
        int a = 0;

        for(int i=0; i<N; i++){
            a = 2+5;
            a = 4*7;
            a = 6*8;
        }
    }

    public static void prrocess2(){ // 时间复杂度 O(n)
        int N = 1000;
        int a = 0;

        for(int i=0; i<N; i++){
            a = 3 | 6;
            a = 3 & 4;
            a = 4 ^ 785;
        }
    }
// prrocess1和prrocess2的时间复杂度一样,实际运行时间决定算法流程的好坏
选择排序、冒泡排序细节的讲解与复杂度分析
    /*
    [7,3,4,3,6,5]
     0,1,2,3,4,5
    
    第一次循环:[0]~[n-1]  排好1个数
    [0]~[1]  [3,7,4,3,6,5]
    [1]~[2]  [3,4,7,3,6,5]
    [2]~[3]  [3,4,3,7,6,5]
    [3]~[4]  [3,4,3,6,7,5]
    [4]~[5]  [3,4,3,6,5,7]
    
    第一次循环:[0]~[n-3]  排好1个数
    第一次循环:[0]~[n-4]  排好1个数
    ....
    
    最后表达式可表示为:f(N) = aN² + b N + c
    去最高阶项,且忽略其系数,时间复杂度为O(N²)
    */

	public static
	 void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		// 额外空间O(1) 包括:i、minUdex、j
		for (int i = 0; i < arr.length - 1; i++) { // i ~ n-1 选最小值
			int minIndex = i; // 最小值
			for (int j = i + 1; j < arr.length; j++) { // i ~ n-1 上找最小值的下标
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			swap(arr, i, minIndex);
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}
时间复杂度O(N^2),额外空间复杂度O(1)
	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int e = arr.length - 1; e > 0; e--) { // 0~e的范围
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}


	//  交换arr的i和j位置上的值  异或预算  不同为1  相同为0
	public static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
插入排序细节的讲解与复杂度分析
	public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		// 0~0 有序的
		// 0~1 想有序
		for (int i = 1; i < arr.length; i++) { // 0~1想做到有序
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}
    // i和j是同一位置会出错
	public static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}

时间复杂度O(N^2),额外空间复杂度O(1)
额外空间复杂度O(1):可理解为开辟有限个空间,例如:int i = 1

算法流程按照最差情况来估计时间复杂度
二分法的详解与扩展

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

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

3)局部最小值问题

异或运算的性质与扩展

1)0^N == N N^N == 0
2)异或运算满足交换律和结合率
3)不用额外变量交换两个数
4)一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数
5)一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两个数加粗样式
异或运算:

1)、0 ^ N = N    N^N =0
2)、满足交换和结合
a^b = b^a     a^b^c = a^(b^c)
3)、集合 ^ 

a:1 0 1 1 0
b:0 0 1 1 1 
----------------------------
^:1 0 0  0 1
&:0 0 1  1 0


分析:
int a=甲;
int b=乙;
a=a^b;  a=甲^乙        b=乙
b=a^b;  b=甲^乙        b=甲^乙^乙=甲^0=甲
a=a^b;  a=甲^乙^甲=0^乙=乙   b=甲 

由异或运算衍生的算法题:
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数?
思路: 一直由异或运算即可

	public static void printOddTimesNum1(int[] arr) {
		int eO = 0;
		for (int cur : arr) {
			eO ^= cur;
		}
		System.out.println(eO);
	}

一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数?
思路:
1、eor = a^b
2、cur & rightOne 只能是a或者b
3、再次^

提取出最右的1的原因:a^b至少有一个二进制位上是一个地方不同,一个为0,一个为1,并以此分类。
假设该位置为21位,a的21位为1,b的21位为0(总共是32位)。因此能够将数组所有数分为2类。 21位为1(有a无b)、21位为0(有b无a)。
最后仅需选择一类进行&,即可得出a或者b

	public static void printOddTimesNum2(int[] arr) {
		int eor = 0;
		for (int curNum : arr) {
			eor ^= curNum;
		}
       // eor = a^b
       // eor ! = 0
       // eor必然有一个位置上是1
		int eorhasOne = 0int rightOne = eor & (~eor + 1);// 提取出最右的1
        /*       
        eor:             1010111100
        ~eor:             0101000011
        ~eor+1:           0101000100
        eor & (~eor + 1)   0000000100
        */
    */
		for (int cur : arr) {
			if ((cur & rightOne) != 0) {
				eorhasOne ^= cur;
			}
		}
		System.out.println(eorhasOne + " " + (eor ^ eorhasOne));
	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值