蓝桥杯《算法很美》第二章 查找与排序C/C++【四】

本文深入探讨了算法设计中的双指针法和堆的应用,包括有序数组中寻找和因子、确定最短排序子数组以及实现topK算法。通过具体实例,解析了这些算法的工作原理和优化策略,旨在提升读者的算法理解和编程技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【1】有序数组中的和因子

/**
    有序数组中找和的因子,即打印所有相加为K的数对
    头尾双指针法
*/
void SumK(int A[], int N, int K){
    int left = 0;
    int right = N-1;
    int sum;
    while(left < right){
        sum = A[left] + A[right];
        if(sum < K){
            left++;
        }else if(sum > K){
            right--;
        }else{
            cout<<"("<<A[left]<<","<<A[right]<<")"<<endl;
            left ++;
        }
    }
}

【2】需要排序的最短子数组

/**需要排序的最短子数组
这个题没有套路:只适用于升序
降序的需要后续再思考一下,找到升序与降序两个不同角度中最短的数组输出
*/
int findSegment(int A[], int n){
    int p1 = -1, p2 = -1;//待排序的左边界,升序
    int p1d = -1, p2d = -1;//待排序的右边界
    int max = A[0];
    int min = A[n-1];
    int i = 0;
    //拓展右端点:更新历史最高,只要右侧出现比历史最高低的,就应该将右边界扩展到此处
    for(i=0; i<n; i++){
//        if((i < n-1) && (A[i] > A[i+1]) && (p1 == -1))
//            p1 = i;
        if(A[i] > max)
            max = A[i];
        //只要低于历史高峰,就要扩展排序区间的右端点
        if(A[i] < max)
            p2 = i;
    }
    //找左端点:更新历史最低,只要左侧额出现比历史最低高的,就应该将左边界扩展到此处
    for(i=n-1; i>=0; i--){
        if(A[i] < min){
            min = A[i];
        }
        if(A[i] > min)
            p1 = i;
    }
    if(p1 == -1){
        cout <<"[0,0]"<<endl;
        return 0;
    }
    else{
        cout<<"["<<p1<<"," <<p2<<"]"<<endl;
        return (p2-p1+1);
    }
}

【3】topK

#include <iostream>
#include <iomanip>

using namespace std;
#define MAX 10000
/*小根堆排序*/
void MinHeapFixDown(int A[], int i, int n){//A是待排序数组,i是待调整节点
    //找到左右孩子
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if(left >=  n){//如果左孩子越界(不存在),那么i就是叶子节点,不用继续了
        return ;
    }
    int min = left;
    if(right >= n){
        min = left;//右孩子越界(不存在),那么最小值就是左孩子
    }
    else{//左右孩子都存在,较小者为min
        if(A[right] < A[left])
            min = right;
    }
    //此时min就指向了左右孩子中较小的那个
    //如果A[i]比两个孩子都要小,不用调整
    if(A[i] <= A[min])
        return;
    //否则,找到两个孩子中较小的,和i交换
    int temp = A[i];
    A[i] = A[min];
    A[min] = temp;
    //小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整(由于发生了位置变换,所以要继续调整保证变换后堆的性质依旧成立)
    MinHeapFixDown(A,min,n);
}
void MinHeap(int A[], int n){//堆化
    for(int i=(n/2-1); i>=0; i--){
        MinHeapFixDown(A,i,n-1);
    }
}

/*大根堆排序*/
void MaxHeapFixDown(int A[], int i, int n){//A是待排序数组,i是待调整节点
    //找到左右孩子
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if(left >=  n){//如果左孩子越界(不存在),那么i就是叶子节点,不用继续了
        return ;
    }
    int max = left;
    if(right >= n){
        max = left;//右孩子越界(不存在),那么最大值就是左孩子
    }
    else{//左右孩子都存在,较大者为max
        if(A[right] > A[left])
            max = right;
    }
    //此时max就指向了左右孩子中较大的那个
    //如果A[i]比两个孩子都要大,不用调整
    if(A[i] >= A[max])
        return;
    //否则,找到两个孩子中较大的,和i交换
    int temp = A[i];
    A[i] = A[max];
    A[max] = temp;
    //大孩子那个位置的值发生了变化,i变更为大孩子那个位置,递归调整(由于发生了位置变换,所以要继续调整保证变换后堆的性质依旧成立)
    MaxHeapFixDown(A,max,n);
}
void MaxHeap(int A[], int n){//堆化
    for(int i=(n/2-1); i>=0; i--){
        MaxHeapFixDown(A,i,n-1);
    }
}
/**
 * 求海量数据(正整数)按逆序排列的前k个数(topK),因为数据量太大,不能全部存储在内存中,只能一个一个地从磁盘或者网络上读取数据,
 * 请设计一个高效的算法来解决这个问题
 第一行:用户输入K,代表要求得topK
 随后的N(不限制)行,每一行是一个整数代表用户输入的数据
 用户输入-1代表输入终止
 请输出topK,从小到大,空格分割
 */
int heap[MAX];
int index = 0;
int k;
void Printarr(){
    int i;
    for(i=0; i<k; i++){
        cout << heap[i] << " ";
    }
    cout << endl;
}
void deal(int x){
    if(index < k){
        heap[index++] = x;
        if(index == k){
        //堆化
        MaxHeap(heap,k);
        Printarr();
        }
    }else{
    //x和堆顶进行比较,如果x大于堆顶,x将堆顶挤掉
        if(x < heap[0]){
            heap[0] = x;
            MaxHeapFixDown(heap, 0, k);
            Printarr();
        }
    }
}
int main() {
    int x;
    cin >> k;
    cin >> x;
    while(x!=-1){
        deal(x);//处理x
        cin >> x;
    }
    Printarr();
}

【4】串B是否包含串A的全部字符

#include <iostream>
#include <iomanip>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
#define MAX 10000
/**
 * Case02串B是否包含串A的全部字符:
 * 判断字符串A中的字符是否全部出现在字符串B中(大众点评笔试题)
 * */
 bool ContainsAll(string s1, string s2){
//     const char *s2_arr = s2.data();
     sort(s2.begin(), s2.end());
    int i;
    for(i=0; i<s1.length(); i++){
        char a = s1.at(i);
        int index = s2.find(a);
        if(index < 0)
            return false;
     }
     return true;
 }
int main() {
    string s1,s2;
    cin >> s1 >> s2;
    cout << ContainsAll(s1, s2);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值