无符号右移和右移的区别:
无符号右移:不管正负标志位为0还是1,将该数的二进制码整体右移,左边部分总是以0填充,右边部分舍弃。
右移:该数对应的二进制码整体右移,左边的用原有标志位补充,右边超出的部分舍弃。
1)求5!+4!+3!+2!+1!
int sum=0; for(int i=1;i<=5;i++){ int ret=1; for(int j=1;j<=i;j++){ ret=ret*j; } sum=sum+ret; } System.out.println(sum); }
注意while循环后面不能加分号
int a=10; while(a>=0){ if(a==9){ continue; } a--; }
break是结束所有循环,continue是结束本次循环,当你输入的数据有很多数据的时候,优先处理字符串
2)求一个整数中,二进制中1的个数
假设现在有一个数字7,变成二进制之后是111,那么我们可以将这个数字7变成二进制的每一位依次和001进行按位与操作,这个特点是都是1结果才是1
int count=0; for(int i=0;i<32;i++){ if(((n>>i)&1)==1){ count++; } } return count; } 不管是什么数字都必须是移动32次
输入-1的话,结果就是32个1,所以不管什么数字,都必须移动31次,负数的话没有影响,因为负数只移动了31次
我们还有第二种做法,那么就是说如果在移位的过程中是正数,发现移位的过程中变成0了,那么没有必要进行移位操作了
int n = -1; int count = 0; while (n != 0) { if ((n & 1) != 0) { count++; } n = n >>> 1;//这里面必须是无符号右移,因为如果是普通的右移的话况且如果是负数的话,那么这个代码就会会死循环 } System.out.println(count); }
第三种做法:我们如果说想要求二进制中1的个数,那么可以让N一直&N-1,每一次&的过程中,原有N的1的个数都会减少
第一次按位与操作: 7--->0111 6--->0110 --------------- 第二次按位与操作 6--->0110 5--->0101 ---------------- 第三次按位与操作 4--->0100 3--->0011 --------------- 0--->00000 我们最终发现,每进行一次按位与操作,原有数字的二进制的位数就少1
int n = -1; int count = 0; while (n != 0) { n=n&(n-1); count++; } System.out.println(count);
3)计算1-1/2+1/3-1/4.....+1/100
double sum=0; int flag=-1; for(int i=1;i<=100;i++){ if(i%2==1){ flag=1; }else{ flag=-1; } sum=sum+(1.0)/i*flag; } System.out.println(sum);
4)打印一个X图案,比如说输入数字5,那么两条反斜线相交都是5
* * * * * * * * * 我们进行定义横坐标是i,定义纵坐标是j,如果说最后i==j或者是i+j+1=5满足打印*号
Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i==j||i+j+1==n){ System.out.print("*"); }else{ System.out.print(" "); } } System.out.println(); }
5)打印99乘法表:
Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); for(int i=1;i<n;i++){ for(int j=1;j<=i;j++){ System.out.print(i+"*"+j+"="+i*j+" "); } System.out.println(); }
6)获取一个二进制序列的所有偶数位数和奇数位数,分别输出二进制序列
int n=7;//注意二进制的循环控制位数从0开始,但是实际上以1开始 for(int i=31;i>=1;i=i-2){ System.out.print((n>>i)&1); } System.out.println(); for(int i=30;i>=0;i=i-2){ System.out.print((n>>i)&1); }
7)判断一个数是否是素数
素数:只能被1和它本身能够整除的数:
Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); int i=0; for( i=2;i<n;i++){ if(n%i==0){ System.out.println("当前数字不是素数"); break; } } //当前循环跳出只能有一种情况是素数,就是说i==n,其他条件情况下跳出都不是素数 if(i==n){ System.out.println("当前数字是素数"); }
Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); int i=0; for( i=2;i<=n/2;i++){ if(n%i==0){ System.out.println("当前数字不是素数"); break; } } if(i>n/2){ System.out.println("当前数字是素数"); } }
Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); int i=0; for( i=2;i<=Math.sqrt(n);i++){ if(n%i==0){ System.out.println("当前数字不是素数"); break; } } if(i>Math.sqrt(n)){ System.out.println("当前数字是素数"); }
函数结束之后形参将会被系统回收
我们实际上要交换两个两个变量的值,是不能获取到栈上面的地址的,如果真正想要进行交换两个变量,就要把这两个变量放在堆上面
1)直接放在一个函数里面进行交换
2)new 一个数组对象,进行交换数组里面的值
3)也可以new 一个对象
重载:方法名称相同,参数列表不同(个数,类型,顺序),通过一个方法就知道调用谁了,不需要记住那么多的方法名了,降低很多的成本
8)创建方法求两个数的最大值max2,然后再写一个求三个数的最大值函数max3,要求在max3函数里面调用max2函数,从而实现3个数的最大值计算
public static int max(int a,int b){ return a>b?a:b; } public static int MaxDown(int a,int b,int c){ int max=max(a,b); return max>c?max:c; } public static void main(String[] args) throws InterruptedException { int max=MaxDown(12,67,89); System.out.println(max); }
练习题1:求斐波那契数列的第n项,1,1,2,3,5,8 ,13,21,34;
fib(N)=fib(N-1)+fib(N-2)
public static int fib1(int n)
{
if(n==1||n==2)
{
return 1;
}else{
return fib1(n-1)+fib1(n-2);
}
}
public static int SumFib(int n){
int f1=1;
int f2=1;
int f3=1;
for(int i=3;i<=n;i++){
f3=f1+f2;
f1=f2;
f2=f3;
}
return f3;
}
对于菲博那齐数列来说,当我们的N变得越来越大,那么递归重复计算的次数也就越多,从下图可知,f(2)就重复进行计算了两次
f(5) f(4) f(3) f(3) f(2) f(2) f(1) f(2) f(1)
练习题2:青蛙跳台阶问题:1,2,3,5,8..........
练习题3:汉诺塔问题
我们的这个游戏的规则是现在有三个盘子,ABC,总而言之就是说必须把A上面的盘子,都按照大小顺序放在C盘子上面:
1)假设A上面有1个盘子,那么直接把A上面的盘子移动到C上面:
A--->C
2)假设A上面有两个盘子,那么先把A上面顶的盘子移动到B上面,再把A上面的盘子移动到C上面,再把B上面的盘子移动到C上面:
A--->B
A---->C
B----->C
3)假设现在有3个盘子在A上面,那么它的移动顺序就是:
总结:
1)无论有多少个盘子,假设现在A上面有N个盘子,咱们是要进行借助C,先把N-1个盘子移动到B上面
2)把A上面剩余的一个盘子直接从A移动到C上面
3)最后再把B上面的N-1个盘子借助A移动到C上面
pos1:属于起始位置
pos2:属于中转位置
pos3:属于最终位置
public static void move(char pos1,char pos3)
{
System.out.println(pos1+"->"+pos3)//直接从pos1位置移动到pos3的位置
}
public static void hannuo(int n,char pos1,char pos2,char pos3)
{
if(n==1)
{
move(pos1,pos3);
return;
}else{
hannuo(n-1,pos1,pos3,pos2);//先把n-1个盘子从pos1位置借助pos3位置移动到pos2位置
move(pos1,pos3);//把pos1上面的盘子直接移动到pos3位置
hannuo(n-1,pos2,pos1,pos3);//把pos2上面的n-1个盘子借助pos1移动到pos3上面
}
}
JVM也就是说JAVA虚拟机运行的时候的数据区
1)由所有线程共享的数据区:方法区和堆
2)由线程隔离的数据区:虚拟机栈,本地方法栈,程序计数器
本地方法栈:属于本地方法,底层是由C/C++代码实现的方法
虚拟机栈:JAVA方法,存放方法调用的时候的一些相关信息,每一个方法在执行的时候,都会进行创建一些栈祯,栈祯里面包含着局部变量表,操作数链,动态连接以及返回地址等一些重要的信息,最终保存的都是与方法执行的一些信息,比如说局部变量
堆:JVM所进行管理的最大的内存区域,使用new创建的对象都是在堆上面进行保存的,堆随着程序开始运行而去创建,随着程序的退出而销毁,堆上面的数据只要还在进行使用,那么就不会被销毁
所有的数组都叫做对象
int a=10; int b=20; int[] array={1,2,3,4,5};
引用之间还是可以赋值的
int array=array1; 说明此时array这个引用指向了array1所指向的对象
1)况且这个时候在栈争里面array和array1里面保存的都是0x3344,从这里面我们就可以看出引用变量并不会直接存储对象本身,可以简单理解成是存储的是对象在堆空间中的起始地址,这时候就可以根据引用变量来去操作对象了
2)一个引用不可以指向多个对象,但是一个对象可以被多个引用所指向
3)null表示是一个无效的内存位置,因此我们不可以对这个内存进行任何的读写操作,一旦尝试进行读写,就会发生空指针异常
public class UserController { public static void func1(int[] array){ array=new int[10];//修改形参的指向 } public static void func2(int[] array){ array[0]=90;//修改形参指向对象的值 } public static void main(String[] args) { int[] array=new int[]{1,2,3,4,5}; //func1(array);打印12345 //func2(array);打印90,2,3,4,5 System.out.println(Arrays.toString(array)); } }
此时通过数组就可以进行交换两个变量的值(不过这两个元素必须是数组的值),还有就是在JAVA中,形参接受的时候会进行开辟一段新的空间
所以我们说不是传递引用就可以进行修改原来的值:
public class UserController { public static void func(int[] temp){ temp={1,2,3,4,5,6,7}//会直接报错 } public static void main(String[] args) { int[] array={1,2,3,4,5,6,7};//不会报错 int[] array1;//直接会报错 array1={1,2,3,4,5} } }
数组初始化是属于整体初始化,那么这个整体初始化就只有一次机会,就是在定义的时候进行初始化,如上图所示:
_________________________________________________________
2.一维数组
1)创建数组
1)数组是一块连续的存储空间,他进行存储的是相同数据类型的元素,下标是从0开始的结束的位置是N-1;
2)数组的初始化分为动态初始化和静态初始化
2.1)动态初始化:
在我们进行创建数组的时候,直接指定数组中的元素的个数int[] array=new int[10]
2.2)静态初始化:
在进行创建数组的时候不进行指定元素个数,而是按照具体的数据内容来进行制定:
T[] 数组名称=new T[]{data1,data2,data3,data4} int[] array=new int[]{1,2,3,4,5,6,7}; double[] array1=new double[]{1.0,2.0,3.0}; int[] array2={1,2,3,4,5,6,7};
1)我们的静态初始化虽然没有进行指定数组的长度,但是编译器会在编译的过程中根据{}中的元素个数来自动进行确定数组的长度
2)在我们进行静态初始化的时候,{}中的数据类型必须和[]前面的数据类型保持一致
3)静态初始化可以进行简写,可以进行省略后面的new int[],但是编译器会自动进行还原
4)如果说没有对数组进行初始化,那么数组中的元素有默认值
int[] array; array=new int[]{1,2,3,4,5,6};
5)在JAVA中,如果定义了一个数组,那么这个数组没有进行初始化,那么就会有一个默认值
byte 0 short 0 int 0 long 0 float 0.0f double 0.0 char /u0000 boolean false 引用类型 null
int[] arr1={1,2,3,4,5,6};//直接赋值,静态初始化
int[] array=new int[4];//只分配内存,不进行初始化,不进行赋值,只有默认值
int[] array1=new int[]{1,2,3,4,5,6};
它们没有本质的区别,都是在堆上面
2)遍历数组,for循环可以拿到数组的下标,但是foreach不行;
//1.for循环进行打印
for(int i=0;i<arr1.length;i++)
{
System.out.println(arr1[i]);
}
//2.通过foreach的方式来进行打印
for(int s:arr1)
{
System.out.println(s);
}
//3直接调用静态方法,把数组中的所有元素转换成字符串进行返回
System.out.println(Arrays.toString(arr1));
}
3)自己实现一个数组转化成字符串的例子
public static String toString(int [] arr1)
{
if(arr1==1) return;
String ret="[";
for(int i=0;i<arr1.length;i++)
{
ret+=arr1[i];
if(i!=arr1.length-1)
{
ret+=",";
}
}
ret+="]";
return ret;
}
public static void main(String[] args) {
int[] arr1 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(toString(arr1));
}
数组转化成字符串
Arrays.toString(arr1)
int arr1[]=new int[]{1,2,3,4,5};
String str1=Arrays.toString(arr1);
System.out.println(str1);
4)数组的拷贝
什么是拷贝?
拷贝就是说你要有原内容,最终产生一个和原内容一模一样的内容,叫做拷贝
int[] array={1,2,3,4,5,6,7}; int[] array1=array;//不算拷贝,因为没有产生一份一个和原内存一模一样的内存地址
public static void main(String[] args) { int[] array1 = new int[]{1, 2, 3, 4, 5, 6}; int[] array2 = new int[array1.length]; for (int i = 0; i < array1.length; i++) { array2[i] = array1[i]; } System.out.println(Arrays.toString(array2)); }
给数组进行扩容:
int[] array1=new int[]{1,2,3,4}; int[] array2=Arrays.copyOf(array1,2*array1.length); System.out.println(Arrays.toString(array2)); //拷贝数组:两个参数必须都是整数,copyOf后面的参数必须是整数 public static int[] copyOf(int[] original, int newLength) { int[] copy = new int[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));//也是支持局部的拷贝的 //第一个参数:原始数组-->sourcepos //第二个参数:从哪一个位置开始进行拷贝 //第三个参数:目标数组--->movepos //第四个参数:开始放到目标数组的那一个位置 //第五个参数:你要进行拷贝多长 return copy; }
注意:当我们的数组中存储的是基本数据类型的时候,无论如何拷贝都不会出现问题,但是说如果存储的是引用数据类型,那么此时就需要进行考虑深浅拷贝的问题
public static double Avg(int[] array){ int sum=0; for(int i=0;i<array.length;i++){ sum=sum+array[i]; } return (sum/array.length)*(1.0); }
1)返回值是一个新的数组=Arrays.CopyOf(一开始要拷贝的数组,要拷贝的数组的长度)
int arr1={1,2,3,4,5,6,7,8,9};
int[] ret=Arrays.CopyOf(arr1,arr1.length());
System.out.println(Arrays.toString(ret));
2)System.ArrayCopy(原数组,来时拷贝的原数组的位置,现在数组开始拷贝的位置,长度);
int array[]={1,2,3,4,5,6,7};
int ret=new int{array.length];
System.ArrayCopy(array,0,ret,0,array.length);
System.out.println(Arrays.toString(ret));
3)Object的克隆方法,会对原对象拷贝一个副本
int[] arr1={1,2,3,4,5,6,7};
int ret[]=arr1.clone();
4)数组的各个部分进行赋值
5)数组中的其他常用方法
1)查找数组的元素
Array.binarySearch(原数组,开始查找的位置,最终查找到哪里,要查什么)
int arr1[]={1,2,3,4,5,6,7};
System.out.println(Arrays.binarySearch(arr1,2,5,4);
这个代码的语句是从二号位置到五号位置找4
2)拷贝数组的区间
int[] ret={1,2,3,4,5,6,7};
int[] arr1=Arrays.CopyOfRange(arr1,2,4);//左闭右开区间
3)判断两个数组是否相同
直接调用Arrays.equals(数组一,数组二);返回值是一个boolean类型
4)填充
int arr1[]=new int[10];
Arrays.fill(arr1,8);
是向arr1这个数组里面填充8里面的每一个元素都是8;
Arrays.fill(arr1,2,5,8);像这个数组的2到5位置填8;
5)检查一个数组是否有序
Arrays.sort(arr1);
6)数组转化成字符串
int arr1={1,2,3,4,5,6,7,8};
String html=Arrays.toString(arr1);
数组是在栈上,数组中的每一个元素在堆上
6)实现数组的二分查找
int[] array={1,2,3,4,5,6,7,8};
int ret=Arrays.binarySearch(array,8);
System.out.println(ret);
//1实现数组的二分查找
public static int binarysearch(int[] arr1,int key)
{
int left=0;
int right=arr1.length-1;
int mid=0;
while(left<=right)
{ mid=(right+left)/2;
if(arr1[mid]>key)
{
right=mid-1;
}else if(arr1[mid]<key)
{
left=mid+1;
}else{
return mid;
}
}
return -1;
}
数组的逆序
思路:前面i遇到偶数停下来,后面j遇到奇数停止下来,都停止了进行交换,直到两者进行交换
1)我们先定义两个下标,分别指向第一个元素和最后一个元素,进行交换两个位置的元素
2)我们让前一个下标自增,后一个下标进行自减,循环继续即可
//2实现数组的逆序
public static int[] resverse(int arr1[])
{
int left=0;
int right=arr1.length-1;
while(left<=right)
{
int temp=0;
temp=arr1[left];
arr1[left]=arr1[right];
arr1[right]=temp;
left++;
right--;
}
return arr1;
}
在一个数组中要求我们最后要把所有的偶数都移动挪到奇数前面
思路:
1)定义两个双指针,i下标指向0号位置的元素,让j指向array.length-1号未知的元素
2)在每一次循环当中i向后走,遇到奇数停下来,j向前走,遇到偶数停下来,本次循环即将结束的时候,将两个值进行交换
//3 实现所有偶数都移到奇数前面
public static void func(int[] arr1)
{
int left=0;
int right=arr1.length-1;
while(left<=right)
{
while (left<=right&&arr1[left]%2==0)
{
left++;//此时就可以保证left下标一定指向奇数
}
while(left<=right&&arr1[right]%2!=0)
{
right--;
}
int temp=arr1[left];
arr1[left]=arr1[right];
arr1[right]=temp;
}
}
并实现冒泡排序:
1)思路:
1)我们是将数组中的相邻相邻元素从前向后依次进行比较,如果说前一个元素比后一个元素大,那么就进行交换,这样一来,这一趟下来最大的元素就在数组的末尾
2)中心思路就是比较两个数组相邻的元素,进行交换
3)外层循环比较的是比较数字的趟数,比较数字的趟数=数字的总数-1,每一趟都比上次少比较一次(n-1-i)---->其实本质上来说不减这个i也是可以的
注意一下:
2)在第二层循环里面之前想着有三种写法:
1)array.length-1:可以,每一次j下标都会遍历整个数组的元素进行比较,比较到最后一个位置
2)array.length:这是不可以的,因为如果是这个条件,那么j最后就会走到array.length的位置,那么此时下面进行判断if(array[j]>array[j+1])的时候,j+1就会发生数组越界
3)array.length-1-i:比上一次少比较一次,因为后面的元素已经有序了
3)如果说在某一趟j进行向后遍历的过程中发现一次比较也没有发生,那么说明这个数组已经有序了,那么接下来接下来也不用再有多余的趟数了,检查某一趟之后是否有序
//4 实现冒泡排序
public static void sort1(int[] arr1)
{
for(int i=0;i<arr1.length-1;i++)
{ boolean flag=false;//这个条件不能放在循环的最外边,因为以后一直是true了
for(int j=0;j<arr1.length-1-i;j++)
{
if(arr1[j]>arr1[j+1])
{
int temp=arr1[j];
arr1[j]=arr1[j+1];
arr1[j+1]=temp;
flag=true;//如果有交换
}
}
if(flag==false)
{
return;
}
}
}
7)数组的循环右移
第一种解决思路:一次一次的进行旋转
假设数组里面的所有的值是:1,2,3,4,5,6,7,假设数组此时进行循环右移,我们来看一下三次的结果有什么规律吗?
1,2,3,4,5,6,7--->现在我们进行右移第一次--->7,1,2,3,4,5,6
7,1,2,3,4,5,6--->现在我们进行右移第二次--->6,7,1,2,3,4,5
6,7,1,2,3,4,5--->现在我们进行右移第三次--->5,6,7,1,2,3,4
我们在这里面希望做的是每一次循环右移一位的时候,用一个变量temp保存数组中最后一个位置的元素,然后让剩余数组的元素向后移,然后再让数组的第一个位置的元素等于刚才的temp的变量
class Solution { public void rotate(int[] nums, int k) { for(int i=0;i<k;i++){ int temp=nums[nums.length-1]; for(int j=nums.length-2;j>=0;j--){ nums[j+1]=nums[j]; } nums[0]=temp; } } }
时间复杂度:O(K*N)
第二种解决思路:
第二种思路就是以空间来进行换取时间,咱们可以把数组的后K个元素放到新开辟的数组的前K个位置,再把原数组剩余的N-K个元素放到新开辟数组的末尾
class Solution { public void rotate(int[] nums, int k) { if(k>nums.length){ k=k%nums.length;//这个情况是进行处理要旋转的次数大于数组的长度 } int[] array=new int[nums.length]; int i=0; int count=0; for(i=nums.length-k;i<nums.length;i++){ array[count]=nums[i]; count++; }//先将数组的后K个元素进行拷贝 i=k; for(int j=0;j<nums.length-k;j++){ array[i]=nums[j]; i++; }//再将数组的N-k个元素放在数组的新位置 for(i=0;i<array.length;i++){ nums[i]=array[i]; }//将新数组的原来的数据放在新位置 } } 控制台
第三种解决思路:
先将数组的后k个进行逆置
再将数组的n-k个进行逆置
最后再将整体进行逆置
假设我们要进行旋转的数组是1,2,3,4,5,6,7;K=3
先将后K个进行逆置:1,2,3,4,7,6,5
再将N-K个进行逆置:4,3,2,1,7,6,5
再将整体进行逆置:5,6,7,1,2,3,4
leetcode189题,旋转数组
public static void resverse(int [] arr1,int left,int right)
{
while(left<=right)
{
int temp=arr1[left];
arr1[left]=arr1[right];
arr1[right]=temp;
left++;
right--;
}
}
public static void reverseKey(int[] arr1,int n)
{ if(n>arr1.length-1)
{
n=n%arr1.length-1;
}
resverse(arr1,arr1.length-n,arr1.length-1);
resverse(arr1,0, arr1.length-1-n);
resverse(arr1,0, arr1.length-1);
}
public static void main(String[] args) {
int [] array={1,2,3,4,5,6,7,8,9};
reverseKey(array,3);
System.out.println(Arrays.toString(array));
}
8)判断一个数是否是素数
public static int isyear(int n)
{
for(int i=2;i<=Math.sqrt(n);i++)
{
if(n%i==0)
{
return 0;
}
}
return 1;
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int num=scanner.nextInt();
if(isyear(num)==1)
{
System.out.println(num+"是素数");
}else{
System.out.println(num+"不是素数");
}
}
下面这两个题全部用异或来进行解决:
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)思路:使用异或(相同为0,不相同为1)
两个相同的数字进行异或之后结果是0,0和任何数字进行异或之后结果还是原来这个数
leetcode
1)给定一个非空整数数组,数组num中包含从0到n的所有整数,但是其中缺少了一个,找出缺失的整数,要求在O(N)的时间内完成输入用例:9,6,4,2,3,5,7,0,1
输出用例:8
2^3=1
00010
00011
相同的数异或之后就没了,0和任何数字异或结果都是原来的数字,相同为0,相异为1比如说1,3,1进行异或,不用考虑先后顺序,结果就是1
思路:将数组中的数依次和0-N的数进行异或
0001------>1 0011------>3 -------- 0010-----进行异或的结果在和1进行异或 0001----->1 ------- 0011--->结果就是3
让数组中的数依次和0-N的所有数进行异或,最后剩下的那个数字就是缺的那个数字public static int func(int[] arr1) { int ret=0; for(int i=0;i<arr1.length;i++) { ret=ret^arr1[i]; } for(int j=0;j<arr1.length+1;j++) { ret=ret^j; } return ret; }
2)给定一个非空的整数数组,除了某一个元素只出现一次之外,其余每个元素均只出现了两次,找到那个只出现了一次的元素:
输入:2,2,1 输出:1
运算符也存在交换率,因为两个相同的数字异或之后结果是0,0又和任何数字异或还是本身,那么
public static int GetOnlyOne(int[] array){ int ret=array[0]; for(int i=1;i<array.length;i++){ ret=ret^array[i]; } return ret; } public static void main(String[] args) { int[] array={1,1,3}; System.out.println(GetOnlyOne(array)); }
实在不行就把这几个数的二进制序列写出来,自己试一下进行异或,最后的结果就是相同的数字消失了
拓展:假设如果是说在一个数组中有两个不一样的数字,那么该如何找出两个不一样的数字呢?恰好有两个数字超过一次
1)我们先将整体的所有数字进行异或,其实最终得到的结果就是两个单身狗得到的结果,然后再去按照这个结果找到比特位为1的位置,既然他们的比特位是1了,那么这两位的数字肯定是不相同的,我们就可以按照这个标准来进行分组:
2)比如说现在有一组数:1,2 ,3 ,6,2,1咱们开始进行写出它们的二进制序列:
1->0001
2->0010
3->0011
4->0100
6->0110
咱们将数组中的所有数字进行异或得到最终结果是:0101,我们观察到最终异或的结果中二进制序列为1的位置,是他们不一样的二进制位,咱们按照第一个二进制位是否是1来进行分成两个不同的组,分别对两个组来进行异或:
3)第一组:[2,2,6]第二组:[1,1,3]这样对两组分别进行异或,最终两组都得到一个值
4)也可以用哈希表来做
9)判断一个数是否是水仙花数
水仙花数是指一个 3 位 数,它的每个位上的数字的 3次幂之和等于它本身。例如:1^3 + 5^3+ 3^3 = 153
思路:
1)先算是几位数
2)求每一位数字是几,位的次幂求出来进行求和
public static boolean isNum(int n)
{
//1 首先判断它是几位数
int num=n;
int s1=0;
int sum=0;
int count=0;
while(num!=0)
{
num=num/10;
count++;
}
num=n;
while(num!=0)
{
s1=num%10;
num=num/10;
sum=(int)(sum+Math.pow(s1,count));
}
if(sum==n)
{
return true;
}else{
return false;
}
}
10)求两个数的最大公约数
第一种方法: int min=0; if(num1>num2) { min=num2; }else{ min=num1; } while(min>0) { if(num1%min==0&&num2%min==0) { return min; }else{ min--; } } return min;
辗转相除法:求直到取模为0之后最近的那一个除数
以24和18来进行举例:
1)24%18=6
2)18%6=3
Scanner scanner=new Scanner(System.in); int m=scanner.nextInt(); int n=scanner.nextInt(); int model=m%n; while(model!=0){ m=n; n=model; model=m%n; } System.out.println("最大公约数是"+n); }
在Arrays类中重写equals方法,表示比较数组中的每一个元素是否相等
public static boolean equals(int[] a, int[] a2) { if (a==a2) return true;//如果说他们指向一个对象,直接返回true if (a==null || a2==null) return false; int length = a.length;//在进行比较它们的长度,长度不等直接返回false if (a2.length != length) return false; for (int i=0; i<length; i++) if (a[i] != a2[i])//一个元素不相等直接返回false return false; return true; }
3)二维数组:他的本质上是一个一维数组,只不过二维数组中的每一个元素又是一维数组;
定义二维数组:分成规则的二维数组和不规则的二维数组
int array[][]={{1,2,3},{4,5,6}};
int arr2[][]=new int[][]{{1,2,3},{4,5,6}};//不需要写行数列数
int arr1[][]=new int[2][];//必须指定行
int array1=new int[2][3];//没有给数组进行赋值
在Java中二位数组要指定行,列可以自己进行推导
int[][]array=new int[2][]{{1,2,3},{4,5,6}};
array[0]=new int[3]; {1,2,3}
array[1]=new int[2]; {4,5,6}
[]中写个数就不要写初始化,能够进行初始化就不要写个数
int[][] array=new int[3][]; //二维数组中的每一个元素,也就是说每一个一维数组是空,三个一位数组是空
打印二维数组:
已知行数和列数: int[][] array=new int[][]{{1,2,3,4},{5,6,7,8}}; for(int i=0;i<2;i++){ for(int j=0;j<4;j++){ System.out.print(array[i][j]); } System.out.println(); }
咱们的二维数组,其实本质上二维数组的每一个元素就是一种特殊的一维数组:
假设现在有一个二行三列的二维数组:
二维数组:
行数---->二维数组中每一个一维数组有多少个
这样就是说二维数组中的每一个一维数组长度都不是固定的,上面所述的东西都是在堆上面
//1打印二维数组 for(int i=0;i<array.length;i++){ for(int j=0;j<array[i].length;j++){ System.out.print(array[i][j]+" "); } System.out.println(); } //2以字符串的形式打印出来 System.out.println(Arrays.deepToString(array)); Arrays.toString(array);//这是打印出来的是两个地址 //3运用foreach进行循环打印 for(int[] temp:array)//这里面不可以用整形来接受,拿一个一维数组来进行接收 { for(int x:temp)//因为二维数组中的每一个元素都是一维数组,我们先用temp数组来接受二维数组中的每一个元素,然后再进行循环打印 { System.out.println(x); } }
多数元素:力扣
假设数组元素是10个,那么你需要找到数组元素个数超过n/2也就是超过出现大于5次的元素:
方法1:对数组进行排序,返回中间元素
class Solution { public int majorityElement(int[] nums) { Arrays.sort(nums); return nums[nums.length/2]; }
方法2:我们先固定一个i位置的元素不动,每一次循环让j=i,j从i位置一直向后走,定义一个count变量,如果说他出现的次数超过了数组长度的1/2,直接进行返回
class Solution { public int majorityElement(int[] array) { int count=0; for(int i=0;i<array.length;i++){ count=0; for(int j=i;j<array.length;j++){ if(array[i]==array[j]){ count++; } } if(count>array.length/2){ return array[i]; } } return -1; } }
方法三:使用HashMap来进行结题:
class Solution { public static int majorityElement(int[] array) { int key=array.length/2; HashMap<Integer,Integer> map=new HashMap<>(); for(int i=0;i<array.length;i++){ if(!map.containsKey(array[i])){ map.put(array[i],1); }else{ int count=map.get(array[i]); count++; map.put(array[i],count);//这里面不可以这么写map.put(array[i],count++); } } Set<Map.Entry<Integer,Integer>> set=map.entrySet(); for(Map.Entry<Integer,Integer> entry:set){ if(entry.getValue()>key){ System.out.println(entry.getKey()); return entry.getKey(); } } return -1; } public static void main(String[] args) { int[] nums = {2,2,1,1,1,2,2}; majorityElement(nums); } }
四:使用投票法来进行解决:
思路:摩尔投票法,以此遍历数组,将其中不相同的元素进行抵消删除,直到遍历完整个数组,剩下的即为数组中总数超过一半的元素
1)我先让ret=array[0],然后写出for循环,i向后进行遍历,如果说array[i]=ret,那么说明支持ret的人增多,就++
2)反之ret!=array[i],那么计数器就减减,说明反对ret的值也就越多
3)啥时候count=0,我们就让ret=array[i+1]
class Solution { public int majorityElement(int[] array) { int ret=array[0]; int count=0; for(int i=0;i<array.length;i++){ if(ret==array[i]){ count++; }else{ count--; } if(count==0){ ret=array[i+1]; } } return ret; } }
给定一个数组:判断数组中连续三个数字是否都是奇数:
1)定义一个变量,计数器向后++,如果计数器的值等于3,那么直接返回true,ruguo
lass Solution { public boolean threeConsecutiveOdds(int[] array) { int count=0; for(int i=0;i<array.length;i++){ if(array[i]%2==1){ count++; if(count==3){ return true; } }else{ count=0; } } return false; } }
2)从数组的第3个位置开始向后进行遍历,依次比较相邻元素是否是奇数
class Solution { public boolean threeConsecutiveOdds(int[] array) { for(int i=2;i<array.length;i++){ boolean flag=IsTrue(i,i-1,i-2,array); if(flag==true){ return true; } } return false; } public boolean IsTrue(int first,int second,int third,int[] array){ if(array[first]%2==1&&array[second]%2==1&&array[third]%2==1){ return true; } return false; } }