7.9 Acwing 基础算法(一)快速排序 归并排序 双指针算法 数组简单&中等 283 189

基础算法(一)

要求:看课理解思想,课下背模板,练模板题—>提高熟练度:重复3-5次

快速排序

快速排序是一种基于分治思想的排序算法。具体过程如下:
1. 确定分界点:可以选取区间的左端点、中间点或右端点作为分界点。
2. 调整区间:使用两个指针(i 和 j)从数组两端向中间移动,将小于等于分界点的元素放在左边,大于等于分界点的元素放在右边。
3. 递归处理左右两段:对分界点左侧和右侧的子数组分别进行递归排序。
重点是第2步:不需要开辟额外空间,使用两个指针来调整元素位置。

void quick_sort(int q[],int l,int r){
	if(l >= r)return;
	int i = l - 1 ,j = r + 1 ,x = q[l + r >> 1];
	while(i < j){
		do i++; while(q[i]<x);
		do j--; while(q[j]>x);
		if(i<j) swap(q[i],q[j]);
	}
	quick_sort(q,l,j),quick_sort(q,j+1,r);//这里的j也是,换成i的话就是(q,l,i-1) (q,i,r) 且此时的x不能取q[l]; 为j的时候 x不能取q[r] 否则会死循环
}

注意:
分界点可以选取 q[l]、q[(l+r)/2] 或 q[r]。
递归调用时的区间选择需要根据分界点调整。

归并排序

归并排序也是一种基于分治思想的排序算法,不同于快速排序,归并排序总是将数组一分为二。具体过程如下:

  1. 确定分界点:通常选取中间点 mid = (l + r) / 2。
  2. 递归排序:分别对左右两个子区间进行递归排序。
  3. 归并:使用双指针将两个有序子区间合并为一个有序区间。
void merge_sort(int q[], int l ,int r){
	if(l>=r)return;
	int mid = l+r>>1;
	merge_sort(q,l,mid);
	merge_sort(q,mid+1,r);

	int k = 0, i = 1 , j = mid +1;
	while( i <= mid && j <=r){
		if(q[i] <= q[j])tmp[k++]=q[i++];
		else tmp[k++]=q[j++];

	while( i<=mid ) tmp[k++] =q[i++];
	while( j<=r )tmp[k++] = q[j++];

	for(i = l ,j = 0;i<=r;i++,j++){
		q[i] =tmp[j];
	}
	}
}

单向!双指针算法

双指针算法常见于以下两种情况:

  1. 对于一个序列,用两个指针维护一段区间。
  2. 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列。
    使用双指针最好是单向,也就是i和j是在同一个方向上移动的,以减少因逻辑混乱而导致的bug
for(int  i = 0 , j = 0;i<n;i++){
	while(j<i && check(i,j)) j++;
	//具体问题逻辑;
}

283 移动零

把0都挪到数组末尾

class Solution {
public:
    bool check(int i , int j){
        return i!=j;
    }
    void moveZeroes(vector<int>& nums) {
        //使用双指针算法进行解题
        //i指向头部,j指向尾部
        //如果i指向0,需要与j进行swap,j--
        //如果i指向不为0,j不变,i++直至i==j
        int n = nums.size() , i = -1 ,j = n;
        while(i<j){
            do i++;while(nums[i] != 0);
            do j--;while(nums[i] == 0);
            if(i<j) swap(nums[i] ,nums[j]);
        }       
    }
};

如果出现数组中全是非零,j越界,或者全为零,i越界!!!

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        //使用双指针算法进行解题
        int n = nums.size() , i = 0 , j = 0;
        while(j < n ){
            if(nums[j] != 0){
                swap(nums[i] , nums[j]);
                i++;
            }
            j++;
        }
    }
};

理解:一个出去办事,满足条件找家里人(j),一个标识家的位置(i)

189 轮转数组

要是使用双指针会很麻烦这道题,我chat了一下他给我的方案是使用双指针反转数组:)
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n; // 处理 k 大于数组长度的情况
        vector<int> newArray(n);

        for (int i = 0; i < n; ++i) {
            newArray[(i + k) % n] = nums[i];
        }

        for (int i = 0; i < n; ++i) {
            nums[i] = newArray[i];
        }
    }
};
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
    //反转数组
        int n = nums.size(), t = k % n;
        reverse(nums.begin(),nums.end());
        reverse(nums.begin(),nums.begin()+t);
        reverse(nums.begin()+t,nums.end());
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值