三大排序算法
通过使用对数器来检验,可以保证代码的正确性。
1)选择排序
package com.harrison.one;
import java.util.Arrays;
//选择排序
public class Class01_SelectionSort {
// 选择排序核心代码
public static void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
// i~N-1
// 最小值在哪个位置上,i~N-1
int minIdx = i;
for (int j = i + 1; j < arr.length; j++) {
// i~N-1上找最小值的下标
minIdx = arr[j] < arr[minIdx] ? j : minIdx;
}
swap(arr, i, minIdx);
}
}
// 交换两个元素
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 产生随机数组,长度随机,且值也随机
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;
}
// copy数组
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;
}
// 写一个比较器的方法:系统提供的排序的方法,肯定是对的
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// 比较两个数组是否完全一样
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;
}
//打印数组
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 = 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)) {
// 相同返回1,不相同返回0
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
selectionSort(arr);
printArray(arr);
}
}
由于只有核心部分代码不一样,所以只放核心部分代码
2)冒泡排序
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
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);
}
}
}
}
3)插入排序
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
// 0~0位置上有序
return;
}
// 1~i位置上有序
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
认识随机函数
- Math.random():[0,1)范围上所有的小数等概率返回一个(前闭后开)
- Math.random()*N:[0,N)
- (int)(Math.random()*N):[0,N-1]
- [0,N]:(int)(N+1)* ((maxValue+1)*Math.random());
- [-?,+?]:(int)((maxValue+1)* Math.random())-(int)(maxValue*(maxValue+1) *Math.random());
认识二分法
在一个有序数组中:1)找某个数是否存在;2)找>=某个数最左侧的位置;3)找<=某个数最右侧的位置
1)找某个数是否存在
package com.harrison.one;
import java.util.Arrays;
//在一个有序数组中,找某个数是否存在
public class Class04_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;
while (L < R) {
mid = L + ((R - L) >> 1);
if (num == sortedArr[mid]) {
return true;
} else if (num < sortedArr[mid]) {
R = mid - 1;
} else {
L = mid + 1;
}
}
return sortedArr[L] == num;
}
//另写的一个方法,不使用二分法
public static boolean test(int[] sortedArr, int num) {
for (int cur : sortedArr) {
if (cur == num) {
return true;
}
}
return false;
}
// 产生随机数组,长度随机,且值也随机
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!");
}
}
2)找>=某个数最左侧的位置
package com.harrison.one;
import java.util.Arrays;
//在一个有序数组中,找>=某个数最左侧的位置
public class Class05_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 (value <= arr[mid]) {
R = mid - 1;
index = mid;
} else {
L = mid + 1;
}
}
return index;
}
//另写的一个方法,不使用二分
public static int test(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= value) {
return i;
}
}
return -1;
}
// 产生随机数组,长度随机,且值也随机
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 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!");
}
}
3)找<=某个数最右侧的位置
// 在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 (value >= arr[mid]) {
L = mid + 1;
index = mid;
} else {
R = mid - 1;
}
}
return index;
}
补充
使用二分法时,mid=(L+R)/2,如果L和R数值很大很大时,mid会溢出,所以比较好的一种方式是写成:
mid=L+(R-L)/2 => mid=L+((R-L)>>1); >>:带符号右移1位;>>>:不带符号位右移(不管最高位是0还是1,都用0来补)。
另一个写成这样的原因就是位运算就是比除运算快!!!
4)局部最小值问题
题目:给定一个数组,无序,任意两个相邻的数都不相等,返回任意一个局部最小
分析:可以用二分法,不一定要有序才能二分!只要有二分的标准,在二分的过程中,有一种排他性的原则出现,就可以二分!!!
关键代码:mid>mid-1 => right=mid-1 mid>mid+1 => left=mid+1
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(mid>mid-1) {
right=mid-1;
}else if(mid>mid+1) {
left=mid+1;
}else {
return mid;
}
}
return left;
}
认识异或运算
- 异或运算:相同为0,不同为1
- 同或运算:相同为1,不同为0
容易记混!更好的记忆方式:异或运算即无进位相加
异或运算的性质
- 0^N==N
- N^N==0
- 异或运算满足交换律和结合律(即同样一批数异或的结果与顺序无关)
利用异或运算的性质进行骚操作
1)不用额外变量交换两个数
- int a=甲 int b=乙
- a=a^b
- b=a^b
- a=a^b
前提:a和b不能指向同一片内存,哪怕值相同也行,但是就是不能指向同一片内存
2)一个数组中有一种数出现了奇数次,其它数都出现了偶数次,怎么找到并打印这种数
3)怎么把一个整型的数,提取出最右侧的1来 => int Ans=N&((~N)+1)
4)一个数组中有两种数出现了奇数次,其它数都出现了偶数次,怎么找到并打印这两种数
5)给出一个数,求该数二进制1的个数
public static int bit1Counts(int N) {
int count=0;
// N 00100110100
// rightOne 00000000100
// N 00100110000
// rightOne 00000010000
while(N!=0) {
int rightOne=N&((~N)+1);
count++;
N^=rightOne;
}
return count;
}
代码:
package com.harrison.one;
//一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
public class Class07_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
//因为a和b不相等,所以eor!=0
//eor必然在某个位置上有1
//找eor最右侧的1
int rightOne=eor&((~eor)+1);
int onlyOne=0;//eor'
for(int i=0; i<arr.length; i++) {
//如果一个数 和 一个位置上有1的数 按位与 不等于0,说明这个数的这个位置上为1
if((arr[i]&rightOne)!=0) {
//eor'重新异或arr数组,但是只异或最右侧位置上为1的数
onlyOne^=arr[i];
}
}
System.out.println(onlyOne+" "+(eor^onlyOne));
}
//给出一个数,求该数二进制1的个数
public static int bit1Counts(int N) {
int count=0;
// N 00100110100
// rightOne 00000000100
// N 00100110000
// rightOne 00000010000
while(N!=0) {
int rightOne=N&((~N)+1);
count++;
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);
}
}