【心情篇】:
记得上一次学习算法是在大学的时候,那个时候大概看到了冒泡排序,是在一本C语言书里——《C语言从入门到精通》,清晰的记得那一系列的精通书都对新手不太友好,印象很深的是那一本书的第一个示例程序就用上了自定义方法,然而当时我printf函数都没搞太清楚,<#include>什么意思,int main(){} ,retun 0 之类的;
又打开了一年前学习Java的笔记,这一次心中五味杂陈;
【一些基础概念】:
常数操作;非常数操作;常数项时间;大O表示法;额外空间复杂度;位运算基操;对数器。
【选择排序】:
import java.util.Arrays;
public class Code01_选择排序 {
public static void main(String[] args) {
int[] aaa={4,5,7,8,1,2,3,4};
selectionSort(aaa);
Arrays.stream(aaa).forEach(System.out::print);
System.out.println();
int[] bbb={4,5,7,8,1,99,66,2,3,4};
comparator(bbb);
Arrays.stream(bbb).forEach(System.out::print);
}
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
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);
}
}
【冒泡排序】:
import java.util.Arrays;
public class Code01_冒泡排序 {
public static void main(String[] args) {
int[] aaa={5,44,6,3,2,1,99};
bubbleSort(aaa);
Arrays.stream(aaa).forEach( e -> { System.out.print(" "+e);});
}
public static void bubbleSort( int[] arr ){
if ( arr==null || arr.length < 2 ){
return;
}
for ( int e = arr.length ; e > 0 ; e-- ) { // 0 ~ i
for (int i = 0; i < e-1; i++) {
if ( arr[i] > arr[i+1]){
swap(arr, i, i+1);
}
}
}
}
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];
}
}
【插入排序】:
import java.util.Arrays;
public class Code01_插入排序 {
public static void insertionSort( int[] arr ){
if ( arr==null || arr.length<2 ){
return;
}
// 0 ~ 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);
}
}
}
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];
}
public static void comparator(int[] arr){
Arrays.sort(arr);
}
public static int[] generateRandomArray( int maxSize , int maxValue ){
// Math.random() -> [0,1) 所有的小数 , 等概率返回一个;
// Math.ramdom() * N -> [0,N)所有小数,等概率返回一个;
// (int)(Math.ramdom() * 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;
}
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 boolean isEqual(int[] arr1 , int[] arr2){
boolean flag = true;
if (arr1.length != arr2.length ){
return false;
}
//走到这里说明数组长度相同
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]){
return false;
}
}
return flag;
}
public static void main(String[] args) {
int[] a = {5,4,2,3,6,77,1};
insertionSort(a);
Arrays.stream(a).forEach(System.out::println);
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$");
int testTime = 50; //测试的次数~
int maxSize = 100;
int maxValue = 100;
boolean succed = 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)){ //isEqual()方法是检验两个数组是否每一个位置都一样???
succed = false;
break;
}
Arrays.stream(arr1).forEach(e->{
System.out.print("-- ");
});
System.out.println();
Arrays.stream(arr1).forEach( e -> { System.out.print(" "+e);});
System.out.println();
Arrays.stream(arr2).forEach( e -> { System.out.print(" "+e);});
System.out.println();
}
System.out.println(succed?"成功~~~":"失败!!!");
System.out.println(succed?"成功~~~":"失败!!!");
System.out.println(succed?"成功~~~":"失败!!!");
}
}
【评价算法优劣的标准】:
评价一个算法流程的好坏,先看时间复杂度的指标 , 然后再分析不同数据样本下的实际运行时间,也就是——常数项时间。
【经典面试题】:
在一个int[ ] 中,只有一种数出现了奇数次 , 其余的数均出现了偶数次;
(1)如何找到出现奇数次的数 ?
(2)已知有两种数出现了奇数次 , 其余的数均出现偶数次,怎么找到这两种数呢 ?
【1】:
全都异或一次,最后得到的一定是那个奇数!!!
【2】:
将这个数组分拨;(按照两个奇数的不同位数(看a^b的最高位))
一波全异或一遍会得到一个奇数数;用这个奇数去和已经得到的【a^b】来比较即可~
package class01;
public class Code01_两个奇数 {
public static void printOddTimesnum1(int[] arr){
int eor=0;
for (int cur : arr){
eor^=cur;
}
System.out.println(eor);
}
public static void printOddTimesnum2(int[] arr){
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
int rightOne = eor & (~eor + 1) ;
int onlyOne = 0; // eor'
for ( int cur:arr
) {
if ( (cur & rightOne)==0 ){ //这里不管写 0/1 都是一样的,把数组划成了两波;
onlyOne^=cur;
}
}
System.out.println(onlyOne + " " + (eor ^ onlyOne));
}
public static void main(String[] args) {
int[] a={22,44,5,6,6,5,5,8,8,2,2,22,44};
int[] b={1,2,1,1,2,3,3,3,4,4};
printOddTimesnum1 ( a );
printOddTimesnum2( b );
}
}
【局部最小值问题】:
- 让我想起了《高数》中的罗尔中值定理;
局部最小值问题;
数组arr无序,但是任何两个相邻的数一定不相等 , 然后定义一个东西叫——《局部最小》, 局部最小:首位,末位只要比邻位小即可,但对于中间位置必须是谷底才行!!!
在这样的一个数组中,我只要求出一个局部最小就可以了~ ~ ~
【问】:
能否设计一个算法 , 时间复杂度优于 O( N ) ???
下为自己写的一个算法(不一定为最简的):
public class Code01_局部最小_相邻的数不相等 {
public static void main(String[] args) {
int[] a={4,6,7};
int[] b={9,7,6,3,8,9,6,5,1,4,5,6,7,7,8};
int[] c={5,2,3,4,5,6,7,8,9,11,12,22,33,44,55,66,77,88,99};
printJu(a);
printJu(b);
printJu(c);
}
public static void printJu(int[] arr ){
int start = liangDuan(arr);
if (start!=-1){
System.out.println("局部最小为"+start);
return;
}
int erNum = forMin(arr,0, arr.length);
if (erNum!=-1){
System.out.println("局部最小为"+erNum);
return;
}
System.out.println("所输入的数组没有局部最小值~");
return;
}
public static int liangDuan( int[] arr ){
int L=0;
int R= arr.length-1;
if (arr[L]<arr[L+1] ){
return arr[L];
}
if ( arr[R] < arr[R-1] ){
return arr[R];
}
return -1;
}
public static int forMin( int[] arr , int L , int R ){
int M=L+( (R-L) >>1 );
if ( (M-1)< 0 || (M+1)> arr.length-1 ){ //数组首元素另写了一个方法来判断,这里就不管了。
return -1;
}
if (arr[M-1]>arr[M] && arr[M]<arr[M+1] ){
return arr[M];
}
return (arr[M]<arr[M+1]) ? forMin(arr, L, M-1) : forMin(arr, M+1 , R) ;
}
}
【值得一提的是】:
题目中的——任何两相邻的数一定不相等,这一点非常重要!!!
因为如果出现相同数字紧挨着的情况的话,往哪一侧二分呢?没有数学规律表明一定在一侧有解。
所以存在相等数字紧挨的话,就不能使用二分法来计算。
【算法优化方向】:
- 数据状况
- 问题标准