选择、冒泡、插入、二分、位运算

选择

public class SelectSort {

    //选择排序
    public static void main(String[] args) {
        int[] arr = {2, 4, 3, 1, 5, 3, 7, 5, 9};
        selectSort(arr);

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    private static void selectSort(int[] arr) {

        if (arr == null || arr.length < 2) return;

        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;

            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[minIndex] < arr[j] ? minIndex : j;
            }
            swap(arr, i, minIndex);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

  • 过程
    arr[0~N-1]范围上,找到最小值所在的位置,然后把最小值交换到0位置。
    arr[1~N-1]范围上,找到最小值所在的位置,然后把最小值交换到1位置。
    arr[2~N-1]范围上,找到最小值所在的位置,然后把最小值交换到2位置。

    arr[N-1~N-1]范围上,找到最小值位置,然后把最小值交换到N-1位置。

  • 时间复杂度
    很明显,如果arr长度为N,每一步常数操作的数量,如等差数列一般所以,总的常数操作数量 = a*(N^2) + b*N + c (a、b、c都是常数),去除常数项时间复杂度为O(n^2)

  • 空间复杂度
    没有额外开辟空间,所以空间复杂度为O(1)

冒泡

public class BubbleSelectTwo {
    //冒泡排序
    public static void main(String[] args) {
        int[] arr = {2, 4, 3, 1, 5, 3, 7, 5, 9};
        bubbleSelect(arr);

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    private static void bubbleSelect(int[] arr) {
        if (arr == null || arr.length < 2) return;

        for (int i = arr.length-1; i >0 ; i--) {
            for (int j =0;j<i-1;j++){
                if (arr[j]>arr[j+1]){
                    swap(arr,j,j+1);
                }
            }
        }
    }

    private 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];
    }
}

  • 过程
    在arr[0~N-1]范围上:arr[0]和arr[1],谁大谁来到1位置;arr[1]和arr[2],谁大谁来到2位置…arr[N-2]和arr[N-1],谁大谁来到N-1位置
    在arr[0~N-2]范围上,重复上面的过程,但最后一步是arr[N-3]和arr[N-2],谁大谁来到N-2位置
    在arr[0~N-3]范围上,重复上面的过程,但最后一步是arr[N-4]和arr[N-3],谁大谁来到N-3位置

    最后在arr[0~1]范围上,重复上面的过程,但最后一步是arr[0]和arr[1],谁大谁来到1位置

  • 时间复杂度
    很明显,如果arr长度为N,每一步常数操作的数量,如等差数列一般所以,总的常数操作数量 = a*(N^2) + b*N + c (a、b、c都是常数),去除常数项时间复杂度为O(n^2)

  • 空间复杂度
    没有额外开辟空间,所以空间复杂度为O(1)

插入

  • 过程
    想让arr[0~0]上有序,这个范围只有一个数,当然是有序的。
    想让arr[0~1]上有序,所以从arr[1]开始往前看,如果arr[1]<arr[0],就交换。否则什么也不做。

    想让arr[0~i]上有序,所以从arr[i]开始往前看,arr[i]这个数不停向左移动,一直移动到左边的数字不再比自己大,停止移动。
    最后一步,想让arr[0~N-1]上有序, arr[N-1]这个数不停向左移动,一直移动到左边的数字不再比自己大,停止移动。
  • 时间复杂度
    在最差情况下,如果arr长度为N,插入排序的每一步常数操作的数量,还是如等差数列一般所以,总的常数操作数量 = a*(N^2) + b*N + c (a、b、c都是常数)所以插入排序排序的时间复杂度为O(N^2)。
  • 空间复杂度
    没有额外开辟空间,所以空间复杂度为O(1)

二分

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;
		// L..R
		while (L < R) {
			// mid = (L+R) / 2;
			// L 10亿  R 18亿
			// mid = L + (R - L) / 2
			// N / 2    N >> 1
			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;
	}
  • 过程
    讲数组分为arr[0]-arr[mid],arr[mid]-arr[arr.length-1]两部分
    判断查找的数是否等于arr[mid] ,不等于折判断在哪个分区
    判断完后讲对应分区的范围以mid下标再进行二分,重复查找
  • 时间复杂度
    每次都是二分,所以2^x=n,时间复杂度为O(log2 n)
  • 空间复杂度
    每次二分都要占用额外的空间,空间复杂度也是O(log2 n)

位运算

  • 题目一: 如何不用额外变量交换两个数
		arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];

两个数A:5,B:7
二进制下:A:00…101 B: 00…111
A=A^B = 010
B=A^B=101=5
A=A^B=111=7

  • 题目二: 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
A=arr[0]^arr[1]^arr[2].....arr[n]

// 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);
	}

相同的数异或为0,将数组所有数异或则为最后结果

  • 题目三: 怎么把一个int类型的数,提取出最右侧的1来
int a=7;
//二进制位 0000...111取出最右侧1
//将a取反  1111....000
~a;
//将取反结果+1  1111..001
~a+1;
//得到结果 0000...001
a & ((~a)+1);

N & ((~N) + 1)

  • 题目四: 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
// arr中,有两种数,出现奇数次
	public static void printOddTimesNum2(int[] arr) {
		int eor = 0;
		for (int i = 0; i < arr.length; i++) {
			eor ^= arr[i];
		}
		// eor = a ^ b
		// eor != 0
		// eor必然有一个位置上是1
		// 0110010000
		// 0000010000
		int rightOne = 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) {
				onlyOne ^= arr[i];
			}
		}
		System.out.println(onlyOne + " " + (eor ^ onlyOne));
	}

首先获取a^b

a^b=arr[0]^arr[1]^arr[2].....arr[n]

想办法将a,b数分为两组

  1. a^b得到的二进制数000…1010,最右侧的第一个1一定在a或b中存在,可以通过这个最右侧的1将a,b分开
  2. A = eor & (~eor + 1)
  3. 然后遍历 & 进行分组
if ((arr[i] & rightOne) != 0) {
				onlyOne ^= arr[i];
			}
  1. 最后找到得到a,然后异或得到b
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值