Java基础算法

一、二分法

什么是二分法?使用二分法时需要注意什么?如何用代码实现?

  二分法查找(Binary Search)也称折半查找,是指当每次查询时,将数据分为前后两部分,再用中值和待搜索的值进行比较,如果搜索的值大于中值,则使用同样的方式(二分法)向后搜索,反之则向前搜索,直到搜索结束为止。
  二分法使用的时候需要注意:二分法只适用于有序的数据,也就是说,数据必须是从小到大,或是从大到小排序的。

/**
 * 二分查找,返回该值第一次出现的位置(下标从 0 开始)
 * @param arr      查询数组
 * @param key      要查找的值
 * @return int
 */
private static int binarySearch(int[] arr,int key){
  int min = 0;
  int max = arr.length-1;
  int mid;
  if(key<arr[min] || key>arr[max]){
    return -1;
  }
  while(min<=max){
    mid = (min+max)/2;
    if(key==arr[mid]){
       return mid;
    }else if(key<arr[mid]){
      max = mid-1;
    }else{
      min = mid+1;
    }
  }
}

二、斐波那契数列

什么是斐波那契数列?用代码如何实现?

  斐波那契数列(Fibonacci Sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711…… 在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用。
  斐波那契数列的特征:第三项开始(含第三项)它的值等于前两项之和。

/**
 * 斐波那契数列,返回第n次数列的值(下标从 0 开始)
 * @param n      次数
 * @return int
 */
private static int fibnacci(int n){
  if(n==0 || n==1){
    return n;
  }else{
    return fibnacci(n-1)+fibnacci(n-2);
  }
}

三、兔子问题

一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?请使用代码实现。

先来分析一下,

第一个月:有 1 对小兔子;
第二个月:小兔子变成大兔子;
第三个月:大兔子下了一对小兔子;
第四个月:大兔子又下了一对小兔子,上个月的一对小兔子变成了大兔子;
……
最后总结的规律如下列表所示:

/**
 * 兔子问题其实就是斐波那契数列,返回第n次数列的值(下标从 0 开始)
 * @param n      第n个月
 * @return int   兔子n月后的数量
 */
private static int rabbit(int n){
  if(n==0 || n==1){
    return n;
  }else{
    return rabbit(n-1)+rabbit(n-2);
  }
}

四、冒泡排序

冒泡排序的执行流程是:

  对数组中相邻的数据依次作比较;
  如果前面的数大于后面的数,则两个数交换位置,经过一轮比较后,大数就会跑到最后面;
  再用相同的方法依次对相邻的数据作比较,这样就会得到一组从小到大的数据,即升序数组。

/**
 * 冒泡排序,对数组进行排序
 * @param arr      需要排序的数组
 * @return void
 */
private static void bubbleSort(int[] arr){
  int temp;
  for(int x=0;x<arr.length;x++){
    for(int y=0;y<arr.length-1-x;y++){
      if(arr[y]>arr[y+1]){
          temp = arr[y+1];
          arr[y+1]=arr[y];
          arr[y]=temp;
       }
    }
  }
}

五、选择排序

选择排序,其实现思路是每一轮循环找到最小的值,依次排到数组的最前面,这样就实现了数组的有序排列。

/**
 * 选择排序,对数组进行排序
 * @param arr      需要排序的数组
 * @return void
 */
private static int[] selectSort(int[] arr){
  int temp;
  int index;
  for(int x=0;x<arr.length-1;x++){
    index=x;
    for(int y=x+1;y<arr.length;y++){
       if(arr[index]>arr[y]){
          index=y; 
        }
    }
    if(index!=x){
      temp=arr[index];
      arr[index]=arr[x];
      arr[x]=temp;
    }
  }
  return arr;
}

六、插入排序

插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,选择一个未排序的数据,在已排序序列中从后向前扫描(进行大小比较),找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

public static int[] insertSort(int[] arr){
  for(int x=1;x<arr.length;x++){
    int temp = arr[x];
    int y;
    for(y=x;y>0 && temp>arr[y-1];y--){
      arr[y]=arr[y-1];
    }
    arr[y]=temp;
  }
  return arr;
}


七、快速排序

  • 首先设定一个分界值,通过该分界值把数组分为左右两个部分;
  • 将大于等于分界值的元素放到分界值的右边,将小于分界值的元素放到分界值的左边;
  • 然后对左右两边的数据进行独立的排序,在左边数据中取一个分界值,把小于分界值的元素放到分界值的左边,大于等于分界值的元素,放到数组的右边;右边的数据也执行同样的操作;
  • 重复上述操作,当左右各数据排序完成后,整个数组也就完成了排序。

  详细参考博客链接 点击这里跳转

private static void quickSort(int[] arr,int left,int right){
  if(left>right){
    return;
  }

  int key=arr[left];
  int i=left;
  int j=right;
  while(i<j){
    //从后向前找到比key小的值
    while(key>=arr[j] && i<j){
      j--;
    }
    //从前向后找到比key大的值
    while(key<=arr[i] && i<j){
      i++:
    }
    //如果i和j依旧没重合,i与j对应的值交换
    if(i<j){
      int temp=arr[i];
      arr[i]=arr[j];
      arr[j]=temp;
    }
    //交换之后,比key小的值都在左边,比key大的值都在右边
  }
  //交换key值(原来的key值在左端)
  arr[left]=arr[i];
  arr[i]=key;
  
  //递归对key值左端比较和交换
  quickSort(arr,left,i-1);
  //递归对key值右端端比较和交换
  quickSort(arr,i+1,right);
}



八、两数之和

  给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。*
  假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现,可以按任意顺序返回答案。

private static int[] twoSum(int[] nums, int target){
  for(x=0;x<nums.length;x++){
    for(y=nums.length-1;x<y;y--){
      if((nums[x]+nums[y])==target){
        return new int[]{x,y};
      }
    }
  }
  return new int[0];
}

  复杂度分析

  • 时间复杂度:O(N^2),其中 N是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。

  • 空间复杂度:O(1)。

  • 方法二:哈希表

  注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)降低到 O(1)。
  这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

private static int[] twoSum(int[] nums, int target){
  Map<Integer,Integer> hashtable = new HashMap<Integer,Integer>();
  for(x=0;x<nums.length;++x){
    if(hashtable.containsKey(target-nums[x]){
      return new int[]{x,hashtable.get(target-nums[x])};
    }
    hashtable.put(nums[x],x);
  }
  return new int[0];
}

  复杂度分析

  • 时间复杂度:O(N),其中 NN 是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
  • 空间复杂度:O(N),其中 NN 是数组中的元素数量。主要为哈希表的开销
posted @ 2021-07-27 13:27  落花桂  阅读(207)  评论(0编辑  收藏  举报
返回顶端
Live2D