2021-08-12

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

常数时间的操作

一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。

时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。

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

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

选择排序

时间复杂度0(N^2),额外空间复杂度0(1)

//升序排列
    public static void selectionSort(int arr[]){
        if(arr == null|| arr.length<2){
            return;
        }
        for(int i=0;i<arr.length-1;i++){ // 0 ~ N-1
            int minValueIndex=i;
            for (int j = i+1; j <arr.length ; j++) { // i ~ N-1
                minValueIndex=arr[j]<arr[minValueIndex]?j:minValueIndex;
            }
            swap(arr,i,minValueIndex);
        }
    }
    public static void swap(int []arr,int i,int j){
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

冒泡排序

时间复杂度0(N^2),额外空间复杂度0(1)

public static void bubbleSort(int[] arr){
        if(arr == null || arr.length<2){
            return;
        }
        for(int e=arr.length-1;e>0;e--){
            for (int i = 0; i < e; i++) {
                if(arr[i]>arr[i+1]){
                    swap(arr,i,i+1);
                }
            }
        }
    }
    public static void swap(int []arr,int i,int j){
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

补充知识点:异或运算


0^N==N

N^N==N

异或运算满足交换律结合律
、利用异或运算不用额外变
量交换两个数
例如,如上swap代码可改为如下形式:

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];
        /*int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;*/
    }

但是要注意,若要使用异或运算交换两个数,这两个数指向的内存空间一定不能相同,即在函数swap中,一定要保证i≠j
、一个数组中有一种数出现了奇数次,其他数出现了偶数次,怎么找这一个数

/*解决思路就是将所有数异或一遍
偶数个数的数异或后为0,奇数个异或后剩下原来的数
	3^3^2^2^23^23^7^7^7^34^34
=	(3^3)^(2^2)^(23^23)^(7^7)^7^(34^34)
=	0^0^0^0^7^0
=	7
*/
public static void main(String[] args) {
        int [] arr=new int[]{3,3,2,2,23,23,7,7,7,34,34};
        int num=0;
        for(int i:arr){
            num^=i;
        }
        System.out.println(num);
    }

、一个数组中有两种数出现了奇数次,其它数出现了偶数次,怎么找到这两个数

/*
解决思路:
对于数组arr[1...N],要找到出现奇数次的数,定义为a和b
eor=arr[1]^a[2]^...^arr[N]=a^b
又因a和b一定不相等(题目给定条件),那么eor一定不为0,eor的二进制形式一定存在某一位为1,假设eor二进制形式的第7位为1,那么将数组分为两大阵营:数的二进制形式第7位为1,定义为arr1,数得二进制形式第7位为0,定义为arr2,那么可以知道数a,b分别在两个数组中
则:
onlyOne=arr1[1]^arr[2]^...^arr[m]=a|b
此时,求出其中一个数为onlyOne,另一个数为onlyOne^eor
*/
public static void main(String[] args) {
        int [] arr=new int[]{3,3,2,2,23,23,7,7,7,34,34,666,666,666};
        int eor=0;
        for(int i:arr){
            eor^=i; //a^b
        }
        int rightOne=eor & (~eor +1);//提取出最右边的1
        int onlyOne = 0;
        for(int i:arr){
            if((i&rightOne)==1){
                onlyOne^=i;
            }
        }
        System.out.println(onlyOne+" "+(onlyOne^eor));
    }

二分法

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

//二分查找 --对于升序数组
    public static int binarySearch(int arr[],int target){
        int left=0,right=arr.length-1;
        if(left>right||target>arr[right]||target<arr[left]) return -1
        while(left<=right){
            int mid=left+((right-left)>>1);
            if(arr[mid]==target) {
                return mid;        //找到了
            }else if(arr[mid]<target){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        return -1;                 //没找到
    }

2.在一个有序数组中,查找元素第一次出现的位置

//查找元素第一次出现的位置 --- 升序数组(有重复)
    public static int binarySearch_FirstAppear(int arr[],int tartet){
        int left=0,right=arr.length-1;
        int mid=0;
        while(left<right){
            mid=left+((right-left)>>1);
            if(arr[mid]>=tartet){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        if(arr[left]==tartet) return left; //找到了
        else return -1;//没找到
    }

3.在一个有序数组中,查找元素最后一次出现的位置

//查找元素最后一次出现的位置 --- 升序数组(有重复)
//mid=(left+right+1)/2;---注意
    public static int binarySearch_LeastAppear(int arr[],int target){
        int left=0,right=arr.length-1,mid=0;
        while(left<right){
            mid=(left+right+1)/2;//注意
            if(arr[mid]<=target){
                left=mid;
            }else{
                right=mid-1;
            }
        }
        if(arr[left]==target) return left; //找到了
        else return -1; //没找到
    }

4.局部最小值问题:已知一个无序数组,求该数组中一个局部最小的位置(若有多个局部最小位置,找出一个即可)。

//局部最小
/*
思路:
一个无序数组arr[],一定存在局部最小,设mid为该数组的
中间位置,如果该位置比它的左边元素大,那么局部最小一定
在它的左边存在;如果该位置比它右边元素大,那么局部最小
一定在它的右边存在;如果该位置小于左右两边,那么该位置
是一个局部最小位置。情况分为如下:
1.arr == null || arr.length == 0:数组不存在或数组元素空
2.arr.length==1:只有一个元素,该元素即局部最小
3.arr.length>2:
1)情况一:局部最小在数组第一个位置 arr[0]<arr[1]
2)情况二:局部最小在数组最后一个位置 arr[arr.length-1]<arr[arr.length-2]
3)情况三:局部最小在数组最后一个和第一个的中间某个位置
*/
    public static int localSmallest(int arr[]){
        if(arr==null||arr.length<1) return -1;//数组不存在或没有元素:不存在局部最小
        if(arr.length==1) return 0;//只有一个元素,该元素就是局部最小
        //情况一:局部最小在数组第一个位置
        if(arr[0]<arr[1]){
            return 0;
        }
        //情况二:局部最小在数组最后一个位置
        if(arr[arr.length-1]<arr[arr.length-2]){
            return arr.length-1;
        }
        //情况三:局部最小在数组最后一个和第一个的中间某个位置
        int low=1,high=arr.length-2,mid=0;
        while(low<high){
            mid=low+((high-low)>>1);
            if(arr[mid]>arr[mid-1]){
                high=mid-1;
            }else if(arr[mid]>arr[mid+1]){
                low=mid+1;
            }else{
                return mid;
            }
        }
        return low;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值