常见算法浅析
针对Java工程师职位,介绍几种常见的算法
1、二分查找
解析:
要求待查的序列有序,每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程。直到查到为止,否则序列中没该关键字!
代码演示:
package com.my.day02;
/**
* @author shi_meng_yong
* @date 2020/6/25 14:45
* 二分查找算法演示
*/
public class Demo01 {
public static void main(String[] args) {
int arr[]={1,3,4,5,6,7,9,10};
int a=9;
int search = search(arr, a);//查询出的数组下标+1
System.out.println(search);//打印出该关键字所在位置
System.out.println(arr[6]);//验证是否是下标+1
}
public static int search(int [] arr,int a){
int left=0;//定义为0d的变量,便于后面判断
int right=arr.rightgth-1;//数组一半的长度
int mid;//用于更新每次数组的中间长度
while (left<=right){//开始循环 条件rightgth>=0
mid=(left+right)/2;//取中间长度
if(arr[mid]==a){//取中间数 判断是否是关键字
return mid+1;//
}else if(arr[mid]<a){//若中间数小于关键字,则向后半部分查询
left=mid+1;//长度更改为中间数+1
}else if(arr[mid]>a){//若中间数大于关键字则向前半部分查询
right=mid-1;//长度更改为中间数-1
}
}
return -1;//未找到为-1
}
}
2、冒泡排序算法
步骤:
- 比较前后相邻的两个数据,如果前面数据大于后面的,就将这两个数据互换
- 照此方法,对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就落在数组的第N-1位置
- N=N+1,如果N不为0,则重复上面两个步骤,否则排序完成
代码演示:
package com.my.day02;
import java.util.Arrays;
/**
* @author shi_meng_yong
* @date 2020/6/25 15:24
*
* 冒泡排序算法
*/
public class Demo02 {
public static void main(String[] args) {
int arr[]={2,3,5,1,9,8,6,22,21,10};//定义无序数组
int b=arr.length;//获取数组长度
bubbleSort(arr,b);//执行冒泡排序方法
System.out.println(Arrays.toString(arr));//打印出数组
}
public static void bubbleSort(int[]arr,int b){
int m, n;
for (m=0;m<b;m++){
for(n=1;n<b-m;n++){
if(arr[n-1]>arr[n]){
int temp;
temp=arr[n-1];
arr[n-1]=arr[n];
arr[n]=temp;
}
}
}
}
}
3、插入排序算法
解析:
对未排序数据,在已排序序列中从后向前扫描,找到相应位置插入。插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着, 一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。 为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。
代码演示:
package com.my.day02;
import java.util.Arrays;
/**
* @author shi_meng_yong
* @date 2020/6/25 15:42
*/
public class Demo03 {
public static void main(String[] args) {
int arr[] = {2, 4, 5, 1, 6, 8, 0, 9};//构建无序序列
sort(arr);//执行插入排序
System.out.println(Arrays.toString(arr));//打印
}
public static void sort(int arr[]){
for (int i = 1; i < arr.length; i++) {
//插入的数
int insertVal = arr[i];
//被插入的位置(准备和前一个数比较)
int index = i - 1;
//如果插入的数比被插入的数小
while (index >= 0 && insertVal < arr[index]) {
//将把 arr[index] 向后移动
arr[index + 1] = arr[index];
// 让 index 向前移动
index--;
}
// 把插入的数放入合适位置
arr[index + 1] = insertVal;
}
}
}
4、快速排序算法
解析:
选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的),一般选择序列的第一个元素。开始循环后,从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。 找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值来说,这样左右两边就是有序的
代码演示:
package com.my.day02;
import java.util.Arrays;
/**
* @author shi_meng_yong
* @date 2020/6/25 16:06
*
* 快速排序算法
*/
public class Demo04 {
public static void main(String[] args) {
int arr[]={1,5,2,4,3,23,99,88,33,11,0,42,67,63,87,98,101,92,114};//定义无序序列
int high=arr.length-1;//获取序列最大长度
sort(arr,0,high);//执行快速排序方法
System.out.println(Arrays.toString(arr));//打印排序后的序列
}
public static void sort(int[] a,int low,int high){
int start = low;
int end = high;
int key = a[low];
while(end>start) {
//从后往前比较
while (end > start && a[end] >= key)
// 如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较
end--;
if (a[end] <= key) {
int temp = a[end];
a[end] = a[start];
a[start] = temp;
}
//从前往后比较
while (end > start && a[start] <= key) {
//如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置
start++;
if (a[start] >= key) {
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用
}
//递归
if (start > low) sort(a, low, start - 1);//左边序列。第一个索引位置到关键值索引-1
if (end < high) sort(a, end + 1, high);//右边序列。从关键值索引+1 到最后一个
}
}
}
5、希尔排序算法
解析:
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序” 时,再对全体记录进行依次直接插入排序
步骤:
-
操作方法:选择一个增量序列 t1, t2, …, tk,其中 ti>tj, tk=1;
-
按增量序列个数 k,对序列进行 k 趟排序;
-
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
代码演示:
package com.my.day02;
import java.util.Arrays;
/**
* @author shi_meng_yong
* @date 2020/6/25 16:44
* 希尔排序算法
*/
public class Demo05 {
public static void main(String[] args) {
int arr[]={4,2,0,5,1,3};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void shellSort(int[] a) {
int dk = a.length/2;
while( dk >= 1 ){
ShellInsertSort(a, dk); dk = dk/2;
}
}
private static void ShellInsertSort(int[] a, int dk) {
//类似插入排序,只是插入排序增量是 1,这里增量是 dk,把 1 换成 dk 就可以了
for (int i = dk; i < a.length; i++) {
if (a[i] < a[i - dk]) {
int j;
int x = a[i];//x 为待插入元素
a[i] = a[i - dk];
for (j = i - dk; j >= 0 && x < a[j]; j = j - dk) {
//通过循环,逐个后移一位找到要插入的位置。
a[j + dk] = a[j];
}
a[j + dk] = x;//插入
}
}
}
}
6、归并排序算法
解析:
将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
代码演示:
package com.my.day02;
/**
* @author shi_meng_yong
* @date 2020/6/25 16:51
* 归并排序算法
*/
public class Demo06 {
public static void main(String[] args) {
int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data);
mergeSort(data);
System.out.println("排序后的数组: ");
print(data);
}
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
if (left >= right)
return; // 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
sort(data, left, center);
// 对右边数组进行递归
sort(data, center + 1, right);
// 合并
merge(data, left, center, right);
print(data);
}
/**
* 将两个数组进行归并,归并前面 2 个数组已有序,归并后依然有序 *
* @param data
* 数组对象
* @param left
* 左数组的第一个元素的索引
* @param center
* 左数组的最后一个元素的索引, center+1 是右数组第一个元素的索引
* @param right * 右数组最后一个元素的索引
* */
public static void merge(int[] data, int left, int center, int right) {
// 临时数组
int[] tmpArr = new int[data.length];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个 while 只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = data[mid++];
}
while (left <= center){
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原 left-right 范围的内容被复制回原数组)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
7、桶排序算法
解析:
把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最后合并,计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况
1.找出待排序数组中的最大值 max、最小值 min
2.我们使用 动态数组 ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量(maxmin)/arr.length+1
3.遍历数组 arr,计算每个元素 arr[i] 放的桶
4.每个桶各自排序
package com.my.day02;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
/**
* @author shi_meng_yong
* @date 2020/6/25 19:53
* 桶排序算法
*/
public class Demo07 {
public static void main(String[] args) {
int arr[]={2,3,1,5,0};
int[] ints = bucketSort(arr, 3);
System.out.println(Arrays.toString(ints));
}
public static int[] bucketSort(int arr[], int bucketCount) {
int len = arr.length;
int[] result = new int[len];
int min = arr[0];
int max = arr[0];
//找到最大值和最小值
for (int i = 1; i < len; i++) {
min = min <= arr[i] ? min: arr[i];
max = max >= arr[i] ? max: arr[i];
}
//求出每一个桶的数值范围
int space = (max - min + 1) / bucketCount;
//先创建好每一个桶的空间,这里使用了泛型数组
ArrayList <Integer> [] arrList = new ArrayList[bucketCount];
//把arr中的数均匀的的分布到[0,1)上,每个桶是一个list,存放落在此桶上的元素
for (int i = 0; i < len; i++) {
int index = (int) Math.floor((arr[i] - min) / space);
if (arrList[index] == null) {
//如果链表里没有东西
arrList[index] = new ArrayList <Integer> ();
arrList[index].add(arr[i]);
} else {
//排序
int k = arrList[index].size() - 1;
while (k >= 0 && (int) arrList[index].get(k) > arr[i]) {
if (k + 1 > arrList[index].size() - 1) {
arrList[index].add(arrList[index].get(k));
} else {
arrList[index].set(k + 1, arrList[index].get(k));
}
k--;
}
if (k + 1 > arrList[index].size() - 1) {
arrList[index].add(arr[i]);
} else {
arrList[index].set(k + 1, arr[i]);
}
}
}
//把各个桶的排序结果合并 ,count是当前的数组下标
int count = 0;
for (int i = 0; i < bucketCount; i++) {
if (null != arrList[i] && arrList[i].size() > 0) {
Iterator<Integer> iter = arrList[i].iterator();
while (iter.hasNext()) {
int d = (int) iter.next();
result[count] = d;
count++;
}
}
}
return result;
}
}
8、基数排序算法
解析:
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列
代码演示:
package com.my.day02;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author shi_meng_yong
* @date 2020/6/25 20:29
* 基数排序算法
*/
public class Demo08 {
public static void main(String[] args) {
radixSort();
}
public static void radixSort(){
int a[]={49,38,65,97,76,13,27,49,78};//定义未排序数组
sort(a);//执行基数排序算法
System.out.println(Arrays.toString(a));//打印
}
public static void sort(int[] array){
//首先确定排序的趟数;
int max=array[0];
for(int i=1;i<array.length;i++){
if(array[i]>max){ max=array[i];
}
}
int time=0;
//判断位数;
while(max>0){
max/=10;
time++;
}
//建立 10 个队列;
List<ArrayList> queue=new ArrayList<ArrayList>();
for(int i=0;i<10;i++){
ArrayList<Integer>queue1=new ArrayList<Integer>();
queue.add(queue1);
}
//进行 time 次分配和收集;
for(int i=0;i<time;i++){
//分配数组元素;
for(int j=0;j<array.length;j++){
//得到数字的第 time+1 位数;
int x=array[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10, i);
ArrayList<Integer>queue2=queue.get(x);
queue2.add(array[j]);
queue.set(x, queue2);
}
int count=0;//元素计数器;// 收集队列元素;
for(int k=0;k<10;k++){
while(queue.get(k).size()>0){
ArrayList<Integer>queue3=queue.get(k);
array[count]=queue3.get(0);
queue3.remove(0); count++;
}
}
}
}
}
9、算法题
题目一:
假设今日是2015年3月1日,星期日,请算出13个月零6天后是星期几,距离现在多少天?
请用代码实现,谢绝调用API方法
代码实现:
package com.my.day02;
import java.util.Scanner;
/**
* @author shi_meng_yong
* @date 2020/6/25 20:57
* 算法题
*/
public class Demo09 {
public static String[] week =
{"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
public static int i = 0;
public static int[] monthday1 =
{0,31,28,31,30,31,30,31,31,30,31,30,31};
public static int[] monthday2 =
{0,31,29,31,30,31,30,31,31,30,31,30,31};
//查看距离当前天数的差值
public static String distance(int year,int month,int day,int newMonth,int newDay) {
int sum = 0;
//设定初始距离天数
if (month + newMonth >= 12) {
if (((year + 1) % 4 == 0 && (year + 1) % 100 != 0)||(year + 1) % 400 == 0) {
sum += 366 + newDay;
for(int i = 0;i < newMonth - 12;i++) {
sum += monthday1[month + i];
}
} else {
sum += 365 + newDay;
for(int i = 0;i < newMonth - 12;i++) {
sum += monthday1[month + i];
}
}
}else{
for(int i = 0;i < newMonth;i++) {
sum += monthday1[month + i];
}
sum += newDay;
}
return week[sum%7];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入当前年份");
int year = scanner.nextInt();
System.out.println("请输入当前月份");
int month = scanner.nextInt();
System.out.println("请输入当前天数");
int day = scanner.nextInt();
System.out.println("请输入当前是星期几:以数字表示,如:星期天 为 0");
int index = scanner.nextInt();
System.out.println("今天是:" + year + "-" + month + "-" + day + " " + week[index]);
System.err.println("请输入相隔月份");
int newMonth = scanner.nextInt();
System.out.println("请输入剩余天数");
int newDay = scanner.nextInt();
System.out.println("经过" + newMonth + "月" + newDay + "天后,是" + distance(year,month,day,newMonth,newDay)); }
}
题目二:
数据里有{1,2,3,4,5,6,7,8,9},请随机打乱顺序,生成一个新的数组,再把新数组已某种排序算法排序
比如希尔排序算法
代码实现:
package com.my.day02;
import java.util.Arrays;
/**
* @author shi_meng_yong
* @date 2020/6/25 21:02
* 算法题
*/
public class Demo10 {
public static void main(String[] args) {
//定义有序数组
int[] arr = {0,1,2,3,4,5,6,7};
int length = arr.length;
for(int i=0;i<length;i++){//随机数组下标调换位置
int iRandNum = (int)(Math.random() * length);
int temp = arr[iRandNum];
arr[iRandNum] = arr[i];
arr[i] = temp;
}
//打印这个无序数组,验证是否成功
System.out.println(Arrays.toString(arr));
System.out.println("---------------------------------");
shellSort(arr);
System.out.println(Arrays.toString(arr));//采用希尔排序后打印这个无序数组,验证是否成功
}
/**
*对全体记录进行依次直接插入排序
* @param a
*/
private static void shellSort(int[] a) {
int dk = a.length/2;
while( dk >= 1 ){
ShellInsertSort(a, dk); dk = dk/2;
}
}
/**
* 整个待排序的记录序列分割成为若干子序列分别进行直接插入排序
* @param a
* @param dk
*/
private static void ShellInsertSort(int[] a, int dk) {
//类似插入排序,只是插入排序增量是 1,这里增量是 dk,把 1 换成 dk 就可以了
for (int i = dk; i < a.length; i++) {
if (a[i] < a[i - dk]) {
int j;
int x = a[i];//x 为待插入元素
a[i] = a[i - dk];
for (j = i - dk; j >= 0 && x < a[j]; j = j - dk) {
//通过循环,逐个后移一位找到要插入的位置。
a[j + dk] = a[j];
}
a[j + dk] = x;//插入
}
}
}
}