• 需重点掌握的三大排序: 快速排序 + 归并排序 + 堆排序


    关于排序算法的考察,需重点掌握   快速排序 、归并排序  、 堆排序 。

    1.  快速排序

        1.1 快速排序算法的思想与实现

         快速排序的重点是要掌握其划分的思想,划分算法需要先挑选一个 元素作为 pivot。在划分执行完之后,

    pivot左边的元素都小于等于它,右边的元素都大于等于它,pivot 的位置就是数组排序完成后它的最终位置。

    每执行一次 划分算法,确定数组中一个元素的位置。

     1 //1. 快速排序
     2     void quickSort(vector<int>& nums)
     3     {
     4         const int len = nums.size();
     5         if(len <= 1) return;
     6         std::random_shuffle(nums.begin(),nums.end());//打乱是为了避免快速排序陷入最差情况
     7         quickSortHlper(nums,0,len - 1);
     8         return;
     9     }
    10      void quickSortHlper(vector<int>& nums,int l,int r)
    11      {
    12          
    13         if(l >= r) return;
    14         int mid = Partition(nums,l,r);
    15         quickSortHlper(nums,l,mid - 1);
    16         quickSortHlper(nums,mid+1,r);
    17          return;
    18      }
    19     //划分
    20     int Partition(vector<int>& nums,int l,int r)
    21     {
    22         int i = l + 1, j = r;
    23         while(true)
    24         {
    25             while(i <= j && nums[i] <= nums[l]) i++;
    26             while(i <= j && nums[j] >= nums[l]) j--;
    27             // 1. 此时 [j+1,r]的元素一定都比 nums[l]大,[l+1,i-1] 内的元素一定都比 nums[l]小
    28             // 2. 因为 i > j ,[l+1,j] 一定是[l+1,i-1] 的子集,[l+1,j] 内的元素一定都比 nums[l]小
    29             // 3. [l+1,j] 内的元素一定都比 nums[l]小,[j+1,r]的元素一定都比 nums[l]大,所以
    30             //执行 std::swap(nums[l],nums[j])后,j 就是nums[l]最终的位置。
    31             if(i > j)//此时 [j+1,r]的元素一定都比 nums[l]大,[l+1,j] 内的元素一定都比 nums[l]小
    32             {
    33                 std::swap(nums[l],nums[j]);
    34                 break;
    35             }
    36             std::swap(nums[i],nums[j]);
    37         }
    38         return j;
    39     }

           1.2 快速排序算法的应用: LeetCode   215. 数组中的第K个最大元素

           

     1 class Solution {
     2 public:
     3     int findKthLargest(vector<int>& nums, int k)
     4     {
     5         const int N = nums.size();
     6         int k_max_index = N - k;
     7         int l = 0;
     8         int r = N -1;
     9         while(l <= r)
    10         {
    11             int mid = quickSelect(nums,l,r);
    12             if(mid == k_max_index)
    13             {
    14                 return nums[mid];
    15             }
    16             else if(mid < k_max_index)
    17             {
    18                 l = mid + 1;
    19             }
    20             else
    21             {
    22                 r = mid - 1;
    23             }
    24         }
    25         return INT_MAX;//不会执行到这一步
    26     }
    27     
    28 //快速排序划分算法,以 nums[l] 为 pivot,执行完成,确定 nums[l]最终排序好的位置
    29     int quickSelect(vector<int>& nums, int l,int r)
    30     {
    31         int i = l + 1, j = r;
    32         while(true)
    33         {
    34             while(i <= j && nums[i] <= nums[l]) i++;
    35             while(i <= j && nums[j] >= nums[l]) j--;
    36             // 1. 此时 [j+1,r]的元素一定都比 nums[l]大,[l+1,i-1] 内的元素一定都比 nums[l]小
    37             // 2. 因为 i > j ,[l+1,j] 一定是[l+1,i-1] 的子集,[l+1,j] 内的元素一定都比 nums[l]小
    38             // 3. [l+1,j] 内的元素一定都比 nums[l]小,[j+1,r]的元素一定都比 nums[l]大,所以
    39             //执行 std::swap(nums[l],nums[j])后,j 就是nums[l]最终的位置。
    40             if(i > j)//此时 [j+1,r]的元素一定都比 nums[l]大,[l+1,j] 内的元素一定都比 nums[l]小
    41             {
    42                 std::swap(nums[l],nums[j]);
    43                 break;
    44             }
    45             std::swap(nums[i],nums[j]);
    46         }
    47         return j;
    48     }
    49 };

    2. 归并排序

       2.1 归并排序的思想与实现

        归并排序是分治思想的典型应用,使用了一个辅助数组。时间 O(N *logN) 空间 (N)

              

     1 void mergeSort(vector<int>& nums)
     2     {
     3         const int len = nums.size();
     4         if(len <= 1) return;
     5         vector<int> tmp(len);
     6         mergeSortHelper(nums,0,len - 1,tmp);
     7         return;
     8     }
     9     void mergeSortHelper(vector<int>& nums,int l,int r,vector<int>& tmp)
    10     {
    11         if(l >= r)
    12         {
    13             return;
    14         }
    15         int mid = l + (r - l)/2;
    16         mergeSortHelper(nums,l,mid,tmp);
    17         mergeSortHelper(nums,mid+1,r,tmp);
    18         if(nums[mid] <= nums[mid+1])
    19         {
    20             return;
    21         }
    22         int i = l, j = mid + 1,k = 0;
    23         while(i <= mid && j <= r)
    24         {
    25             if(nums[i] <= nums[j])
    26             {
    27                 tmp[k++] = nums[i++];
    28             }
    29             else
    30             {
    31                 tmp[k++] = nums[j++];
    32             }
    33         }
    34         while(i <= mid)
    35         {
    36             tmp[k++] = nums[i++];
    37         }
    38         while(j <= r)
    39         {
    40             tmp[k++] = nums[j++];
    41         }
    42         for(i = l,k = 0; i <= r;++i)
    43         {
    44             nums[i] = tmp[k++];
    45         }
    46         return;
    47     }

          2.2  归并排序算法的应用: LeetCode    剑指 Offer 51. 数组中的逆序对

        

     1 class Solution {
     2 public:
     3     vector<int> tmp;//归并排序的辅助数组
     4 
     5     int reversePairs(vector<int>& nums) {
     6         const int len = nums.size();
     7         if(len < 2) return 0;
     8         tmp.assign(len,0);
     9         return merge_sort(nums,0,len - 1);
    10     }
    11  
    12     long long int merge_sort(vector<int>& nums,int l,int r)
    13     {
    14         if(l >= r)//归并排序递归出口
    15         {
    16             return 0;
    17         }
    18         int mid = l + (r - l)/2;//将区间一分为二
    19         long long ans = merge_sort(nums,l,mid) + merge_sort(nums,mid + 1,r);//递归地求左右子数组的逆序对个数和
    20         //剪枝优化: 子数组[l,r] 已经有序,横跨两个区间的逆序对个数为 0
    21         if(nums[mid + 1] >= nums[mid])
    22         {
    23            return ans;
    24         }
    25         //计算 横跨左右区间的逆序对
    26         int k = 0,i = l,j = mid + 1;
    27         while(i <= mid && j <= r)
    28         {
    29             if(nums[i] <= nums[j])
    30             {
    31                 tmp[k++] = nums[i++];
    32             }
    33             else
    34             { //执行上述递归之后,左右子数组都已经排好序
    35               //此处,nums[i:mid] 都比nums[j] 大,都和num[j] 构成逆序对
    36                 ans += (mid - i + 1);
    37                 tmp[k++] = nums[j++];
    38             }
    39         }
    40         //将左右区间未移动到tmp的继续移到tmp
    41         while(i <= mid) tmp[k++] = nums[i++];
    42         while(j <= r) tmp[k++] = nums[j++];
    43         //将排序好的元素移回原数组,放在原来的区间上,然后nums[l:r]已经排序好了 
    44         for(i = l,j = 0;i <= r; )
    45         {
    46             nums[i++] = tmp[j++];
    47         }
    48         return ans;
    49     }
    50 };

    3.   堆排序

      3.1 堆排序 是在数组上建立一个完全二叉树的堆,堆排序分为 建堆 和 排序 两个过程。

        建堆,通过下沉操作建堆效率更高,具体过程是,从最后一个非叶子节点开始,然后从后往前,对每个非叶子节点 执行下沉操作。

    这样之后,nums[0:N-1]上就是一个大顶堆。

        排序,将堆顶元素(代表最大元素)与堆的最后一个元素交换,同时从堆范围缩小一个元素,然后新的堆顶元素进行下沉操作,

    遍历执行上诉操作,则可以完成排序。

         

     1 //3. 堆排序 
     2     void heapSort(vector<int>& nums)
     3     {
     4        int len = nums.size();
     5        for(int i = (len - 1)/2; i >= 0; --i)
     6        {
     7            siftDown(nums,i,len -1);
     8        }
     9        for(int i = len -1 ;i >= 1;)
    10        {
    11            swap(nums[0],nums[i]);
    12            siftDown(nums,0,--i);
    13        }
    14        return;
    15     }
    16  /**
    17      * @param nums
    18      * @param k    当前下沉元素的下标
    19      * @param end  [0, end] 是堆的有效部分,[end+1,nums.size() - 1] 是nums中排好序的部分
    20      */
    21     void siftDown(vector<int>& nums,int k,int end)
    22     { 
    23         while(2 * k + 1 <= end)// k为叶节点跳出
    24         {
    25             int j = 2 * k + 1 ;//j是k的左子节点索引
    26             if(j + 1 <= end && nums[j+1] >= nums[j])
    27             {
    28                 j++;
    29             }
    30             //nums[j]是 nums[k]值最大的子节点
    31             if(nums[j] > nums[k])
    32             {
    33                 std::swap(nums[k],nums[j]);
    34             }
    35             else
    36             {
    37                 break;
    38             }
    39             k = j;
    40         }
    41     }

          3.2    堆排序的应用,c++ STL 中 的 优先队列  std::priority_queue 就是堆实现的。

     LeetCode    剑指 Offer 51. 数组中的逆序对  同样可以使用堆解决。

     1    int findKthLargest(vector<int>& nums, int k) 
     2     {
     3         std::priority_queue<int,vector<int>,greater<int>> pq;
     4         for(auto num : nums)
     5         {
     6             if(pq.size() == k && pq.top() >= num ) continue;
     7             if(pq.size() == k)
     8             {
     9                 pq.pop();
    10             }
    11             pq.push(num);
    12         }
    13         return pq.top();
    14     }

         

  • 相关阅读:
    mac PHP安装imageMagic扩展
    使用AWS Lambda,API Gateway和S3 Storage快速调整图片大小
    composer Changed current directory to没反应
    mongodb批量update更新数据
    php 判断图片文件的真实类型
    C#选择文件、选择文件夹、打开文件(或者文件夹)
    C#连接数据库
    湿寒阳虚体质如何艾灸
    女人艾灸穴位
    四个穴位掌控全身健康
  • 原文地址:https://www.cnblogs.com/wangxf2019/p/14674854.html
Copyright © 2020-2023  润新知