选择
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数分为两组
- a^b得到的二进制数000…1010,最右侧的第一个1一定在a或b中存在,可以通过这个最右侧的1将a,b分开
- A = eor & (~eor + 1)
- 然后遍历 & 进行分组
if ((arr[i] & rightOne) != 0) {
onlyOne ^= arr[i];
}
- 最后找到得到a,然后异或得到b