数组和字符串常用技巧和知识点

本文详细介绍了归并排序的分治思想,包括如何通过递归划分和合并实现排序,并讲解了在归并过程中统计逆序对的方法。此外,还探讨了数组和字符串处理的常见技巧,如双指针、滑动窗口以及C++中的数学函数和容器操作。同时,文章提到了字符串拼接、substring函数的使用以及map和set的排序与操作。
摘要由CSDN通过智能技术生成

数组 和 字符串

注:博客内容只限于个人学习。图片和内容可能整合了多渠道的信息来源,如侵联系可删。

归并排序 分治思想

分: 不断将数组从中点位置划分开(即二分法),将整个数组的排序问题转化为子数组的排序问题;
治: 划分到子数组长度为 1 时,开始向上合并,不断将 较短排序数组 合并为 较长排序数组,直至合并至原数组时完成排序;

Picture1.png

参考

https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h/

/*
merge_sort() 归并排序与逆序对统计:

1.终止条件: 当 l≥r 时,代表子数组长度为 1 ,此时终止划分;
2.递归划分: 计算数组中点 m ,递归划分左子数组 merge_sort(l, m) 和右子数组 merge_sort(m + 1, r) ;
3.合并与逆序对统计:
 1.暂存数组 nums 闭区间 [i, r] 内的元素至辅助数组 tmp ;
 2.循环合并:设置双指针 i , j 分别指向左 / 右子数组的首元素;
  * 当 i = m + 1 时: 代表左子数组已合并完,因此添加右子数组当前元素tmp[j] ,并执行 j = j + 1 ;
  * 否则,当 j = r + 1 时: 代表右子数组已合并完,因此添加左子数组当前  元素 tmp[i] ,并执行 i = i + 1 ;
  * 否则,当tmp[i]≤tmp[j] 时: 添加左子数组当前元素  tmp[i] ,并执行 i=i+1;
  * 否则(即 tmp[i]>tmp[j])时: 添加右子数组当前元素  tmp[j] ,并执行 j=j+1 ;此时构成m−i+1 个「逆  序对」,统计添加至 res ;
 4.返回值: 返回直至目前的逆序对总数 res;
    
reversePairs() 主函数:

1.初始化: 辅助数组 tmp ,用于合并阶段暂存元素;
2.返回值: 执行归并排序 merge_sort() ,并返回逆序对总数即可
*/

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        vector<int> tmp(nums.size());
        return mergeSort(0, nums.size() - 1, nums, tmp);
    }
private:
    int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
        // 终止条件
        if (l >= r) return 0;
        // 递归划分
        int m = l + (r-l)>> 2;
        int res = mergeSort(l, m, nums, tmp) + mergeSort(m + 1, r, nums, tmp);
        // 合并阶段
        int i = l, j = m + 1;
        for (int k = l; k <= r; k++)
            tmp[k] = nums[k];
        
        for (int k = l; k <= r; k++) {//填补tmp数组对应位置
            if (i == m + 1)
                nums[k] = tmp[j++];
            else if (j == r + 1 || tmp[i] <= tmp[j])
                nums[k] = tmp[i++];
            else {
                nums[k] = tmp[j++];
                res += m - i + 1; // 统计逆序对
            }
        }
        return res;
    }
};


数组题常见套路

1.双指针:双指针灵活擦操作,可以起到降低一个等级复杂度的作用

2.滑动窗口:重要性质是窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)。(又可以分为固定窗口,可变窗口)

在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 r 指针,和一个用于「收缩」窗口的 l 指针。在任意时刻,只有一个指针运动,而另一个保持静止

3.根据数组中数据操作的需求可以选取适当数据结构,如栈,堆,队列等

需要保留丢弃相邻的元素:栈

//c++上下取整数

#include <math.h> double floor(double  x ); 
float floorf(float x); 
long double floorl(long double x);

double floor(double x);S

double ceil(double x);

使用floor函数。floor(x)返回的是小于或等于x的最大整数。
如:     floor(10.5) == 10    floor(-10.5) == -11


使用ceil函数。ceil(x)返回的是大于x的最小整数。
如:     ceil(10.5) == 11    ceil(-10.5) ==-10

    
floor()是向负无穷大舍入,floor(-10.5) == -11ceil()是向正无穷大舍入,ceil(-10.5) == -10

滑动窗口例题:

209. 长度最小的子数组

904. 水果成篮
76. 最小覆盖子串

滑动窗口总结:https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247485141&idx=1&sn=0e4583ad935e76e9a3f6793792e60734&scene=21#wechat_redirect

C++ STL中Map的按Value排序

#include<bits/stdc++.h>
using namespace std;

typedef pair<string, int> PAIR;

bool cmp_by_value(const PAIR& l, const PAIR& r) {
    return l.second < r.second;
}
int main() {
    map<string, int> name_score_map;
    name_score_map["LiMin"] = 90;
    name_score_map["ZiLinMi"] = 79;
    name_score_map["BoB"] = 92;
    name_score_map.insert(make_pair("BoB",99));//不会插入或者更新
    name_score_map.insert(make_pair("Albert",86));
    //把map中元素转存到vector中,以pair的形式
    vector<PAIR> name_score_vec(name_score_map.begin(), name_score_map.end());
    sort(name_score_vec.begin(), name_score_vec.end(), cmp_by_value);
    for (auto iter: name_score_vec) {//这里的ater不是迭代器,是指代元素,要注意
        cout << iter.first << endl;//注意:pair不能以元素遍历形式取出,只能以.first或者.second取key或者value。(map迭代器则用->,如下)
    // for (auto iter=name_score_map.begin();iter!=name_score_map.end();iter++) {
    //     cout << iter->second<< endl;
    }    
   
    return 0;
}
//技巧:也可以把key和value颠倒组成pair,然后存进vector,这样不用写cmp

map set pair 好题:(2034. 股票价格波动)

收获:

  • map赋值的两种方式:map[key]=value ,如果已经有该key,会覆盖原始值 ,map.insert(pair<int,int>(2,4))形式则不会插入也不会修改

  • 灵活处理map,set和pair。核心:pair数据类型,map和set是容器,且这两个容器都会自动排序(map根据key。set如果存的是pair类型,则会根具pair第一个元素)

  • //map的erase()可以这样写:
        auto iter = m.find(1);//找到要删除的迭代器
        if(iter!=m.end()) m.erase(iter);
    //set的erase(),括号中是set中的元素
    
  • //set常用操作:
    除了insert()erase(),size(),empty()常用:
    rbegin():返回指向最后一个(注意,是已排好序的最后一个)元素的迭代器
    find(val):在set容器中查找值为val的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和end() 方法一样的迭代器
    count(val)	在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1
    
  • set,map和vector容器互相转换的问题:
    //vector和set可以自由转换
    vec = { 1, 2, 3, 4, 8, 9, 3, 2, 1, 0, 4, 8 };
    set<int> st(vec.begin(), vec.begin()+3);两者互相截取迭代器区间即可
    //vector和map转换
    map.insert(pair<intint>(1,2));//pair类型赋值
    vector<pair<int,int>> vec(map.begin(),map.end());//map类型转进vec,类型为pair
    
    

字符串拼接可以直接用‘+’

//substr用法
#include<string>
#include<iostream>
using namespace std;
int main()
{
  string s("12345asdf");
  string a = s.substr(0,5);     //获得字符串s中从第0位开始的长度为5的字符串
  cout << a << endl;
//substr越界问题:
  https://blog.csdn.net/saiolive/article/details/53102298?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link

string 对象支持互相直接赋值

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

moletop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值