面试题:几个基础算法

二分查找

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


    }
    private static int binarySearch(int[] a,int t){
        int l =0,r=a.length-1,m;
        while (l<=r){
            m=(l+r)>>>1;//解决整数溢出(推荐)
//            方式2:int = l+(r-l)/2
            if (a[m]==t){
                return m;
            }else if (a[m]>t){
                r=m-1;
                m=(l+r)>>>1;
            }else {
                l=m+1;
                m=(l+r)>>>2;
            }
        }
        return -1;
    }
}

面试填空:

  • 比较几次

    • 奇数二分取中间(奇数是数的个数,这点自己容易迷)
    • 偶数二分取中间靠左
  • 求比较次数最多不超过多少次?

    • 2的多少次方等于这个数,结果就是几
    • 是小数的话,则舍去小数 ,整数部分加1

排序

  • 需要手写冒泡、快排的代码

  • 了解各个排序算法的特性,如时间复杂度、是否稳定

冒泡排序

文字描述(以升序为例)

  • 1.依次比较数组中相邻两个元素大小,若a[i]>a[j],则交换两个元素,两两都比较一遍称为冒泡,结果让最大的元素排至最后
  • 2.重复以上步骤,直到整个数组有序
//冒泡排序
public class BubbleSort {
    public static void main(String[] args) {
        int[] a={5,9,7,4,1,3,2,8};
//        int[] a={1,2,3,4,5,6,7,8,9};
        bubble(a);

    }


    public 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;
    }
}

优化1

//冒泡排序优化1
public class BubbleSort2 {
    public static void main(String[] args) {
        int[] a={5,9,7,4,1,3,2,8};
//        int[] a={1,2,3,4,5,6,7,8,9};
        bubble(a);

    }


    public static void bubble(int[] a){
        for (int j=0;j<a.length-1;j++){
            for (int i=0;i<a.length-1-j;i++){
                System.out.println("比较次数"+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;
    }
}

优化2(推荐)

//冒泡排序优化2
public class BubbleSort3 {
    public static void main(String[] args) {
//        int[] a={5,9,7,4,1,3,2,8};
        int[] a={1,2,3,4,5,6,7,8,9};
        bubble(a);

    }


    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++){
                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;
    }
}

最终优化(推荐)

//冒泡排序:最终优化
public class BubbleSort4 {
    public static void main(String[] args) {
//        int[] a={5,9,7,4,1,3,2,8};
        int[] a={1,2,3,4,5,6,7,8,9};
        bubble(a);

    }


    public static void bubble(int[] a){
        int n = a.length-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){
                break;
            }
        }

    }



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

选择排序

文字描述(以升序为例)

  • 1.将数组分为两个子集,排序的和未排序的,每一轮从未排序的子集中选出最小的元素,放入排序的子集
  • 2.重复以上步骤,直到整个数组有序
  • 3.优化方式
    • 为减少交换次数,每一轮可以找到最小的索引,在每轮最后交换元素
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;i++){
            //i代表每轮选择最小元素要交换到的目标索引
            int s=i;
            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));
        }

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

冒泡和选择的比较

  • 1.二者平均时间复杂度都是O(n2)
  • 2.选择排序一般要快于冒泡,因为其交换次数少
  • 3.但如果集合有序度高,冒泡优于选择
  • 4.冒泡属于稳定的排序算法,而选择属于不稳定的排序

插入排序

文字描述(以升序为例)

  • 1.将数组分为两个区域,排序区域和未排序区域,每一轮从未排序区域中取出第一个元素,插入到排序区域(需要保证顺序)
  • 2.重复以上步骤,直到整个数组有序

优化方式

  • 1.待插入元素进行比较时,遇到比自己小的元素,就代表找到了插入位置,无需进行后续比较-----代码中的这个部分:

    else {
        break;//退出循环,减少比较次数
    }
    
  • 2.插入时进行直接移动元素,而不是交换元素

以上优化均在代码中体现

public class InsertSort {
    public static void main(String[] args) {
        int[] a={9,3,7,2,5,8,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));
        }

    }
}

插入与选择排序进行比较

  • 1.二者平均时间复杂度都是O(n2)
  • 2.大部分情况下,插入都略优于选择
  • 3.有序集合插入的时间复杂度未O(n)
  • 4.插入属于稳定排序算法,而选择属于不稳定排序

快速排序-实现方式

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

  • 1.选择最右边元素作为基准点元素
  • 2.j指针负责找比基准点小的元素,一旦找到则与i进行交换
  • 3.i指针维护小于基准点元素的边界,也是每次交换的目标索引
  • 4.最后基准点与i交换,i即为分区位置
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);//右边分区的范围确定
    }



    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){
                if (i!=j){
                    swap(a,i,j);
                }

                i++;
            }
        }
        if (i!=h){
            swap(a,h,i);

        }

        System.out.println(Arrays.toString(a)+" i=" + i);

        //返回值代表了基准点元素所在的正确索引,用它缺点下一轮分区的边界
        return i;
    }
    public static void swap(int[] a,int i,int j){
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

}

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

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

  • 2.j指针负责从右向左找比基准点小的元素,i指针赋值从左到右

    找比基准点大的元素,一旦找到二者交换,直至i,j相交

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

public class QuickSort2 {
    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);//右边分区的范围确定
    }


    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 i;

    }

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

}

3.双边循环几个要点

  • 1.基准点在左边,并且要先j后i
  • 2.while(i<j && a[j]>pv) j–
  • 3.while(i<j && a[i] <= pv) i++

4.快速排序的特点

  • 1.平均时间复杂度是O(n log2n),最坏的时间复杂度O(n2)
  • 2.数据量较大时,优势非常明显
  • 3.属于不稳定排序

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值