哈夫曼编码
原理
1.统计输入文本中各个字符的频率。
2.根据字符频率构建哈夫曼树:创建一个优先队列(最小堆),每个节点都包含字符的频率,将每个字符视为一个独立的树节点,将它们插入到优先队列中,然后从优先队列中取出频率最小的两个节点(两棵树),将它们合并成一棵新树,新树的根节点频率为两个子树的频率之和,将新树插入回优先队列,重复上述步骤,直到队列中只剩下一个节点。
3.根据哈夫曼树生成编码:从根节点开始遍历哈夫曼树,路径中的左子树标记为0,右子树标记为1,当到达叶子节点时,记录路径上的0和1,即为该叶子节点对应的字符的哈夫曼编码。
4.使用生成的编码将原始数据进行编码压缩:将输入文本中的每个字符替换为其对应的哈夫曼编码。
Java实现:
import java.util.*;
class HuffmanNode implements Comparable<HuffmanNode>
{
int frequency;
char data;
HuffmanNode left, right;
public HuffmanNode(int frequency, char data)
{
this.frequency = frequency;
this.data = data;
left = right = null;
}
public int compareTo(HuffmanNode node)
{
return frequency - node.frequency;
}
}
class Huffman
{
public static void printCodes(HuffmanNode root, String code)
{
if (root == null)
return;
if (root.data != '\0')
{
System.out.println(root.data + ": " + code);
}
printCodes(root.left, code + "0");
printCodes(root.right, code + "1");
}
public static void buildHuffmanTree(String text)
{
HashMap<Character, Integer> frequencyMap = new HashMap<>();
for (int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1);
}
PriorityQueue<HuffmanNode> priorityQueue = new PriorityQueue<>();
for (char key : frequencyMap.keySet())
{
priorityQueue.offer(new HuffmanNode(frequencyMap.get(key), key));
}
while (priorityQueue.size() > 1)
{
HuffmanNode leftNode = priorityQueue.poll();
HuffmanNode rightNode = priorityQueue.poll();
HuffmanNode newNode = new HuffmanNode(leftNode.frequency + rightNode.frequency, '\0');
newNode.left = leftNode;
newNode.right = rightNode;
priorityQueue.offer(newNode);
}
HuffmanNode root = priorityQueue.poll();
printCodes(root, "");
}
}
最小生成树——Prim算法
原理:
1.将连通网中的所有顶点分为两类(假设为 A 类和 B 类)。 初始状态下,所有顶点位于 B 类;
2.选择任意一个顶点,将其从 B 类移动到 A 类;
3.从 B 类的所有顶点出发,找出一条连接着 A 类中的某个顶点且权值最小的边,将此边连接着的 A 类中的顶点移动到 B 类;
重复执行第 3 步,直至 B 类中的所有顶点全部移动到 A 类,恰好可以找到 N-1 条边,这些边就构成了图的最小支撑树。
Java实现:
import java.util.*;
class Edge implements Comparable<Edge>
{
int source;
int destination;
int weight;
public Edge(int source, int destination, int weight)
{
this.source = source;
this.destination = destination;
this.weight = weight;
}
public int compareTo(Edge edge)
{
return this.weight - edge.weight;
}
}
class Graph
{
private final int vertices;
private final List<List<Edge>> adjacencyList;
public Graph(int vertices)
{
this.vertices = vertices;
adjacencyList = new ArrayList<>(vertices);
for (int i = 0; i < vertices; i++) {
adjacencyList.add(new ArrayList<>());
}
}
public void addEdge(int source, int destination, int weight)
{
Edge edge = new Edge(source, destination, weight);
adjacencyList.get(source).add(edge);
Edge reverseEdge = new Edge(destination, source, weight);
adjacencyList.get(destination).add(reverseEdge);
}
public void primMST()
{
boolean[] visited = new boolean[vertices];
int[] parent = new int[vertices];
int[] key = new int[vertices];
for (int i = 0; i < vertices; i++)
{
key[i] = Integer.MAX_VALUE;
visited[i] = false;
}
key[0] = 0;
parent[0] = -1;
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>();
priorityQueue.add(new Edge(0, 0, 0));
while (!priorityQueue.isEmpty())
{
int u = priorityQueue.poll().destination;
visited[u] = true;
for (Edge edge : adjacencyList.get(u))
{
int v = edge.destination;
int weight = edge.weight;
if (!visited[v] && weight < key[v])
{
priorityQueue.remove(new Edge(v, v, key[v]));
key[v] = weight;
priorityQueue.add(new Edge(v, v, key[v]));
parent[v] = u;
}
}
}
System.out.println("Edge \tWeight");
for (int i = 1; i < vertices; i++)
{
System.out.println(parent[i] + " - " + i + "\t" + key[i]);
}
}
}
class Main
{
public static void main(String[] args)
{
int vertices = 5;
Graph graph = new Graph(vertices);
graph.addEdge(0, 1, 5);
graph.addEdge(0, 3, 2);
graph.addEdge(1, 2, 5);
graph.addEdge(1, 3, 8);
graph.addEdge(1, 4, 5);
graph.addEdge(2, 4, 7);
graph.addEdge(3, 4, 9);
graph.primMST();
}
}
最小生成树——Kruskal算法
- 将每个顶点都看作一个独立的树,即共有 V 个树,其中 V 是图中顶点的数量。
- 将图中的所有边按照权值从小到大进行排序。
- 按照排序后的顺序依次遍历边。
- 对于当前遍历到的边,检查它所连接的两个顶点是否在同一个树中。如果不在同一个树中,说明加入这条边不会形成环路。
- 如果边的两个顶点不在同一个树中,将这条边加入最小生成树,同时合并这两个顶点所在的树。
- 重复步骤3和步骤4,直到最小生成树中的边数达到 V-1,其中 V 是图中顶点的数量。
Java实现:
import java.util.*;
class KruskalMST
{
static class Edge implements Comparable<Edge>
{
int src, dest, weight;
public int compareTo(Edge compareEdge)
{
return this.weight - compareEdge.weight;
}
}
static class Subset
{
int parent, rank;
}
int V, E;
Edge[] edge;
KruskalMST(int v, int e)
{
V = v;
E = e;
edge = new Edge[E];
for (int i = 0; i < e; ++i)
edge[i] = new Edge();
}
int find(Subset[] subsets, int i)
{
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent);
return subsets[i].parent;
}
void Union(Subset[] subsets, int x, int y)
{
int xroot = find(subsets, x);
int yroot = find(subsets, y);
if (subsets[xroot].rank < subsets[yroot].rank)
subsets[xroot].parent = yroot;
else if (subsets[xroot].rank > subsets[yroot].rank)
subsets[yroot].parent = xroot;
else
{
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
}
void KMST()
{
Edge[] result = new Edge[V];
int e = 0;
int i;
for (i = 0; i < V; ++i)
result[i] = new Edge();
Arrays.sort(edge);
Subset[] subsets = new Subset[V];
for (i = 0; i < V; ++i)
subsets[i] = new Subset();
for (int v = 0; v < V; ++v)
{
subsets[v].parent = v;
subsets[v].rank = 0;
}
i = 0;
while (e < V - 1)
{
Edge next_edge ;
next_edge = edge[i++];
int x = find(subsets, next_edge.src);
int y = find(subsets, next_edge.dest);
if (x != y)
{
result[e++] = next_edge;
Union(subsets, x, y);
}
}
for (i = 0; i < e; ++i)
System.out.println(result[i].src + " -- " + result[i].dest + " == " + result[i].weight);
}
public static void main(String[] args)
{
int V = 4; // 顶点个数
int E = 5; // 边的个数
KruskalMST graph = new KruskalMST(V, E);
// 添加边
graph.edge[0].src = 0;
graph.edge[0].dest = 1;
graph.edge[0].weight = 10;
graph.edge[1].src = 0;
graph.edge[1].dest = 2;
graph.edge[1].weight = 6;
graph.edge[2].src = 0;
graph.edge[2].dest = 3;
graph.edge[2].weight = 5;
graph.edge[3].src = 1;
graph.edge[3].dest = 3;
graph.edge[3].weight = 15;
graph.edge[4].src = 2;
graph.edge[4].dest = 3;
graph.edge[4].weight = 4;
graph.KMST();
}
}
最短路径——Djakarta算法
原理:
1.初始时令 S={V0},T={其余顶点},T 中顶点对应的距离值。若存在,d(V0,Vi)为弧上的权值;若不存在<V0,Vi>,d(V0,Vi)为 MAXINT;
2.从 T 中选取一个其距离值为最小的顶点 W 且不在 S 中,加入 S;
3.对其余 T 中顶点的距离值进行修改:若加进 W 作中间顶点,从 V0 到 Vi 的距离值缩短,则修改此距离值;
4.重复上述步骤 2、3,直到 S 中包含所有顶点,即 W=Vi 为止
Java实现:
class Dijkstra
{
private static final int MAXSIZE = 11;
private static final int MAXINT = Integer.MAX_VALUE;
public static void dijkstra(Graph graph, int v, int v1)
{
int[] dist = new int[MAXSIZE];
int[] path = new int[MAXSIZE];
int[] s = new int[MAXSIZE];
String[] vexsC = {"", "", "", "", "", "", "", "", "", "", ""};
for (int i = 1; i <= graph.n; i++)
{
s[i] = 0;
dist[i] = graph.edges[v][i];
path[i] = v;
}
s[v] = 1;
dist[v] = 0;
for (int i = 1; i <= graph.n; i++)
{
int k = -1;
int min = MAXINT;
for (int j = 1; j <= graph.n; j++)
{
if (s[j] == 0 && dist[j] < min)
{
min = dist[j];
k = j;
}
}
if (k == -1 || min == MAXINT)
{
break;
}
s[k] = 1;
for (int j = 1; j <= graph.n; j++)
{
int newD = dist[k] + graph.edges[k][j];
if (s[j] == 0 && dist[j] > newD)
{
dist[j] = newD;
path[j] = k;
}
}
}
for (int i = 1; i <= graph.n; i++)
{
if (v1 == i)
{
System.out.printf("从 %d(%s)到 %d(%s)的短经过路线为:\n", v, vexsC[v - 1], i, vexsC[i - 1]);
System.out.printf("%d(%s)", i, vexsC[i - 1]);
int pre = path[i];
while (pre != v) {
System.out.printf("<- %d(%s)", pre, vexsC[pre - 1]);
pre = path[pre];
}
System.out.printf("<- %d(%s)", v, vexsC[v - 1]);
System.out.printf(" 路线距离:%d\n\n", 100 * dist[i]);
}
}
}
}
附:Graph
class Graph
{
private static final int MAXSIZE = Integer.MAX_VALUE;
private final int[] vexs;
private final int[][] edges;
private final int n;
private final int e;
public Graph()
{
vexs = new int[MAXSIZE];
edges = new int[MAXSIZE][MAXSIZE];
n = 0;
e = 0;
}
public int[] getVexs()
{
return vexs;
}
public int[][] getEdges()
{
return edges;
}
public int getN()
{
return n;
}
public int getE()
{
return e;
}
}
最短路径——Floyd算法
原理:
1.定义n×n的方阵序列D-1, D0 , … Dn-1,
2.初始化: D-1=C D-1 [i] [j]=边<i,j>的长度,表示初始的从i到j的最短路径长度,即它是从i到j的中间不经过其他中间点的最短路径。
3.迭代:设Dk-1已求出,如何得到Dk(0≤k≤n-1)? Dk-1 [i] [j]表示从i到j的中间点不大于k-1的最短路径p:i…j, 考虑将顶点k加入路径p得到顶点序列q:i…k…j, ...
4.因为q的两条子路径i…k和k…j皆是中间点不大于k-1的最短路径,所以从i到j中间点不大于k的最短路径长度为: Dk [i] [j]=min { Dk-1 [i] [j], Dk-1 [i] [k] +Dk-1 [k] [j] }
Java实现:
Graph代码同上。
public static void floyd(Graph graph, int[][] P, int[][] D, int n1, int n2)
{
int v, w, k;
// 初始化floyd算法矩阵
for (v = 1; v < graph.getN(); v++)
{
for (w = 1; w < graph.getN(); w++)
{
D[v][w] = graph.getEdges()[v][w];
P[v][w] = w;
}
}
// 设k为中间点
for (k = 1; k < graph.getN(); k++)
{
// v为起点
for (v = 1; v < graph.getN(); v++)
{
// w为终点
for (w = 1; w < graph.getN(); w++)
{
if (D[v][w] > (D[v][k] + D[k][w]))
{
D[v][w] = D[v][k] + D[k][w]; // 更新最小路径
P[v][w] = P[v][k]; // 更新最小路径中间顶点
}
}
}
}
String[] name1 = {"", "", "", "", "", "", "", "", "", "", ""};
System.out.printf("\n%d (%s) -> %d(%s) 的最小路径为:%d\n", n1, name1[n1], n2, name1[n2], D[n1][n2]*100);
k = P[n1][n2];
System.out.printf("路线是: %d ( %s )", n1, name1[n1]); // 打印起点
while (k != n2)
{
System.out.printf("-> %d ( %s )", k, name1[k]); // 打印中间点
k = P[k][n2];
}
System.out.printf("-> %d( %s )\n", n2, name1[n2]); // 打印终点
}
本文仅为学习记录,如有错误欢迎指出。