java基础查找和排序算法(二分查找 冒泡 选择 插入 希尔 快排算法)

1、二分查找实现思路? 

   1:已有排序数组A(假设已经排好)

   2:确定左右边界(L R),对应数组下标值

   3:   获取中间索引m = Floor(L + R) / 2 

   4:   比较目标值t和中间值a[m]的大小,不断缩小边界,循环查找

        a: a[m] == t     找到,直接返回中间值下标索引。

        b: a[m]  > t   中间值大于目标值,说明右侧的值都大于t,无需比较,中间索引左边去找

            设置右边界为m - 1,重新查找。

        c: a[m]  < t   中间值小于目标值,说明左侧的值都小于t,无需比较,中间索引右边去找

            设置左边界为m + 1,重新查找。

    5:当L > R时,左边界大于右边界,说明没有找到,应结束循环

2、二分查找代码

代码如下(示例):

package com.xiang;

public class BinarySearch {
    public static void main(String[] args) {
       int[] array = {1,5,8,11,19,22,31,35,40,45,48,49,50};
       int target = 48;
       int idx = binarySearch(array, target);
       System.out.println(idx);

    }

    //二分查找,找到返回元素索引,找不到返回-1
    private static int binarySearch(int[] a, int t) {
        //定义左边界,右边界,中间值索引
        int l = 0, r = a.length - 1, m;
        while(l <= r){
            m = (l + r) / 2;//获取中间值索引
            if(a[m] == t){
                return m;
            }else if(a[m] < t){
                l = m + 1;
            }else{
                r = m - 1;
            }

        }
         return -1;//没找到返回-1
    }
}

3、冒泡排序思路?

1:将数组中相邻的两个元素进行比较,大的交换到后面,依次进行,直到最大值冒泡到最后面。

2:重复第1步,找到第二大的值,第三大的值......

4、冒泡排序代码

4.1 冒泡排序代码:

package day01.sort;

import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
        int[] a = {5, 9, 7, 4, 1, 3, 2, 8};
        bubble(a);
    }

    private static void bubble(int[] a) {
        for (int j = 0; j < a.length - 1; j++) {
            //一轮冒泡
            for(int i = 0; i < a.length - 1; i++){
                if(a[i] > a[i+1]){ //前一个元素大于后一个元素,交换位置
                    swap(a,i,i+1);
                }
            }
            System.out.println("第"+j+"轮冒泡:"+Arrays.toString(a));
        }

    }


    public static void swap(int[] a, int i, int j){
         int t = a[i];
         a[i] = a[j];
         a[j] = t;
    }

}

4.2 冒泡排序代码优化:

package day01.sort;

import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
       int[] a = {5, 2, 7, 4, 1, 3, 8, 9};
//     int[] a = {1, 2, 3, 4, 5, 7, 8, 9};
       bubble_v2(a);
    }


    //最终优化版(综合 减少比较次数 和 冒泡次数)
    public static void bubble_v2(int[] a) {
        int n = a.length - 1; //n 表示下一次要比较的次数,初始比较次数为数组长度 - 1
        while(true){
            int last = 0;
            for (int i = 0; i < n; i++) {
                System.out.println("比较次数:" + i);
                if(a[i] > a[i + 1]){
                    swap(a, i,i + 1);
                    last = i; //获取每一轮冒泡最后一次交换索引
                }
            }
            n = last;
            System.out.println("第轮冒泡:"
                    + Arrays.toString(a));
            if(n == 0){ //n等于0时,跳出外层循环
                break;
            }
        }

    }


    //优化,减少比较次数 减少冒泡次数
    public static void bubble(int[] a) {
        for (int j = 0; j < a.length - 1; j++) {
            // 一轮冒泡
            boolean swapped = false; //是否发生了交换
            for(int i = 0; i < a.length - 1 - j; i++){ // -j 减少比较次数
                System.out.println("比较次数:" + i);
                if(a[i] > a[i+1]){ //前一个元素大于后一个元素,交换位置
                    swap(a,i,i+1);
                    swapped = true;
                }
            }
            System.out.println("第"+j+"轮冒泡:"+Arrays.toString(a));
            // 一轮冒泡后没有发生交换,跳出循环
            if(!swapped){
                break;
            }
        }

    }


    public static void swap(int[] a, int i, int j){
         int t = a[i];
         a[i] = a[j];
         a[j] = t;
    }

}

5、选择排序

5.1 选择排序思路

1:将数组分为两个子集,已排序的和未排序的,每一轮从未排序的子集中选择最小的元素,放入       排序子集。

2:重复以上步骤,直到整个数组有序。

3:优化方式:为减少交换次数,每一轮先找到最小的索引,在每轮最后再进行交换。

5.2 选择排序代码

代码如下:

package day01.sort;

import java.util.Arrays;

public class SelectionSort {
    public static void main(String[] args) {
        int[] a = {5,3,7,2,1,9,8,4};
        selection(a);
    }

    //选择排序
    private static void selection(int[] a){
        for (int i = 0; i < a.length - 1; i++) {
            // i 代表每轮选择最小元素要交换到的目标索引
            int s = i; // s 代表最小元素的索引,初始为0
            for (int j = s + 1; j < a.length; j++) {
                if(a[s] > a[j]){
                    s = j;
                }
            }
            if(s != i){
                swap(a,s,i);
            }
            System.out.println(Arrays.toString(a));
        }
    }

    private static void swap(int[] a, int s, int i) {
        int t = a[s];
        a[s] = a[i];
        a[i] = t;
    }

}

5.3 冒泡排序与选择排序的比较

1:二者的平均时间复杂度都是O(n^2)

2:选择排序一般要快于冒泡,因为交换次数少

3:但如果集合有序度高,冒泡优于选择

4:冒泡属于稳定排序算法,选择属于不稳定排序算法

6、插入排序

6.1 插入排序思路

1:将数组分为两个区域,已排序区域和未排序区域,每一轮从未排序区域中取出第一个元素,插       入到排序区域(需保证顺序)

2:重复以上步骤,直到整个数组有序

3:优化方式

      a. 待插入元素进行比较时,遇到比自己小的元素,就代表找到了插入位置,无需进行后续比较        b.  插入时可以直接移动元素,而不是交换元素    

6.2 插入排序代码

代码如下:

package day01.sort;

import java.util.Arrays;

public class InsertSort {
    public static void main(String[] args) {
       int[] a = {9,3,7,2,5,1,4};
       insert(a);
    }

    private static void insert(int[] a) {
        // i 表示待插入元素的索引
        for (int i = 1; i < a.length; i++) {
            int t = a[i]; //保存待插入元素的值
            int j = i - 1; //代表已排序区域的元素的索引
            while (j >=0) {
                if(t < a[j]){
                  a[j + 1]  = a[j];
                }else {
                    break; //退出循环,减少比较的次数
                }
                j--;
            }
            a[j + 1] = t;
            System.out.println(Arrays.toString(a));
        }
    }
}

6.3 插入排序与选择排序的比较

1:二者的时间复杂度都是O(n^2)

2:大部分情况下,插入都略优于选择

3:有序集合插入时间复杂度为O(n)

4:插入属于稳定排序算法,而选择属于不稳定排序算法

6.4 希尔排序

7、快速排序

7.1 快速排序_文字描述

1. 每一轮选择一个基准点(pivot)进行分区

     1. 让小于基准点的元素进入一个分区,大于基准点的元素进入另一个分区

     2. 当分区完成的时候,基准点元素的位置就是其最终位置

2. 在子分区内重复以上过程,直至子分区元素个数少于或等于 1  ,分而治之的思想

7.2 快速排序_实现方式

1. 单边循环快排(lomuto洛穆托分区方案)

    1. 选择最右元素作为基准点元素

    2. j 指针负责找到比基准点小的元素,一旦找到则与 i 进行交换

    3. i 指针维护小于基准点元素的边界,也是每次交换的目标索引

    4. 最后基准点与 i 交换,i 即为分区位置

2. 双边循环快排(并不完全等价于 hoare 霍尔分区方案)

    1. 选择最左元素作为基准点元素

    2. j 指针负责从右向左找比基准点小的元素,i 负责从左向右找比基准点大的元素,一旦找到二            者交换,直到 i,j 相交为止

    3. 最后基准点与 i (此时 i 和 j 相等) 交换,i 即为分区位置

3. 双边循环快排的几个要点

    1. 基准点在左边,并且要先 j 后 i

    2. while (i < j && a[j] > pv)j--

    3. while (i < j && a[i] <= pv)i++

4. 块速排序_特点

    1. 平均时间复杂度是O(nlog2n),最坏时间复杂度是O(n^2)

    2. 数据较大时,优势非常明显

    3. 属于不稳定排序

7.3 单边循环_快排代码

代码如下:

package day01.sort;

import java.util.Arrays;

//单边循环_快速排序
public class QuickSort1 {
    public static void main(String[] args) {
         int[] a = {5,3,7,2,9,8,1,4};
         quick(a,0, a.length - 1);
    }

    //递归调用
    public static void quick(int[] a, int l, int h){
        //结束递归的条件
        if(l >= h){
            return;
        }
        int p = partition(a, l, h); // p 索引值
        quick(a,l,p-1); //左边界分区范围确定
        quick(a,p + 1,h); //右边界分区范围确定

    }

    //l 表示分区的左边界,h 表示分区的右边界
    private static int partition(int[] a, int l, int h){
         int pv = a[h];
         int i = l;
         for (int j = l; j < h; j++) {
            if(a[j] < pv){ //找到比基准点小的元素,与 i 交换
                if(j != i){
                    swap(a,j,i);
                }
                i++;
            }
         }
         if(h != i){
             swap(a,h,i);
         }
         System.out.println(Arrays.toString(a)+ "i=" + i);
         //返回值代表了基准点元素所在的正确索引,用它确定下一轮分区的边界
         return i;
    }

    private static void swap(int[] a, int j, int i) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

}

7.4 双边循环_快排代码

代码如下:

package day01.sort;

import java.util.Arrays;

//双边循环_快速排序
public class QuickSort2 {
    public static void main(String[] args) {
        int[] a = {5,3,7,2,9,8,1,4};
        System.out.println(Arrays.toString(a));
        quick(a,0, a.length - 1);
    }

    //递归调用
    public static void quick(int[] a, int l, int h){
        //结束递归的条件
        if(l >= h){
            return;
        }
        int p = partition(a, l, h); // p 索引值
        quick(a,l,p-1); //左边界分区范围确定
        quick(a,p + 1,h); //右边界分区范围确定

    }

    private static int partition(int[] a, int l, int h){
        int pv = a[l];
        int i = l;
        int j = h;
        while (i < j){
            // j 从右找小的
            while (i < j && a[j] > pv){
                j--;
            }
            // i 从左找大的
            while (i < j && a[i] <= pv){
                i++;
            }
            swap(a,i,j);
        }
        swap(a,l,j); // 与基准点元素交换
        System.out.println(Arrays.toString(a)+ "j=" + j);
        return j;
    }

    private static void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

}


总结

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值