算法笔记

这篇博客深入探讨了算法的各个方面,包括排序算法(如全排序、归并排序、快排等)、查找技术(如二分查找、插值查找)、动态规划问题(如0-1背包、最长公共子序列)以及数据结构的应用(如栈、链表、数组)。此外,还涉及了二叉树、数学问题、字符串处理、链表操作和数组操作等经典算法题目,是提升算法技能的重要参考资料。
摘要由CSDN通过智能技术生成

目录

排序

全排序Next Permutation

归并排序 -- 数组中的逆序对个数

快排

稳定快排

堆排序 -- TopK问题

希尔排序(缩小增量排序,优化的插入排序)

查找

二分查找-- 最大最小值问题 力扣410

大数据的中位数--快排分割思想+外存

递增二维矩阵,查找某个值

二维矩阵找第K元素

大数据中查找数(外存排序、压缩+哈希)

有序数组之和的TopK问题

动态规划

限制长度的最大连续子序列和(dp+滑窗+前缀和数组)

最小编辑距离

最长公共子序列LCS

0-1背包

完全背包

二维地图左下到右上路线总数(障碍)

地图中左上角走到右下,再回到左上,两条路径不能重复的最小代价

字符串分割成字典个数

数字转字母的编码数(1-a,26-z)

数字字符串 移掉K位数字后最小

中序转后序

滑动窗口

最大无重复子串长度

最小包含字串长度

二叉树

最近公共祖先节点

数学

'全错位'排列问题

判断[2,n]内素数个数

布隆过滤器

十进制转二进制、十六进制

字符串

KMP模板匹配

字典序最小问题

链表

单链表排序

环入口点

两个链表是否相交,找到相交点

反转

数组

整数数组,求和为K的对数

正方形顺时针90度旋转

图论

拓扑排序 

 最大匹配(最小覆盖)--匈牙利算法

智力题

公司真题

虾皮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:

  1. 计数时,不能前半数组元素比后半大时计算,应该要<=时计算,这时才知道最后i应该加几个逆序,否则会出现重加的问题
  2. 当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长度的时间,划分点选择任一概率相同,T(n) = \frac{1}{n}\sum_{k=0}^{n-1}(T(k-1)+T(n-k)) = \frac{2}{n}\sum_{k=0}^{n-1}(T(k)),k-1和n-k在k=0,1,...,n-1都算过,相当于每个k求了两次,然后通过T(n)-T(n-1)求递推式的解

稳定快排

给记录多加一个编号域,做双关键字的快排

学习自  我已飞过 的答案

堆排序 -- TopK问题

维护一个含K个数的最小堆,遍历数组,比根小则丢弃,最后堆剩下的即为TopK,O(NlogK)

希尔排序(缩小增量排序,优化的插入排序)

查找

二分查找-- 最大最小值问题 力扣410

插值查找(二分查找的优化)

思想:当要查找的数位于数组的边缘时,可以将二分的分割点倾斜以减小查找区间大小

mid = \frac{X-V[i]}{V[r]-V[l]]}*(r-l)

大数据的中位数--快排分割思想+外存

递增二维矩阵,查找某个值

  • 比较右上角元素与值
    • 若大,则右边一列可以扔掉,查看第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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值