时间复杂度就是统计一个算法中的常数操作
常数操作有 数组寻址操作、循环遍历操作、加减乘除等,与数据量有关都不是常数操作
空间复杂度指程序执行所需要使用的空间大小 常数为O(1)


无进位相加
通过使用异或的性质 ^ :
- 0 ^ N = N N ^ N = 0
- (a^b)^c = a^(b^c)
- a^b^a = b^(a^a) = b^0 = b
//使a、b交换值 真的妙 a = a ^ b; b = a ^ b; ---> 结果 b = (a^b)^b = a^(b^b) = a^0 = a; a = a ^ b; ---> 结果 a = a^a^b = 0^b = b; //简化 //防止出现两指针位置相同可以排除额外情况进行交换 if(a == b){ return; } a ^= b; b ^= a; a ^= b;
使用这种方式的前提是 a和b值可以相同但一定不会指向同一块地址,如果他们指向同一块地址(位置) 说明a和b完全相同,则异或会为0,使用异或交换的本质是交换地址,基本类型是值交换
按位与( & ): 对应位数都为1 结果为1 否则为0
按位或( | ):对应位数为0 结果为0 否则为1
按位异或( ^ ):对应位数 相同为0 不同为1
一个数组中只有一种数是奇数个,其余为偶数个,求这个奇数个的数
题解:对数组中所有的元素进行异或,根据异或的特性,相同数异或为0,0与任何数异或都是他本身。
- public static void num(String[] args){
- int[] arr = {1,2,3,2,1};
-
- for(int i=1; i
- arr[0] ^= arr[i];
- }
- System.out.println(arr[0]);
- }
面试题二、
一个数组中,有两种数是奇数个a、b,其他都为偶数个,求这两个奇数个的数
题解:对数组元素进行异或,最终得到的结果是两个奇数个数a、b, 异或的结果err,然后获取err中位数为1的位数(因为a与b不相同,所以他们异或的结果肯定至少有一位不为一),那么a和b在此位中一个为0一个为1,找出此位不为一的所有数进行异或,得到的结果就是a或者b,再将这个结果与err进行异或得到另一个值
- public static void main(String[] args){
- int arr[] = {1,2,3,4,5,3,2,1};
- //异或结果
- int err = 0;
- for(int i=0;i
- err ^= arr[i];
- }
-
- //a、b不相同 进行异或至少有一位为1
- //获取结果err最右侧为1的位数 000010
- int onlyOne = err & (~err +1); //原数与其补码与运算得到最右侧的1
-
- //err2为 a或者b
- int err2 = 0;
- for(int i=1;i < arr.length;i++){
- //找出所有onlyOne对应位上不为1的所有数进行异或得到a或b
- //例如 100101 & 000010 -> 000000
- if((arr[i] & onlyOne) == 0){
- err2 ^= arr[i];
- }
- }
-
- System.out.println("第一个数为" + err2);
- System.out.println("第二个数为" + err ^ err2);
-
- }
三、排序算法 O(n^2)
1、插入排序
插入排序就像是打扑克牌时进行排序一样,抽到一张牌然后一次找到合适的位置将他插入,先在0~2范围内进行排序 然后0~3范围内进行排序,相当于将第三张牌在前两个位置找到合适地方插入,如果第三张牌比前面的大就进行交换
- public class insertSort {
- //插入排序
- public static void main(String[] args) {
- int[] arr = {1,2,8,9,7,6,5};
-
- //依次获取 0~1 0~2 0~3 0~3 0~i
- //不需要判断0~0 所以从1开始
- for(int i = 1 ; i < arr.length; i++ ) {
- //依次在每个范围内进行判断大小并交换
- //取最后一个数进行排序 相当于抽一张扑克牌放到抽好的范围内
- for(int j = i - 1 ; j >= 0 && arr[j] > arr[j + 1]; j--) {
- //满足条件 j大于等于0 防止数组越界
- //并且左边的值比右边大进行交换
- swap(arr,j,j+1);
- }
- }
-
- System.out.println(Arrays.toString(arr));
- }
-
- /**
- * 必须传入数组,才能交换数据 交换地址引用
- */
- public static void swap(int[] arr,int i,int j) {
- arr[i] ^= arr[j];
- arr[j] ^= arr[i];
- arr[i] ^= arr[j];
- }
-
- }
2、选择排序
选择排序就是每次将最小值移到最前面,每次定义循环第一位为最小值,如果遍历到比最小值还小的数,就进行交换 (选择排序更优)
- public class bubbleSort {
-
- public static void main(String[] args) {
-
- int[] arr = {1,2,8,9,7,6,5};
-
- //定义循环次数
- for(int i = 0 ; i < arr.length; i++) {
- //将每次的循环第一位作为最小值
- int minIndex = i;
- //从最小值后一位向后遍历 如果比最小值还小就进行交换
- for(int j = i + 1 ; j < arr.length; j++) {
- if(arr[j] < arr[minIndex]) {
- swap(arr,j,minIndex);
- }
- }
- }
-
- System.out.println(Arrays.toString(arr));
- }
-
- public static void swap(int[] arr,int i,int j) {
- arr[i] ^= arr[j];
- arr[j] ^= arr[i];
- arr[i] ^= arr[j];
- }
-
- }
3、冒泡排序
冒泡排序区别于选择排序,冒泡排序是每次找出最大值,并且是在两个相邻元素之间进行交换,如果前一个数比当前数小那么就交换两数,最终获取一轮循环的最大值,在进行n-1次循环,依次向后找出每轮的最大值并交换到数组末尾
- public class bubbleSort {
-
- public static void main(String[] args) {
- int[] arr = {1,5,9,7,6,3,2};
-
- for(int i = arr.length - 1; i > 0 ; i--) {
- for(int j = 0; j < i; j++) {
- if(arr[j] > arr[j + 1]) {
- swap(arr,j,j+1);
- }
- }
- }
- System.out.println(Arrays.toString(arr));
- }
-
- public static void swap(int[] arr,int i,int j) {
- arr[i] ^= arr[j];
- arr[j] ^= arr[i];
- arr[i] ^= arr[j];
- }
- }
补充:二分查找
- public void twoSelect() {
- int[] arr1 = {1,2,3,5,7,9};
- int val = 7;
- int n = arr1.length;
-
- int left = 0;
- int right = n - 1 ;
-
- while(left <= right) {
- //int mid = left + (right - left)/2 右移一位比除二更快;
- int mid = left + (right - left)>>1;
- if(arr1[mid] < val) {
- left = mid + 1;
- }else {
- right = mid - 1 ;
- }
- }
- System.out.println(left);
- }
4、递归算法 O(NlogN)
master公式 计算递归算法的时间复杂度
前提:每次递归调用子问题的规模都是等量的
T(N)= a * T(N/b) + O(N^d)
母问题 = 子问题被调用的次数 * 子问题每次调用的规模 + 剩下的时间复杂度


5、归并排序 -- O(NlogN)、空间复杂度O(N)
采用了分治思想:将大的事物通过递归逐步划分成小的具体实现 使用空间换时间

- public class mergeSort {
- public static void main(String[] args) {
- int[] arr = {1,5,9,7,2,3,4,6};
- process(arr,0,arr.length-1);
- System.out.println(Arrays.toString(arr));
- }
-
- public static void process(int[] arr,int L,int R) {
- if(L == R) {
- return;
- }
- int mid = L + ((R - L) >> 1);
- //分治思想 先递归
- process(arr,L,mid);
- process(arr,mid +1,R);
-
- //再合并排序
- merge(arr,L,mid,R);
- }
-
- public static void merge(int[] arr,int L,int M,int R) {
- //L~R上一共有多少个数就开多大空间
- int[] temp = new int[R-L+1];
- //定义一个变量作为temp的指针
- int i = 0;
- //定义两个指针分别指向两个数组的起始位置
- //指向左部分
- int p1 = L;
- //指向右部分
- int p2 = M + 1;
-
- while(p1 <= M && p2 <= R) {
- temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
- }
-
- //经过了循环后查看哪一个指针没有越界
- //将没越界的全部复制进去
- while(p1 <= M) {
- temp[i++] = arr[p1++];
- }
-
- while(p2 <= R) {
- temp[i++] = arr[p2++];
- }
-
- //对临时数组进行复制
- for(i = 0 ; i < temp.length; i++) {
- arr[i + L] = temp[i];
- }
- }
- }
-
相关阅读:
【信号处理】扩展卡尔曼滤波EKF(Matlab代码实现)
听说还有比LabVIEW更好用的仪器程控软件?你知道吗?
[附源码]计算机毕业设计JAVA校园一卡通管理信息系统台
docker快速安装redis,mysql,minio,nacos等常用软件【持续更新】
初始化使用花括号还是圆括号?
Bypass Windows Defender Dump Lsass(手法拙劣)
xcode iOS 在app文件中开启访问 Document Directory
Qt QObject::connect: Cannot queue arguments of type ‘***’
迁移学习(SPI)《Semi-Supervised Domain Adaptation by Similarity based Pseudo-label Injection》
自动控制原理8.2---常见非线性特性及其对系统运动的影响
-
原文地址:https://blog.csdn.net/m0_56044262/article/details/127716592