【数据结构与算法】从0-1小白学习之路(进阶篇)

算法的分类:

  • 根据时间复杂度分类
  • 根据稳定性分类
  • 其他比如鸡尾酒排序,猴子排序,睡眠排序。

如何分析算法:

  • 给面试官说清:最好情况,最坏情况,平均情况时间复杂度
  • 时间复杂度的系数,常数,低阶
  • 比较次数和交换次数
  • 排序算法的内存消耗
  • 排序算法的稳定性

  • 常用算法:
    在这里插入图片描述

冒泡排序以及4种算法优化
冒泡排序顾名思义就是整个过程像气泡一样往上升,单向冒泡排序的基本思想是(假设由小到大排序):对于给定n个记录,从第一个记录开始依次对相邻的两个记录进行比较,当前面的记录大于后面的记录时,交换位置,进行一轮比较和换位后,n个记录的最大记录将位于第n位,然后对前(n-1)个记录进行第二轮比较;重复该过程,直到记录剩下一个为止。

  • 基础: 两层for循环,外层控制所有次数执行,内层控制所有数据交换排序。
   //1-基础冒泡排序
    public static void bubbleSort_1(int [] intarr){
        //冒泡排序
        //先进行所有元素迭代遍历
        int count = 0;
        //由于两两比较,则肯定是有个数不需要比较的所以这里需要减去1
        for (int i = 0;i < intarr.length-1;i++){
            //比较其中的每一个元素element,由于冒泡排序是下沉的小数在前,
            大数在后,先下沉大数,
            则每次外部循环的次数正好是已经排序好的不需要再排序的,所以这里是需要减去已经排好序的,由于i=0开始,那么不需要自己相比较,所以j从1开始。
            for(int j = 1;j < intarr.length - i;j++){
                //如果当前的元素>之后比较的元素,则交换位置,
                将小的元素放至在当前的元素的位置
                if(intarr[j-1]>intarr[j]){
                    int temp = intarr[j-1];
                    intarr[j-1] = intarr[j];
                    intarr[j] = temp;

                }
                count++;
                //假设数组2,3,1
                //则arr[0]=2与arr[1],比较,小于则通过,与arr[2]比较,大于则交换,arr[0]=1,arr[2]=3.则经历过第一次循环以后,{1,3,2}
                //则arr[1]=3与arr[1],比较,等于则通过,与arr[2]比较,大于则交换,arr[1]=2,arr[2]=3,则经历过第二次循环以后,{1,2,3}
                //第三次循环,arr[2]=2与arr[1],比较,等于则通过,与arr[2]比较,小于则通过,所以最终顺序为{1,2,3}
            }
            System.out.println("bubbleSort_1::"+Arrays.toString(intarr));
        }
        System.out.println("bubbleSort_1比较次数:"+count);
    }

在这里插入图片描述

当最好的情况,也就是要排序的序列本身就是有序的,需要进行(n-1)次比较,没有数据交换,时间复杂度为O(n).
当最坏的情况,即待排序的表是逆序的情况,此时需要比较次数为:1+2+3+…+(n-1)=n(n-1)/2 次,并作等数量级的记录移动,因此总的时间复杂度为O(n2)

  • 优化1:对于已经排序过的无需排序,标记为sorted,减少多余排序比较(如果是完全逆序的情况下,则和上面的基础排序没有区别)
//2-增加标记冒泡排序
    public static void bubbleSort_2(int [] intarr){
        //根据上面的冒泡排序我们知道其中我们每次遍历的时候都进行了所有的元素的比较
        //那么我们可以增加标记,减少不必要的重复比较
        int k = intarr.length;
        boolean sorted = true;
        int count = 0;
        //循环遍历
        while(sorted){
            //首次进入的时候我们需要设置为未排序过
            sorted = false;
            //交换一遍顺序
            for (int i = 1;i < k ;i++){
                if (intarr[i-1] > intarr[i]){
                    int temp = intarr[i-1];
                    intarr[i-1] = intarr[i];
                    intarr[i] = temp;
                    sorted = true;
                }
                count++;
            }
            System.out.println("bubbleSort_2::"+Arrays.toString(intarr));
            k--;
        }
        System.out.println("bubbleSort_2比较次数:"+count);
    }
  • 优化2:基于上面讲每次无序遍历的内层循环更新为最后一层交换元素的位置,减少一些已经排序好的比较(假设都是无序状态,则与上面的优化1没有区别甚至可能不如优化1,如果后面都是有序,例如{1,5,4,8,9,42,5,7,7,8,9,10,11,12})
 public static void bubbleSort_3(int [] intarr) {
    //基于上面讲每次无序遍历的内层循环更新为最后一层交换元素的位置,
    减少一些已经排序好的比较、、
    假设我们前面大部分是无序的,后面是有序的
//(1,2,5,7,4,3,6,8,9,10)这种场景,
则我们可以记录最后一次的交换位置,
下一次排序从第一个比较到上次记录的位置就可以了
        //所以基于以上的排序算法更改如下,初始化尾边界
        int tailBorder = intarr.length;
        int count = 0;
        int length = 0;
        while(tailBorder > 0){//尾边界排序未结束
            //遍历两两比较和交换位置
            length = tailBorder;
            tailBorder = 0;
            for (int i = 1;i < length;i++) {
                if (intarr[i-1] > intarr[i]){
                    int temp = intarr[i-1];
                    intarr[i-1] = intarr[i];
                    intarr[i] = temp;
                    tailBorder = i;
                }
                //假设已经排序好了m个,后面n个待排序

                count++;
            }

            System.out.println("bubbleSort_3::"+Arrays.toString(intarr));

        }

        System.out.println("bubbleSort_3比较次数:"+count);
    }

基础比较次数91次,优化1比较次数46次,优化2比较次数37次,优化3比较次数16次
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 优化3:鸡尾酒排序实现思路:外层循环所有元素,内层一共2套逻辑,第一套逻辑从左向右比较,第二套从右向左比较,类似钟摆一样来回比较。缺点是代码增多,适用场景是大部分有序的情况下。
 //鸡尾酒的排序实现
    public static void bubbleSort_4(int [] intarr) {
        //外层循环所有元素
        int count = 0;
        boolean sorted;//初始是没有排序
        for (int i = 0; i < intarr.length/2;i++){
        //每次初始化默认值
           sorted = false;
            //left->right比较
            System.out.println("bubbleSort_4:left->right 比较前:"+Arrays.toString(intarr));
            for (int j = i;j < intarr.length - 1 - i;j++){
                if (intarr[j] > intarr [j+1]){
                    int temp = intarr[j];  
                    intarr[j] = intarr[j+1];
                    intarr[j+1] = temp;
                    sorted = true;
                    count++;
                }
            }

            System.out.println("bubbleSort_4:left->right 比较后:"+Arrays.toString(intarr));
            //right->left比较

            System.out.println("bubbleSort_4:right->left 比较前:"+Arrays.toString(intarr));
            for (int j = intarr.length - 1 - i;j > i;j--){
                if (intarr[j-1] > intarr [j]){
                    int temp = intarr[j-1];
                    intarr[j-1] = intarr[j]; 
                    intarr[j] = temp;
                    sorted = true;
                    count++;
                }
            }
            //如果是排序完成或者排序好的则退出

            System.out.println("bubbleSort_4:right->left 比较后:"+Arrays.toString(intarr));
            if (sorted == false){
                break;
            }
        }

        System.out.println("bubbleSort_4比较次数:"+count);
    }

在这里插入图片描述

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始(末尾)位置,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。

  • 如何判断排序算法的是否稳定
  • 排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同
  • 如果相同,则是稳定的排序方法。
  • 如果不相同,则是不稳定的排序方法
  • 稳定排序的好处
    如果我们只对一串数字排序,那么稳定与否确实不重要,因为一串数字的属性是单一的,就是数字值的大小。但是排序的元素往往不只有一个属性,例如我们对一群人按年龄排序,但是人除了年龄属性还有身高体重属性,在年龄相同时如果不想破坏原先身高体重的次序,就必须用稳定排序算法.
    只有当在“二次”排序时不想破坏原先次序,稳定性才有意义

  • 选择排序的原理:
    每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始(末尾)位置,直到全部待排序的数据元素排完

  • 根据上面的冒泡排序其实我们这里的规律是相似的:

  • 一个数组是需要n-1趟排序的(因为直到剩下一个元素时,才不需要找最大值)
  • 每交换1次,再次找最大值时就将范围缩小1
  • 查询当前趟数最大值实际上不用知道最大值是多少(上面我查出最大值,还要我手动数它的角标),知道它的数组角标即可,交换也是根据角标来进行交换
    粗略代码实现:
 public static void main(String args[]){
        int[] arrays = {2, 3, 1, 4, 3, 5, 1, 6, 1, 2, 3, 7, 2, 3};
        insertSort_1(arrays);
    }
    // 一个数组是需要n-1趟排序的(因为直到剩下一个元素时,才不需要找最大值)
    //每交换1次,再次找最大值时就将范围缩小1
    //查询当前趟数最大值实际上不用知道最大值是多少
    // 知道它的数组角标即可,交换也是根据角标来进行交换
    public static void insertSort_1(int [] arrays){
        int count =0;//记录下排序次数
        //需要保留最大的值的下标
        int index =0;
        //一个数组是需要n-1趟排序的
        for (int i = 0; i < arrays.length-1; i++) {
            //每次重新比较都需要从0开始,因为我们将最大值放到了最大下标处。
            index =0;
            //比较数据,每比较一次减少一次比较
            for (int j=1;j<arrays.length-i;j++){
                if (arrays[j] > arrays[index]) {
                    //记录最大下标值
                    index = j;
                    count++;
                }
            }
            //比较完一次,进行下数据交换
            int temp = arrays[arrays.length-1-i];
            //最大值放到最后面
            arrays[arrays.length-1-i] =arrays[index];
            //原来的下标位置存放现有的数据
            arrays[index] = temp;

        }
        System.out.println("插入排序:insertSort_1排序后::"+Arrays.toString(arrays));
        System.out.println("插入排序:insertSort_1排序次数::"+count);
    }
    插入排序:insertSort_1排序后::[1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 5, 6, 7]
    插入排序:insertSort_1排序次数::20

插入排序

插入排序(Insertion sort)是一种简单直观且稳定的排序算法。如果有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
规律:

  • 首先将已排序的数据看成一个整体
  • 一个数组是需要n-1趟排序的,总是用后一位跟已排序的数据比较(第一趟:第二位跟已排序的数据比,第二趟:第三位跟已排序的数据比)
    用第三位和已排序的数据比,实际上就是让第三位数跟两个数比较,只不过这两个数是已经排好序的而已。而正是因为它排好序的,我们可以使用一个循环就可以将我们比较的数据插入进去
第一种实现
 public static void main(String args[]){
        int [] intarrs = {10,9,8,7,6,5,4,3,2,1};
        insertSort_1(intarrs);
    }
    public static void insertSort_1(int [] intarrs){
        //由于为了好演示,我这里改为逆序的数组
        //首先我们按照原理出牌,就是在已经排序好的数组下,从外部插入一个数值。
        //实现步骤:最外层控制遍历次数,但是从1开始,因为将0位看成了有序数组
        //临时变量
        int temp;

        //外层循环控制需要排序的趟数(从1开始因为将第0位看成了有序数据)
        for (int i = 1; i < intarrs.length; i++) {
            temp = intarrs[i];
            //如果前一位(已排序的数据)比当前数据要大,那么就进入循环比较
            while (i >= 1 &&intarrs[i - 1] > temp) {
                //往后退一个位置,让当前数据与之前前位进行比较
                intarrs[i] = intarrs[i - 1];
                //不断往前,直到退出循环
                i--;
            }
            //退出了循环说明找到了合适的位置了,将当前数据插入合适的位置中
            intarrs[i] = temp;
        }

        System.out.println("插入排序:insertSort_1排序后::"+Arrays.toString(intarrs));
    }
  • 二分查找插入排序的原理:是直接插入排序的一个变种,区别是:在有序区中查找新元素插入位置时,为了减少元素比较次数提高效率,采用二分查找算法进行插入位置的确定。

希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。

  • 原理:
    就是把数列进行分组(不停使用插入排序),直至从宏观上看起来有序,最后插入排序起来就容易了(无须多次移位或交换)。
    在这里插入图片描述
 public static void shellSort_1(int [] intarrs){
     
        System.out.print("排序之前:"+Arrays.toString(intarrs));
        
        //希尔排序 先定义长度变量并初始化
        int length =intarrs.length;
        //当长度为1的时候终止循环条件
        while(true)
        {
            //每次的增量分组
            length = length/2;
            //一共需要遍历的次数
            for(int x=0;x< length;x++)
            {
                //以增量分组的形式比较数据
                for(int i=x+length;i<intarrs.length;i=i+length)
                {
                    //当前增量值后的value
                    int postValue=intarrs[i];
                    //定义交换条件
                    int j;
                    //当前未增量前的value,并且需要满足该值从下标0开始,并且该值大于交换后的值。
                    //说明大值在后,小值在前,所以交换
                    for(j=i-length;j>=0&&intarrs[j]>postValue;j=j-length)
                    {
                        intarrs[j+length]=intarrs[j];
                    }
                    //如果小在前,大在后,则需要将当前值重新赋值给当前位置
                    intarrs[j+length]=postValue;
                }
            }
            if(length==1)
            {
                break;
            }
        }
        System.out.println();
        System.out.println("排序之后:"+Arrays.toString(intarrs));

    }

在这里插入图片描述

计数排序(基数排序)
适用场景:当数列最大和最小值差距不大,当数列元素是整数时候。

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位 

开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序 

列。 

   public class radixSort { 

     inta[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,101,56,17,18,23,34,15,35,2 

  5,53,51}; 

     public radixSort(){ 

       sort(a); 

       for(inti=0;i<a.length;i++){ 

            System.out.println(a[i]); 

       } 

    } 

     public  void sort(int[] array){ 

       //首先确定排序的趟数; 

       int max=array[0]; 

       for(inti=1;i<array.length;i++){ 

           if(array[i]>max){ 
           max=array[i]; 

         } 

     } 

     int time=0; 

     //判断位数; 

     while(max>0){ 

        max/=10; 

        time++; 

     } 

      //建立 10 个队列; 

      List<ArrayList> queue=newArrayList<ArrayList>(); 

     for(int i=0;i<10;i++){ 

           ArrayList<Integer>queue1=new ArrayList<Integer>(); 

        queue.add(queue1); 

     } 

       //进行 time 次分配和收集; 

     for(int i=0;i<time;i++){ 

        //分配数组元素; 

        for(intj=0;j<array.length;j++){ 

           //得到数字的第 time+1 位数; 

             int x=array[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10, i); 

             ArrayList<Integer>queue2=queue.get(x); 

             queue2.add(array[j]); 

             queue.set(x, queue2); 

        } 

        int count=0;//元素计数器; 

       //收集队列元素; 

        for(int k=0;k<10;k++){ 

           while(queue.get(k).size()>0){ 

              ArrayList<Integer>queue3=queue.get(k); 

               array[count]=queue3.get(0); 

               queue3.remove(0); 

               count++; 

           } 

        } 

     } 

   } 

} 

桶排序

  • 求数列最大最小值,运算量为n
  • 创建空桶,运算量为n
  • 把原始数列的元素分配到各个桶中,运算量为n
  • 每个通内部排序,元素分布相对均匀情况下,所有桶运算量之和为n
  • 输出排序数列,运算量为n
桶排序的基本思想是: 把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最 

后合并 。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。 

1.找出待排序数组中的最大值 max、最小值 min 

2.我们使用 动态数组 ArrayList  作为桶,桶里放的元素也用 ArrayList  存储。桶的数量为(max- 

min)/arr.length+1 

3.遍历数组 arr ,计算每个元素arr[i] 放的桶 

4.每个桶各自排序 

   public static void bucketSort(int[] arr){ 

   int max = Integer.MIN_VALUE; 

   int min = Integer.MAX_VALUE; 

   for(int i = 0; i < arr.length; i++){ 
     max = Math.max(max, arr[i]); 

     min = Math.min(min, arr[i]); 

   } 

    //创建桶 

   int bucketNum = (max - min) / arr.length + 1; 

   ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); 

   for(int i = 0; i < bucketNum; i++){ 

     bucketArr.add(new ArrayList<Integer>()); 

   } 

     //将每个元素放入桶 

   for(int i = 0; i < arr.length; i++){ 

     int num = (arr[i] - min) / (arr.length); 

     bucketArr.get(num).add(arr[i]); 

   } 

     //对每个桶进行排序 

   for(int i = 0; i < bucketArr.size(); i++){ 

    Collections.sort(bucketArr.get(i)); 

   } 

  } 

剪枝算法
在这里插入图片描述
回溯算法
在这里插入图片描述
最短路径算法
在这里插入图片描述
最大子数组算法
最长公共子序算法
最小生成树算法
在这里插入图片描述
在这里插入图片描述

快速排序
堆排序
归并排序
二分查找
线性查找
深度优先
广度优先
Dijkstra
动态规划
朴素贝叶斯

小诚信驿站 CSDN认证博客专家 腾讯云+原创作者 云+优秀创作者 CSDN博客专家
公众号《小诚信驿站》,网名:小诚信驿站,小七,晓成。
工作经历:创业公司、京东、腾讯、目前在滴滴做一线研发。
业务经验:互联网保险、第三方支付、大促营销提报和效果分析、电商商业化、客服策略模型工程。
活动区域:CSDN-小诚信驿站,腾讯云-小诚信驿站,InfoQ-小诚信驿站,GitHub-小诚信驿站,公众号-小诚信驿站。想要找到我的话,小诚信驿站或者wolf_love666一般就可以找到啦。个人微信:lxc354555
相关推荐
程序员的必经之路! 【限时优惠】 现在下单,还享四重好礼: 1、教学课件免费下载 2、课程案例代码免费下载 3、专属VIP学员群免费答疑 4、下单还送800元编程大礼包 【超实用课程内容】  根据《2019-2020年中国开发者调查报告》显示,超83%的开发者都在使用MySQL数据库。使用量大同时,掌握MySQL早已是运维、DBA的必备技能,甚至部分IT开发岗位也要求对数据库使用和原理有深入的了解和掌握。 学习编程,你可能会犹豫选择 C++ 还是 Java;入门数据科学,你可能会纠结于选择 Python 还是 R;但无论如何, MySQL 都是 IT 从业人员不可或缺的技能!   套餐中一共包含2门MySQL数据库必学的核心课程(共98课时)   课程1:《MySQL数据库从入门到实战应用》   课程2:《高性能MySQL实战课》   【哪些人适合学习这门课程?】  1)平时只接触了语言基础,并未学习任何数据库知识的人;  2)对MySQL掌握程度薄弱的人,课程可以让你更好发挥MySQL最佳性能; 3)想修炼更好的MySQL内功,工作中遇到高并发场景可以游刃有余; 4)被面试官打破沙锅问到底的问题问到怀疑人生的应聘者。 【课程主要讲哪些内容?】 课程一:《MySQL数据库从入门到实战应用》 主要从基础篇,SQL语言篇、MySQL进阶篇三个角度展开讲解,帮助大家更加高效的管理MySQL数据库。 课程二:《高性能MySQL实战课》主要从高可用篇、MySQL8.0新特性篇,性能优化篇,面试篇四个角度展开讲解,帮助大家发挥MySQL的最佳性能的优化方法,掌握如何处理海量业务数据和高并发请求 【你能收获到什么?】  1.基础再提高,针对MySQL核心知识点学透,用对; 2.能力再提高,日常工作中的代码换新貌,不怕问题; 3.面试再加分,巴不得面试官打破沙锅问到底,竞争力MAX。 【课程如何观看?】  1、登录CSDN学院 APP 在我的课程中进行学习; 2、移动端:CSDN 学院APP(注意不是CSDN APP哦)  本课程为录播课,课程永久有效观看时长 【资料开放】 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化。  下载方式:电脑登录课程观看页面,点击右侧课件,可进行课程资料的打包下载。
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页