Leetcode 摆动排序 II 快排的灵活使用

在这里插入图片描述
分析: 题意很简单,让偶数位的数字大于其左右两边的数字,但是有一个要求就是 时间复杂度为O(n) 空间复杂度为O(1)。 这意味这我们必须使用一个原地算法。 同时想要使他们有序,最简单的办法就是让他们的奇数位上的数是前一半的数字,偶数位上的数字为后一半的数字。

这时候的第一反应就是快速排序,我们知道,快速排序的核心思想就是把一个数组分为两半一半小于一个数,另一半大于一个数,然后继续递归去求解,

所以我的思路是:

a. 找到数组正中间的元素
b. 从这个元素开始(包括这个元素)以及比它大的元素,统统放到偶数位。之前的元素统一放到奇数位
c. 这时候有一个问题,就是多个中位数时,可能会碰面,所以调整数组,把中位数一个从开头开始放,一个从结尾开始放。

a. 找到数组正中间的元素(即求解中位数)

线复习一下快速排序,我喜欢用的方法就是填坑法,
step1,选择第一个元素,拿出它的值,它现在的位置就是一个坑
step2,开始迭代找到该值的位置
step3,开始从该该值的前一个位置和后一个位置重新迭代

void quickSort(vector<int>& nums, int begin, int end){
	if(begin >= end) return;
	int x = nums[begin];  //step1 选择第一个元素,拿出它的值,它现在的位置就是一个坑
	int i = begin, j = end;
	while(i < j){   //step2 开始迭代找到该值的位置
		while(i < j && nums[j] >= x)  --j;
		if(i < j){//填坑,现在的坑成了j的位置
			int temp = nums[i]; nums[i++] = nums[j]; nums[j] = temp;
		}
		while(i < j && nums[i] <= x) ++i;
		if(i < j){//填坑,现在的坑成了i的位置
			int temp = nums[j]; nums[j--] = nums[i]; nums[i] = temp;
		}
	}
	nums[i] = x;
	quickSort(nums, begin, i -1); //递归排序
	quickSort(nums, i + 1, end);
}

我们怎么把快排分治的思想,用到这道题目呢?
即用快排的思想取中位数,想法很简单
a 用第一个数进行迭代找到它的位置,设为x 对应上面代码的i
b 如果x 小于mid,那么下一次迭代就是x to end
c 如果x 大于mid,那么下一次的迭代就是begin to x

    int search(vector<int>& nums, int l, int r){
        int x = nums[l];
        
        while(r > l){
            while(r > l && nums[r] >= x)
                r--;
            if(r > l) nums[l++] = nums[r];
            
            while(r > l && nums[l] <= x)
                l++;
            if(r > l) nums[r--] = nums[l];
        }
        
        nums[l] = x;
        return l;
    }
    
    int findMidNum(vector<int>& nums, int mid){
        int l = 0, r = nums.size() - 1;
        int j = search(nums, l, r);
       
        while(r > l){
            if (j == mid) break;
            
            if(j > mid)
                r = j-1;
            else
                l = j+1;
            
            j = search(nums, l, r);
        }
        
        return nums[mid];
    }

search函数就是找当前第一个数在数组中的位置,理论上这个办法找到中位数的时间O(n)

b. 从这个元素开始(包括这个元素)以及比它大的元素,统统放到偶数位。之前的元素统一放到奇数位

p:第一个后半部分数的值的位置,每次+2
q: 后边部分数现在的位置
count: 记录后半部分有几个中位数

	p是追不上q的  
	假设小明和小红相距x m,小明以 1 m/s 的速度前进  小红以 2 m/s 的速度前进
	小红追上小明需要 比小明多走 x m,这时候就到数组最后了
		int p = 1, q = mid + 1, count = 0;
        
        while(q < nums.size()){
            if(nums[q] == midNum) count++;
            int temp = nums[q]; nums[q] = nums[p]; nums[p] = temp;
            p = p + 2; q++;
        }
        

c. 这时候有一个问题,就是多个中位数时,可能会碰面,所以调整数组,把中位数一个从开头开始放,一个从结尾开始放。

l ,r : 对应后面调整的标志位

        //left 
        int l = 0; p = 2;
        while(p < nums.size()){
            if(nums[p] == midNum){
                //exchage
                int temp = nums[p]; nums[p] = nums[l]; nums[l] = temp;
                l = l + 2;
            }
            p = p + 2;
        }
        
        int r = nums.size() % 2 == 0 ? nums.size() - 1 : nums.size() - 2; q = r;
        while(q > 0){
            if(nums[q] == midNum){
                //exchange
                int temp = nums[q]; nums[q] = nums[r]; nums[r] = temp;
                r = r - 2;
            }
            q = q - 2;
        }

最后附上最终的code

int search(vector<int>& nums, int l, int r){
        int x = nums[l];
        
        while(r > l){
            while(r > l && nums[r] >= x)
                r--;
            if(r > l) nums[l++] = nums[r];
            
            while(r > l && nums[l] <= x)
                l++;
            if(r > l) nums[r--] = nums[l];
        }
        
        nums[l] = x;
        return l;
    }
    
    int findMidNum(vector<int>& nums, int mid){
        int l = 0, r = nums.size() - 1;
        int j = search(nums, l, r);
       
        while(r > l){
            if (j == mid) break;
            
            if(j > mid)
                r = j-1;
            else
                l = j+1;
            
            j = search(nums, l, r);
        }
        
        return nums[mid];
    }
    
    void wiggleSort(vector<int>& nums) {
        if(nums.size() <= 1) return;
        
        int mid = 0 + (nums.size() - 1) / 2;
        
        int midNum = findMidNum(nums, mid);
        
        int p = 1, q = mid + 1, count = 0;
        
        while(q < nums.size()){
            if(nums[q] == midNum) count++;
            int temp = nums[q]; nums[q] = nums[p]; nums[p] = temp;
            p = p + 2; q++;
        }
        
        if(count == 0) return;
        
        //left 
        int l = 0; p = 2;
        while(p < nums.size()){
            if(nums[p] == midNum){
                //exchage
                int temp = nums[p]; nums[p] = nums[l]; nums[l] = temp;
                l = l + 2;
            }
            p = p + 2;
        }
        
        int r = nums.size() % 2 == 0 ? nums.size() - 1 : nums.size() - 2; q = r;
        while(q > 0){
            if(nums[q] == midNum){
                //exchange
                int temp = nums[q]; nums[q] = nums[r]; nums[r] = temp;
                r = r - 2;
            }
            q = q - 2;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值