算法优劣的核心指标
时间复杂度(流程决定)
额外空间复杂度(流程决定)
常数项时间(实现细节决定,常忽略)
常数时间操作
执行时间固定的操作
常见的算数运算(+ - * / %等)
常见位运算(>>带符号右移 >>>不带符号右移全补0 << | & ^等)
赋值 比较 自增 自减
数组寻址 (Linked List不是,需要遍历数数)
时间复杂度
选择排序O(N^2)
package Demo01; import java.util.Arrays; //选择排序 public class Code01_SelectionSort { 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找一次放第一个 1——n-1找一次放第一个... int minIndex=i;//假设最小值在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 temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } public static void main(String[] args) { int arr[]={6,2,5,7,4,3,8}; SelectionSort(arr); System.out.println(Arrays.toString(arr)); } }
冒泡排序 O(N^2)
package Demo01; import java.util.Arrays; //冒泡排序 public class Code02_BubbleSort { public static void SelectionSort(int[]arr) { if (arr == null || arr.length < 2) { return; } for (int i=0;i<arr.length-1;i++){ for (int j = 0; j < arr.length-i-1 ; j++) { if (arr[j]>arr[j+1]){ swap(arr,j,j+1); } } } } public static void swap(int arr[],int i,int j){ int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } public static void main(String[] args) { int arr[]={6,2,5,7,4,3,8}; SelectionSort(arr); System.out.println(Arrays.toString(arr)); } }
插入排序(最差情况) O(N^2)
package Demo01; import java.util.Arrays; //插入排序 public class Code03_InsertSort { public static void SelectionSort(int[]arr) { if (arr == null || arr.length < 2) { return; } for (int i=1;i<arr.length;i++){ for (int j =i-1;j>=0&&arr[j]>arr[j+1];j--) { swap(arr,j,j+1); } } } public static void swap(int arr[],int i,int j){ int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } public static void main(String[] args) { int arr[]={6,2,5,7,4,3,8}; SelectionSort(arr); System.out.println(Arrays.toString(arr)); } }
二分法相关(log2N)
package Demo01; //二分法,在有序数组中找某个数是否存在 public class Code04_BSExit { public static boolean exist(int[] sortedArr,int num){ if (sortedArr==null||sortedArr.length==0){ return false; } int L=0; int R=sortedArr.length-1; int mid=0; while (L<R){ mid=L+((R-L)>>1);//mid=(L+R)/2 位运算比除运算快 if (sortedArr[mid]==num){ return true; }else if (sortedArr[mid]>num){ R=mid-1; }else { L=mid+1; } } return sortedArr[L]==num;//返回最边界的值 } public static void main(String[] args) { int[]arr={1,2,3,4,5,6,7}; System.out.println(exist(arr,8)); } }
package Demo01; //二分法,在有序数组中找>=某个数最左侧位置 public class Code05_BSNearLeft { public static int NearestIndex(int []arr,int value){ int L=0; int R=arr.length-1; int index=-1; while (L<=R){ int mid=L+((R-L)>>1); if (arr[mid]<=value){//<=某个数 index=mid; L=mid+1;//最右侧位置 } else { R=mid-1; } } return index; } public static void main(String[] args) { int[]arr={9,8,6,4,1}; System.out.println(NearestIndex(arr,6)); } }
package Demo01; //局部最小问题 public class Code06_BSAwesome { public static int getLessIndex(int[] arr){ if (arr==null||arr.length==0){ return -1; } if (arr.length==1||arr[0]<arr[1]){ return 0;//左侧局部最小 } if (arr[arr.length-1]<arr[arr.length-2]){ return arr.length-1;//右侧局部最小 } int left=1; int right= arr.length-2; int mid=0; while (left<right){ mid=(left+right)/2; if (arr[mid]>arr[mid-1]){ right=mid-1; }else if (arr[mid]>arr[mid+1]){ left=mid+1; }else { return mid; } } return left; } public static void main(String[] args) { int[]arr={9,7,5,8}; System.out.println(getLessIndex(arr)); } }
额外空间复杂度
O(1) :流程中不需要开辟额外空间,只需要有限个几个变量完成任务
O(N):流程中需要开辟额外空间
对数器
package Demo01; import java.util.Arrays; public class Test { public static void main(String[] args) { int testTime=50000; int maxSize=100; int maxValue=100; boolean succeed=true; for (int i = 0; i <testTime ; i++) { int []arr1=generateRandomArray(maxSize,maxValue); int []arr2=copyArray(arr1); Code01_SelectionSort.SelectionSort(arr1); compare(arr2); if (!isEqual(arr1,arr2)){ succeed=false; System.out.println("出错样本:"); printArray(arr1); printArray(arr2); break; } } System.out.println(succeed?"Nice!":"Shit!"); int[]arr=generateRandomArray(maxSize,maxValue); printArray(arr); Code01_SelectionSort.SelectionSort(arr); printArray(arr); } public static int[] generateRandomArray(int maxSize,int maxValue){//产生随机数组 // Math.random() [0,1) // (int)(Math.random()*N) [0,N-1] int[]arr=new int[(int)((maxSize+1)*Math.random())];//长度随机 for (int i = 0; i <arr.length ; i++) { arr[i]=(int)((maxValue+1)*Math.random())-(int)(maxValue*Math.random());//值随机 +1保证正数 } return arr; } public static int[] copyArray(int[] arr){ if (arr==null){ return null; } int res[]=new int[arr.length]; for (int i = 0; i < arr.length ; i++) { res[i]=arr[i]; } return res; } public static void compare(int []arr){ Arrays.sort(arr); } public static boolean isEqual(int[]arr1,int[]arr2){ if ((arr1==null&&arr2!=null)||(arr1!=null)&&arr2==null){ return false; } if (arr1==null&&arr2==null){ return true; } if (arr1.length!=arr2.length){ return false; } for (int i = 0; i < arr1.length; i++) { if (arr1[i]!=arr2[i]){ return false; } } return true; } public static void printArray(int arr[]){ System.out.println(Arrays.toString(arr)); } }
异或运算
相同为0,不同为1(二进制不进位相加)
0^N==N N^N==0
满足交换律和结合律
a=a^b
b=a^b (不申请额外变量两个值【地址要不同】交换)
a=a^b
题目二
一个数组中有一种数出现了奇数次,其他数出现偶数次,怎么找到并打印这种数
题目二
把一个int类型的数,提取出最右侧的1来(N& ~N+1)
题目三
数组中有两种数出现了奇数次,其他数都出现偶数次,怎么找到并打印这两种数
(所有数异或起来就是奇数次的a异或b,不等于0,某个位i上有1,整个数组划分为两类,(1)第i位上为1的数(2)第i位上为0的数,a和b属于其中一类,若同属于一类奇+奇=偶,则i位会等于0,只异或(1)类的数可以找到eor'=a或b,随后eor eor‘就是另一个 a^b^a=b)
package Demo01; public class Code07_EvenTimesOddTimes { // arr中,只有一种数,出现奇数次 public static void printOddTimesNum1(int[] arr) { int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i];//用异或除掉偶数次数 } System.out.println(eor);//打印出奇数次数 } // arr中,有两种数,出现奇数次 public static void printOddTimesNum2(int[] arr) { int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i]; } // a 和 b是两种数 // eor != 0 // eor最右侧的1,提取出来 // eor : 00110010110111000 // rightOne :00000000000001000 int rightOne = eor & (-eor); // 提取出最右的1 eor & (~eor+1) 最右侧的1剩下,其余变零 int onlyOne = 0; // eor' for (int i = 0 ; i < arr.length;i++) { // arr[1] = 111100011110000 // rightOne= 000000000010000 if ((arr[i] & rightOne) != 0) {//arr[i]上的最右侧那一位都是0 onlyOne ^= arr[i];//就是a和b的其中一个 eor' } } System.out.println(onlyOne + " " + (eor ^ onlyOne)); } public static int bit1counts(int N) {//二进制中1的个数 int count = 0; // 011011010000 // 000000010000 1 // 011011000000 // while(N != 0) { int rightOne = N & ((~N) + 1);//提取最右侧1 count++;//提取一回就计数 N ^= rightOne;//抹掉最后右侧1 // N -= rightOne N若是负数不行 } return count;//抹掉的数加最后一次计数 } public static void main(String[] args) { int a = 5; int b = 7; a = a ^ b; b = a ^ b; a = a ^ b; System.out.println(a); System.out.println(b); int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 }; printOddTimesNum1(arr1); int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 }; printOddTimesNum2(arr2); System.out.println(bit1counts(6));//110 两个1 } }