基础算法(一)
要求:看课理解思想,课下背模板,练模板题—>提高熟练度:重复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]。
递归调用时的区间选择需要根据分界点调整。
归并排序
归并排序也是一种基于分治思想的排序算法,不同于快速排序,归并排序总是将数组一分为二。具体过程如下:
- 确定分界点:通常选取中间点 mid = (l + r) / 2。
- 递归排序:分别对左右两个子区间进行递归排序。
- 归并:使用双指针将两个有序子区间合并为一个有序区间。
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];
}
}
}
单向!双指针算法
双指针算法常见于以下两种情况:
- 对于一个序列,用两个指针维护一段区间。
- 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列。
使用双指针最好是单向,也就是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());
}
};