力扣刷题--数组1

一:移动零(力扣283)

void moveZeroes(vector<int>& nums) {
        //申请一个辅助数组,存放非零元素,然后再赋值给原数组
        vector<int> aux;
        for(int i=0;i<nums.size();i++){
            if(nums[i]) 
                aux.push_back(nums[i]);//借用向量的push_back函数
        }
        for(int i=0;i<aux.size();i++){
            nums[i]=aux[i];
        }
        //补0
        for(int i=aux.size();i<nums.size();i++){
            nums[i]=0;
        }
    }

很简单的一个思路:

       申请额外数组存储原数组非零元素(利用push_back),然后赋值回给原数组,最后在末位编变成0

法二:

 void moveZeroes(vector<int>& nums) {
       /*
       原地实现该功能,不申请额外空间:
       定义一个索引k,指向数组第一个元素,i用于遍历数组,如果是非零元素把值赋给k位置。
       k更新到下一个位置,直至遍历结束,从k开始往后补0
       */
        int k=0;//nums中[0,k)存储非零元素
        //for循环,不断把非零元素依次赋值给k
        for(int i=0;i<nums.size();i++)
            if(nums[i]){//nums[i]不为0
                nums[k++]=nums[i];//别忘了移动k索引
            }
        //补零
        for(int i=k;i<nums.size();i++)
            nums[i]=0;
    }

法三:零与非零的交换

void moveZeroes(vector<int>& nums) {
        int k=0;//nums中[0,k)存储非零元素
        for(int i=0;i<nums.size();i++)
            if(nums[i]){
                swap(nums[i],nums[k]);
                k++;
            }
    }

与法二相比,由于直接交换,少了重新赋值为0操作

优化细节:

避免自己和自己交换,可以考虑i与k的大小关系

if(i!=k)  swap(nums[k++],nums[i]);

else k++;

二:挑选颜色

void sortColors(vector<int>& nums) {
        /*
        因为元素只有0,1,2三种可能,所以采用计数排序思想:
        分别统计出:0的个数,1的个数以及2的个数,赋值即可
        */
        int count[3]={0};
        for(int i=0;i<nums.size();i++){
            assert(nums[i]>=0&&nums[i]<=2);
            count[nums[i]]++;//数值直接对应频次
        }
        //定义一个不断累计的索引
        int index=0;
        for(int i=0;i<count[0];i++)
            nums[index++]=0;
        for(int i=0;i<count[1];i++)
            nums[index++]=1;
        for(int i=0;i<count[2];i++)
            nums[index++]=2;
    }

几个代码小细节:

  • 因为只有0,1,2且想累计出现个数,直接以元素值计算频次
  • 由于从头一次按照个数赋值0,1,2;所以直接建立一个不断累计的index

 其实可以把三路快排的思想移到这

这是最后想达到的效果:

 其实关键在于对遍历索引i的元素处理:

 分以下三种情况:

 如果元素为1,i右移

 如果元素是2,和two前一个元素交换,i不动,two--

 如果元素是1,和zero后元素交换,zero++,i++

代码实现

#include<iostream>
#include<vector>
using namespace std;
class soluction{
public:
   void sortColors(vector<int>& nums){
    /*
    功能实现:
    [0,zero]全是0----也就是zero初始化为-1
    [two,n-1]全是1---也就是two初始化为n(假设nums长度为n)
    索引i遍历数组,遇到1右移,控制(zero,two)范围全是1
    */
   int zero=-1;
   int two=nums.size();
   int i=0;//从头遍历数组
   while( i<two ){//遍历结束条件
       if(nums[i]==1) i++;
       else if(nums[i]==2){
        two--;
        swap(nums[two],nums[i]);
       }
       else{//nums[i]==0
       zero++;
       swap(nums[i],nums[zero]);
       i++;
       }
   }
   }
};
int main(){
   int arr[]={1,0,2,0,2,1,1,2,0};
   vector<int> vec(arr,arr+sizeof(arr)/sizeof(int));
   soluction().sortColors(vec);
   for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)
      cout<<(*it)<<" ";
}

快速排序两种实现

#include<iostream>
#include<vector>
using namespace std;
int partition(vector<int>& a,int L,int R){
    /*
    索引i记录小于基准元素的最后一个位置,初始化为基准元素位置
    索引j用于遍历
    j遇到的元素≥基准元素---往后遍历
    j遇到的元素<基准元素---交换i后位置和当前位置,i++
    */
   int e=a[L];//以最左边元素为基准元素
   int i=L;
   int j=L+1;
   for(;j<=R;j++){
    if(a[j]<e){
        swap(a[++i],a[j]);
    }
   }
   swap(a[L],a[i]);
   return i;
}
void quickSortHelper(vector<int>& a,int L,int R){
    if(L>=R) return;//递归结束
    int p=partition(a,L,R);
    quickSortHelper(a,L,p-1);
    quickSortHelper(a,p+1,R);
}
void quickSort(vector<int>& a){//一定要加&
    quickSortHelper(a,0,a.size()-1);
}
int main(){
    vector<int> arr={1,22,3,55,66,7,2,11,1,3,4};
    quickSort(arr);
    vector<int>::iterator it;
    for(it=arr.begin();it!=arr.end();it++){
        cout<<(*it)<<" ";
    }
    return 0;
}

注意要在vector加上*,否则原向量不改变

 /*
    双指针法:
    i不断右移,使得其之前的元素均≤基准元素
    j不断左移,使得其之后的元素均≥基准元素
    如果i,j均停止,交换两者所指元素,然后i右移,j左移
    最后----把j位置,即小于基准元素最后一个元素和基准元素交换
    */
   int e=a[L];
   int i=L+1,j=R;//i,j指向除基准元素的两头
   while(1){
      while(i<=j&&a[i]<=e) i++;
      while(i<=j&&a[j]>=e) j--;
      if(i>j) break;
      swap(a[i++],a[j--]);
   }
   swap(a[L],a[j]);
   return j;
}

 三:寻找第k个大的元素

 这里要用到一个快速排序分区的性质:

每次快排后,都可以确定该元素在顺序中的正确位置。

也就是说:排序后,倒数第一个最大,倒数第二个第二大。。。。

只要快排的索引落在了该位置,该位置就可以确定第几大或第几小

 int partition(vector<int>& nums,int l,int r){
        int e=nums[l];
        int i=l;
        int j=l+1;
        for(;j<=r;j++){
            if(nums[j]<e) 
               swap(nums[j],nums[++i]);
        }
        swap(nums[l],nums[i]);
        return i;
    }
    int findKthLargest(vector<int>& nums, int k) {
        int target=nums.size()-k;
        int l=0;
        int r=nums.size()-1;
        while(1){//条件:L<=R,但是该题一定有解,无需判断
          int p=partition(nums,l,r);
          if(p==target) return nums[p];
          else if(p>target) r=p-1;
          else l=p+1;
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值