java 异或 排序_认识复杂度、排序、二分、异或

1 时间复杂度、空间复杂度、排序、异或运算

1.1 时间复杂度

常数时间操作:

算数运算:+ - * /

位运算:>>(带符号右移动)、 >>>(不带符号右移动) 、 <

带符号就是最高位补符号位,不带符号就是最高位补0

赋值操作:比较,自增,自减操作

数组寻址等

总之,执行时间固定的操作都是常数时间的操作。反之执行时间不固定的操作,都不是常数时间的操作

通过基本动作的常数时间,推导时间复杂度

对于双层循环来说,n(常数)+ (n-1)(常数)+ ... + 2(常数) + 1(常数) => 推导出

y = an^2 + bn + c

忽略掉低阶项,忽略掉常数项,忽略掉高阶项的系数,得到时间复杂度为n^2

1.1.1 排序操作

1.1.1.1 选择排序

package class01;

import java.util.Arrays;

public class Code01_SelectionSort {

public static void selectionSort(int[] arr) {

if (arr == null || arr.length < 2) {

return;

}

// 0 ~ N-1

// 1~n-1

// 2

for (int i = 0; i < arr.length - 1; i++) { // i ~ N-1

// 最小值在哪个位置上 i~n-1

int minIndex = 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 tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

}

// for test

public static void comparator(int[] arr) {

Arrays.sort(arr);

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

// Math.random() [0,1)

// Math.random() * N [0,N)

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

}

return arr;

}

// for test

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;

}

// for test 对数器

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;

}

// for test

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

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

System.out.print(arr[i] + " ");

}

System.out.println();

}

// for test

public static void main(String[] args) {

int testTime = 500000;

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

selectionSort(arr1);

comparator(arr2);

if (!isEqual(arr1, arr2)) {

succeed = false;

printArray(arr1);

printArray(arr2);

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

int[] arr = generateRandomArray(maxSize, maxValue);

printArray(arr);

selectionSort(arr);

printArray(arr);

}

}

1.1.1.2 冒泡排序

package class01;

import java.util.Arrays;

public class Code02_BubbleSort {

public static void bubbleSort(int[] arr) {

if (arr == null || arr.length < 2) {

return;

}

// 0 ~ N-1

// 0 ~ N-2

// 0 ~ N-3

for (int e = arr.length - 1; e > 0; e--) { // 0 ~ e

for (int i = 0; i < e; i++) {

if (arr[i] > arr[i + 1]) {

swap(arr, i, i + 1);

}

}

}

}

// 交换arr的i和j位置上的值

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

}

// for test

public static void comparator(int[] arr) {

Arrays.sort(arr);

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

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());

}

return arr;

}

// for test

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;

}

// for test

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;

}

// for test

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

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

System.out.print(arr[i] + " ");

}

System.out.println();

}

// for test

public static void main(String[] args) {

int testTime = 500000;

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

bubbleSort(arr1);

comparator(arr2);

if (!isEqual(arr1, arr2)) {

succeed = false;

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

int[] arr = generateRandomArray(maxSize, maxValue);

printArray(arr);

bubbleSort(arr);

printArray(arr);

}

}

1.1.1.3 插入排序

package class01;

import java.util.Arrays;

public class Code03_InsertionSort {

public static void insertionSort(int[] arr) {

if (arr == null || arr.length < 2) {

return;

}

// 0~0 有序的

// 0~i 想有序

for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序

for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {

swap(arr, j, j + 1);

}

}

}

// i和j是一个位置的话,会出错

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

}

// for test

public static void comparator(int[] arr) {

Arrays.sort(arr);

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

// Math.random() -> [0,1) 所有的小数,等概率返回一个

// Math.random() * N -> [0,N) 所有小数,等概率返回一个

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

}

return arr;

}

// for test

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;

}

// for test

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;

}

// for test

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

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

System.out.print(arr[i] + " ");

}

System.out.println();

}

// for test

public static void main(String[] args) {

int testTime = 500000;

int maxSize = 100; // 随机数组的长度0~100

int maxValue = 100;// 值:-100~100

boolean succeed = true;

for (int i = 0; i < testTime; i++) {

int[] arr1 = generateRandomArray(maxSize, maxValue);

int[] arr2 = copyArray(arr1);

insertionSort(arr1);

comparator(arr2);

if (!isEqual(arr1, arr2)) {

// 打印arr1

// 打印arr2

succeed = false;

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

int[] arr = generateRandomArray(maxSize, maxValue);

printArray(arr);

insertionSort(arr);

printArray(arr);

}

}

插入排序和前面两种排序的不同是在于,插入排序跟数组初始顺序有关,在初始有序的情况下,有可能时间复杂度为O(N),有可能为O(N ^2),但是我们估计时间复杂度要按照最差的情况来估计,所以插入排序的时间复杂度仍然O(N ^2)

1.2 空间复杂度

申请有限几个变量,和样本量n没关系,就是空间复杂度O(1),如果要开辟一个空间数组和样本量n是一样大,用来支持我们的算法流程那么O(N)。反之用户就是要实现数组拷贝,我们开辟一个新的n大小数组用来支撑用户的需求,那么仍然是O(1)

1.3 常数项时间复杂度

如果两个相同时间复杂度的算法要比较性能,这个时候需要比较单个常数项时间,对能力要求较高,没有意义,不如样本量试验实际测试来比较

1.4 算法最优解

我们认为最优解的考虑顺序是,先满足时间复杂度指标,再去使用较少的空间。一般来说,算法题,ACM等不会卡常数项时间

1.5 常见时间复杂度

依次从好到坏 O(1) -> O(logN) -> O(N) -> O(N*logN) -> O(N^2) -> O(N^3) ... -> O(N!)

1.6 算法和数据结构脉络

知道怎么算的算法

知道怎么试的算法(递归)

1.7 认识对数器

准备你想要测试的方法a

实现一个复杂度不好,但是容易实现的方法b

实现一个随机样本产生器

把方法a和方法b跑相同的随机样本,看看得到的结果是否一样

如果有一个随机样本使得对比结果不一致,打印样本进行人工干预,改对方法a和方法b

当样本数量很多,测试对比依然正确,可以确定方法a已经正确

1.8 认识二分法

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

二分查找值,基于有序数组,算法复杂度为二分了多少次,O(log2N)可以写成O(logN)

123579

package class01;

import java.util.Arrays;

public class Code04_BSExist {

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;

}

// for test

public static boolean test(int[] sortedArr, int num) {

for(int cur : sortedArr) {

if(cur == num) {

return true;

}

}

return false;

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

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());

}

return arr;

}

public static void main(String[] args) {

int testTime = 500000;

int maxSize = 10;

int maxValue = 100;

boolean succeed = true;

for (int i = 0; i < testTime; i++) {

int[] arr = generateRandomArray(maxSize, maxValue);

Arrays.sort(arr);

int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());

if (test(arr, value) != exist(arr, value)) {

succeed = false;

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

}

}

在一个有序数组中,找>=某个数最左侧的位置

122222333578888999999 找大于等于2最左侧的位置

package class01;

import java.util.Arrays;

public class Code05_BSNearLeft {

// 在arr上,找满足>=value的最左位置

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;

R = mid - 1;

} else {

L = mid + 1;

}

}

return index;

}

// for test

public static int test(int[] arr, int value) {

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

if (arr[i] >= value) {

return i;

}

}

return -1;

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

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());

}

return arr;

}

// for test

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

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

System.out.print(arr[i] + " ");

}

System.out.println();

}

public static void main(String[] args) {

int testTime = 500000;

int maxSize = 10;

int maxValue = 100;

boolean succeed = true;

for (int i = 0; i < testTime; i++) {

int[] arr = generateRandomArray(maxSize, maxValue);

Arrays.sort(arr);

int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());

if (test(arr, value) != nearestIndex(arr, value)) {

printArray(arr);

System.out.println(value);

System.out.println(test(arr, value));

System.out.println(nearestIndex(arr, value));

succeed = false;

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

}

}

在一个有序数组中,找<=某个数最右侧的位置

package class01;

import java.util.Arrays;

public class Code05_BSNearRight {

// 在arr上,找满足<=value的最右位置

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;

}

// for test

public static int test(int[] arr, int value) {

for (int i = arr.length - 1; i >= 0; i--) {

if (arr[i] <= value) {

return i;

}

}

return -1;

}

// for test

public static int[] generateRandomArray(int maxSize, int maxValue) {

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());

}

return arr;

}

// for test

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

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

System.out.print(arr[i] + " ");

}

System.out.println();

}

public static void main(String[] args) {

int testTime = 500000;

int maxSize = 10;

int maxValue = 100;

boolean succeed = true;

for (int i = 0; i < testTime; i++) {

int[] arr = generateRandomArray(maxSize, maxValue);

Arrays.sort(arr);

int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());

if (test(arr, value) != nearestIndex(arr, value)) {

printArray(arr);

System.out.println(value);

System.out.println(test(arr, value));

System.out.println(nearestIndex(arr, value));

succeed = false;

break;

}

}

System.out.println(succeed ? "Nice!" : "Fucking fucked!");

}

}

局部最小值问题

无序数组,任意两个相邻的数不相等,返回一个局部最小值

package class01;

public class Code06_BSAwesome {

public static int getLessIndex(int[] arr) {

if (arr == null || arr.length == 0) {

return -1; // no exist

}

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;

}

}

1.9 认识异或运算

异或运算:相同为0,不同为1

同或运算:相同为1, 不同为0,不掌握

上述特别不容易记住,异或运算就记成无进位相加:比如十进制6异或7,就理解为110和111按位不进位相加,得到001

所以 0^N = N , N^N = 0

异或运算满足交换律和结合律,所以A异或B异或C = A异或(B异或C) = (A异或C)异或B

题目一:如何不用额外变量就交换两个数

a = x b = y两个数交换位置

a = a ^ b # 第一步操作,此时 a = x^y , b=y

b = a ^ b # 第二步操作,此时 a = x^y , b = x^y^y => b = x^0 => b = x

a = a ^ b # 第三步操作,此时 a = x^y^x, b = x, a=> x^x^y => a=y

三步操作,实现交换ab的值

package class01;

public class Test {

public static void main(String[] args) {

int a = 6;

int b = 6;

a = a ^ b;

b = a ^ b;

a = a ^ b;

System.out.println(a);

System.out.println(b);

int[] arr = {3,1,100};

System.out.println(arr[0]);

System.out.println(arr[2]);

swap(arr, 0, 0);

System.out.println(arr[0]);

System.out.println(arr[2]);

}

public static void swap (int[] arr, int i, int j) {

// arr[0] = arr[0] ^ arr[0];

arr[i] = arr[i] ^ arr[j];

arr[j] = arr[i] ^ arr[j];

arr[i] = arr[i] ^ arr[j];

}

}

注意,如果a和b指向同一块内存,改方法不可行

题目二:一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数

[2,2,1,3,2,3,2,1,1] 数组中存在四个2,两个3,三个1,定义一个常量等于0,分别对该数组中的数遍历一遍进行异或,最后,该变量等于多少,那么奇数的值就是多少。因为异或运算满足交换和结合律

题目三:怎么把一个int类型的数,提取出最右侧的1来

n与上(n取反加1)即可 => N & ( (~N)+1 )

题目四:一个数组中有两种不相等的数出现了奇数次,其他数出现了偶数次,怎么找到并打印这两种数

定义一个常量eor = 0,分别对该数组每个数异或,最终结果为a异或b,其中a和b就是这两个奇数,由于a!=b所以a异或b不等于0,即eor的值某一位上一定为1(有可能不止一个1随便选一个例如第八位),用该位做标记对原有数组的数进行分类,那么a和b由于第八位不相同一定被分开,再定义常量eor' = 0分别对第八位为0的数异或,那么得到的值,就是a和b其中一个,由于之前eor = a异或b,那么在用eor和eor'异或,就是另外一个值。一般来说,随便找一个1我们就找最右侧的那个1,如题目三

package class01;

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

}

// 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[i] = 111100011110000

// rightOne= 000000000010000

if ((arr[i] & rightOne) != 0) {

onlyOne ^= arr[i];

}

}

System.out.println(onlyOne + " " + (eor ^ onlyOne));

}

public static int bit1counts(int N) {

int count = 0;

// 011011010000

// 000000010000 1

// 011011000000

//

while(N != 0) {

int rightOne = N & ((~N) + 1);

count++;

N ^= rightOne;

// N -= rightOne

}

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

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值