JAVA基础语法以及一些常见的练习

无符号右移和右移的区别:

无符号右移:不管正负标志位为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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值