目录
地图中左上角走到右下,再回到左上,两条路径不能重复的最小代价
虾皮19年秋招 笔试题4 二维01数组,找0中到所有1距离最小
虾皮19年秋招 笔试题3 实现Linux * 匹配0-多个字符
排序
全排序Next Permutation
- 从后往前,找第一个满足V[i]<V[i+1]的,此时[i+1,最后)是递减
- 然后从后往前找第一个j,V[j]>V[i]
- 交换i,j,让[i+1,最后)变成递增序列
归并排序 -- 数组中的逆序对个数
void mergeSortCalNiXu(vi &V,int l,int r,int &ans){ if((r-l)<=1) return; int mid = l+(r-l)/2; //递归合并左右子数组 mergeSortCalNiXu(V,l,mid,ans); mergeSortCalNiXu(V,mid,r,ans); vi data(r-l); int k=0; int i=l,j=mid; while(i<mid and j<r){ if(V[i]<=V[j]){ data[k++]=V[i++]; ans+=j-mid;//i能走到这,说明它比j之前的都要大,仅仅比j小 }else{ data[k++]=V[j++]; } } while(i<mid){ ans+=r-mid; data[k++]=V[i++]; } while(j<r) data[k++]=V[j++]; UF(i,l,r) V[i]=data[i-l]; }
note:
- 计数时,不能前半数组元素比后半大时计算,应该要<=时计算,这时才知道最后i应该加几个逆序,否则会出现重加的问题
- 当i还有剩时,加上右边数组长度的逆序对数目
快排
区间左闭右开,第二重while循环 移动指针时用开,i或j==哨兵时,交换哨兵,最后递归时注意mid不要再参与
//采取交换pilot两侧的目标,更快速 template <typename T> void my_qsort(vector<T> &V,int start,int end){//qucik sort if(start>=end) return; int mid=start+(end-start)/2;//pilot int i=start,j=end; while(true){ while(V[i]<V[mid]) i++;//找pilot前面比pilot大的 while(V[mid]<V[j]) j--;//找pilot后面比pilot小的 if(i>j) break; swap(V[i],V[j]); if(i==mid) mid=j;//哨兵位置发生变化 else if(j==mid) mid=i; i++; j--; } //上述操作之后 哨兵位于i位置,j在其左侧 my_qsort(V,start,j); my_qsort(V,i,end); }
平均复杂度计算 :假设T(k)表示k长度的时间,划分点选择任一概率相同,,k-1和n-k在k=0,1,...,n-1都算过,相当于每个k求了两次,然后通过T(n)-T(n-1)求递推式的解
稳定快排
给记录多加一个编号域,做双关键字的快排
学习自 我已飞过 的答案
堆排序 -- TopK问题
维护一个含K个数的最小堆,遍历数组,比根小则丢弃,最后堆剩下的即为TopK,O(NlogK)
希尔排序(缩小增量排序,优化的插入排序)
查找
二分查找-- 最大最小值问题 力扣410
插值查找(二分查找的优化)
思想:当要查找的数位于数组的边缘时,可以将二分的分割点倾斜以减小查找区间大小
大数据的中位数--快排分割思想+外存
递增二维矩阵,查找某个值
- 比较右上角元素与值
- 若大,则右边一列可以扔掉,查看第1-倒数第二列的矩阵
- 若小,则左边一行可以扔掉,查看第2-最后一行的矩阵
二维矩阵找第K元素
- 每个位置遍历,在左下角、右上角进行二分,查找比当前位置小的元素个数
- 复杂度O(nm*log(nm))
大数据中查找数(外存排序、压缩+哈希)
大数据由于数据量较大,本质还是查找,所以依旧可以分为两种思路:散列、排序再查找,大数据的与众不同在于数据不能直接放进内存,需要拆分,通过外存作为中转,外存排序
压缩+哈希
举例:40亿32bit数组找是否存在某个数
- 40亿*32bit = 2^2*2^5^10亿 = 2^7*10*9 = 2^37,因为2^10=1024可以约等于1000
- 而32bit系统内存最大是2^32 bit,所以存不下
- 但是这题设置32bit,以及40亿这个数,就是想我们用压缩的方式,bitmap,用1bit表示一个32bit数是否存在的情况,这样内存就够用了
- 解法:32bit内存全用来存储,第i位=1表示i这个数存在于数组,然后查这个数位置上的0,1就判断出是否存在了
举例2:手机号MD5加密之后,如何快速查找?1000万的数据2G的内存怎么实现高效?
- MD5加密后得到128bit=2^7bit
- 1000万*2^7 = 10^7*2^7 = 2^27*10 = 2^30*1.25
- 2G = 2^31次方
- 内存存的下,这题是128bit数据就不能像上面题那么做了,bitmap内存需要2^128
- 这题用hash表
外存排序
举例:40亿32bit数组找是否存在某个数
- 大小分析见上文
- 编程珠玑里提供了方案,通过根据第一位是否为0,将数组分成两部分,再根据第二位,分成4部分,依次类推,分到某一部分4G内存可以完全存下了,就可以扔到内存hash或者排序解决了
有序数组之和的TopK问题
大顶堆,堆中保存(数值,左元素编号,右元素编号),先让A[n-1]+B[m-1]入堆,
然后弹出堆顶元素,将堆顶元素相近的两个元素入堆(i-1,j)(i,j-1),调整堆结构,
重复上一行操作,直至弹出K个
#include<algorithm> #include <functional> using namespace std; //C++中的堆 int maint(){ vector<int> V = {3,5,2,6,4); make_heap(V.begin(),V.end(),greater<int>());//建堆 //最后插入元素 V.push_back(7); push_heap(V.begin(),V.end());//调整最后一个元素的堆位置 pop_heap(V.begin(),V.end());//弹出堆顶元素 int x = V.pop_back(); sort_heap(V.begin(),V.end(),greater<int>());//堆排序 }
动态规划
限制长度的最大连续子序列和(dp+滑窗+前缀和数组)
- dp[i]表示以i结尾时答案,s[i]表示该答案下子序列长度
- 状态转移方程:
- if s[i-1] < m:
- if V[i]>=V[i]+dp[i-1]:
- dp[i] = V[i]
- s[i] = 1
- else:
- dp[i] = V[i]+dp[i-1]
- s[i] = s[i-1]+1
- else:
- j = i-s[i-1] //找i-1答案的左边界+1,左边界直接扔掉
- while(j<i and V[i]<=0) j++;
- 求[j,i-1)和sum,可用前缀和优化
- if V[i]>=V[i]+sum:
- dp[i] = V[i]
- s[i] = 1
- else:
- dp[i] = V[i]+sum
- s[i] = (i-1-j)+1
最小编辑距离
dp[i][j]表示s1串前i子串与s2前j子串的最小编辑距离
- if s1[i]==s2[j]) dp[i][j] = dp[i-1][j-1];
- else dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1)
- 3个数依次表示
- 删除i或在j后插入
- 删除j或在i后插入
- 修改i或j