C++双指针快排算法

简介(什么是双指针)

有什么优势

  • 时间复杂度通常较低,很多情况下能达到线性时间复杂度O(n),避免了暴力解法中可能出现的双重循环导致的O(n^2)时间复杂度,提高了算法效率。

时间复杂度

  • O(n)

指针思想and基本原理

  • 利用两个指针,通常命名为 left 和 right ,或者 slow 和 fast 等,它们在数据结构上移动,通过不同的移动策略来解决各种问题。本文指针均用l和r代表左右指针

实例代码1(快排算法)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;

// 数组q用于存储待排序的元素
int q[N];

// 快速排序函数quick_sort,参数为数组q,以及待排序区间的左右边界l和r
void quick_sort(int q[], int l, int r);

int main() {
    //读取操作
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> q[i];
    
    quick_sort(q, 0, n - 1);
    //输出快排后的结果
    for (int i = 0; i < n; i++)  
        cout << q[i] << " ";
    return 0;
}


void quick_sort(int q[], int l, int r) {
    // 如果左边界l大于等于右边界r,说明区间内元素个数等于1或者0,无需排序,直接返回
    if (l >= r)
        return;
    // 选择数组中间元素q[l+r]作为基准元素x,初始化左指针i为l-1,右指针j为r+1
    //左指针之所以为l-1,右指针之所以为r+1,是为了便利后续操作,因为后续操作为++i,++j,是先移动指针i和j,然后再和基准元素x做比较
    int x = q[l+r], i = l - 1, j = r + 1;
    // 当左指针i小于右指针j时,继续循环,进行元素交换和指针移动
    while (i < j) {
        // 从左向右移动左指针i,直到找到一个大于等于基准元素x的元素才停止
        while (q[++i] < x);
        // 从右向左移动右指针j,直到找到一个小于等于基准元素x的元素才停止
        while (q[--j] > x);
        // 如果i小于j,说明找到了一对需要交换位置的元素,交换它们
        if (i < j) swap(q[i], q[j]);
    }
    // 递归调用快速排序函数,对左子数组(即基准元素x左边的子数组)进行排序
    quick_sort(q, l, j);
    // 递归调用快速排序函数,对右子数组(即基准元素x右边的子数组)进行排序
    quick_sort(q, j + 1, r);
}

代码1的详细说明,逻辑

1.quick_sort函数原理

-首先选择一个基准元素(这里是q[l])。

  • 通过两个指针i和j从数组的两端向中间移动,i从左向右找大于等于基准的元素,j从右向左找小于等于基准的元素,当i < j时,交换这两个元素。
  • 当i和j相遇时,将基准元素与j指向的元素交换,此时基准元素左边的元素都小于它,右边的元素都大于它。
  • 然后递归地对基准元素左边和右边的子数组进行快速排序。
2.main函数功能
  • 读取输入的数组大小n。
  • 读取n个整数到数组q中。
  • 调用quick_sort函数对数组q进行排序。
  • 最后输出排序后的数组元素。
3. * *递归终止条件 * *
  • if (l >= r) return; :如果左边界大于等于右边界,说明子数组的元素个数为0或1,已经是有序的,直接返回,不再进行排序操作。
4. * *选择基准元素并初始化指针 * *
  • int x = q[l], i = l - 1, j = r + 1; :选择数组的第一个元素q[l]作为基准元素x。然后初始化两个指针iji初始化为左边界l的左边一个位置(l - 1),j初始化为右边界r的右边一个位置(r+1)。
5. * *划分操作(双指针法) * *

-while (i < j):只要i小于j,就进行以下操作。

  • do i++; while (q[i] < x); :从左向右移动i指针,直到找到一个大于等于基准元素x的元素。
  • do j--; while (q[j] > x); :从右向左移动j指针,直到找到一个小于等于基准元素x的元素。
  • if (i < j) swap(q[i], q[j]); :如果i小于j,说明找到了一对需要交换的元素,将它们交换,以使得左边的元素都小于等于基准元素,右边的元素都大于等于基准元素。
6. * *递归调用 * *
  • quick_sort(q, l, j); :对基准元素左边的子数组(左闭右闭区间[l, j])进行快速排序。
  • quick_sort(q, j + 1, r); :对基准元素右边的子数组(左闭右闭区间[j + 1, r])进行快速排序。

实例代码2

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, k;
int q[N];

int quick_sort(int q[], int l, int r,int k);


int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        cin >> q[i];
    cout << quick_sort(q, 0, n - 1, k)<< " ";
    return 0;
}


int quick_sort(int q[], int l, int r,int k) {
    if (l >= r)
        return q[l];
    int x = q[l], i = l - 1, j = r + 1;
    while (i < j) {
        while (q[++i] < x);
        while (q[--j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    int sl = j - l + 1;//这里的i和j可以互换,因为此时的i和j都指向了同一个位置,
    if(k<=sl)
        return quick_sort(q, l, j, k);
    return quick_sort(q, j + 1, r, k-sl);
}

代码2的详细说明,逻辑

1.quick_sort函数原理
  • 首先选择一个基准元素(这里是q[l])。
  • 通过两个指针i和j从数组的两端向中间移动,i从左向右找大于等于基准的元素,j从右向左找小于等于基准的元素,当i < j时,交换这两个元素。
  • 当i和j相遇时,将基准元素与j指向的元素交换,此时基准元素左边的元素都小于它,右边的元素都大于它。基准元素左边的元素构成s1,基准元素右边的元素构成s2
  • 然后递归地对基准元素左边和右边的子数组进行快速排序。
2.main函数功能
  • 读取输入的数组大小n。
  • 读取n个整数到数组q中。
  • 调用quick_sort函数对数组q进行排序。
  • 最后输出排序后的数组元素。
3. * *递归终止条件 * *
  • if (l >= r) return; :如果左边界大于等于右边界,说明子数组的元素个数为0或1,已经是有序的,直接返回,不再进行排序操作。
4. * *选择基准元素并初始化指针 * *
- `int x = q[l], i = l - 1, j = r + 1; `:选择数组的第一个元素`q[l]`作为基准元素`x`。然后初始化两个指针`i`和`j`,`i`初始化为左边界`l`的左边一个位置(`l - 1`),`j`初始化为右边界`r`的右边一个位置(`r+1`)。
5. * *划分操作(双指针法) * *
  • while (i < j):只要i小于j,就进行以下操作。
  • do i++; while (q[i] < x); :从左向右移动i指针,直到找到一个大于等于基准元素x的元素。
  • do j--; while (q[j] > x); :从右向左移动j指针,直到找到一个小于等于基准元素x的元素。
  • if (i < j) swap(q[i], q[j]); :如果i小于j,说明找到了一对需要交换的元素,将它们交换,以使得左边的元素都小于等于基准元素,右边的元素都大于等于基准元素。
6. * *递归调用 * *
  • quick_sort(q, l, j); :对基准元素左边的子数组(左闭右闭区间[l, j])进行快速排序。
  • quick_sort(q, j + 1, r); :对基准元素右边的子数组(左闭右闭区间[j + 1, r])进行快速排序。

总结

### 使用双指针算法解决 LeetCode 题目 #### 解决最长不含重复字符的子串 (Length of Longest Substring) 在处理字符串问题时,滑动窗口技术常常与双指针相结合来解决问题。对于寻找最长无重复字符子串的问题,可以通过设置两个指针 `left` 和 `right` 来表示当前考察的窗口范围,并利用哈希表记录字符出现次数。 当右端指针遍历到新字符时将其加入窗口;如果该字符已经在窗口内存在,则移动左端指针缩小窗口直至移除掉多余的相同字符。每次调整好窗口后更新最大长度值。 ```cpp class Solution { public: int lengthOfLongestSubstring(string s) { int left = 0, right = 0, n = s.size(), ret = 0; int hash[128] = {0}; // 使用数组模拟哈希表 while(right < n){ hash[s[right]]++; // 进入窗口 // 判断是否有重复字符 while(hash[s[right]] > 1){ hash[s[left++]]--; // 出窗口 } ret = max(ret, right - left + 1); right++; } return ret; } }; ``` 此方法的时间复杂度为 O(n),其中 n 是输入字符串的长度[^2]。 #### 寻找最大连续1的数量 (Max Consecutive Ones) 针对这个问题,同样采用双指针策略,不过这里只需要关注于统计连续 '1' 的数量即可: - 当遇到非‘1’ 或者到达字符串结尾时重置计数器并比较保存最大值; - 继续向后扫描直到完成整个列表。 这种方法能够在线性时间内找到最大的连续一段全由‘1’组成的区间[^1]。 #### 实现最接近目标值的三个整数之和 (3Sum Closest) 为了求解这个经典面试题,先对数组进行排序以便更好地控制两头逼近的过程。设定固定元素 i 后,再用另外一对左右指针 j,k 分别指向剩余部分两端来进行探索。根据 sum=s[i]+s[j]+s[k] 是否大于还是小于 target 来决定收缩哪一侧边界从而逐步优化结果。 具体实现如下所示: ```cpp int threeSumClosest(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); int ans = nums[0] + nums[1] + nums[2]; for(int i=0;i<nums.size()-2;++i){ int l=i+1,r=nums.size()-1; while(l<r){ int sum = nums[i]+nums[l]+nums[r]; if(abs(sum-target)<abs(ans-target)) ans=sum; if(sum>target)--r; else if(sum<target)++l; else return sum; // 如果恰好等于则直接返回 } } return ans; } ``` 上述代码实现了高效的查找过程,时间复杂度主要取决于排序阶段即O(NlogN)[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值