复杂度分析、排序算法、二分法、堆的上调和下调

1.认识复杂度与排序算法

  1. 复杂度:认识时间复杂度就是看进行了多少次常数操作。(什么是常数操作?赋值、加减乘除运算等都是。调用API操作就不是如list.get(i)。)

    • 时间复杂度:在常数操作的数量级表达式中不要低阶项,只要最高阶项,并忽略系数。
    • 空间复杂度:系统需要额外空间去保存变量。
  2. 选择排序:

    • 概念:一个n长度的数组,从0-n-1找最小的数,然后和第一个数交换,第一个数不动。再从1-n-1找最小的数,然后和第二个数交换,第二个数不动,依次查找。

    • 时间复杂度分析:若时间复杂度指标相同,可以实际测试来看那个好。O(n^2)

    • 空间复杂度分析:看代码,minIndex每次进入循环都重新申请,完了又释放,所以复杂度为O(1)

    • 代码框架:

      public static void selectionSort(int[] arr){
          int len = arr.length();
          if(arr == null || len < 2)
              return;
          for(int i=0;i<len-1;i++){
              int minIndex = i;
              for(int j=i+1;j<len;j++){
                  minIndex = arr[j] < arr[minIndex] ? j : minIndex;
              }
              swap(arr,i,minIndex);//把找到的最小值和第一个数交换
          }
      }
      
  3. 冒泡排序:

    • 概念:相邻两个比较,大的往后排,第一轮找到最大的,第二轮找到次大的,依次.....

    • 时间复杂度分析:O(n^2)

    • 空间复杂度分析:对arr数组中的每个数进行比较,申请了空间为n的数组,则O(n)

    • 代码框架:

      public static void bubbleSort(int[] arr){
          if(arr == null || arr.length() < 2)
              return;
          for(int i=0;i<n-1;i++){
              for(int j=0;j<n-i-1;j++){
                  if(arr[j]>arr[j+1])
                      swap(arr[j],arr[j+1]);
              }
          }
      }
      
  4. 异或^

    • 概念:相同的为0,不同的为1。也可理解为无进位相加,1+0=1,1+1=2,不进位后,1+1=0。

    • 性质:

      • a^a = 0,a^0 = a
      • 满足交换律、结合律
    • 举例运算:

      public static void swap(int a,int b){
          a = a^b;
          b = a^b;
          a = a^b;
          //完成交换
      }
      

    • 例子:可以求一个数组中出现奇数次的数。(全部异或)
  5. 插入排序

    • 概念:(非降序)一个数组a[n],①先看0-0区间,已经排序;②看0-1区间,如果a[1]<a[0],就交换两个数,排好序;③看0-2区间,a[2]依次与前面的那个数比较。

    • 时间复杂度分析:最差情况下,要实现降序,如【1,2,3,4,5,6,7】每一次都要与前面交换i次,因此表达式为1+2+3+...+n,则时间复杂度为O(n2)。最好情况下,如【5,4,3,2,1】,每一次都只需要看前面一眼,因此为O(n)。所以插入排序时间复杂度为O(n2)

    • 代码框架:

      public static void insertSort(int[] arr){
          if(arr == null || arr.length() < 2)
              return;
          for(int i=1;i<arr.length();i++){
              for(int j=i-1;j>=0;j--){
                  if(arr[j]>arr[j+1])
                      swap(arr,j,j+1);
              }
          }
      }
      
  6. 二分查找

    • 时间复杂度:O(logN),每一次都砍一半。

    • 代码框架:

      public static int twoSort(int[] arr,int i,int j){
          int midIndex = (i+j)/2;
          if(arr[midIndex] == num)
              return midIndex;
          if(arr[midIndex] > num)
              twoSort(arr,midIndex+1,j);
          else
              twoSort(arr,i,midIndex-1);
          return midIndex;
      }
      

2.堆

  1. 上调:

    • 应用:堆的初始化、向堆中添加一个元素

    • 过程:

    • 代码:

      #include <iostream>
      #include <vector>
      using namespace std;
      
      vector<int> heap;//存储堆
      vector<int> index;
      
      /**上调
       **参数:位置i
      **/
      void shiptUp(int i){
          if(i==1)
              return ;
          int flag=0;  //标记是否需要交换
          while(i!=1 && flag==0){
              if(heap[i] < heap[i/2])
                  swap(heap[i],heap[i/2]);
              else
                  flag=1; //不用交换
              i/=2;
          }
      }
      
      int main(){
          //题目给出一个数组,要建成小顶堆
          int n;
          for(int i=1;i<=n;i++){
              cin>>heap[i];
              shiftUp(i);  
          }
          return 0;
      }
      
  2. 下调

    • 应用:用某数替换最小值/最大值(根)。

    • 过程:

    • 代码:

      //大顶堆
      
      #include <iostream>
      #include <vector>
      using namespace std;
      
      vector<int> heap;//存储堆
      vector<int> index;
      
      /**上调
       **参数:位置i
      **/
      void shiftDown(int i){
          int flag=0,t;
          while(i*2<n&&flag==0){//如果有左孩子,左孩子比根大,则要考虑根左孩子换
              if(heap[i]<heap[i*2])
                  t=i*2;
              else
                  t=i;
              if(i*2+1<n){//如果有右孩子,右孩子比左孩子还大,则要根右孩子换
                  if(heap[t]<heap[i*2+1])
                      t=i*2+1;
              }
              //如果发现t不是自己
              if(i!=t){
                  swap(heap[i],heap[t]);
                  i=t;
              }else{
                  flag=1; //不用换
              }
          }
      }
      
      int main(){
          int n;
          shiftDown(0); //根节点位置为0
          return 0;
      }
      

      注:小顶堆只需要将heap[i] < heap[i2] 改为heap[i] >heap[i2](包含右孩子比较的)即可。

posted @ 2022-08-22 17:09  湘summer  阅读(60)  评论(0编辑  收藏  举报