• 左神算法笔记


    一、时间复杂度与空间复杂度

    时间复杂度就是统计一个算法中的常数操作

    常数操作有 数组寻址操作、循环遍历操作、加减乘除等,与数据量有关都不是常数操作

    空间复杂度指程序执行所需要使用的空间大小  常数为O(1)

     

     在不使用额外空间的情况下更换两数的值

    无进位相加

    通过使用异或的性质 ^ :

    1. 0 ^ N = N    N ^ N = 0
    2. (a^b)^c = a^(b^c)
    3. a^b^a = b^(a^a) = b^0 = b
    1. //使a、b交换值 真的妙
    2. a = a ^ b;
    3. b = a ^ b; ---> 结果 b = (a^b)^b = a^(b^b) = a^0 = a;
    4. a = a ^ b; ---> 结果 a = a^a^b = 0^b = b;
    5. //简化
    6. //防止出现两指针位置相同可以排除额外情况进行交换
    7. if(a == b){
    8. return;
    9. }
    10. a ^= b;
    11. b ^= a;
    12. a ^= b;

    使用这种方式的前提是 a和b值可以相同但一定不会指向同一块地址,如果他们指向同一块地址(位置) 说明a和b完全相同,则异或会为0,使用异或交换的本质是交换地址,基本类型是值交换

    二、位运算

    按位与( & ): 对应位数都为1 结果为1  否则为0

    按位或( | ):对应位数为0 结果为0  否则为1

    按位异或( ^ ):对应位数 相同为0  不同为1

    面试题一、

    一个数组中只有一种数是奇数个,其余为偶数个,求这个奇数个的数

    题解:对数组中所有的元素进行异或,根据异或的特性,相同数异或为0,0与任何数异或都是他本身。

    1. public static void num(String[] args){
    2. int[] arr = {1,2,3,2,1};
    3. for(int i=1; i
    4. arr[0] ^= arr[i];
    5. }
    6. System.out.println(arr[0]);
    7. }

    面试题二、

    一个数组中,有两种数是奇数个a、b,其他都为偶数个,求这两个奇数个的数

    题解:对数组元素进行异或,最终得到的结果是两个奇数个数a、b, 异或的结果err,然后获取err中位数为1的位数(因为a与b不相同,所以他们异或的结果肯定至少有一位不为一),那么a和b在此位中一个为0一个为1,找出此位不为一的所有数进行异或,得到的结果就是a或者b,再将这个结果与err进行异或得到另一个值

    1. public static void main(String[] args){
    2. int arr[] = {1,2,3,4,5,3,2,1};
    3. //异或结果
    4. int err = 0;
    5. for(int i=0;i
    6. err ^= arr[i];
    7. }
    8. //a、b不相同 进行异或至少有一位为1
    9. //获取结果err最右侧为1的位数 000010
    10. int onlyOne = err & (~err +1); //原数与其补码与运算得到最右侧的1
    11. //err2为 a或者b
    12. int err2 = 0;
    13. for(int i=1;i < arr.length;i++){
    14. //找出所有onlyOne对应位上不为1的所有数进行异或得到a或b
    15. //例如 100101 & 000010 -> 000000
    16. if((arr[i] & onlyOne) == 0){
    17. err2 ^= arr[i];
    18. }
    19. }
    20. System.out.println("第一个数为" + err2);
    21. System.out.println("第二个数为" + err ^ err2);
    22. }

    三、排序算法  O(n^2)

    1、插入排序

    插入排序就像是打扑克牌时进行排序一样,抽到一张牌然后一次找到合适的位置将他插入,先在0~2范围内进行排序 然后0~3范围内进行排序,相当于将第三张牌在前两个位置找到合适地方插入,如果第三张牌比前面的大就进行交换

    1. public class insertSort {
    2. //插入排序
    3. public static void main(String[] args) {
    4. int[] arr = {1,2,8,9,7,6,5};
    5. //依次获取 0~1 0~2 0~3 0~3 0~i
    6. //不需要判断0~0 所以从1开始
    7. for(int i = 1 ; i < arr.length; i++ ) {
    8. //依次在每个范围内进行判断大小并交换
    9. //取最后一个数进行排序 相当于抽一张扑克牌放到抽好的范围内
    10. for(int j = i - 1 ; j >= 0 && arr[j] > arr[j + 1]; j--) {
    11. //满足条件 j大于等于0 防止数组越界
    12. //并且左边的值比右边大进行交换
    13. swap(arr,j,j+1);
    14. }
    15. }
    16. System.out.println(Arrays.toString(arr));
    17. }
    18. /**
    19. * 必须传入数组,才能交换数据 交换地址引用
    20. */
    21. public static void swap(int[] arr,int i,int j) {
    22. arr[i] ^= arr[j];
    23. arr[j] ^= arr[i];
    24. arr[i] ^= arr[j];
    25. }
    26. }

    2、选择排序

    选择排序就是每次将最小值移到最前面,每次定义循环第一位为最小值,如果遍历到比最小值还小的数,就进行交换   (选择排序更优)

    1. public class bubbleSort {
    2. public static void main(String[] args) {
    3. int[] arr = {1,2,8,9,7,6,5};
    4. //定义循环次数
    5. for(int i = 0 ; i < arr.length; i++) {
    6. //将每次的循环第一位作为最小值
    7. int minIndex = i;
    8. //从最小值后一位向后遍历 如果比最小值还小就进行交换
    9. for(int j = i + 1 ; j < arr.length; j++) {
    10. if(arr[j] < arr[minIndex]) {
    11. swap(arr,j,minIndex);
    12. }
    13. }
    14. }
    15. System.out.println(Arrays.toString(arr));
    16. }
    17. public static void swap(int[] arr,int i,int j) {
    18. arr[i] ^= arr[j];
    19. arr[j] ^= arr[i];
    20. arr[i] ^= arr[j];
    21. }
    22. }

    3、冒泡排序

    冒泡排序区别于选择排序,冒泡排序是每次找出最大值,并且是在两个相邻元素之间进行交换,如果前一个数比当前数小那么就交换两数,最终获取一轮循环的最大值,在进行n-1次循环,依次向后找出每轮的最大值并交换到数组末尾

    1. public class bubbleSort {
    2. public static void main(String[] args) {
    3. int[] arr = {1,5,9,7,6,3,2};
    4. for(int i = arr.length - 1; i > 0 ; i--) {
    5. for(int j = 0; j < i; j++) {
    6. if(arr[j] > arr[j + 1]) {
    7. swap(arr,j,j+1);
    8. }
    9. }
    10. }
    11. System.out.println(Arrays.toString(arr));
    12. }
    13. public static void swap(int[] arr,int i,int j) {
    14. arr[i] ^= arr[j];
    15. arr[j] ^= arr[i];
    16. arr[i] ^= arr[j];
    17. }
    18. }

    补充:二分查找

    1. public void twoSelect() {
    2. int[] arr1 = {1,2,3,5,7,9};
    3. int val = 7;
    4. int n = arr1.length;
    5. int left = 0;
    6. int right = n - 1 ;
    7. while(left <= right) {
    8. //int mid = left + (right - left)/2 右移一位比除二更快;
    9. int mid = left + (right - left)>>1;
    10. if(arr1[mid] < val) {
    11. left = mid + 1;
    12. }else {
    13. right = mid - 1 ;
    14. }
    15. }
    16. System.out.println(left);
    17. }

    4、递归算法  O(NlogN)

    master公式  计算递归算法的时间复杂度

    前提:每次递归调用子问题的规模都是等量的

    T(N)=      a    *      T(N/b)      +      O(N^d)

    母问题 = 子问题被调用的次数  *  子问题每次调用的规模  + 剩下的时间复杂度

     

    5、归并排序   --  O(NlogN)、空间复杂度O(N)

    采用了分治思想:将大的事物通过递归逐步划分成小的具体实现    使用空间换时间

    1. public class mergeSort {
    2. public static void main(String[] args) {
    3. int[] arr = {1,5,9,7,2,3,4,6};
    4. process(arr,0,arr.length-1);
    5. System.out.println(Arrays.toString(arr));
    6. }
    7. public static void process(int[] arr,int L,int R) {
    8. if(L == R) {
    9. return;
    10. }
    11. int mid = L + ((R - L) >> 1);
    12. //分治思想 先递归
    13. process(arr,L,mid);
    14. process(arr,mid +1,R);
    15. //再合并排序
    16. merge(arr,L,mid,R);
    17. }
    18. public static void merge(int[] arr,int L,int M,int R) {
    19. //L~R上一共有多少个数就开多大空间
    20. int[] temp = new int[R-L+1];
    21. //定义一个变量作为temp的指针
    22. int i = 0;
    23. //定义两个指针分别指向两个数组的起始位置
    24. //指向左部分
    25. int p1 = L;
    26. //指向右部分
    27. int p2 = M + 1;
    28. while(p1 <= M && p2 <= R) {
    29. temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
    30. }
    31. //经过了循环后查看哪一个指针没有越界
    32. //将没越界的全部复制进去
    33. while(p1 <= M) {
    34. temp[i++] = arr[p1++];
    35. }
    36. while(p2 <= R) {
    37. temp[i++] = arr[p2++];
    38. }
    39. //对临时数组进行复制
    40. for(i = 0 ; i < temp.length; i++) {
    41. arr[i + L] = temp[i];
    42. }
    43. }
    44. }

  • 相关阅读:
    【信号处理】扩展卡尔曼滤波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