Skip to content

Latest commit

 

History

History
281 lines (211 loc) · 7.01 KB

排序.md

File metadata and controls

281 lines (211 loc) · 7.01 KB

排序算法

一 时间复杂度为 O(n2)的算法
1 插入排序

思想:分为已排序和未排序部分,将未排序部分依次插入已排好序的部分

时间复杂度:最好情况下 O(n) 最坏情况下 逆序 o(n2)

插入相比于冒泡 性能要好 因为 冒泡每次交换时需要3次赋值,而插入算法则只有依次赋值操作

 public void insertSort(int[] nums){
        for (int i=1;i<nums.length;i++){
            int tmp=nums[i];
            int j=i-1;
            for (;j>=0;j--){
                if (nums[j]>tmp){
                    nums[j+1]=nums[j];
                }else {
                    break;
                }
            }
            nums[j+1]=tmp;
        }
    }
2 冒泡排序

每一次比较相邻两各数的大小

时间复杂度:最好情况下 O(n)(优化后的) 最坏情况下 逆序 o(n2)

public void  buble(int[] nums){
    
        for(int i=0;i<nums.length;i++){
            //提前退出冒泡循环
            boolean flag=false;
            for (int j=0;j<nums.length-i-1;j++){
                if (nums[j]>nums[j+1]){
                    int tmp=nums[j];
                    nums[j]=nums[j+1];
                    nums[j+1]=tmp;
                    flag=true;
                }
            }
            if(!flag){
                break;
            }
     }
 }
二 时间复杂度为 n(logn)
1 归并排序

归并算法很稳定 ,最坏和最优以及平均时间复杂度都是 O(n)

思路:先划分,然后在合并 使用递归方式

public class MergeSort {
    public void mergeSort(int[] nums){

        merge(nums,0,nums.length-1);
    }

    private  void merge(int[] nums,int start,int end){
           if(start>=end){
               return;
           }
           int mid=(start+end)/2;
        	//先排序左侧
            merge(nums,start,mid);
        	//排序右侧
            merge(nums,mid+1,end);
           //将两个数组合并
            int i=start;
            int j=mid+1;
            int k=0;
            int[] tmp=new int[end-start+1];
            while (i<=mid &&j<=end){
                if (nums[i]>nums[j]){
                    System.out.println("k:"+k);
                    tmp[k++]=nums[j++];
                }else {
                    tmp[k++]=nums[i++];
                }
            }
            if(i>mid){
                while (j<=end){
                    tmp[k++]=nums[j++];
                }
            }else {
                while (i<=mid){
                    tmp[k++]=nums[i++];
                }
            }
            for (int n=0;n<tmp.length;n++){
                    nums[start+n]=tmp[n];

            }
    }
}

归并排序的优化

当 if( nums[mid] > nums[mid+1] ) 再进行合并

归并排序的优化二:

if( r-l < 15 ) 则使用插入排序

2 自底向上的归并排序算法

3 快速排序

当 有序时,复杂度为O(n2) 平均复杂度 O(nlogn)

  • 思路:取首位 v 大于 V 小于 v 三部分 ,然后对各个部分进行排序
 public void quickSort(int nums[],int l,int r){
        if (l>=r){
            return;
        }
        int index=partation(nums,l,r);
        quickSort(nums,l,index-1);
        quickSort(nums,index+1,r);
    }

    //划分为3部分 index 比index 小 比index大
    private int partation(int[] nums, int l, int r) {
     int tmp=nums[l];
     int index=l;
        //nums[l+1,index] <nums[index]  nums[index+1,r)> nums[index]
     for (int i=l+1;i<=r;i++){
         if (nums[i]<tmp){
             swap(nums,index+1,i);
             index++;
         }
     }
     swap(nums,index,l);
     return index;
    }

    private void swap(int[] nums, int j, int k) {
        int tmp=nums[j];
        nums[j]=nums[k];
        nums[k]=tmp;
    }

  • O(n) 时间复杂度 求无序数组的第k大元素

  • 十个接口访问日志文件,合并为一个文件

先构建十条io流,分别指向十个文件,每条io流读取对应文件的第一条数据,然后比较时间戳,选择出时间戳最小的那条数据,将其写入一个新的文件,然后指向该时间戳的io流读取下一行数据,然后继续刚才的操作,比较选出最小的时间戳数据,写入新文件,io流读取下一行数据,以此类推,完成文件的合并, 这种处理方式,日志文件有n个数据就要比较n次,每次比较选出一条数据来写入,时间复杂度是O(n),空间复杂度是O(1),几乎不占用内存,这是我想出的认为最好的操作了,希望老师指出最佳的做法!!!


三 二分查找法

1 求平方根,要求精确到小数点后6位

2 查找第一个值等于给定值的元素,有序数组且从大到小

public int bsearch(int[] nums,int target){
        int low=0;
        int high=nums.length-1;
        while (low<=high){
            int mid=low+((high-low)>>1);
            if (nums[mid]>target){
                high=mid-1;
            }else if(nums[mid]<target){
                low=mid+1;
            }else {
                if(mid==0 || nums[mid-1]!=target){
                    return mid;
                }
                high=mid-1;
            }
        }
        return -1;
    }

3 查找第一个大于等于给定值的元素

public int bsearch2(int[] nums,int target){
        int low=0;
        int high=nums.length-1;
        while (low<=high){
            int mid=low+((high-low)>>1);
            if (nums[mid]>=target){
                if (mid==0 ||nums[mid-1]<target){
                    return  mid;
                }
               high= mid-1;
            }else{
                low=mid+1;
            }
        }
        return -1;
    }

4 查找最后一个小于等于给定值的元素

public int bsearch3(int[]nums,int target){
    int low=0;
    int high=nums.length-1;
    while (low<=high){
        int mid=low+((high-low)>>1);
        if(nums[mid]<=target){
            if(mid==0 || nums[mid+1]>target){
                return mid;
            }
            low=mid+1;
        }else {
            high=mid-1;
        }
    }
    return -1;
}

5 如何快速定位 ip对应的省份地址

[202.102.133.0, 202.102.133.255]  山东东营市 
[202.102.135.0, 202.102.136.255]  山东烟台 
[202.102.156.34, 202.102.157.255] 山东青岛 
[202.102.48.0, 202.102.48.255] 江苏宿迁 
[202.102.49.15, 202.102.51.251] 江苏泰州 
[202.102.56.0, 202.102.56.255] 江苏连云港

在庞大的地址库中逐一比对 IP 地址所在的区间,是非常耗时的,
假设我们有 12 万条这样的 IP 区间与归属地的对应关系,如何快速定位出一个 IP 地址的归属地呢?

1将ip地址转换为 32 整型
2 从小到大排序
3 找到最后一个小于要找的ip的起始ip区间,在这个区间查找,没有则不存在

6 如果有一个有序循环数组,比如4,5,6,1,2,3。针对这种情况,如何实现一个求“值等于给定值”的二分查找算法?

leetcode 33