本篇文章旨在分享一些常用算法的伪代码以及部分算法的具体实现,后面也会更新我在刷算法题中学到的或者从别的地方看到的经典算法思维

本博客并不提供算法说明,算法证明,算法分析,算法测试等内容,只提供算法的伪代码表示,在理解算法的前提下,很容易通过此文章了解并实现算法。


文章目录


类别一:Sorting Algorithms(排序算法)

Bucket Sort(桶排序)

BUCKET_SORT(A)
n = A.length
let B[0...n-1] be a new array
for i = 0 to n-1
make B[i] an empty list
for i = 1 to n
insert A[i] to B[A[i]]
for i = 0 to n
sort list B[i] with insertion sort
concatenate the lists B[0]...B[n] together in order

COUNTING_SORT(计数排序)

COUNTING_SORT(A,B,k)
n = A.length
let C[0...k] be a new array
for i = 0 to k
C[i] = 0
for i = 1 to n
C[A[i]] = C[A[i]]+1
for i = 1 to k
C[i] = C[i-1]+C[i]
for i = n down to 1
B[C[A[i]]] = A[i]
C[A[i]] = C[A[i]]-1

RADIX_SORT(基数排序)

RADIX_SORT(A,d)
for i = 1 to b
user a stable sort to sort array A on digit i

example use COUNTING_SORT:

COUNTING_SORT(A,exp)
n = A.length
let B[1...n] C[0...k] be new arrays
for i = 0 to k
C[i] = 0
for i = 1 to n
C[(A[i]/exp)%10] = C[(A[i]/exp)%10] +1
for i = 1 to k
C[i] = C[i-1] + C[i]
for i = n down to 1
B[C[(A[i])/exp]%10] = A[i]
C[(A[i])/exp]%10] = C[(A[i])/exp]%10]-1
for i = n down to 1
A[i] = B[i]

RADIX_SORT(A,k)
exp = 1
while (k/exp)%10!=0
COUNTING_SORT(A,k)
exp = exp*10

INSERT_SORT(直接插入排序)

INSERT_SORT(A)
for i = 2 to n
key = A[i]
j = i-1
while j > 0 and A[j]>key
A[j+1] = A[j]
j = j -1
A[j+1] = key

C语言实现

void insert_sort(int A[], int n) {
int i = 0, j = 0;
for (i = 1; i < n; i++)
{
int key = A[i];
for (j = i - 1; j >=0 && A[j] > key; j--)
{
A[j + 1] = A[j];
}
A[j + 1] = key;
}
}

MERGE_SORT(归并排序)

MERGE_SORT(A,low,high)
if low<high
mid = (low+high)/2
MERGE_SORT(A,low,mid-1)
MERGE_SORT(A,mid,high)
MERGE(A,low,high,mid)

MERGE(A,low,high,mid)
let LA[1..mid-low] RA[1...high-mid+1] be new arrays
for i = 1 to mid-low
LA[i] = A[i+low]
for j = 1 to high-mid+1
RA[i] = A[i+mid-1]
l = 1
r = 1
k = low
while l<=i and r <= j
if(LA[l]>RA[r])
A[k++] = LA[l++]
else A[k++] = RA[r++]
while l<=i
A[k++] = LA[l++]
while r<=j
A[k++] = RA[r++]

C语言实现

void merge(int A[], int low, int mid, int high)
{
int i = 0, j = 0, k = 0;
for (i = low; i <= high; i++)
{
B[i] = A[i];
}
for (i = k = low, j = mid+1; i <= mid && j <= high; k++)
{
if (B[i] < B[j]) A[k] = B[i++];
else A[k] = B[j++];
}
while (j<=high)
{
A[k++] = B[j++];
}
while (i <= mid)
{
A[k++] = B[i++];
}
}

void merge_sort(int A[], int low, int high)
{
if (low < high)
{
int mid = (low + high) / 2;
merge_sort(A, low, mid);
merge_sort(A, mid + 1, high);
merge(A, low, mid, high);
}
}

QUICK_SORT(快速排序)

QUICK_SORT(A,p,r)
if p<r
q = PARTITION(A,p,r)
QUICK_SORT(A,p,q-1)
QUICK_SORT(A,q+1,r)

PARTITION(A,p,r)
i = p-1
for j = p to r-1
if A[j]<A[r]
i = i+1
exchange A[j] with A[i]
exchange A[i+1] with A[r]
return i+1

C语言实现

int partition(int A[], int low, int high)
{
int pivot = A[low];
while (low < high)
{
while (low < high && A[high] > pivot) high--;
A[low] = A[high];
while (low < high && A[low] < pivot) low++;
A[high] = A[low];
}
A[low] = pivot;
return low;
}

void quick_sort(int A[], int low, int high)
{
if (low < high)
{
int pivot = partition(A, low, high);
quick_sort(A, low, pivot - 1);
quick_sort(A, pivot + 1, high);
}
}

HEAP_SORT(堆排序)

HEAP_SORT(A)
BUILD_MAX_HEAP(A)
for i = [A.length]/2 down to 1
MAX_HEAPIFY(A,i)

MAX_HEAPIFY(A,i)
l = LEFT(i)
r = RIGHT(i)
if l <=A.heap-size and A[l]>A[i]
largest = l
else largest = i
if r <= A.heap-size and A[r]> A[i]
largest = r
if largest != i
exchange A[i] with A[largest]
MAX_HEAPITY(A,largest)

BUILD_MAX_HEAP(A)
A.heap-size = A.length
for i = A.heap-size/2 down to 1
MAX_HEAPITY(A,i)

C语言实现

void adjust_heap(int A[], int k, int len)
{
A[0] = A[k];
for (int i = 2*k; i <= len; i *= 2)
{
if (i < len && A[i] < A[i + 1])
{
i++;
}
if (A[0] >= A[i])
{
break;
}
else
{
A[k] = A[i];
k = i;
}
}
A[k] = A[0];
}

void build_max_heap(int A[], int len)
{
for (int i = len / 2; i > 0; i--)
{
adjust_heap(A, i, len);
}
}

void heap_sort(int A[], int len)
{
build_max_heap(A, len);
for (int i = len; i > 0; i--)
{
swap(&A[1], &A[i]);
adjust_heap(A, 1, i - 1);
}
}

类别二:BINARY TREE ALGORITHMS(二叉树算法)

TREE_SEARCH(二叉树搜索)

TREE_SEARCH(x,k)
while x!=NIL and k! = x.key
if k<x.key
x = x.left
else x = x.right
if x!=NIL
return x

TREE_MINIMUM(查找BST最小值)

TREE_MINIMUM(x,k)
whiel x!= NIL and k!=x.key
if k<x.key
x = x.left
else x= x.right
return x

TREE_MAXIMUM(查找BST最大值)

TREE_MAXIMUM(x)
while x.right != NIL
x = x.right
return x

TREE_INSERT(BST插入)

TREE_INSERT(T,z)
y = NIL
x = T.root
while x!=NIL
y = x
if z.key < x.key
x = x.left
else x = x.right
z.p = y
if(y==NIL)
T.root = z
else if z.key > y.key
y.right = z
else y.left = z

TREE_DELETE(BST删除)

TREE_DELETE(T,z)
if z.left == NIL
TRANSPLANT(T,z,z.right)
else if z.right == NIL
TRANSPLANT(T,z,z.left)
else y = TREE_MINIMUM(z.right)
if y.p!=z
TRANSPLANT(T,y,y.right)
y.right = z.right
y.right.p = y
TRANSPLANT(T,z,y)
y.left = z.left
y.left.p = y

TRANSPLANT(T,u,v)
if u.p == NIL
T.root = v
else if u == u.p.left
u.p.left = v
else u.p.right = v
if v!=NIL
v.p = u.p

Minimum spanning tree(最小生成树)

GENETRIC_MST(A,W)
A =
while A doesn't form a spanning tree
find an edge(u,v) that is safe for A
A = A∪(u,v)
return A

MST_KRUSKAL(Kruskal生成树算法)

MST_KRUSKAL(G,w)
A =
for each vertex v G.V
MAKE SET(v)
sort the edges of G.E into nondecreasing order by weight w
for each edge (u,v) G.V taken in nondecreasing order by weight
if FIND-SET(u) != FIND-SET(v)
A = A∪(u,v)
UNION(u,v)
return A

MST_PRIM(Prim生成树算法)

MST_PRIM(G,w,r)
for each u G.V
u.key =
u.π = NIL
r.key = 0
Q = G.V
while Q !=
u = EXTRACT-MIN(Q)
for each v∈G.Adj(u)
if v∈Q and w(u,v)<v.key
v.π = u
v.key = w(u,v)

类别三:Graph Algorithms(图类算法)

BFS(广度优先搜索)

BFS(G,s)
for each vertex u∈G.V-{s}
u.color = WHITE
u.d =
u.π = NIL
s.color = GRAY
s.d = 0
s.π = NIL
Q=
ENQUEUE(Q,s)
while Q!=
u = DEQUEUE(Q)
for each v G.Adj(u)
v.color = GRAY
v.d = u.d + 1
v.π = u
ENQUEUE(Q,v)
u.color = BLACK

DFS(深度优先搜索)

DFS(G)
for each vertex u G.V
u.color = WHITE
u.π = NIL
time = 0
for each vertex u G.V
if u.color = WHITE
DFS-VISIT(G,u)

DFS-VISIT(G,u)
time = time +1
u.d = time
u.color = GRAY
for each v G.Adj(u)
if v.color = WHITE
DFS-VISIT(G, v)
u.color = BLACK
time = time +1
u.f = time

DIJKSTRA(迪杰斯特拉算法)

DIJKSTRA(G,w,s)
S =
Q = G.V
while Q!=
u = EXTRACT-MIN(Q)
S = S {u};
for each v Q.Adj(u)
RELAX(u, v, w)


RELAX(u,v,w)
if v.d > u.d + w(u,v)
v.d = u.d + w(u,v)
v.π = u

类别四:模式匹配算法

朴素字符串匹配算法

NAIVE-STRING-MATCHER(T, P)
n = T.length
m = P.length
for s = 0 to n-m
if P[1...m] == T[s+1...s+m]
print "Pattern occurs with shift"s

预处理时间:​​0​​​ 时间复杂度:​​O((n-m+1)*m)​

Rabin-Karp算法

RABIN-KARP-MATCHER(T, P)
n = T.length
m = p.length
h = d^m-1 mod q
p = 0
t0 = 0
//预处理
for i = 1 to m
p = (d*p + P[i]) mod q
t_0 = (d*t_0 + T[i]) mod q
//匹配
for s = 0 to n-m
if p == t_s
if P[1..m] == T[s+1..s+m]
print "Pattern occurs with shift"s
if s < n-m
t_(s+1) = (d*(t_s - T[s+1]*h) + T[s+m+1])mod q

预处理时间​​O(m)​​​ 最坏时间复杂度​​O((n-m+1)*m)​​ 一般时间复杂度​​O(n) + O(m(v + n/q))​

基于有限状态机的字符串匹配算法

FINITE-AUTOMATION-MATCHER(T, δ, m)
//δ为状态转移函数
n = T.length
q = 0
for i = 1 to n
q = δ(q, T[i])
if q == m
print "Pattern occurs with shift"s
COMPUTE-TRANSITION-FUNCTION(P, )
//∑为字符串的字母表
m = P.length
for q = 0 to m
for each character a
k = min(m+1, q+2)
repeat
k = k-1
until P_k P_qa的后缀
δ(q,a) = k
return δ

KMP 算法

KMP-MATCHER(T,P)
n = T.length
m = P.length
//Π是后缀函数,Π(q) = max{k| k<q and P_k是P_q的后缀}
Π = COMPUTE-PREFIX-FUNCTION(P)
q = 0
for i=1 to n
while q > 0 and P[q+1]!=T[i]
q = Π[q]
if P[q+1] == T[i]
q = q+1
if q == m
print "Pattern occurs with shift"s
q = Π[q]
COMPUTE-PERFIX-FUNCTION(P)
m = P.length
let Π[1..n] be a new array
Π[1] = 0
k = 0
for q = 2 to m
while k > 0 and P[k+1] != P[q]
k = Π[k]
if P[k+1] == P[q]
k = k+1
Π[q] = k
return Π

类别五:常用算法思维

递归

void function(规模){
if base_state
return base_result
else function(子规模)
}

分治算法

所谓分治算法就是将整个问题分解为子问题求解,再将子问题的解合并为原问题的解
不同于动态规划算法和贪心算法,分支算法的子问题是不包含重叠子问题的,子问题的形式求解方法与原问题是一致的,只是规模不同

void devide_conquer(问题)
if 问题规模 < 某一规模
conquer(问题)
子问题集合 = divide(问题)
for each 子问题∈子问题集合
divide_conquer(子问题)

动态规划

使用动态规划之前,首先要确定问题是否具有最优子结构性质以及无后效应,总来的说就是是否符合最优性原则

最优子结构性质指的是整体问题的最优解包含其子问题的最优解

无后效应是指从现在所在的状态往后所做的所有决策只于当前状态有关,而与当前状态之前的状态或决策无关,并不关心当前状态是怎么来的

最优性原则就是说无论初始状态或者初始决策是什么,以后的决策一定相对于初始状态构成一个最优决策序列

既然动态规划解决问题,首先要根据问题画出FSM(有限状态机),搞清楚每个状态的前一个状态是什么,什么操作会导致什么状态变化,然后根据FSM可以写出状态转移方程,用代码表示出状态转移方程即可。

dp[基本状态] = 初始值
void dynamic_programming()
for each 状态∈状态列表
dp[状态] = 择优(前一个状态做选择1,前一个状态做选择2....)

贪心算法

贪心算法和动态规划算法有点像,首先都具有最优子结构性质,只不过贪心算法区别于动态规划算法的一点就是贪心算法求解的问题需要具有贪心选择性质

贪心选择性质:整体的最优解可以通过一系列局部最优选择,也就是贪心选择来获取

最优解 = 输入序列[1…n]的一个子集

void recursive_greedy(问题,选择列表, 解集)
for each 选择∈选择列表
if 选择满足局部最优
解集 = 解集 选择
recursive_greedy(子问题, 选择列表, 解集)

回溯算法

void backtrack(解集, 选择序列, 选择列表)
if 满足结束条件
解集 = 解集 选择序列
for each 选择∈选择列表
//做选择
选择序列 = 选择序列 选择
//下一步
backtrack(解集, 选择序列, 选择列表)
//撤销选择
选择序列 = 选择序列 - 选择

双指针

滑动窗口