排序和查找

一、冒泡排序

冒泡排序是排序算法中较为简单的一种,英文称为Bubble Sort。它遍历所有的数据,每次对相邻元素进行两两比较,如果顺序和预先规定的顺序不一致,则进行位置交换;这样一次遍历会将最大或最小的数据上浮到顶端,之后再重复同样的操作,直到所有的数据有序。


for (int i = 0; i < arr.length - 1; i++) {//控制趟数
            //定义boolean类型变量,用来标识每趟是否存在交换值
            boolean flag = false;
            for (int j = 0; j < arr.length - 1 - i; j++) {//控制次数
                //比较相邻元素的值
                if(arr[j] > arr[j+1]){
                    int t = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = t;
                    flag = true;//说明这趟存在交换值情况
                }
            }
            System.out.println(Arrays.toString(arr));
            if(!flag){ //取反
                break;//若flag为false,表示此趟不存在交换值的情况,提前结束循环
            }

}

二、选择排序

选择排序简单直观,英文称为Selection Sort,先在数据中找出最大或最小的元素,放到序列的起始;然后再从余下的数据中继续寻找最大或最小的元素,依次放到排序序列中,直到所有数据样本排序完成。
 


        if (arr == null || arr.length == 1) {
            return;
        }

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


    public static void main(String[] args) {
        int[] arr = {3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
        System.out.println("排序前:" + Arrays.toString(arr));
        selectSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

三、插入排序

插入排序英文称为Insertion Sort,它通过构建有序序列,对于未排序的数据序列,在已排序序列中从后向前扫描,找到相应的位置并插入,类似打扑克牌时的码牌。插入排序有一种优化的算法,可以进行拆半插入。

基本思路是先将待排序序列的第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;然后从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置,直到所有数据都完成排序;如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。


        int[] arr = {56, 46473, 1, 0, 46, 12};
        insertionSort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
 
    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        //0~0有序
        //想0~i有序
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                swap(arr, j, j + 1);
            }
        }
    }
 
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;}

四、希尔排序

希尔排序也称递减增量排序,是插入排序的一种改进版本,英文称为Shell Sort,效率虽高,但它是一种不稳定的排序算法。

插入排序在对几乎已经排好序的数据操作时,效果是非常好的;但是插入排序每次只能移动一位数据,因此插入排序效率比较低。

希尔排序在插入排序的基础上进行了改进,它的基本思路是先将整个数据序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全部数据进行依次直接插入排序。

public class ShellSort {
    public static void main(String[] args) {
        int[] arr={49,38,65,97,76,13,27,49,78,34};
        shell(arr);
    }

    public static void shell(int[] arr){
        int temp=0;
        int count=0;
        //步长
        for (int gap= arr.length/2;gap>=1;gap=gap/2){
            count++;
            for (int i=gap;i< arr.length;i++){
                //分组
                for (int j=i-gap;j>=0;j=j-gap){
                    if (arr[j]>arr[j+gap]){
                        temp=arr[j];
                        arr[j]=arr[j+gap];
                        arr[j+gap]=temp;
                    }
                }
            }
            System.out.println("第"+count+"轮排序后的数据:");
            System.out.println(Arrays.toString(arr));
        }

    }

五、归并排序

归并排序英文称为Merge Sort,归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。它首先将数据样本拆分为两个子数据样本, 并分别对它们排序, 最后再将两个子数据样本合并在一起; 拆分后的两个子数据样本序列, 再继续递归的拆分为更小的子数据样本序列, 再分别进行排序, 直到最后数据序列为1,而不再拆分,此时即完成对数据样本的最终排序。

归并排序严格遵循从左到右或从右到左的顺序合并子数据序列, 它不会改变相同数据之间的相对顺序, 因此归并排序是一种稳定的排序算法.

作为一种典型的分而治之思想的算法应用,归并排序的实现分为两种方法:

1、自上而下的递归;

2、自下而上的迭代;

public class MergeSort {
    private MergeSort() {
    }
    
    public static <E extends Comparable<E>> void sort2(E[] arr) {
        E[] temp = Arrays.copyOf(arr, arr.length);
        sort2(arr, 0, arr.length - 1,temp);
    }


    private static <E extends Comparable<E>> void sort2(E[] arr, int l, int r,E[] temp) {
        if (r - l <= 15) {
            InsertSort.sort(arr,l,r);
            return;
        }
        int mid = (l + r) / 2;
        sort2(arr, l, mid,temp);
        sort2(arr, mid + 1, r,temp);

        merge2(arr, l, mid, r,temp);
    }


    private static <E extends Comparable<E>> void merge2(E[] arr, int l, int mid, int r,E[] temp) {

        System.arraycopy(arr,l,temp,l,r-l+1);  // 将arr[l:r]拷贝到temp中去
        int i = l;
        int j = mid + 1;
        // 每轮循环为arr[k]赋值
        for (int k = l; k <= r; k++) {
            if (i > mid) {
                arr[k] = temp[j]; // temp中装的是arr[l,r],所以temp的索引对应arr的索引有偏移
                j++;
            } else if (j > r) {
                arr[k] = temp[i];
                i++;
            } else if (temp[i].compareTo(temp[j]) < 0) {  // 如果都没越界
                arr[k] = temp[i];
                i++;
            } else {
                arr[k] = temp[j];
                j++;
            }
        }
    }

}

六、快速排序

快速排序,英文称为Quicksort,又称划分交换排序 partition-exchange sort 简称快排。

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。首先从数列中挑出一个元素,并将这个元素称为「基准」,英文pivot。重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。之后,在子序列中继续重复这个方法,直到最后整个数据序列排序完成。

public static int[] quickSort(int arr[],int left,int right) {
        int pivot = arr[left];//轴值
        int i = left;//左下标
        int j = right;//右下标
        while (i < j) {
            //在右边找到一个比中值小或者相等的值
            while (i < j && arr[j] > pivot) {
                j--;
            }
            //在左边找到一个比中值大或者相等的值
            while (i < j && arr[i] < pivot) {
                i++;
            }
            //在i和j没有相遇时,如果 arr[i] == arr[j] 此时让i+1
            //即让arr[i+1] 与arr[j]进行交换 ,使两个相同的数在一起
            if (arr[i] == arr[j] && i < j) {
                i++;
            } else {//交换
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        //左半部递归
        if (i-1 > left) {
            arr=quickSort(arr,left,i-1);
        }
        //右半部递归
        if (j+1 < right) {
            arr=quickSort(arr,j+1,right);
        }

        return arr;
    }

七、堆排序

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

1、大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;

2、小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

算法步骤:

1、创建一个堆 H[0……n-1];

2、把堆首(最大值)和堆尾互换;

3、把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

4、重复步骤 2,直到堆的尺寸为 1

public static void sort(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }
        buildMaxHeap(array);

        int temp;
        for(int arg:array){
            System.out.print(arg+" ");
        }
        for (int i = array.length - 1; i >= 1; i--) {

            temp = array[0];
            array[0] = array[i];
            array[i] = temp;
           
            maxHeap(array, i, 0);
            System.out.println(" ");
            System.out.println("-------"+i+"-------");
            for(int arg:array){
                System.out.print(arg+" ");
            }
        }
    }

    private static void buildMaxHeap(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        int half = array.length / 2;
        for (int i = half; i >= 0; i--) {
            maxHeap(array, array.length, i);
        }
    }

    private static void maxHeap(int[] array, int heapSize, int index) {
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        int largest = index;
        if (left < heapSize && array[left] > array[index]) {
            largest = left;
        }

        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        if (index != largest) {

            int temp;
            temp = array[index];
            array[index] = array[largest];
            array[largest] = temp;
           
            maxHeap(array, heapSize, largest);
        }
    }

查找

一、顺序查找

顺序查找又称为线性查找,是一种最简单的查找方法。适用于线性表的顺序存储结构和链式存储结构。

  • 基本思路
    从第一个元素m开始逐个与需要查找的元素x进行比较,当比较到元素值相同(即m=x)时返回元素m的下标,如果比较到最后都没有找到,则返回-1。
  • public class Shunxu {
    
    public static void main(String[] args){
    
    int[] array = {
    12,3,43,5,9};
    int target = 43;
    int[] newArray = new int[array.length+1];
    newArray[0] = target;
    for(int i=0;i<array.length;i++){
    
    newArray[i+1] = array[i];
    }
    int result = sequenceSearchPlus(newArray,target)-1;
    if(result != -1){
    
    System.out.println("要查找的元素,在数组中的下标是:"+result);
    }else{
    
    System.out.println("要查找的元素不在数组中");
    }
    }
    public static int sequenceSearchPlus(int[] arr,int key){
    
    int n=arr.length-1;
    arr[0]=key;
    while(arr[n]!=key){
    
    n--;
    }
    return n;
    }
    }

    二、二分查找

    二分查找,是一种在有序数组中查找某一特定元素的查找算法。

  • 基本思路
    用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。
  • public static int binarySearch2(int array[],int left,int right,int target){
    
    if(left<=right){
    
    int mid=(left+right)/2;
    /*搜索到对应元素*/
    if(array[mid]==target){
    
    return mid;
    }else if(array[mid]<target){
    
    /*array[mid]小于target,即要寻找的元素在右半边,所以需要设定左边界为mid+1,搜索右半边*/
    return binarySearch2(array,mid+1,right,target);
    }else{
    
    /*array[mid]大于target,即要寻找的元素在左半边,所以需要设定右边界为mid-1,搜索左半边*/
    return binarySearch2(array,left,mid-1,target);
    }
    }else{
    
    return -1;
    }
    }

    三、插值查找

    在二分查找中,每次都是从待查找序列的中间点开始查找,这样的做法在正确性上固然没什么问题,但假如要查找的值距离某个边界比较近,还从中间点开始查找,就有点浪费时间了。举个例子来说说明,假如在在一个{1,2…,100}的数组中,要查找88这个值,还一直采用和中间点比较的策略,就显得不太明智,因为明显可以明显从较为靠后的位置去检索。为了克服这种弊端, 引入了插值查找。

  • 基本思路
    插值查找是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的 查找方法,其核心就在于插值的计算公式 (key-array[low])/(array[high]-array[low])*(high-low)。简而言之,基于二分查找算法,将查找点的选择改进为自适应选择。
  • private static int insertSearch1(int arr[],int target){
    
    /*初始化左右搜索边界*/
    int left=0,right=arr.length-1;
    int mid;
    while(left<=right){
    
    mid=left+(target-arr[left])/(arr[right]-arr[left])*(right-left);
    /*arr[mid]大于target,即要寻找的元素在左半边,所以需要设定右边界为mid-1,搜索左半边*/
    if(target<arr[mid]){
    
    right=mid-1;
    /*arr[mid]小于target,即要寻找的元素在右半边,所以需要设定左边界为mid+1,搜索右半边*/
    }else if(target>arr[mid]){
    
    left=mid+1;
    /*搜索到对应元素*/
    }else if(target==arr[mid]){
    
    return mid;
    }
    }
    /*搜索不到返回-1*/
    return -1;
    }

    四、树表查找

    二叉排序树是最简单的树表查找算法,该算法需要利用待查找的数据,进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,然后再进行查找。

  •  public static void createBST(BinaryTree root, int element){
    
    BinaryTree newNode = new BinaryTree(element);
    if(element > root.value){
    
    if(root.right == null)
    root.right = newNode;
    else
    createBST(root.right, element);
    }else if(element < root.value){
    
    if(root.left == null)
    root.left = newNode;
    else
    createBST(root.left, element);
    }else{
    
    System.out.println("该节点" + element + "已存在");
    return;
    }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值