计数排序备忘

计数排序是线性排序,是基数排序的一种。

主要先上代码和输出,可以看一下。

import java.util.Arrays;

public class CountSort {
	public static void main(String[] args) {
		int[] arr = {2,4,2,3,7,1,1,0,0,5,6,9,8,5,7,4,0,9};
		System.out.println("原数组 arr:");
		System.out.println(Arrays.toString(arr));
		int[] result = sort(arr);
		System.out.println("排序后数组  result:");
		System.out.println(Arrays.toString(result));
	}

	private static int[] sort(int[] arr) {
		int[] result = new int[arr.length];
		int[] count = new int[10];
		for (int i = 0; i < arr.length; i++) {
			count[arr[i]]++;
		}
		System.out.println("计数数组 count:");
		System.out.println(Arrays.toString(count));
		for (int i = 0,j=0; i < count.length;i++) {
			while(count[i]-- > 0) result[j++] = i;
		}
//		for (int i = 1; i < count.length; i++) {
//			count[i] = count[i-1] + count[i];
//		}
//		System.out.println("累加数组 count:");
//		System.out.println(Arrays.toString(count));
//		for (int i = arr.length-1;i>=0;i--) {
//			result[--count[arr[i]]] = arr[i];
//		}
		return result;
	}
}

输出:

原数组 arr:
[2, 4, 2, 3, 7, 1, 1, 0, 0, 5, 6, 9, 8, 5, 7, 4, 0, 9]
计数数组 count:
[3, 2, 2, 1, 2, 2, 1, 2, 1, 2]
排序后数组  result:
[0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 9, 9]

 计数数组下标处对应的值为下标数字的次数。即i = count[arr[i]]。

但这样同时也容易带来一个问题,就是这样的计数排序是不稳定的。即如果我有两个数a,b,a与b数值是相等的,且在原数组中a在b前的,但在排序只后,就有可能出现a在b之后的情况。

如果这么说还明确这种不稳定性不便之处的话,可以想象是对象的话,排序出现这种情况会带来什么影响。排序的根据主要是比较对象的某一个属性,如果说两个属性值相同而其他的属性值不同的话,如果是这种不稳定的排序,则会导致两个对象相对的顺序被打乱。再或者是订单信息,初始是以时间顺序写入的,当你想以价格高低来排序的话,你肯定想能相同价格时,时间的先后顺序也是整齐的。如果排序是不稳定的,则会出现价格相同时,下单时间信息顺序错乱。

所以如果想以一种方法或者设计保证排序的稳定性时,可以用累加数组,将数组从后向前扫面,一一放入排序数组。

片段如下:

for (int i = 1; i < count.length; i++) {
    count[i] = count[i-1] + count[i];
}

for (int i = arr.length-1;i>=0;i--) {
    result[--count[arr[i]]] = arr[i];
}

输出:

原数组 arr:
[2, 4, 2, 3, 7, 1, 1, 0, 0, 5, 6, 9, 8, 5, 7, 4, 0, 9]
计数数组 count:
[3, 2, 2, 1, 2, 2, 1, 2, 1, 2]
累加数组 count:
[3, 5, 7, 8, 10, 12, 13, 15, 16, 18]
排序后数组  result:
[0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 9, 9]

<think>好的,用户想了解如何计算拓扑排序的不同方案数。首先,我需要回顾拓扑排序的基本概念。拓扑排序是针对有向无环图(DAG)的,生成一个线性序列,满足所有边的方向要求。用户的问题是关于可能的排序方案数量,这涉及到组合数学和图的结构。 首先,我得确定计算拓扑排序数量的方法。常规的拓扑排序算法如Kahn算法或DFS,主要用于生成一个有效的排序,而不是计算总数。用户需要的是算法来统计所有可能的拓扑序数量,这可能涉及到动态规划或回溯法。 接下来,考虑可能的算法思路。对于小规模图,可以使用回溯法,枚举所有可能的顶点排列,并验证是否符合拓扑顺序。但时间复杂度很高,是指数级的,不适用于大图。对于较大图,动态规划可能更高效。例如,使用状态压缩DP,状态表示已选顶点的集合和最后一个顶点,转移时添加满足条件的顶点。这种方法的时间复杂度是O(n*2^n),对于n较大的情况依然不现实。 然后,想到分解图的连通组件。如果图由多个互不连通的组件组成,每个组件的拓扑序数量可以独立计算,然后相乘。此外,对于有层次结构的图,可能可以分阶段计算各层的排列组合。 另外,记忆化搜索结合递归可能有效。对于每个节点,计算其所有前驱已被处理后的情况数,递归求解子问题。需要避免重复计算,使用备忘录存储已计算的状态。 然后,考虑到引用中提到的拓扑排序的应用,比如任务调度,用户可能是在处理类似问题,需要知道有多少种合法的调度顺序。这时候可能需要更优化的算法,比如利用树分解或其他图结构特性来降低复杂度。 最后,总结可能的算法方法,并指出各自的优缺点。同时,提醒用户不同方法的时间复杂度,根据图的规模选择合适的算法。可能还需要提到相关的NP难问题,但拓扑排序计数是否属于此类需要确认,不过通常认为对于一般DAG,计算拓扑排序数量是#P难的,因此没有多项式时间解法,只能依靠动态规划或特定结构优化。</think>要计算有向无环图(DAG)的拓扑排序方案数,需根据图的结构特性采用不同的算法策略。以下是主要方法及实现步骤: --- ### 一、递归回溯法(小规模图适用) **算法思想**:遍历所有顶点排列组合,验证每个排列是否满足拓扑顺序 **时间复杂度**:$O(n! \cdot n)$(n为顶点数,适用于n≤10的情况) **Python实现**: ```python def count_topo_orders(graph): visited = [False] * len(graph) count = 0 in_degree = [len([u for u in range(len(graph)) if v in graph[u]]) for v in range(len(graph))] def backtrack(path): nonlocal count if len(path) == len(graph): count += 1 return for v in range(len(graph)): if not visited[v] and in_degree[v] == 0: visited[v] = True for neighbor in graph[v]: in_degree[neighbor] -= 1 backtrack(path + [v]) visited[v] = False for neighbor in graph[v]: in_degree[neighbor] += 1 backtrack([]) return count ``` --- ### 二、动态规划法(中等规模图适用) **算法思想**:使用状态压缩DP,状态表示为$dp[mask][last]$,其中mask是已选顶点的位掩码,last是最后添加的顶点 **时间复杂度**:$O(n^2 \cdot 2^n)$(适用于n≤15的情况) **实现步骤**: 1. 预处理每个顶点的前驱集合 2. 初始化状态:$dp[1<<i][i] = 1$(当顶点i无前驱时) 3. 状态转移:若新顶点j的前驱都已包含在mask中,则$dp[mask|j][j] += dp[mask][last]$ 4. 最终结果为所有$dp[2^n-1][*]$的和 --- ### 三、分解连通组件(优化策略) 若图由k个独立连通组件构成,总方案数为: $$Total = \prod_{i=1}^k C_i \cdot \frac{n!}{\prod_{i=1}^k n_i!}$$ 其中$C_i$是第i个连通组件的拓扑序数量,$n_i$为组件大小[^1] --- ### 四、记忆化搜索法(树状图优化) **适用场景**:当DAG呈树状结构(每个节点至多一个父节点) **递推公式**: $$Count(u) = \prod_{v \in Children(u)} Count(v) \cdot \frac{(Size(u)-1)!}{\prod_{v \in Children(u)} Size(v)!}$$ 其中$Size(u)$表示以u为根的子树节点数 --- ### 五、矩阵计算法(特殊结构图) 使用邻接矩阵$A$的特征值分解,通过矩阵树定理计算特定结构的拓扑序数量。例如对分层图(layer-based DAG),总方案数为各层节点数的阶乘乘积: $$Total = \prod_{i=1}^k (m_i!)$$ 其中$m_i$是第i层节点数[^2] --- ### 算法选择建议 | 图规模 | 推荐算法 | 时间复杂度 | 空间复杂度 | |-------|---------|-----------|-----------| | n≤10 | 回溯法 | O(n!) | O(n) | | n≤15 | 动态规划 | O(n²2ⁿ) | O(n2ⁿ) | | 树状结构 | 记忆化搜索 | O(n) | O(n) | | 分层图 | 矩阵计算 | O(k) | O(1) | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值