数据结构和算法Java实现——八大排序和四大查找

一、八大排序

排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。
排序分类
在这里插入图片描述
总体对比
在这里插入图片描述

1 冒泡排序

基本思想

  • 冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

  • 因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)

代码实现:

public static void bubbleSort(int[] arr){
        int temp;
        boolean flag = false; // 标识变量,表示是否进行过交换
        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]){
                    flag = true;
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }

            }
            if (!flag) { // 在一趟排序中,一次交换都没有发生过
                break;
            } else {
                flag = false; // 重置flag!!!, 进行下次判断
            }

        }
    }

2 选择排序

基本思想

  • 选择排序(select sorting)也是一种简单的排序方法。
  • 它的基本思想是:第一次从arr[0]~arr[n-1] 中选取最小值,与arr[0]交换,
  • 第二次从arr[1]~ arr[n-1]中选取最小值,与arr[1]交换,
  • 第三次从arr[2]~ arr[n-1]中选取最小值,与arr[2]交换,…,
  • 第i次从arr[i-1]~ arr[n-1]中选取最小值,与arr[i-1]交换,…,
  • 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,
    总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

代码实现:

    public static void selectSort(int[] arr){

        int temp;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = i+1; j < arr.length; j++) {
                if (arr[i]>arr[j]){
                    temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }

3 插入排序

基本思想:

  • 插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

代码实现:

    //插入排序
    public static void insertSort(int[] arr){
        int temp;
        for (int i = 1; i < arr.length; i++) {
//            for (int j = i; j >0; j--) {
//                if (arr[j]<arr[j-1]){
//                    temp = arr[j-1];
//                    arr[j-1] = arr[j];
//                    arr[j] = temp;
//                }
            // 优化
            int j =i;
            while (j>0 && (arr[j]<arr[j-1])){
                temp = arr[j-1];
                arr[j-1] = arr[j];
                arr[j] = temp;
                j--;
            }

        }
    }

4 希尔排序

基本思想

  • 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
    在这里插入图片描述
    代码实现
    // 优化
    public static void shellSort2(int[] arr){
        int temp;
        for (int i = arr.length/2; i >0 ; i/=2) {// 希尔排序的灵魂
            for (int j = i; j <arr.length ; j++) {// 下面类似如插入排序
                int k = j-i;                      
                while (k>=0&&(arr[k]>arr[k+i])){  //这里注意是k和k+i
                    temp = arr[k];
                    arr[k] = arr[k+i];
                    arr[k+i] = temp;
                    k -=i;
                }

            }

        }
    }

5 快速排序

基本思想

  • 快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

代码实现

public static void quickSort2(int[] arr,int left, int right){
        if (left>right){    // 结束条件
            return;
        }
        int low =left;      
        int high = right;
        int base = arr[left];  //第一个位基准位
        int temp;
        while (low <high){
            while (arr[high]>=base && low<high){///找到小于基准位的数
                high--;
            }
            while (arr[low]<=base && low<high){// 找到大于基准位的数
                low++;
            }
            if (low<high){                     // 交换
                temp = arr[high];
                arr[high] = arr[low];
                arr[low] = temp;
            }
        }
        arr[left] = arr[low];                 // 交换基准位和中间值
        arr[low] = base;
        quickSort2(arr,left,low-1);      //左边递归
        quickSort2(arr,low+1,right);       // 右边递归
    }

6 归并排序

基本思想

  • 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
    在这里插入图片描述
    代码实现
    //分+合方法
    public static void mergeSort(int[] arr, int left, int right, int[] temp){
        if (left<right){
            int mid = (left+right)/2;

            //向左递归进行分解
            mergeSort(arr,left,mid,temp);
            //向右递归进行分解
            mergeSort(arr,mid+1,right,temp);

            //合并
            merge(arr,left, mid,right,temp);
        }
    }

    //合并的方法
    /**
     *
     * @param arr 排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 做中转的数组
     */
    public static void merge(int[] arr, int left, int mid, int right, int[] temp){
        int i = left;
        int j = mid+1;
        int t  = 0;

        //(一)
        //先把左右两边(有序)的数据按照规则填充到temp数组
        //直到左右两边的有序序列,有一边处理完毕为止
        while (i<=mid && j<=right){
            //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
            //即将左边的当前元素,填充到 temp数组
            //然后 t++, i++
            if (arr[i]<=arr[j]){
                temp[t] = arr[i];
                i++;
                t++;
            }else{//反之,将右边有序序列的当前元素,填充到temp数组
                temp[t] = arr[j];
                j++;
                t++;
            }
        }

        //(二)
        //把有剩余数据的一边的数据依次全部填充到temp
        while (i<=mid){//左边的有序序列还有剩余的元素,就全部填充到temp
            temp[t] = arr[i];
            i++;
            t++;
        }

        while (j<=right){//右边的有序序列还有剩余的元素,就全部填充到temp
            temp[t] = arr[j];
            j++;
            t++;
        }

        //(三)
        //将temp数组的元素拷贝到arr
        //注意,并不是每次都拷贝所有
        int tempLeft = left;
        t = 0;
        while (tempLeft<=right){
            arr[tempLeft] = temp[t];
            tempLeft++;
            t++;
        }
    }
}

7 基数排序

基本思想

  • 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    代码实现
    //基数排序方法
    public static void radixSort(int[] arr){

        // 找出最大数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i]>max){
                max = arr[i];
            }
        }

        //最大数的长度
        int maxLength = (max+"").length();

        // 定义桶
        int[][] bucket = new int[10][arr.length];

        // 定义每个桶的中数据多少,用于取数据。
        int[] bucketElementCounts = new int[10];

        //
        for (int i = 0,n=1; i <maxLength ; i++,n*=10) {
            // 数据放入桶中
            for (int j = 0; j < arr.length; j++) {
                //
                int digitOfElement = arr[j]/n%10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }

            // 从桶中取数据
            int index = 0;

            for (int k = 0; k < bucketElementCounts.length; k++) {
                if (bucketElementCounts[k]!=0){
                    for (int l = 0; l <bucketElementCounts[k] ; l++) {
                        arr[index] = bucket[k][l];
                        index++;
                    }
                }
                bucketElementCounts[k] = 0;
            }



        }
    }

8 堆排序

基本思想:

  • 将待排序序列构造成一个大顶堆
    此时,整个序列的最大值就是堆顶的根节点。
    将其与末尾元素进行交换,此时末尾就为最大值。
    然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

代码实现:

    //编写一个堆排序的方法
    public static void heapSort(int arr[]) {
        int temp = 0;
        System.out.println("堆排序!!");

//		//分步完成
//		adjustHeap(arr, 1, arr.length);
//		System.out.println("第一次" + Arrays.toString(arr)); // 4, 9, 8, 5, 6
//
//		adjustHeap(arr, 0, arr.length);
//		System.out.println("第2次" + Arrays.toString(arr)); // 9,6,8,5,4

        //完成我们最终代码
        //将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
        for(int i = arr.length / 2 -1; i >=0; i--) {
            adjustHeap(arr, i, arr.length);
        }

		/*
		 * 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  			3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
		 */
        for(int j = arr.length-1;j >0; j--) {
            //交换
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, j);
        }

        //System.out.println("数组=" + Arrays.toString(arr));

    }

    //将一个数组(二叉树), 调整成一个大顶堆
    /**
     * 功能: 完成 将 以 i 对应的非叶子结点的树调整成大顶堆
     * 举例  int arr[] = {4, 6, 8, 5, 9}; => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
     * 如果我们再次调用  adjustHeap 传入的是 i = 0 => 得到 {4, 9, 8, 5, 6} => {9,6,8,5, 4}
     * @param arr 待调整的数组
     * @param i 表示非叶子结点在数组中索引
     * @param lenght 表示对多少个元素继续调整, length 是在逐渐的减少
     */
    public  static void adjustHeap(int arr[], int i, int lenght) {

        int temp = arr[i];//先取出当前元素的值,保存在临时变量
        //开始调整
        //说明
        //1. k = i * 2 + 1 k 是 i结点的左子结点
        for(int k = i * 2 + 1; k < lenght; k = k * 2 + 1) {
            if(k+1 < lenght && arr[k] < arr[k+1]) { //说明左子结点的值小于右子结点的值
                k++; // k 指向右子结点
            }
            if(arr[k] > temp) { //如果子结点大于父结点
                arr[i] = arr[k]; //把较大的值赋给当前结点
                i = k; //!!! i 指向 k,继续循环比较
            } else {
                break;//!
            }
        }
        //当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
        arr[i] = temp;//将temp值放到调整后的位置
    }

二、查找

1 线性查找

    public static int seqSearch(int[] arr, int value) {
        // 线性查找是逐一比对,发现有相同值,就返回下标
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == value) {
                return i;
            }
        }
        return -1;
    }

2 二分查找

前提条件:数组是排序好的

递归方法:

public static int binarySearch(int[] arr, int left, int right, int findVal){
        if (left>right){ //结束条件
            return -1;
        }
        int mid = (left+right)/2;   //取中
        int midVal = arr[mid];
        if (midVal>findVal){        // 左边递归
            return binarySearch(arr,left,mid-1,findVal);
        }else if (midVal<findVal){   // 右边递归
            return binarySearch(arr,mid+1,right,findVal);
        }else {
            return mid;
        }
    }

非递归方法

public static int binarySearch2(int[] arr, int findVal){
        int head = 0;  // 头
        int end = arr.length-1;//尾
        int mid; //中间
        while (head<=end){//结束条件
            mid = (head+end)/2; //取中
            if (arr[mid] == findVal){//找到
                return mid;
            }else if(arr[mid]>findVal){//在数组左边
                end =mid-1;              //另尾等于mid-1
            }else {
                head = mid+1;//在数组右边,另尾等于mid+1
            }
        }
        return -1;
    }

3 插值查找

在这里插入图片描述

代码实现

public static int insertValueSearch(int[] arr, int left, int right, int findVal) {

        System.out.println("插值查找次数~~");

        //注意:findVal < arr[0]  和  findVal > arr[arr.length - 1] 必须需要
        //否则我们得到的 mid 可能越界
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
            return -1;
        }

        // 求出mid, 自适应
        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);//关键所在
        int midVal = arr[mid];
        if (findVal > midVal) { // 说明应该向右边递归
            return insertValueSearch(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) { // 说明向左递归查找
            return insertValueSearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }

4 斐波那契(黄金分割法)查找

在这里插入图片描述
代码实现

    //因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
    //非递归方法得到一个斐波那契数列
    public static int[] fib(int n){
        int[] f = new int[n];
        f[0] = 1;
        f[1] = 1;
        for (int i=2;i<20;i++){
            f[i] = f[i-1] + f[i-2];
        }
        return f;
    }

    //编写斐波那契查找算法
    //使用非递归的方式编写算法
    /**
     *
     * @param a  数组
     * @param key 我们需要查找的关键码(值)
     * @return 返回对应的下标,如果没有-1
     */
    public static int fibSearch(int[] a, int key){
        int low = 0;
        int high = a.length-1;
        int k = 0;
        int mid;
        int[] f = fib(20);
         // 获取对应的斐波那契数值
        while (high>f[k]-1){
            k++;
        }

        //因为 f[k] 值 可能大于 a 的 长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]
        //不足的部分会使用0填充
        int[] temp = Arrays.copyOf(a,f[k]);

        //实际上需求使用a数组最后的数填充 temp
        //举例:
        //temp = {1,8, 10, 89, 1000, 1234, 0, 0}  => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
        for (int i = high+1; i < temp.length; i++) {
            temp[i] = a[high];
        }

        // 查找
        while (low<=high){
            mid  = low+ f[k-1]-1;
            if (key<temp[mid]){
                high = mid-1;
                //为甚是 k--
                //说明
                //1. 全部元素 = 前面的元素 + 后边元素
                //2. f[k] = f[k-1] + f[k-2]
                //因为 前面有 f[k-1]个元素,所以可以继续拆分 f[k-1] = f[k-2] + f[k-3]
                //即 在 f[k-1] 的前面继续查找 k--
                //即下次循环 mid = f[k-1-1]-1
                k--;
            }else if (key>temp[mid]){
                low = mid + 1;
                //为什么是k -=2
                //说明
                //1. 全部元素 = 前面的元素 + 后边元素
                //2. f[k] = f[k-1] + f[k-2]
                //3. 因为后面我们有f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
                //4. 即在f[k-2] 的前面进行查找 k -=2
                //5. 即下次循环 mid = f[k - 1 - 2] - 1
                k-=2;
            }else {
                if(mid<=high){
                    return mid;
                }else {
                    return high;
                }
            }
        }
        return -1;
    }
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值