在java的世界中,算法也是很重要的,通常情况下,我们的职位对于算法并没有很大的需求,因为一般都是你在未来想做到更高的职位的话,拿个年薪四五十万的时候,算法就是必不可少的。基础阶段掌握一些基本算法还是很有必要的。
在介绍这些必掌握的算法之前,先科普 一下一些基础知识
-
算法消耗的时间:一个算法消耗的时间是指算法中所有语句执行的时间的总和,每条语句执行的时间等于语句执行的次数乘以执行一次所需要的时间。
-
常用的算法时间复杂度:
怎么计算的呢?有一个粗略的计算方法:
看该段程序执行了多少次,求和即可。
for(i= 0;i<=n;i++){
for(j=0;j<=i;j++){
s; //伪代码 (n+1)(n+2)/2 时间复杂度
}
- 空间复杂度:定义为该算法所耗费的 存储空间的数量级,比如定义了一个数组大小为n的数组,则它的空间复杂度就是O(n),如果只是需要一个变量则它的空间复杂度就是O(1)
那就介绍一下大学中用到的一些算法吧(当然其中的二分查找,冒泡与快速排序等,面试的时候也是经常会遇到的哟)
-
稳定的排序方法: 直接插入排序/冒泡排序/归并排序/基数排序
-
不稳定的排序算法:快速排序
/简单选择排序/希尔排序/
二分法查找
定义:
二分法的使用前提就是有序的一串数据,不然无法使用,二分法的核心在于定义两个索引,找到中间值,与我们要找的数值比较,然后再进行求中间值,再进行比较。
代码如下:
public class ceshi01 {
public static void main(String[] args) {
int [] arr={1,2,3,4,5,6,7,8,9,10};
int num=8;
System.out.println("hao");
int index=getindex(arr,num);
System.out.println(index);
}
public static int getindex(int [] arr,int num){
int min=0;
int max=arr.length-1;
int mid;
while(min<max){
mid = (max+min)/2;
if(arr[mid]<num){
min=mid+1;
}else if(arr[mid]>num){
max=mid-1;
}else{
return mid;
}
}
return -1;
}
}
直接插入排序
核心:找到第二个元素为标志,从左到右依次前面的元素比较,直接插到最小的位置,然后标志往后移动,重复上述过程即可。
代码:
public class ceshi05 {
public static void main(String[] args) {
int [] arr={48,62,35,77,55,14,35,98};
for (int i = 1; i < arr.length; i++) {
int j;
int temp=arr[i];//存储要进行插入的元素
for(j=i-1;j>=0&&arr[j]>temp;j--){ //与离它最近的元素开始比较 如果比它大就换位
arr[j+1]=arr[j];
}
arr[j+1]=temp;
printArray(arr);
}
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
}
冒泡排序
核心:通过相邻的元素之间比较,大的放右边,小的放左边。
代码:
public class ceshi02 {
public static void main(String[] args) {
int [] arr={5,2,1,4,3};
maoPao(arr);
}
/*
冒泡排序
*/
private static void maoPao(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j]> arr[j+1]){
int temp= arr[j];
arr[j]= arr[j+1];
arr[j+1]=temp;
}
}
}
printArray(arr);
}
/**
* 打印数组
* @param arr
*/
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
System.out.println();
}
}
归并排序
核心:这个要看你是准备几个为一组,几个为一组就是几路归并排序,比如2-路归并排序就是将待排序列中相邻的两个有序序列合并成一个有序序列。
代码:
public class ceshi06 {
public static void main(String[] args) {
int[] arr = {19, 13, 5, 27, 1, 26, 31, 16,45};
sort(arr,0, arr.length-1);
printArray(arr);
}
//递归进行排序
public static void sort(int[] arr, int left, int right) {
if (left == right) return;
//分两半
int mid = left + (right -left) / 2;
//左边排序
sort(arr, left, mid);
//右边排序
sort(arr, mid + 1, right);
merge(arr, left, mid + 1, right);
}
//对有序的两个部分进行合拼
public static void merge(int[] arr, int left, int right, int rightBound) {
int mid = right - 1;
int[] temp = new int[rightBound - left + 1];
int i = left;
int j = right;
int k = 0;
while (i <= mid && j <= rightBound) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= rightBound) {
temp[k++] = arr[j++];
}
// printArray(temp);
//将排好序的新数组赋值为原来的数组
for (int m = 0; m < temp.length; m++) {
arr[left+m]= temp[m];
}
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
基数排序
基数排序在本质上是 一种多关键字的一种排序,以三位数为例,它首先按照个位数的大小进行排序,然后再按照十位数进行排序,最后再按照百位排序即可。
这里面有一个很重要的思想 在这个思想就是桶思想,所谓的桶思想就是我们把我们的每个数字我在我们实现建立的数组里面,数组的下标就是元素的值,每一次遇到与数组下标相同的值都让数组对应下标的值加1就可以了
/**
* @author: 岐神~汤圆
* @Description: 基数排序
* @Attention:
* @date: 2020/12/8 16:56
* @version:1.0
*/
public class ceshi13 {
public static void main(String[] args) {
int [] arr={421,240,115,532,205,430,124};
int [] result=sort(arr);
System.out.println(Arrays.toString(result));
}
private static int[] sort(int[] arr) {
int [] result=new int[arr.length];
int [] count=new int[10];//这个数组就相当于一个桶 里面存放的是0-9之间数字的个数 也就是 是对应的数字就放进去
//这个循环是为了表示循环的是哪一位数字
for (int i = 0; i < 3; i++) {
int division=(int) Math.pow(10,i);
System.out.println(division);
for (int j = 0; j < arr.length; j++) {
//获取每一位数字 第一次进来的是获得数组中所有的个位数字
int num=arr[j]/division%10;
System.out.print(num+" ");
count[num]++;
}
System.out.println();
System.out.println(Arrays.toString(count));
for (int m = 1; m < count.length; m++) {
count[m]=count[m]+count[m-1];
}
System.out.println("===========");
System.out.println(Arrays.toString(count));
System.out.println("===========");
for (int n = arr.length-1; n>=0;n--) {
int num=arr[n]/division%10;//比如说最后一个元素为2,那它出现的位置就是count[2]的值-1的位置
result[--count[num]]=arr[n];
}
System.arraycopy(result,0,arr,0,arr.length);
Arrays.fill(count,0);
}
return result;
}
}
这个算法的难点在于
这一部分的操作,怕你们不太明白,我在这里简单解释一下,这里面的count[m]=count[m]+count[m-1]代表的是数组中值代表的是与索引值相等的元素的最后一个排完序之后应该出现的位置。
快速排序
核心:以第一个为基数,分别设置两个标志,分别是第一个元素与最后一个元素,分别于基数比较,左边的标志大于基数,右边的标志小于基数式,两者交换,当两个标识在同一个位置的时候与基数交换,这个时候基数就在最终的位置,左边的元素都比其小,右边的元素都比其大,最后递归执行即可。
代码:
public class ceshi04 {
public static void main(String[] args) {
int [] arr={6,1,2,7,9,3,4,5,10,8};
quiteSort(arr,0,arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
/**
* 快速排序的核心代码
* @param arr
* @param left
* @param right
*/
public static void quiteSort(int [] arr,int left,int right) {
if (left >right) {
return;
}
int left0 = left;
int right0 = right;
while (left != right) {
while (arr[right] >= arr[left0] && left < right) {
right--;
}
while (arr[left] <= arr[left0] && left < right) {
left++;
}
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
int temp = arr[left0];
arr[left0] = arr[left];
arr[left] = temp;
quiteSort(arr, left0, left - 1);
quiteSort(arr, left + 1, right0);
}
}
简单选择排序
简单排序的定义:我们第一次查出最小的元素,并与第一个位置交换,然后从第一个以外的元素求出最小值在与剩下元素的第一个交换位置,一直到最后。这样就完成了我们的简单排序。
public class ceshi14 {
public static void main(String[] args) {
int [] arr={1,4,3,6,9,2,8,7};
for (int j = 0; j < arr.length; j++) {
int sum=arr[j];//记录的是最小值
int flag=0;
for (int i = j+1; i < arr.length; i++) {
if(sum>arr[i]){
sum=arr[i];
flag=i;
}
}
if(flag!=0) {
int c = arr[j];
arr[j] = sum;
arr[flag] = c;
}
for (int i : arr) {
System.out.print(i);
}
System.out.println();
}
}
}
希尔排序
希尔排序的思想在于:每一次设置一个间隔。让指定间隔的数据进行排序,所用的排序的方法就是插入排序;进行完一轮的排序之后,再进行缩小间隔,接着开始排序,最后的间隔缩小到1为止。
一般间隔越大,一定的次数越少,间隔越小,移动的距离越少
一般都不怎么用这个算法
/**
* @author: 岐神~汤圆
* @Description: 希尔排序
* @Attention: 这个其实就是在插入排序的基础上进行改进的一种算法
* @date: 2020/12/11 19:05
* @version:1.0
*/
public class ceshi15 {
public static void main(String[] args) {
int [] arr={2,3,21,4,5,15,6,9,10,16,18,13,1,17,11};
sort(arr);
print(arr);
}
/**
* 打印数组
* @param arr
*/
public static void print(int[] arr) {
for (int i : arr) {
System.out.print(i+" ");
}
}
public static void sort(int[] arr) {
//这段代码主要是为了为了确定我们的间隔个数,比二分法的间隔次数更加快, 这个序列叫做;Knuth
int h=1;
while(h<arr.length/3){
h=h*3+1;
}
for(int grap=h;grap>0;grap=(grap-1)/3){
for (int i=grap;i<arr.length;i++){
for (int j=i;j>grap-1;j-=grap){
if(arr[j]<arr[j-grap]){
change(arr,j,j-grap);
}
}
}
}
}
public static void change(int[] arr, int j, int i) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
总结:
知道这些算法的原理是很重要的,具体算法的实现就看个人了,其实刚开始阶段没有掌握也没有问题,但是算法思想一定要有的。
写算法的核心:
- 能将大问题转化为一个小问题,最慢慢去向最终的结果靠拢,只是说算法而言,因为算法难,我们直接从整体考虑不容易写出来
- 先粗略化,写的差不多再考虑算法的优化