数组算法:二分、快排

典型的排序算法思想、二分查找思想在解 LeetCode 题目时很有用。

 


 

5.颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库的sort函数的情况下解决这个问题。

题解:定义前后指针 red(0) blue(2),遍历数组,若元素为0 则放置数组前方,若元素为2 ,则放置数组后方,非0 非2 即为1,使用变量temp,每次将其交换放置中间(非前非后即为中间)。

注意事项:for遍历的边界应为 blue + 1 (blue动态改变),不能为nums.length。不然会将数组后面已整理好的元素重新交换一遍。(此处有疑问可自行上机debug即可解决)

class Solution {
   public void sortColors(int[] nums) {
       //定义temp用于将所有非0 非2元素往中间塞入
       int red = 0,blue = nums.length-1,temp = 0;
       for(int i = 0;i <blue+1;i++){
           if(nums[i] == 0){
               temp = nums[red];
               nums[red++] = nums[i];
               nums[i] = temp;
          }if(nums[i] == 2){
               temp = nums[blue];
               nums[blue--] = nums[i];
               nums[i] = temp;
               //避免遗漏 末尾相邻元素交换,可能发生遗漏
               i--;
          }
      }
  }
}


 

6.数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

思路:快排。

 

快排复习:

首先我们来回顾一下快速排序,这是一个典型的分治算法。我们对数组 a[l⋯r]做快速排序的过程是(参考《算法导论》):

分解: 将数组 a[l⋯r]「划分」成两个子数组 a[l⋯q−1]、a[q+1⋯r],使得 a[l⋯q−1]中的每个元素小于等于 a[q],且 a[q] 小于等于 a[q+1⋯r]中的每个元素。其中,计算下标 q也是「划分」过程的一部分。
解决: 通过递归调用快速排序,对子数组 a[l⋯q−1]和 a[q+1⋯r]进行排序。
合并: 因为子数组都是原址排序的,所以不需要进行合并操作,a[l⋯r]已经有序。
上文中提到的 「划分」 过程是:从子数组 a[l⋯r]中选择任意一个元素 xxx 作为主元,调整子数组的元素使得左边的元素都小于等于它,右边的元素都大于等于它, x 的最终位置就是 q。

 

 

评论区参考题解:

 

class Solution {
   Random rand=new Random();
   public int findKthLargest(int[] nums, int k) {
       return quickSort(nums,k,0,nums.length-1);
  }
   private int quickSort(int[] nums,int k,int left,int right){
       int index=rand.nextInt(right-left+1)+left;
       int flag=nums[index]; //定义随机数组下标对应元素为 flag
       nums[index]=nums[left];//将数组最左边元素赋值给 index指向元素:第一轮循环遍历中最左边会被覆盖,因此提取将 nums[left] 存入 nums[index],反正nums[index] 事先用flag存过了,最后再赋值即可。
       int i=left,j=right;
       while (i<j){
           while (i<j&&nums[j]<=flag) j--; //从右边往左遍历,元素大于flag元素,挪到左边
           nums[i]=nums[j];
           while (i<j&&nums[i]>=flag) i++; //从左边向右边遍历,元素小于flag元素,挪到右边
           nums[j]=nums[i];
      }
       for(int i = left;i < j;i++){
if(i < j && nums[i]<= flag){
nums[j--] = nums[i++];
          }
           if(i < j && num[j] >= flag){
nums[i++] = nums[j--];
}
      }
       nums[i]=flag;  //将之前存好的flag元素插回
       if (i==k-1) return nums[i]; //快排每次唯一确定一个flag数位置
       else if (i<k-1) return quickSort(nums,k,i+1,right);//递归右子数组
       else return quickSort(nums,k,left,i-1); //递归左子数组
  }
}

 

官方题解:垃圾代码,不看也罢。

ps:快排思想简单,实现时需要注意小细节。

 


 

7.合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

题解:

自己写的暴力题解,边界条件没判断好,测试用例无法全部通过

修改思路:用while而不是用for,for需要判断的条件更多。

 

评论区copy答题:

class Solution {
   public void merge(int[] nums1, int m, int[] nums2, int n) {
       if(n == 0) return ;
       if(m == 0){
           for (int i = 0; i < nums1.length; i++) {
               nums1[i] = nums2[i];
          }
           return;
      }
       int x = 0;
       int t = 0;
       int[] temp = new int[nums1.length];
       for(int i = 0;i < temp.length;i++){
           if(x < m && nums1[x] <= nums2[t]){
               temp[i] =nums1[x] ;
               if(nums1.length > 1) x++;
          }else{
               temp[i] = nums2[t];
               if(nums2.length > 1) t++;
          }
      }
       for (int i = 0; i < nums1.length; i++) {
           nums1[i] = temp[i];
      }
  }
}

调库题解:

class Solution {
   public void merge(int[] nums1, int m, int[] nums2, int n) {
       int nums1Length=nums1.length;
       for(int begin=m;begin < nums1Length;begin++){
           nums1[begin]=nums2[begin-m];
      }
       Arrays.sort(nums1);
  }
}

官方题解:

class Solution {
   public void merge(int[] nums1, int m, int[] nums2, int n) {
       int p1 = 0, p2 = 0;
       int[] sorted = new int[m + n];
       int cur;
       while (p1 < m || p2 < n) {
           if (p1 == m) {
               cur = nums2[p2++];
          } else if (p2 == n) {
               cur = nums1[p1++];
          } else if (nums1[p1] < nums2[p2]) {
               cur = nums1[p1++];
          } else {
               cur = nums2[p2++];
          }
           sorted[p1 + p2 - 1] = cur;
      }
       for (int i = 0; i != m + n; ++i) {
           nums1[i] = sorted[i];
      }
  }
}

 


 



posted @ 2022-02-26 10:16  lc已被占用  阅读(186)  评论(0)    收藏  举报