Java面试题之:Java算法

一、二分查找算法(Binary Search)

①算法描述

  二分查找又叫折半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字。

②动图理解

在这里插入图片描述

③代码实现

  相关代码如下:

package com.allen;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{1,3,5,7,9,11,13,15};
        int binarySearch = binarySearch(data,8);
        if(binarySearch!=-1){
            System.out.println("关键字在数组当中的数组下标为:"+binarySearch);
        }else{
            System.out.println("对不起,数组当中没有该关键字");
        }
    }
    public static int binarySearch(int[] array, int a) {
        int lo = 0;
        int hi = array.length - 1;
        int mid;
        while (lo <= hi) {
            //中间位置
            mid = (lo + hi) / 2;
            if (array[mid] == a) {
                return mid;
            } else if (array[mid] < a) {
                //向右查找
                lo = mid + 1;
            } else {
                //向左查找
                hi = mid - 1;
            }
        }
        return -1;
    }
}

④相关链接

java实现二分查找-两种方式

二、冒泡排序算法(Bubble Sort)

①算法描述

  冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

n个记录的冒泡排序可经过n趟冒泡排序得到有序结果。具体算法描述如下:
  1.从第一个开始比较相邻的元素。如果第一个比第二个大(小),就交换它们两个;
  2.对每一对相邻元素作同样的工作,这样在最后的元素就是最大的数,下一轮最后一个不参与;
  3.在剩下的数中重复1~2步骤;
  4.最后一次就剩下第一个数,到此结束

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.Arrays;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] array={5,6,5,4,3,2,8,7,1};
        int[] bubbleSort=bubbleSort(array,array.length);
        System.out.println(Arrays.toString(bubbleSort));
    }

    public static int[] bubbleSort(int [] a, int n){
        int i, j;
        for(i=0; i<n; i++){//表示 n 次排序过程。
            for(j=1; j<n-i; j++){
                if(a[j-1] > a[j]){//前面的数字大于后面的数字就交换
                    //交换 a[j-1]和 a[j]
                    int temp;
                    temp = a[j-1];
                    a[j-1] = a[j];
                    a[j]=temp;
                }
            }
        }
        return a;
    }
}

④相关链接

Java基础(冒泡排序)
Java实现冒泡排序(一)

三、插入排序算法(Insertion Sort)

①算法描述

  通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的
  如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
  1.从第一个元素开始,该元素可以认为已经被排序;
  2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3.如果该元素(已排序)大(小)于新元素,将该元素移到下一位置;
  4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5.将新元素插入到该位置后;
  6.重复步骤2~5。

②动图理解

在这里插入图片描述
在这里插入图片描述

③代码实现

package com.allen;

import java.util.Arrays;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] array={5,2,8,4,6,3,7,9};
        int[] insertionSort=insertionSort(array);
        System.out.println(Arrays.toString(insertionSort));
    }

    public static int[] insertionSort(int array[]) {
        for(int i =1; i<array.length;i++)
        {
            //插入的数
            int insertVal = array[i];
            //被插入的位置(准备和前一个数比较)
            int index = i-1;
            //如果插入的数比被插入的数小
            while(index>=0&&insertVal<array[index])
            {
                //将把 arr[index] 向后移动
                array[index+1]=array[index];
                //让 index 向前移动
                index--;
            }
            //把插入的数放入合适位置
            array[index+1]=insertVal;
        }
        return array;
    }
}

④相关链接

插入排序法(Java实现)

四、选择排序算法(Selection Sort)

①算法描述

  选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
  1.假设第一个数是最小(大)的。
  2.把这个最大的数的下标(M)记录下来。
  3.从第二个数开始,依次使用后面的每一个数与第M个数比较。如果出现比第M数还大(小)的数,则记录新出现的最小(大)值的下标(M)。
  4.每一趟排序循环结束后得到一个真正的最的值M,将这个最大值与第一个数或者最后一个数交换。
  5.重复1~4步骤,直到全部排好序。

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.*;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{6, 8, 9, 2, 5, 1, 7, 4,3};
        int[] selectionSort = selectionSort(data);
        System.out.println(Arrays.toString(selectionSort));
    }

    /**********************************************
     * 选择排序(默认升序)
     *
     * @param array - 序排序数组
     * @return 排好序的数组
     */
    public static int[] selectionSort(int array[]) {
        return selectionSort(array, false);
    }

    /**********************************************
     * 选择排序(可选排序方式)
     *
     * @param array       - 序排序数组
     * @param reverse - 是否倒序:true=倒序
     * @return 排好序的数组
     */
    public static int[] selectionSort(int array[], boolean reverse) {
        for (int i = 0; i < array.length; i++) {
            // 假设第一个是最小的
            int m = i;
            // 下面的循环每一轮循环结束得到一个最小值的下标M
            for (int j = i+1; j < array.length; j++) {
                // 每一个和第M个比较
                //^是Java中的位运算符!用来做按位异或运算的。
                // 异或指的是相同位值相同异或结果为0,相同位异或值不同结果为1
                if (array[j] < array[m] ^ reverse) {
                    m = j;
                }
            }
            //如果第M和i不是同一个,则将第M个最小值与第一个交换
            if(i != m) {
                int t = array[i];
                array[i] = array[m];
                array[m] = t;
            }
        }
        return array;
    }
}

④相关链接

java实现选择排序
Java 选择排序法

五、快速排序算法(Quick Sort)

①算法描述

  快速排序的原理:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
  一次循环:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值来说,左右两边就是有序的了。

②动图理解

在这里插入图片描述
在这里插入图片描述

③代码实现

package com.allen;

import java.util.Arrays;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] array={6,1,2,7,9,3,4,5,10,8};
        int[] newB=quickSort(array,0,array.length-1);
        System.out.println(Arrays.toString(newB));
    }
    public static int[] quickSort(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) {
            quickSort(a, low, start - 1);
        }//左边序列。第一个索引位置到关键值索引-1
        if(end<high) {
            quickSort(a, end + 1, high);//右边序列。从关键值索引+1 到最后一个
        }
        return a;
    }
}

④相关链接

快速排序法(详解)
快速排序—(面试碰到过好几次)

六、希尔排序算法(Shell Sort)

①算法描述

  1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
  基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

  1. 操作方法:选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
  2. 按增量序列个数 k,对序列进行 k 趟排序;
  3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
    在这里插入图片描述

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.Arrays;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] array = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        shellSort(array);
        System.out.println(Arrays.toString(array));
    }

    public static void shellSort(int[] a) {
        int dk = a.length / 2;
        while (dk >= 1) {
            ShellInsertSort(a, dk);
            dk = dk / 2;
        }
    }

    public 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;//插入
            }
        }
    }
}

④相关链接

【排序算法】希尔排序原理及Java实现
 java实现希尔排序(思路与实现)

七、归并排序算法(Merge Sort)

①算法描述

  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
  归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
  1.把长度为n的输入序列分成两个长度为n/2的子序列;
  2.对这两个子序列分别采用归并排序;
  3.将两个排序好的子序列合并成一个最终的排序序列。
在这里插入图片描述

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

/**
 * @author Administrator
 */
public class Test {
    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();
    }
}

④相关链接

Java实现归并排序-有图有真相
【排序算法】归并排序原理及Java实现

八、计数排序算法(Counting Sort)

①算法描述

  计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

  1.找出待排序的数组中最大和最小的元素;
  2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
  3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
  4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

  注意!!!计数排序对将要排序的数组是有限制的
  1.只能是整形数组。
  2.数组元素必须都大于等于0。
  3.计数排序是一种拿空间换时间的排序算法。

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.Arrays;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{2, 3, 8 ,11, 15 , 6 , 5 , 1, 9, 6, 4, 8, 7};
        int[] newB = countingSort(data);
        System.out.println(Arrays.toString(newB));
    }
    /**********************************************
     * 计数排序(默认升序)
     *
     * @param array       - 序排序数组
     * @return 排好序的数组
     */
    public static int[] countingSort(int array[]) {
        // 获取数组的最大值,数组所有值都在0 - max之间
        int max = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] < 0) {
                throw new RuntimeException("数组中有数据小于0");
            }
            if (max < array[i]) {
                max = array[i];
            }
        }
        // 创建一个max+1大小的数组用于表示从0 - max所有数字的重复次数
        int[] countArray = new int[max+1];
        int[] resultArray = new int[array.length];
        // 用于存储排好序的数组
        System.arraycopy(array, 0, resultArray, 0, array.length);
        //统计每个数字出现的次数
        for (int i = 0; i < array.length; i++) {
            // 因为countArray的下标代表array中的数字,
            // 而值代表array中元素的出现次数,所以countArray[array[i]]++
            countArray[array[i]]++;
        }
        System.out.println(Arrays.toString(countArray));
        for (int i = 1; i < countArray.length; i++) {
            // 将countArray中的每一个元素变成与前一个元素相加的和
            countArray[i] = countArray[i] + countArray[i - 1];
        }
        for (int i = 0; i < resultArray.length; i++) {
            // 从array的最后一位开始依次放入resultArray中,放入的位置是
            // --countArray[array[i]]
            array[--countArray[resultArray[i]]] = resultArray[i];
        }
        return array;
    }
}

④相关链接

计数排序(Countsort)之Java实现
Java实现计数排序算法

九、桶排序算法(Bucket Sort)

①算法描述

  桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。
  桶排序的基本思想是: 把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最后合并 。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。

  1.设置一个定量的数组当作空桶;
  2.遍历输入数据,并且把数据一个一个放到对应的桶里去;
  3.对每个不是空的桶进行排序;
  4.从不是空的桶里把排好序的数据拼接起来。

②动图理解

在这里插入图片描述
在这里插入图片描述
图片来源于网络,仅限学习使用,如有侵权,请联系删除

③代码实现

package com.allen;

import java.util.*;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{2, 8, 3, 9, 6, 5, 1, 7, 4,3};
        int[] bucketSort = bucketSort(data);
        System.out.println(Arrays.toString(bucketSort));
    }

    /**********************************************
     * 桶排序(默认升序)
     *
     * @param array - 序排序数组
     * @return 排好序的数组
     */
    public static int[] bucketSort(int array[]) {
        // 新建一个桶的集合
        ArrayList<LinkedList<Integer>> buckets = new ArrayList<LinkedList<Integer>>();
        for (int i = 0; i < 10; i++) {
            // 新建一个桶,并将其添加到桶的集合中去。
            // 由于桶内元素会频繁的插入,所以选择 LinkedList 作为桶的数据结构
            buckets.add(new LinkedList<Integer>());
        }
        // 将输入数据全部放入桶中并完成排序
        for (Integer data : array) {
            int index = data;
            bucket(buckets.get(index), data);
        }
        // 将桶中元素全部取出来并放入 p 中输出
        int index = 0;
        for (LinkedList<Integer> bucket : buckets) {
            for (int data : bucket) {
                array[index++] = data;
            }
        }
        return array;
    }
    /**
     * 选择插入排序作为桶内元素排序的方法 每当有一个新元素到来时,都调用该方法将其插入到恰当的位置
     */
    public static void bucket(List<Integer> bucket, Integer data) {
        ListIterator<Integer> it = bucket.listIterator();
        boolean insertFlag = true;
        while (it.hasNext()) {
            if (data <= it.next()) {
                // 把迭代器的位置偏移回上一个位置
                it.previous();
                // 把数据插入到迭代器的当前位置
                it.add(data);
                insertFlag = false;
                break;
            }
        }
        if (insertFlag) {
            // 否则把数据插入到链表末端
            bucket.add(data);
        }
    }
}

④相关链接

算法:排序算法之桶排序
Java 桶排序,详细分析
【排序】图解桶排序

十、基数排序算法(Radix Sort)

①算法描述

  基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

  1.取得数组中的最大数,并取得位数;
  2.arr为原始数组,从最低位开始取每个位组成radix数组;
  3.对radix进行计数排序(利用计数排序适用于小范围数的特点);

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.*;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{6, 8, 3, 9, 2, 5, 1, 7, 4,3};
        int[] radixSort = radixSort(data);
        System.out.println(Arrays.toString(radixSort));
    }

    /**
     * 基数排序(默认升序)
     * @param array 排序数组
     * @return  排好序的数组
     */
    public static int[] radixSort(int[] array) {
        int max = array[0];
        for (int i = 0; i < array.length; i++) {
            // 找到数组中的最大值
            if (array[i] > max) {
                max = array[i];
            }
        }
        // 关键字的个数,我们使用个位、十位、百位...当做关键字,所以关键字的个数就是最大值的位数
        int keysNum = 0;
        while (max > 0) {
            max /= 10;
            keysNum++;
        }
        List<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();
        // 每位可能的数字为0~9,所以设置10个桶
        for (int i = 0; i < 10; i++) {
            // 桶由ArrayList<Integer>构成
            buckets.add(new ArrayList<Integer>());
        }
        // 由最次关键字开始,依次按照关键字进行分配
        for (int i = 0; i < keysNum; i++) {
            // 扫描所有数组元素,将元素分配到对应的桶中
            for (int j = 0; j < array.length; j++) {
                // 取出该元素对应第i+1位上的数字,比如258,现在要取出十位上的数字,258%100=58,58/10=5
                int key = array[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                // 将该元素放入关键字为key的桶中
                buckets.get(key).add(array[j]);
            }
            // 分配完之后,将桶中的元素依次复制回数组
            // 元素计数器
            int counter = 0;
            for (int j = 0; j < 10; j++) {
                // 关键字为j的桶
                ArrayList<Integer> bucket = buckets.get(j);
                while (bucket.size() > 0) {
                    // 将桶中的第一个元素复制到数组,并移除
                    array[counter++] = bucket.remove(0);
                }
            }
        }
        return array;
    }
}

④相关链接

Java实现基数排序

十一、堆排序算法(Heap Sort)

①算法描述

  堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

  1.将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

②动图理解

在这里插入图片描述

③代码实现

package com.allen;

import java.util.*;

/**
 * @author Administrator
 */
public class Test {
    public static void main(String[] args) {
        int[] data = new int[]{6, 8, 9, 2, 5, 1, 7, 4,3};
        int[] heapSort = heapSort(data);
        System.out.println(Arrays.toString(heapSort));
    }

    /**********************************************
     * 堆排序(默认升序)
     *
     * @param array - 序排序数组
     * @return 排好序的数组
     */
    public static int[] heapSort(int array[]) {
        return heapSort(array, false);
    }

    /**********************************************
     * 堆排序(可选排序方式)
     *
     * @param array       - 序排序数组
     * @param reverse - 是否倒序:true=倒序
     * @return 排好序的数组
     */
    public static int[] heapSort(int array[], boolean reverse) {
        // 创建堆
        for (int i = (array.length - 1) / 2; i >= 0; i--) {
            // 从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(array, i, array.length,reverse);
        }
        // 调整堆结构+交换堆顶元素与末尾元素
        for (int i = array.length - 1; i > 0; i--) {
            // 将堆顶元素与末尾元素进行交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;

            // 重新对堆进行调整
            adjustHeap(array, 0, i, reverse);
        }
        return array;
    }

    /**
     * 调整堆
     *
     * @param array      待排序列
     * @param parent 父节点
     * @param length 待排序列尾元素索引
     */
    private static void adjustHeap(int[] array, int parent, int length, boolean reverse) {
        // 将temp作为父节点
        int temp = array[parent];
        // 左孩子
        int lChild = 2 * parent + 1;
        while (lChild < length) {
            // 右孩子
            int rChild = lChild + 1;
            if (reverse) {
                // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
                if (rChild < length && array[lChild] > array[rChild]) {
                    lChild++;
                }
                // 如果父结点的值已经大于孩子结点的值,则直接结束
                if (temp <= array[lChild]) {
                    break;
                }
            } else {
                // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
                if (rChild < length && array[lChild] < array[rChild]) {
                    lChild++;
                }
                // 如果父结点的值已经大于孩子结点的值,则直接结束
                if (temp >= array[lChild]) {
                    break;
                }
            }
            // 把孩子结点的值赋给父结点
            array[parent] = array[lChild];
            // 选取孩子结点的左孩子结点,继续向下筛选
            parent = lChild;
            lChild = 2 * lChild + 1;
        }
        array[parent] = temp;
    }
}

④相关链接

Java实现堆排序和图解

十二、剪枝算法 (AlphaBeta)

①算法描述

  在搜索算法中优化中,剪枝,就是通过某种判断,避免一些不必要的遍历过程,形象的说,就是剪去了搜索树中的某些“枝条”,故称剪枝。应用剪枝优化的核心问题是设计剪枝判断方法,即确定哪些枝条应当舍弃,哪些枝条应当保留的方法。

②图片理解

在这里插入图片描述

③代码实现

略…

④相关链接

剪枝算法(算法优化)
Alpha-Beta剪枝算法原理介绍及JAVA代码实现
Alpha-Beta剪枝(Alpha Beta Pruning)
Alpha-beta剪枝算法实例分析

十三、回溯算法 (Backtracking)

①算法描述

  回溯算法实际上是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

②图片理解

在这里插入图片描述

③代码实现

略…

④相关链接

回溯算法总结(java)
Java之回溯算法
算法入门6:回溯法
从零开始学回溯算法

十四、最短路径算法

①算法描述

  从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。解决最短路的问题有以下算法,Dijkstra 算法,Bellman-Ford 算法,Floyd 算法和 SPFA算法等。

②图片理解

在这里插入图片描述
在这里插入图片描述

③代码实现

略…

④相关链接

图解最短路径之弗洛伊德算法(Java实现)
最短路径问题—Floyd算法详解
最短路径模板+解析——(FLoyd算法)
弗洛伊德(Floyd)算法求图的最短路径
有权图中的最短路径问题–Dijkstra算法(Java语言实现)
最短路径问题—Dijkstra算法详解(推荐)
数据结构–Dijkstra算法最清楚的讲解
最短路径问题—SPFA算法详解
java实现SPFA算法
java实现BellmanFord算法
java实现Dijkstra算法
java实现Floyd算法

十五、最大子数组算法

①算法描述

  找出数组A的和最大的非空连续子数组,我们称这样的连续子数组为最大子数组。
在这里插入图片描述
在这里插入图片描述

②图片理解

在这里插入图片描述

③代码实现

略…

④相关链接

java求最大子数组 (分治算法)
最大子数组问题三种算法的Java代码实现——暴力求解、分而治之、线性方法
最大子数组和算法(Java实现)
分治算法 解决 最大子数组问题
最大子数组算法

十六、最长公共子序算法

①算法描述

  给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB

  则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA

②图片理解

在这里插入图片描述

③代码实现

略…

④相关链接

Java动态规划 实现最长公共子序列以及最长公共子字符串
最长公共子序列(JAVA实现)
动态规划解决最长公共子序列(Java实现)
求解两个字符串的最长公共子序列

十七、最小生成树算法

①算法描述

  现在假设有一个很实际的问题:我们要在 n 个城市中建立一个通信网络,则连通这 n 个城市需要布置 n-1 一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网?于是我们就可以引入连通图来解决我们遇到的问题,n 个城市就是图上的 n 个顶点,然后,边表示两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有 n 个顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。
  构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质:MST 性质(假设N=(V,{E})是一个连通网,U 是顶点集 V 的一个非空子集,如果(u,v)是一条具有最小权值的边,其中 u 属于 U,v 属于 V-U,则必定存在一颗包含边(u,v)的最小生成树),下面就介绍两种使用 MST 性质生成最小生成树的算法:普里姆算法和克鲁斯卡尔算法。

②图片理解

在这里插入图片描述

③代码实现

略…

④相关链接

Prim最小生成树算法详解以及java实现源代码
java实现Prim算法

十八、常见排序算法归纳

①排序算法的归类:

总的排序算法分为以下几类:

  1.插入类排序:如:直接插入排序,折半插入排序,希尔排序。

  2.交换类排序:如:冒泡排序,快速排序,其中冒泡排序应该是计算机专业的学生最为熟悉的一种排序算法了,而快速排序则是在冒泡排序的基础上一次消除多个逆序对改进而来。

  3.选择类排序:如:简单选择排序,堆排序。

  4.其它类排序:如:归并排序,基数排序等。

②各大排序的特点:

在这里插入图片描述

③算法分类:

  十种常见排序算法可以分为两大类:
  1.非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
  2.线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
在这里插入图片描述

④算法复杂度:

在这里插入图片描述
在这里插入图片描述

注:以上图片来源于网络,感谢分享,如有侵权,请联系删除。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐