排序查找算法

二分查找法

时间复杂度:O(log n)

  • 题目:在一个一维且有序的数组中,查找数组中是否有 flag 的数存在。
    • 首先判断数组中间值是否大于目标值,如果大于,则搜索前半部分数组。如果小于则搜索后半部分数组。
    • 在判断半部分的数组的中间值是否大于目标值,来判断接下来的数组搜索的范围。知道找到该值
    • 循环跳出条件:数组的最小索引值大于数组的最大索引值。跳出还没有找到,则该数组没有该值。

代码实现:

public class search {
    public static int bin_search(int[] array,int key){
        int right = array.length;
        int left = 0;
        int mid = 0;
        while(left <= right){
            mid = (left + right)/2;
            if(array[mid] == key){
                return mid;
            }else if(array[mid] > key){
                right = mid-1;
            }else if (array[mid] < key){
                left = mid+1;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7,8,10,15};
        System.out.println(bin_search(array, 12));

    }
}

在这里插入图片描述

插入排序

用下面一张图来进行演示插入排序,序列为(6 5 3 1 8 7 2 4)(图片来自:流丶觞)
在这里插入图片描述

  • 我们默认第一个数字是有序的即 6。
  • 然后看第二个数字 5 ,5 比 6 小,所以 5 应该插在 6 的前面,该序列变成了 5 6 3 1 8 7 2 4;
  • 然后看第三个数字 3, 3 比 5 小,所以应该插在 5 的前面,序列变成了 3 5 6 1 8 7 2 4;
  • 依次将后面的数字进行插入。

代码实现:

public static void insertSort(int[] array){
        for(int i = 1;i < array.length;i++){
        //从第二个数字开始进行插入
            int temp = array[i];
            int j ;
            //依次将插入位置之后的数据向后移动
            for(j = i;j > 0 && array[j-1] > temp;j--){
                array[j] = array[j-1];
            }
            array[j] = temp;
        }
    }

希尔排序

希尔排序又称缩小增量法是对直接插入排序的优化。序列(9 1 2 5 7 4 8 6 3 5)分组的表达式为(gap = gap/2,可以为其他的表达式)。

  • 第一趟:先选定一个初始组数,将待排序序列分成个组,将距离为相同的数字分为一组,如(9和4)(1和8)(2和6)(5和3)(7和5)进行组内排序。排序过后为(4 1 2 3 5 9 8 6 5 7)
  • 第二趟:将选定的组数进行折半为2组,在进行以上分组排序,两组(4 2 5 8 5)(1 3 6 7 9),组内进行插入排序
  • 第三趟:上面分两组,接下来是分1组,就是第二趟结束后的序列,进行插入排序。
    在这里插入图片描述
    代码实现:
public static void shellSort(int[] array){
        int gap = array.length;
        while(true){
            gap = gap / 2;
            shellWithGap(array,gap);
            if(gap == 1){
                return ;
            }
        }
    }
    public static void shellWithGap(int[] array,int gap){
        for(int i = 0;i < array.length - gap;i++){
            int k = array[i + gap];
            int j;
            for(j = i;j >= 0 && array[j] > k;j -= gap){
                array[j + gap] = array[j];
            }
            array[j + gap] = k;
        }
    }

选择排序

  • 每次从待排序中的数据中选择一个最大的或者最小的数字,存放在序列的其实位置,知道全部排序完成。
    - 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
    - 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
    - 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
    在这里插入图片描述
    代码实现:
public static void selectSort(int[] array){
        for(int i = 0; i < array.length - 1; i++){
            int min = i;
            for(int j = i + 1;j < array.length; j++){
                if(array [j] < array[min]){
                    min = j;
                }
            }
            int temp = array[min];
            array[min] = array[i];
            array[i] = temp;
        }
    }

升级版本的插入排序。

    public static void selsceSortVersion2(int[] array){
        int low = 0;
        int hight = array.length -1;
        while(low < hight){
            int min = low;
            int max = low;
            for(int i = low + 1;i <= hight;i++){
                if(array[i] > array[max]){
                    max = i;
                }if(array[i] > array[min]){
                    min = i;
                }
            }
            swap(array,min,low);
            if(max == low){
                max = min;
            }
            swap(array,max,hight);
            low++;
            hight--;
        }
    }
    private static void swap (int[] array,int m,int n){
        int temp = array[m];
        array[m] = array[n];
        array[n] = temp;
    }

堆排序

  • 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
private static void swap(int[]array,int n,int m){
        int temp = array[n];
        array[n] = array[m];
        array[m] = temp;
    }

    public static void heapSort(int[] array) {

        creatHeap(array, array.length);
        for (int i = 0; i < array.length - 1; i++) {
            // 无序 [0, array.length - i)
            swap(array, 0, array.length - i - 1);
            // 无序 [0, array.length - i - 1)
            // 无序区间的长度: array.length - i - 1
            heapsfy(array, array.length - i - 1, 0);
        }
    }
    private static void heapsfy(int[]array,int size,int k){
        while(true){
            int left = k*2+1;
            //判断是否为叶子结点,
            // 若为叶子结点,则直接返回
            if(left >size){
                return;
            }
            int max = left;
            if(left + 1 < size){
                if(array[max] < array[left+1]){
                    max = left + 1;
                }
                if(array[k] < array[max]){
                    swap(array,max,k);
                    k = max;
                }
            }
        }
    }

冒泡排序

  • 冒泡排序的实质是:在排序的过程中,相邻的元素通过比较来进行不断的交换。
    • 举例子:一个序列为:4 9 7 2 5 1 3(排序为递增序列)
    • 首先 4 和 9 比较,4 比 9 小,不交换;9 比 7 大 交换 (交换后的序列为:4 7 9 2 5 1 3);接下来是 9 和 2 比较,9 比 2 大所以交换(交换为的序列为: 4 7 2 9 5 1 3);下来就是 9 和 5 比较,9 和 1 比较 9 和 3 比较(第一轮交换完毕,最后的序列为:4 7 2 5 1 3 9)。
    • 第一轮交换完 之后进行第二轮交换,方法相同。最后的结果为(4 2 5 1 3 7 9)。
    • 同理,第三轮结果( 2 4 1 3 5 7 9);第四轮(2 1 3 4 5 7 9);第五轮(1 2 3 4 5 7 9);第六轮(1 2 3 4 5 7 9)。第七轮(1 2 3 4 5 7 9)
  • 举的例子比较特殊,可以看出在第五轮的时候,该序列就已经成为有序数列了,但是后面的比较交换还是会执行。
  • 优化:我们可以看出,第一轮通过比较可以确定了9 的位置,第二轮确定了 7 的位置。依次确定该序列中所有元素的位置。所以,在新的一轮比较中,确定好的位置就可以不用进行比较了,来节省时间。在最后一轮(第七轮)可以不进行比较,因为一共七个数字,六个数字都确定好了位置,最后一个最后一个位置当然是哪个没有确定的数字。

代码实现:

public static void sort(int[] array){
        int length = array.length;
        for(int i = 0;i < length - 1;i++){
            for (int j = 0;j < length - 1 - i ;j++){
                if(array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
//打印比较过程:
//4 7 2 5 1 3 9 
//4 2 5 1 3 7 9 
//2 4 1 3 5 7 9 
//2 1 3 4 5 7 9 
//1 2 3 4 5 7 9 
//1 2 3 4 5 7 9 

快速排序

  • 快速排序其实就是将选择序列中的一个数,作为基准点,然后将小于基准点的数字放在左面,大于基准点的数字放在右面。
  • 然后在小于基准点的序列中在找一个基准点,在进行小的放左边,大的放右边。右边大于基准点的数字也是一样的。
  • 如此反复,一直到序列数字为1为止。此时就是有序的序列了。

假设待排序的序列为(6 1 2 7 9 3 4 5 10 8);

0123456789
61279345108
ij
  • 我们选择6为基准点,此时j进行判断操作,j 现在所在位置的数字是8所以我们需要向前挪动(–操作)直到找到比6小的数字,就是5(此时j = 7)。
  • 然后 i 进行向前进行移动(++ 操作),找到比6大的数字 ,找到 7(此时 i = 3)。
  • 再将 5 和 7 进行交换(i = 3 和 j = 7),第一轮交换结束。此时的序列为(6 1 2 5 4 3 9 7 10 8)
  • 第二轮交换开始,先 j 进行寻找比 6 小的数字 (找到4,j = 6),在 i 进行寻找比6大的数字(找到9,i=4),在此进行交换。第二轮交换完毕。(6 1 2 5 4 3 9 7 10 8)
  • 第二轮交换完之后,j 进行寻找比 6 小的数字 (找到3,j = 5),i 进行++操作,寻找比6 大的数字,但是在进行++ 操作时(此时i = 5),i 和 j 在同一位置(相遇),说明 i 和 j 的寻找操作结束,此时基准数 6 和 3 进行交换,交换后的序列为(3 1 2 5 4 6 9 7 10 8) 。
  • 将6作为分界点,左边为比6 小的数字,右边为比 6 大的数字。分为两个序列,在进行上面所述的操作。
    整个过程的交换图:
    在这里插入图片描述

可以看看下面的动态图进行,进一步的理解(图片来自:Angus_lxy)

在这里插入图片描述

代码实现:

//快速排序
public static void quick_sort(int[] array,int left,int right) {
        int i, j, t, temp;
        if (left > right) {
            return;
        }
        temp = array[left];
        i = left;
        j = right;
        while (i != j) {
            while (array[j] >= temp && i < j) {
                j--;
            }
            while (array[i] <= temp && i < j) {
                i++;
            }
            if (i < j) {
                t = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        array[left] = array[i];
        array[i] = temp;
        quick_sort(array,left,i-1);
        quick_sort(array,i + 1, right);
    }

挖坑 + 分治 思想 快排

归并排序

归并排序用一句话来讲就是将已有序的序列合并,得到完全有序的序列。即先将每个子序列有序,再使每个子序列段间有序。如果是两个有序序列合并,则是二路归并,还有更多的多路归并。
在这里插入图片描述
动态图演示:
在这里插入图片描述

代码实现:

//递归实现
    public static void mergeSort(int[] array) {
        mergeSortInternal(array, 0, array.length);
    }

    private static void mergeSortInternal(int[] array, int low, int high) {
        if (low + 1 >= high) {
            return;
        }

        int mid = (low + high) / 2;
        // [low, mid)
        // [mid, high)
        mergeSortInternal(array, low, mid);
        mergeSortInternal(array, mid, high);
        merge(array, low, mid, high);
    }

    private static void merge(int[] array, int low, int mid, int high) {
        int length = high - low;
        int[] extra = new int[length];
        // [low, mid)
        // [mid, high)

        int iLeft = low;
        int iRight = mid;
        int iExtra = 0;

        while (iLeft < mid && iRight < high) {
            if (array[iLeft] <= array[iRight]) {
                extra[iExtra++] = array[iLeft++];
            } else {
                extra[iExtra++] = array[iRight++];
            }
        }

        while (iLeft < mid) {
            extra[iExtra++] = array[iLeft++];
        }

        while (iRight < high) {
            extra[iExtra++] = array[iRight++];
        }

        for (int i = 0; i < length; i++) {
            array[low + i] = extra[i];
        }
    }

//非递归实现
public static void mergeSortNoR(int[] array) {
        for (int i = 1; i < array.length; i = i * 2) {
            for (int j = 0; j < array.length; j = j + 2 * i) {
                int low = j;
                int mid = j + i;
                int high = mid + i;
                if (mid >= array.length) {
                    continue;
                }
                if (high > array.length) {
                    high = array.length;
                }

                merge(array, low, mid, high);
            }
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值