数据结构查找算法和排序算法java实现

本博客纯粹为了记录一下,方便自己回顾知识点。

一、查找算法:

查找算法较为简单,常用的主要有两种:顺序查找和二分查找

顺序查找:按数据存储的顺序进行遍历查找,对存储顺序没有要求,性能较低。

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

(1)顺序查找实现:

/**
     * 顺序查找
     * @param array
     * @param value
     * @return
     */
    public static int search(int[] array, int value) {
        for (int i=0;i <= array.length -1; i ++) {
            if (value == array[i])
                return i + 1;
        }

        return -1;
    }

(2)二分查找实现

递归实现:

    /**
     * 递归实现
     * @param array
     * @param value
     * @return
     */
    public static int biSearch2(int[] array, int value,int low, int high) {

        if (low > high) {
            return -1;
        }

        int middle = (low + high)/2;

        if (array[middle] == value) {
            return middle + 1;
        } else if (array[middle] < value) {
            return biSearch2(array, value, middle + 1, high);
        } else {
            return biSearch2(array, value, low, middle -1);
        }
    }

非递归实现:

    /**
     * 非递归实现
     * @param array
     * @param value
     * @return
     */
    public static int biSearch(int[] array, int value) {
        int low = 0;
        int high = array.length -1;
        int middle;

        while (low <= high) {
            middle = (low + high)/2;
            if (array[middle] == value) {
                return middle + 1;
            }

            if (array[middle] > value) {
                high = middle -1;
            } else {
                low = middle + 1;
            }
        }

        return -1;
    }

测试demo:

    public static void main(String[] args) {
        int[] array = {1, 4, 6, 8, 25, 46, 73, 89,99, 100};

        System.out.println("===================非递归实现=================");
        int i = biSearch(array, 25);
        System.out.println(i);
        System.out.println(array[i-1]);

        System.out.println("===================递归实现=================");
        int m = biSearch2(array, 25, 0, array.length-1);
        System.out.println(m);
        System.out.println(array[m-1]);

        System.out.println("===================顺序查找=================");
        int n = search(array, 25);
        System.out.println(n);
        System.out.println(array[n-1]);
    }

二、排序算法

常用排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等

(1)冒泡排序:

原理:比较两个相邻的元素,将值大的元素交换至右端,最终形成一个从小到大的序列,需要比较比较n-1次即可。

核心实现:

   public static void sort(int[] array) {
        for (int i = 0; i < array.length-1; i ++) {
            for (int j = 0; j < array.length - i-1; j++) {
                if (array[j] > array[j+1]) {
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }

        System.out.println("排序后的数组为:");
        for (int num : array) {
            System.out.print(" " + num);
        }
    }

(2)直接选择排序:

原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。也就是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。基于此思想的算法主要有简单选择排序、树型选择排序和堆排序。(这里只介绍常用的简单选择排序)

基本思想:给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]~arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]~arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~arr[n]中选出最小的数据,将它与r[i]交换, 直到全部排序完成。

核心实现:

    public static void selectSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i ++) {
            int k = i;
            for(int j = k + 1; j < arr.length; j ++) {
                if (arr[j] < arr[k]) {
                    k = j;//找出最小值的下标
                }
            }
            //交换最小值
            if (i != k) {
                int temp = arr[i];
                arr[i] = arr[k];
                arr[k] = temp;
            }
        }

        System.out.println("排序后的顺序:");
        for (int num : arr) {
            System.out.print(" " + num);
        }
    }

(3)直接插入排序

原理:
      利用插入法对无序数组排序时,我们其实是将数组R划分成两个子区间R[1...i-1](已排好序的有序区)和R[i...n](当前未排序的部分,可称无序区)。插入排序的基本操作是将当前无序区的第1个记录R[i]插人到有序区R[1..i-1]中适当的位置上,使R[1...i]变为新的有序区。因为这种方法每次使有序区增加1个记录,通常称增量法。
核心实现:

    public static void insertSort(int[] arr) {
        for (int index=1; index < arr.length; index ++) {
            int temp = arr[index]; //用于比较的数据
            int leftIndex = index -1;

            while (leftIndex >= 0 && arr[leftIndex] > temp) {
                arr[leftIndex+1] = arr[leftIndex];
                leftIndex --;
            }
            arr[leftIndex + 1] = temp;

        }

        System.out.println("排序后的数组:");
        for (int num : arr) {
            System.out.print(" " + num);
        }
    }

(4)希尔排序

原理:

(1)希尔排序(shell sort)这个排序方法又称为缩小增量排序,是基于插入排序的优化。该方法的基本思想是:设待排序元素序列有n个元素,首先取一个整数increment(小于n)作为间隔将全部元素分为increment个子序列,所有距离为increment的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔increment,重复上述子序列划分和排序工作。直到最后取increment=1,将所有元素放在同一个子序列中排序为止。 
(2)由于开始时,increment的取值较大,每个子序列中的元素较少,排序速度较快,到排序后期increment取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。

increment(增量)的取法:

增量increment的取法有各种方案。最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。但由于直到最后一步,在奇数位置的元素才会与偶数位置的元素进行比较,这样使用这个序列的效率会很低。后来Knuth提出取increment=n/3向下取整+1.还有人提出都取奇数为好,也有人提出increment互质为好。应用不同的序列会使希尔排序算法的性能有很大的差异。

核心实现:

    public static void shellSort(int[] array) {
        //初始化间隔
        int h = 1;
        //计算最大间隔
        while (h < array.length/3) {
            h = h * 3 + 1;
        }

        while (h > 0) {
            //进行插入排序
            for (int index=h; index < array.length; index ++) {
                int temp = array[index]; //用于比较的数据
                int leftIndex = index;

                while (leftIndex >= h && array[leftIndex - h] > temp) {
                    array[leftIndex] = array[leftIndex - h];
                    leftIndex -=h;
                }
                array[leftIndex] = temp;
            }

            //减小间隔
            h = (h - 1)/3;
        }

        //打印排序后数组
        System.out.println("排序后数组:");
        for(int a : array) {
            System.out.print(a + " ");
        }
    }

(5)快速排序

思想:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率),然后分别从数组的两端扫描数组,设两个指示标志(left指向起始位置,right指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换left和right位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换left和right位置的值,如此往复循环,直到left>=right然后把基准点的值放到right这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。

算法分两步,

1. 数组划分

    /**
     * 划分区域
     * @param arr
     * @param left
     * @param right
     * @param point
     * @return
     */
    public static int partition(long arr[],int left, int right,long point) {
        int leftPtr = left - 1;
        int rightPtr = right;
        while(true) {
            //做基准点左侧数据处理
            while(leftPtr < rightPtr && arr[++leftPtr] < point);
            //对基准点右侧处理
            while(rightPtr > leftPtr && arr[--rightPtr] > point);
            if(leftPtr >= rightPtr) {
                break;
            } else {
                long tmp = arr[leftPtr];
                arr[leftPtr] =  arr[rightPtr];
                arr[rightPtr] = tmp;
            }
        }
        //
        long tmp = arr[leftPtr];
        arr[leftPtr] =  arr[right];
        arr[right] = tmp;
        return leftPtr;
    }

2. 分区排序:

    public static void sort(long[] arr, int left, int right) {
        if(right - left <= 0) {
            return;
        } else {
            //选择基准点
            long point = arr[right];
            //划分区域
            int partition = partition(arr, left, right, point);
            //对左侧排序
            sort(arr,left,partition - 1);
            //对右侧排序
            sort(arr,partition + 1, right);
        }
    }

 这个算法可以继续进行优化,定点进行划分性能不是最高

(6)归并排序

原理:归并排序利用的是分治的思想实现。对于给定一组数据,利用递归与分治技术将数据序列划分成为越来越小的子序列,之后对子序列排序,最后再用递归方法将排好序的子序列合并成为有序序列。合并两个子序列时,需要申请两个子序列加起来长度的内存,临时存储新的生成序列,再将新生成的序列赋值到原数组相应的位置。
核心代码:

public static void main(String[] args) {
        int[] arr  = new int[10];
        for(int i = 0; i < 10;i++) {
            arr[i] = (int) (Math.random() * 99);
        }

        displayArr(arr);

        mergeSort(arr, 0, arr.length - 1);

        displayArr(arr);
    }

    public static void displayArr(int[] arr) {
        System.out.print("[");
        for(int num : arr) {
            System.out.print(num + " ");
        }
        System.out.print("]");
        System.out.println();
    }

    public static void mergeSort(int[] arr,int left,int right){

        if(left<right){
            int mid = (left+right)/2;
            mergeSort(arr,left,mid);//左边归并排序,使得左子序列有序
            mergeSort(arr,mid+1,right);//右边归并排序,使得右子序列有序
            merge(arr,left,mid,right);//合并两个子序列
        }
    }

    public static void merge(int[] arr, int left, int mid, int right) {
        //申请一个与原数组大小相同的数组
        int[] temp = new int[right - left + 1];
        int i = left;
        int j = mid+1;
        int k = 0;

        while(i<=mid&&j<=right){
            if (arr[i] < arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }

        while(i<=mid){//将左边剩余元素填充进temp中
            temp[k++] = arr[i++];
        }
        while(j<=right){//将右序列剩余元素填充进temp中
            temp[k++] = arr[j++];
        }
        //将temp中的元素全部拷贝到原数组中
        for (int k2 = 0; k2 < temp.length; k2++) {
            arr[k2 + left] = temp[k2];
        }
    }

(7)堆排序

 堆排序需要用到一种被称为最大堆的数据结构,与java或者lisp的gc不一样,这里的堆是一种数据结构,他可以被视为一种完全二叉树,即树里面除了最后一层其他层都是填满的。也正是因为这样,树里面每个节点的子女和双亲节点的序号都可以根据当前节点的序号直接求出。

(8)基数排序

思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。
做法:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

参考:基数排序

核心实现:

public static void main(String[] args) {
        int[] array  = new int[10];
        for(int i = 0; i < 10;i++) {
            array[i] = (int) (Math.random() * 999);
        }
        displayArr(array);
        radixSort(array);    // 基数排序
        displayArr(array);
    }

    /**
    * 基数排序
    *
    * 参数说明:
     *     a -- 数组
    */
    public static void radixSort(int[] a) {
        int exp;    // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;
        int max = getMax(a);    // 数组a中的最大值

        // 从个位开始,对数组a按"指数"进行排序
        for (exp = 1; max/exp > 0; exp *= 10)
            countSort(a, exp);
    }

    /**
     * 对数组按照"某个位数"进行排序(桶排序)
     * 参数说明:
     *     a -- 数组
     *     exp -- 指数。对数组a按照该指数进行排序。
     *    当exp=1表示按照"个位"对数组a进行排序
     *    当exp=10表示按照"十位"对数组a进行排序
     *    当exp=100表示按照"百位"对数组a进行排序
     *    ...
     */
    private static void countSort(int[] a, int exp) {
        int[] output = new int[a.length];    // 存储"被排序数据"的临时数组
        int[] buckets = new int[10];

        // 将数据出现的次数存储在buckets[]中
        for (int i = 0; i < a.length; i++)
            buckets[ (a[i]/exp)%10 ]++;

        // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
        for (int i = 1; i < 10; i++)
            buckets[i] += buckets[i - 1];

        // 将数据存储到临时数组output[]中
        for (int i = a.length - 1; i >= 0; i--) {
            output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
            buckets[ (a[i]/exp)%10 ]--;
        }

        // 将排序好的数据赋值给a[]
        for (int i = 0; i < a.length; i++)
            a[i] = output[i];

        output = null;
        buckets = null;
    }

    /*
     * 获取数组a中最大值
     * 参数说明:
     *     a -- 数组
     */
    private static int getMax(int[] a) {
        int max= a[0];
        for (int i = 1; i < a.length; i++)
            if (a[i] > max) max = a[i];
        return max;
    }

稳定排序和不稳定排序:

  1. 稳定排序:排序前后两个相等的数相对位置不变,则算法稳定。有冒泡排序、插入排序、归并排序和基数排序
  2. 非稳定排序:排序前后两个相等的数相对位置发生了变化,则算法不稳定。有选择排序、快速排序、希尔排序、堆排序

算法对比:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值