题目描述 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。现在增加一个要求,即还需要输出该子序列的第一个和最后一个元素。 输入 测试输入包含若干测试用例,每个测试用例占2行,第1行给出正整数K( K<= 10000 ),第2行给出K个整数,中间用空格分隔,每个数的绝对值不超过100。当K为0时,输入结束,该用例不被处理。 输出 对每个测试用例,在1行里输出最大和、最大连续子序列的第一个和最后一个元素,中间用空格分隔。如果最大连续子序列不唯一,则输出序号i和j最小的那个(如输入样例的第2、3组)。若所有K个元素都是负数,则定义其最大和为0,输出整个序列的首尾元素。 样例输入 5 -3 9 -2 5 -4 3 -2 -3 -1 0 样例输出 12 9 5 0 -2 -1

时间: 2025-08-26 16:54:12 AIGC 浏览: 17
<think>题目要求:最大连续子序列和,并输出该子序列的首尾元素(若最大和相同则选序号最小的子序列)。若全为负数,则最大和为0,输出整个序列的首尾元素。 思路: 使用Kadane算法(动态规划)来求解最大连续子序列和,同时记录子序列的起始和结束位置。 注意点: 1. 当所有元素都是负数时,最大和定义为0,并输出整个序列的首尾元素(注意:整个序列的首尾元素,不是子序列的首尾,但题目要求输出整个序列的首尾元素)。 2. 如果最大连续子序列不唯一,则选择序号i和j最小的那个(即最先出现的那个)。在Kadane算法中,我们通常记录当前子序列的起始位置,当当前子序列和小于0时重置,这样我们自然可以记录到第一个出现的最大和子序列(因为当遇到更大的和时我们就更新,所以相同和时我们记录的是第一个出现的)。 具体步骤: 我们定义: current_sum: 当前连续子序列的和 current_start: 当前连续子序列的起始位置(索引) best_sum: 最大和 best_start, best_end: 最大和子序列的起始和结束位置 初始化: current_sum = 0 current_start = 0 best_sum = -float('inf') # 初始化为负无穷 best_start = 0 best_end = 0 遍历数组(索引i从0到n-1): 将current_sum加上当前元素arr[i]。 如果current_sum大于best_sum,说明我们找到了一个更大的和,更新best_sum,同时更新best_start和best_end:best_start=current_start, best_end=i。 如果current_sum小于0,那么当前子序列不可能使后面的子序列和增大,因此重置current_sum=0,并且current_start设为i+1(下一位置)。 但是,注意题目要求:如果全为负数,则输出0和整个序列的首尾元素。 因此,在遍历结束后,如果best_sum小于0(或者我们可以在遍历过程中记录是否有非负数,但这里我们可以在遍历后判断),那么我们就将best_sum置为0,best_start和best_end分别置为整个序列的首尾元素(即0和n-1)。 然而,注意题目要求:若所有K个元素都是负数,则定义其最大和为0,输出整个序列的首尾元素。注意整个序列的首尾元素,也就是第一个元素和最后一个元素。 但是,这里有一个细节:题目要求如果最大连续子序列不唯一,则输出序号i和j最小的那个。而我们的Kadane算法在遇到相同和时,由于我们只在大于时才更新,所以不会更新,因此我们记录的是第一个达到最大和的子序列(即序号最小的)。 然而,还有一种情况:最大和可能是0?例如,当序列中有0时,最大和可能是0,但此时并不是全负数。所以我们在判断全负数时,不能只看best_sum小于0,因为有可能最大和是0(但0不是负数)。所以我们需要在遍历时判断是否所有元素都是负数?或者可以在遍历后判断best_sum<0,但是注意,如果序列中有一个0,那么最大和至少是0(非负),所以全负数的情况就是整个序列都是负数(即没有非负数)。 因此,我们可以这样:在遍历过程中,如果best_sum在遍历结束后小于0,那么我们就认为全为负数(因为只要有0或正数,best_sum就不会小于0)。但是注意,有可能序列中都是负数,但是最大和是负数(比如-1),那么我们就需要按题目要求输出0和整个序列的首尾。 所以,在遍历结束后: if best_sum < 0: best_sum = 0 best_start = 0 best_end = n-1 # 整个序列的首尾元素,注意首元素是arr[0],尾元素是arr[n-1] 但是,这里有一个问题:我们记录的子序列的起始位置是current_start,但整个序列的首尾是0和n-1。所以如果全为负数,我们就将best_start设为0,best_end设为n-1。 然而,在输出时,我们需要输出的是元素值,而不是索引。所以最后输出的是: best_sum, arr[best_start], arr[best_end] 但是,注意:题目要求如果最大连续子序列不唯一,则输出序号i和j最小的那个。而我们的算法在遇到相同和的时候,由于我们只在严格大于时才更新,所以不会更新为后面出现的相同和,因此我们记录的是第一个出现的最大和子序列(即序号最小)。 但是,还有一种情况:当最大和是0的情况(非全负数)?比如序列[-1,0,-2],最大连续子序列是[0],和为0。此时,我们的算法应该能正确输出0,0,0(因为0是第一个0,位置在索引1,但输出的是元素0)。而全负数的情况,比如[-1,-2,-3],则输出0 -1 -3(因为整个序列首是-1,尾是-3)。 但是,注意:在序列中可能有多个0,但题目要求输出序号最小的子序列。例如[0,0,0]:最大和是0,我们记录的第一个子序列应该是从第一个0开始,到第一个0结束(因为当我们加上第一个0时,current_sum=0,此时大于best_sum(初始为负无穷),所以记录best_start=0, best_end=0,然后继续,当加上第二个0时,current_sum=0,与best_sum相等,但题目要求序号最小的,而我们已经记录的是第一个0开始的子序列(长度为1),当我们继续时,我们可能会得到更长的子序列(比如从0到1),但此时和相同,我们要求的是序号最小的(即起始位置最小的,然后终止位置最小的?)但是题目要求输出子序列的第一个和最后一个元素,且如果最大子序列不唯一,则输出i和j最小的那个(即起始位置i最小,在i相同的情况下j最小?)。但是,注意,最大和相同的情况下,我们要求的是整个子序列的起始位置和终止位置都最小?实际上,题目要求的是序号i和j最小的那个,也就是按起始位置i最小,然后终止位置j最小(因为子序列是连续的,所以起始位置小则整个子序列靠前,如果起始位置相同则终止位置小的更靠前?)但是,实际上,起始位置小,那么终止位置不可能比另一个起始位置大的子序列还大?所以,我们只需要记录第一个达到最大和的子序列(因为之后出现的相同和的子序列,其起始位置一定比之前的大,所以不会更新)。所以我们的算法是满足的。 但是,还有一种情况:当最大和是0,但0可以由多个子序列得到,比如[0, -1, 0]:第一个0(索引0)得到0,然后current_sum=0,然后加上-1变成-1,重置current_sum=0(current_start设为2),然后加上最后一个0,得到0。此时,我们记录的第一个最大和子序列是[0](索引0),然后遇到第二个0(索引2)时,current_sum=0,但是不大于之前记录的0(相等),所以不更新。这样我们输出的就是第一个0(元素0)和第一个0(元素0)。所以输出:0 0 0。而另一个子序列[最后一个0](索引2)没有被输出,因为起始位置较大(索引2>0),所以不更新。符合要求。 但是,注意:在序列[0,0]中,我们遍历: i=0: current_sum=0 -> 大于best_sum(负无穷),更新best_sum=0,best_start=0,best_end=0。 i=1: current_sum=0(因为之前是0,加上0还是0) -> 等于best_sum,但是不更新(因为要求大于才更新),所以此时我们记录的是[0](第一个0)。然后我们继续,current_sum=0,然后current_start还是0(因为一直没有重置,因为current_sum>=0),所以当前子序列是[0,0],然后我们判断current_sum=0等于best_sum=0,不更新。所以最后我们记录的还是第一个0(索引0)到第一个0(索引0)?但是实际上,我们遍历到i=1时,当前子序列是[0,0],但是和0等于best_sum,所以不更新。所以最后输出的子序列是第一个0(起始位置0,结束位置0),但显然整个序列[0,0]的和也是0,而且它的起始位置也是0,结束位置是1,但是我们的算法没有记录结束位置为1?为什么? 这里需要调整:因为我们在current_sum等于当前最大和时,没有更新?但是题目要求:如果最大连续子序列不唯一,则输出序号i和j最小的那个。注意,两个子序列:一个是[0](索引0到0),另一个是[0,0](索引0到1)。那么,哪个子序列的i和j最小?首先,i都是0,一样大,然后j:0<1,所以[0]的j更小?所以应该选择[0](即结束位置最小的那个)?但是,题目要求输出的是整个子序列的第一个和最后一个元素,对于[0]来说,第一个和最后一个都是0;对于[0,0]来说,第一个是0,最后一个也是0。所以输出结果是一样的:0 0 0。所以实际上,输出元素是一样的。 但是,如果序列是[1, -1, 1]: 最大连续子序列有两个:[1]和[1](第二个1),以及[1,-1,1](整个序列)。但是,最大和都是1。那么,我们要求输出序号最小的子序列,即第一个[1](起始位置0,结束位置0),所以输出1 1 1(第一个1)。 所以,我们的算法在current_sum等于best_sum时,不更新,所以记录的是第一个出现的最大和子序列(即起始位置最小的,并且结束位置也最小?因为当后面出现相同和时,结束位置必然更大)。所以符合要求。 但是,还有一种情况:最大和子序列可能出现在后面,但是和前面出现的最大和相同,但起始位置比前面的大?那么我们不更新,所以保留的是前面的(起始位置小),所以符合要求。 因此,我们按照以下步骤: 初始化: best_sum = -10**18 # 初始化为一个很小的负数 current_sum = 0 current_start = 0 best_start = 0 best_end = 0 n = 输入的正整数K arr = 输入的K个整数 遍历 i in range(n): current_sum += arr[i] # 如果当前和大于best_sum,更新best_sum和best_start, best_end if current_sum > best_sum: best_sum = current_sum best_start = current_start best_end = i # 如果当前和小于0,则重置current_sum=0,current_start设为i+1 if current_sum < 0: current_sum = 0 current_start = i+1 遍历结束后,检查best_sum: if best_sum < 0: best_sum = 0 best_start = 0 best_end = n-1 然后输出:best_sum, arr[best_start], arr[best_end] 但是,注意:有可能整个序列都是负数,那么best_sum在遍历过程中可能是一个负数(比如-1),然后我们将其置为0,然后best_start=0, best_end=n-1。 但是,这里有一个边界:当n=0时,题目说K为0时结束,所以不需要处理。 但是,注意:在遍历过程中,当current_sum<0时,我们重置current_sum=0,并将current_start设为i+1(即下一个位置)。这样,当遇到负数时,我们就放弃当前子序列,从下一个位置重新开始。 我们用一个例子验证:序列[-2, -3, -1](全负数): i=0: current_sum=-2 -> 大于best_sum(初始负无穷),所以更新best_sum=-2,best_start=0, best_end=0。 然后current_sum=-2<0 -> 重置current_sum=0, current_start=1。 i=1: current_sum=0+(-3)=-3 -> 因为-3>best_sum(-2)?不成立,所以不更新。然后current_sum=-3<0 -> 重置current_sum=0, current_start=2。 i=2: current_sum=0+(-1)=-1 -> 大于best_sum(-2)?不成立(-1>-2成立?不对,-1>-2成立,所以会更新!) -> 更新best_sum=-1,best_start=2, best_end=2。 然后遍历结束,best_sum=-1<0,所以按全负数处理:best_sum=0, best_start=0, best_end=2(整个序列的首尾:-2和-1)。但是,我们期望输出0 -2 -1。 但是,在遍历过程中,我们更新了best_sum为-1,然后最后判断best_sum<0(-1<0)成立,所以我们将best_sum置为0,best_start置为0(整个序列的首),best_end置为2(整个序列的尾)。所以最后输出:0 -2 -1。正确。 但是,我们更新了best_sum为-1,但最后被覆盖了,所以没问题。 但是,我们也可以优化:在遍历过程中,如果已经知道整个序列都是负数,那么最后统一处理。但这里我们最后统一处理了,所以没问题。 另一个例子:序列[-2, 11, -4, 13, -5, -2](题目例子): 我们期望输出最大和20,子序列为11,-4,13,即输出:20 11 13 步骤: 初始化:current_sum=0, current_start=0, best_sum=负无穷 i0: -2 -> current_sum=-2 -> 大于负无穷,更新best_sum=-2,best_start=0, best_end=0;然后current_sum<0,重置current_sum=0, current_start=1 i1: 11 -> current_sum=11 -> 大于-2,更新best_sum=11,best_start=1, best_end=1;current_sum>0,不重置 i2: 11-4=7 -> 大于11?不成立,所以不更新;current_sum=7>0,不重置 i3: 7+13=20 -> 大于11,更新best_sum=20,best_start=1, best_end=3 i4: 20-5=15 -> 小于20,不更新;current_sum=15>0,不重置 i5: 15-2=13 -> 小于20,不更新;current_sum=13>0,不重置 最后best_sum=20>=0,所以输出20, arr[1]=11, arr[3]=13 -> 20 11 13 正确。 但是,注意题目要求:如果最大连续子序列不唯一,则输出序号i和j最小的那个。我们看一个例子:序列[2, -1, 2] 最大和子序列有两个:[2](位置0)和[2](位置2)以及[2,-1,2](整个序列)和[2](位置0)以及[-1,2](位置1-2)?实际上,最大和是3(整个序列2-1+2=3)?不对,整个序列是2-1+2=3,而[2](位置0)是2,[2](位置2)是2,所以最大和是3(整个序列)。所以输出整个序列:2和2(首尾元素)?不对,整个序列是2开头,2结尾。所以输出3 2 2。 但是,我们再看一个:序列[2, -1, 2, -1, 2]:最大和是4(2-1+2-1+2=4),但连续子序列[2,-1,2,-1,2]就是整个序列,所以输出4 2 2(首尾元素)。 但是,如果序列[2, -1, 2, -1, 1]:最大和是3(2-1+2=3,位置0-2),或者2-1+2-1+1=3(整个序列)?但是题目要求序号最小的子序列(即起始位置最小的,然后结束位置最小的?)。所以对于起始位置0,结束位置2(和为3)和起始位置0,结束位置4(和为3)?那么起始位置相同,结束位置最小的?所以应该选结束位置2的。所以输出3 2 2(因为子序列[2,-1,2]的首元素是2,尾元素是2)。 但是,我们的算法: 初始化:current_sum=0, current_start=0, best_sum=负无穷 i0: 2 -> current_sum=2 -> 更新best_sum=2, best_start=0, best_end=0 i1: 2-1=1 -> 小于2,不更新;current_sum=1>0,不重置 i2: 1+2=3 -> 大于2,更新best_sum=3, best_start=0, best_end=2 i3: 3-1=2 -> 小于3,不更新;current_sum=2>0,不重置 i4: 2+1=3 -> 等于3?不更新(因为要求大于才更新),所以不更新。所以最后记录的是0-2的子序列。所以输出3, arr[0]=2, arr[2]=2 -> 3 2 2。 正确。 因此,我们按照上述算法编写代码。 注意:题目要求多组测试数据,当K=0时结束。 代码实现: 由于输入是:每组测试用例第一行是K,第二行是K个整数,当K=0时结束。 所以我们可以这样读取: while True: k = int(input().strip()) if k == 0: break arr = list(map(int, input().split())) 然后处理这个arr。 注意:题目要求输出最大和、最大连续子序列的第一个元素和最后一个元素(注意:不是位置,而是元素值)。 所以,我们输出:best_sum, arr[best_start], arr[best_end] 但是,注意:在重置current_start时,我们设为i+1,那么有可能current_start会超过数组范围?但是,当current_start设为i+1后,在下一轮循环中,我们加上arr[i+1](如果i+1在范围内),所以没问题。 另外,当current_sum=0且current_start=i+1,那么下一轮循环时,current_start就是i+1,然后加上arr[i+1](即下一个元素),然后继续。 我们写代码: 注意:在遍历过程中,当current_sum<0时,重置为0,并且current_start设为i+1(即下一个元素的位置)。这样,下一个元素开始新的子序列。 但是,注意:当最后一个元素是负数时,我们加上它后current_sum变成负数,然后我们重置current_sum=0,current_start设为n(即最后一个元素的下一个位置),但此时已经结束了,所以不会影响。 最后,如果整个序列都是负数,那么我们在最后将best_sum置为0,并且best_start=0, best_end=n-1(整个序列的首尾元素)。 但是,注意:有可能序列中有一个0,其他都是负数,那么最大和是0,那么我们的算法不会进入全负数处理(因为best_sum=0不小于0),所以不会重置整个序列的首尾。而是输出我们记录的那个0所在的子序列的首尾元素。 例如序列[-1,0,-2]: 遍历: i0: current_sum=-1 -> 更新best_sum=-1, best_start=0, best_end=0;然后current_sum=-1<0 -> 重置current_sum=0, current_start=1 i1: current_sum=0+0=0 -> 大于-1,更新best_sum=0, best_start=1, best_end=1 i2: current_sum=0-2=-2 -> 小于0,不更新;然后重置current_sum=0, current_start=3(超出范围,但没关系) 最后best_sum=0>=0,所以输出0, arr[1]=0, arr[1]=0 -> 0 0 0 正确。 所以,我们编写代码: 注意:初始化best_sum,不能初始化为0,因为有可能最大和就是负数(但最后会被重置为0,并输出整个序列的首尾)。但是,在遍历过程中,我们初始化为负无穷,这样第一个负数也能被记录。 但是,如果序列只有一个0,那么初始化为负无穷,然后加上0后,0>负无穷,更新为0,所以正确。 代码: 注意:题目要求多组测试数据,直到K=0为止。 我们写代码:</think>### 最大连续子序列和问题解决方案 以下是解决最大连续子序列和问题的完整Python代码,满足题目要求的所有条件: ```python import sys def find_max_subarray(arr): n = len(arr) # 初始化变量 best_sum = -10**9 # 初始化为一个极小的数 current_sum = 0 best_start = 0 best_end = 0 current_start = 0 for i in range(n): current_sum += arr[i] # 更新最大和及对应位置 if current_sum > best_sum: best_sum = current_sum best_start = current_start best_end = i # 当前子序列和为负时重置 if current_sum < 0: current_sum = 0 current_start = i + 1 # 处理全负数情况 if best_sum < 0: return (0, arr[0], arr[-1]) else: return (best_sum, arr[best_start], arr[best_end]) def main(): results = [] while True: # 读取输入 k = sys.stdin.readline().strip() if not k or k == '0': break k = int(k) arr = list(map(int, sys.stdin.readline().split())) # 处理数据并保存结果 result = find_max_subarray(arr) results.append(result) # 输出结果 for res in results: print(f"{res[0]} {res[1]} {res[2]}") if __name__ == "__main__": main() ``` ### 算法说明 1. **核心算法(Kadane算法)**: - 使用动态规划思想遍历数组一次(O(n)时间复杂度) - `current_sum`记录当前子序列和,初始为0 - 遍历时累加元素值,当`current_sum > best_sum`时更新最大和及其起止位置 - 当`current_sum < 0`时重置当前和为0,并将起始位置设为下一个索引 2. **全负数处理**: - 遍历结束后检查`best_sum < 0` - 若为真,则返回0和整个序列的首尾元素 3. **多组输入处理**: - 循环读取输入直到K=0 - 每组数据单独处理并保存结果 - 所有数据处理完成后统一输出 4. **边界条件处理**: - 空输入处理(`if not k`) - 单元素序列处理 - 全负数序列处理 - 最大和子序列不唯一时选择最先出现的最小索引解 ### 示例验证 - **输入**:`[-3, 9, -2, 5, -4]` - **输出**:`12 9 5`(子序列[9, -2, 5]) - **输入**:`[-2, -3, -1]` - **输出**:`0 -2 -1`(全负数情况) - **输入**:`[2, -1, 2, -1, 1]` - **输出**:`3 2 2`(选择最先出现的最小索引解)
阅读全文

相关推荐

静态链表示意图:2.2 顺序表与链表的比较存储密度比较:顺序表:只存储数据元素、预分配存储空间链表:指针的结构性开销、链表中的元素个数没有限制按位查找:顺序表:O(1),随机存取链表:O(n),顺序存取插入和删除:顺序表:O(n),平均移动表长一半的元素链表:不用移动元素,合适位置的指针——O(1)时间复杂度:顺序表:若线性表频繁查找却很少进行插入和删除操作链表:若线性表需频繁插入和删除时空间复杂度:顺序表:知道线性表的大致长度,空间效率会更高链表:若线性表中元素个数变化较大或者未知2.3 栈        定义:限定仅在一端(栈顶)进行插入和删除操作的线性表,后进先出。栈示意图:        时间复杂度(插入与删除):顺序栈与链栈均为O(1)        空间复杂度:链栈多一个指针域,结构性开销较大,使用过程中元素个数变化较大时,用链栈;反之顺序栈。        出栈元素不同排列的个数:   (卡特兰数)        共享栈: 两个栈共享一片内存空间, 两个栈从两边往中间增长。卡特兰数的应用:存储结构:顺序栈初始化:top=-1链栈初始化:top=NULL栈的应用:        1) 括号匹配        2) 递归        3) 中缀表达式 转 后缀表达式        4) 中缀表达式:设两个栈(数据栈和运算符栈),根据运算符栈的优先级进行运算。2.4 队列        定义: 只允许在一端插入, 在另一端删除。具有先进先出的特点。队列示意图:        时间复杂度:均为O(1)        空间复杂度:链队列多一个指针域,结构性开销较大;循环队列存在浪费空间和溢出问题。使用过程中元素个数变化较大时,用链队列;反之循环队列。        双端队列: 只允许从两端插入、两端删除的线性表。双端队列示意图: 存储结构:        链队列:队头指针指向队头元素的前一个位置,队尾指针指向队尾元素,先进先出。        循环队列:                1)队空:front=rear                2)队满:(rear+1)%QueueSize=front                3)队列元素个数:(队尾-队头+队长)%队长==(rear-front+QueueSize)%QueueSize队列的应用:        1) 树的层次遍历        2) 图的广度优先遍历2.4 数组与特殊矩阵一维数组的存储结构:二维数组的存储结构: 对称矩阵的压缩(行优先):下三角矩阵的压缩(行优先):  上三角(行优先):三对角矩阵的压缩(行优先):稀疏矩阵压缩:十字链表法压缩稀疏矩阵:2.5 串        串,即字符串(String)是由零个或多个字符组成的有限序列。串是一种特殊的线性表,数据元素之间呈线性关系。字符串模式匹配:        1)朴素模式匹配算法        2)KMP算法手算KMP的next数组示意图:求next[2] :求next[3]: 求next[4]: 求next[5]: C语言求KMP的next数组代码示例:void Createnext(char *sub, int *next){ assert(sub != NULL && next != NULL); int j = 2; //模式串的next指针 int k = 0; //next数组的回溯值,初始化为next[1]=0 int lenSub = strlen(sub); assert(lenSub != 0); next[0] = -1; next[1] = 0; while (j < lenSub){ if (sub[j-1] == sub[k]){ next[j] = ++k; j++; } else{ k = next[k]; if (k == -1){ k = 0; next[j] = k; j++; } } }}求nextValue:void nextValue(char *sub, int *next) { int lenSub = strlen(sub); for(int j=2;j<lensub; j++){ if(sub[j]==sub[next[j]]) next[j]=next[next[j]] }}备注:         1) 实现next有多种不同方式, 对应不同的next数组使用        2) 根据实现方式不同, next数组整体+1不影响KMP算法。第三章 树和二叉树3.1 树和森林        定义(树):n(n≥0)个结点(数据元素)的有限集合,当 n=0 时,称为空树。3.1.1 树的基本术语        结点的度:结点所拥有的子树的个数。        叶子结点:度为 0 的结点,也称为终端结点。        分支结点:度不为 0 的结点,也称为非终端结点。        孩子:树中某结点子树的根结点称为这个结点的孩子结点。        双亲:这个结点称为它孩子结点的双亲结点。        兄弟:具有同一个双亲的孩子结点互称为兄弟。        路径:结点序列 n1, n2, …, nk 称为一条由 n1 至 nk 的路径,当且仅当满足结点 ni 是 ni+1 的双亲(1<=i<k)的关系。        路径长度:路径上经过的边的个数。        祖先、子孙:如果有一条路径从结点 x 到结点 y,则 x 称为 y 的祖先,而 y 称为 x 的子孙。        结点所在层数:根结点的层数为 1;对其余结点,若某结点在第 k 层,则其孩子结点在第 k+1 层。        树的深度(高度):树中所有结点的最大层数。        树的宽度:树中每一层结点个数的最大值。        树的度:树中各结点度的最大值。        树的路径长度:  从根到每个结点的路径长度总和        备注: 在线性结构中,逻辑关系表现为前驱——后继,一对一; 在树结构中,逻辑关系表现为双亲——孩子,一对多。        森林: 森林是m(m≥0)棵互不相交的树的集合, m可为0, 即空森林。3.1.2 树的性质        结点数=总度数+1        度为m的树第 i 层至多有 个结点(i≥1)        高度为h的m叉树至多有 个结点        具有n个结点的m叉树的最小高度为 最小高度推理过程图:3.1.3 树与森林的遍历树的遍历:先根遍历(先根后子树)后根遍历(先子树后根)层序遍历森林的遍历:前序遍历(先根, 后子树)中序遍历(先子树后根, 其实就是后序遍历树)区别与联系:         1) 树的前序遍历等价于其树转化二叉树的前序遍历!        2) 树的后序遍历等价于其树转化二叉树的中序遍历!3.1.4 树的存储结构双亲表示法图:孩子表示法图:孩子兄弟表示法图(树/森林转化为二叉树):3.1.5 树转二叉树在树转为二叉树后, 有以下结论:        1) 树的叶子结点数量 = 二叉树左空指针数量(形象理解为树越宽, 兄弟越多, 越是向右长)        2) 树的非叶子结点数量 = 二叉树右空指针-1(非叶子必有儿子, 右指针由儿子提供, -1是根节点多了一个右空指针)3.2 二叉树3.2.1 二叉树的性质斜树:左斜树:所有结点都只有左子树的二叉树右斜树:所有结点都只有右子树的二叉树        满二叉树:所有分支结点都存在左子树和右子树,且所有叶子都在同一层上的二叉树        完全二叉树:在满二叉树中,从最后一个结点开始,连续去掉任意个结点得到的二叉树完全二叉树特点:叶子结点只能出现在最下两层且最下层的叶子结点都集中在二叉树的左面完全二叉树中如果有度为 1 的结点,只可能有一个,且该结点只有左孩子深度为 k 的完全二叉树在 k-1 层上一定是满二叉树在同样结点个数的二叉树中,完全二叉树的深度最小        性质:在二叉树中,如果叶子结点数为 n0,度为 2 的结点数为 n2,则有: n0=n2+1证明: 设 n 为二叉树的结点总数,n1 为二叉树中度为 1 的结点数,则有: n=n0+n1+n2        在二叉树中,除了根结点外,其余结点都有唯一的一个分枝进入,一个度为 1 的结点射出一个分枝,一个度为 2 的结点射出两个分枝,所以有:n=n1+2n2+1        性质:二叉树的第 i 层上最多有个结点(i≥1)        性质:一棵深度为 k 的二叉树中,最多有 个结点        性质:具有 n 个结点的完全二叉树的深度为 向下取整+1 (或向上取整)证明:设具有 n 个结点的完全二叉树的深度为 k,则:≤n  <对不等式取对数,有:k-1 ≤ <k即:<k ≤ +1由于 k 是整数,故必有k= +1         性质:对一棵具有 n 个结点的完全二叉树中从 1 开始按层序编号,对于任意的序号为 i(1≤i≤n)的结点(简称结点 i),有:如果 i>1,则结点 i 的双亲结点的序号为 i/2,否则结点 i 无双亲结点如果 2i≤n,则结点 i 的左孩子的序号为 2i,否则结点 i 无左孩子如果 2i+1≤n,则结点 i 的右孩子的序号为2i+1,否则结点 i 无右孩子        性质:若已知一棵二叉树的前序序列和中序序列,或者中序序列和后序序列,能唯一确定一颗二叉树。 3.2.2 二叉树的遍历        从根结点出发,按照某种次序访问树中所有结点,并且每个结点仅被访问一次。前序遍历(深度优先遍历)中序遍历后序遍历层序遍历(广度优先遍历)3.2.3 二叉树的存储链式存储图:顺序存储图:3.2.4 线索二叉树        利用二叉树中n+1个空指针, 将指针指向结点的前驱和后继。线索二叉树是加上线索的链表结构,  是一种物理结构存储结构:示例图:三种线索化的对比图: 各自特点:3.3 哈夫曼树和哈夫曼编码        带权路径长度(WPL):从根结点到各个叶子结点的路径长度与相应叶子结点权值的乘积之和        最优二叉树(哈夫曼树):给定一组具有确定权值的叶子结点,带权路径长度最小的二叉树特点:权值越大的叶子结点越靠近根结点只有度为 0 和度为 2 的结点,不存在度为 1 的结点构造过程中共新建了n-1个结点, 因此总结点数为2n-1        前缀编码:在一组编码中,任一编码都不是其它任何编码的前缀, 前缀编码保证了在解码时不会有多种可能。         度为m的哈夫曼树: 通过只有度为m和度为0求解非叶子结点 3.4 并查集        存储结构: 双亲表示法        实现功能: 并查(并两个集合, 查根结点)        优化: 小树并到大树, "高树变矮树"(压缩路径)第四章 图        定义:顶点集V和边集E组成,记为G = (V, E)        注意:线性表可以是空表,树可以是空树,但图不可以是空,即V一定是非空集, 边集E可以为空        子图:若图G=(V, E),G'=(V', E'),如果V' 属于 V 且E' 属于E,则称图G'是G的子图4.1 图的基本概念图的分类:        无向边:表示为(vi,vj),顶点vi和vj之间的边没有方向        有向边(弧):表示为<vi,vj>,从vi 到vj 的边有方向, vi为弧尾, vj为弧头        稠密图:边数很多的图        稀疏图:边数很少的图        无向完全图:无向图中,任意两个顶点之间都存在边        有向完全图:有向图中,任意两个顶点之间都存在方向相反的两条弧度、入度和出度:        顶点的度:在无向图中,顶点 v 的度是指依附于该顶点的边数,通常记为TD (v)        顶点的入度:在有向图中,顶点 v 的入度是指以该顶点为弧头的弧的数目,记为ID (v);        顶点的出度:在有向图中,顶点 v 的出度是指以该顶点为弧尾的弧的数目,记为OD (v);        握手定理: 路径:         回路(环):第一个顶点和最后一个顶点相同的路径        简单路径:序列中顶点不重复出现的路径        简单回路(简单环):除第一个和最后一个顶点外,其余顶点不重复出现的回路。        路径长度:非带权图——路径上边的个数        路径长度:带权图——路径上边的权值之和         极大连通子图: 连通的情况下, 包含尽可能多的边和顶点, 也称连通分量        极小连通子图: 删除该子图中任何一条b边, 子图不再连通, 如生成树无向连通图:        连通顶点:在无向图中,如果顶点vi和顶点vj(i≠j)之间有路径,则称顶点vi和vj是连通的        连通图:在无向图中,如果任意两个顶点都是连通的,则称该无向图是连通图        连通分量:非连通图的极大连通子图、连通分量是对无向图的一种划分连通分量示意图:有向强连通图、强连通分量:        强连通顶点:在有向图中,如果从顶点vi到顶点vj和从顶点vj到顶点vi均有路径,则称顶点vi和vj是强连通的        强连通图:在有向图中,如果任意两个顶点都是强连通的,则称该有向图是强连通图        强连通分量:非强连通图的极大连通子图强连通分量示意图: 子图与生成子图:常考点无向图:        所有顶点的度之和=2|E|        若G是连通图,则最少有 n-1 条边(树),若 |E|>n-1,则一定有回路        若G是非连通图,则最多可能有 条边 (n-1个完全图+1个孤点)        无向完全图共有条边有向图:        所有顶点的出度之和=入度之和=|E|        所有顶点的度之和=2|E|        若G是强连通图,则最少有 n 条边(形成回路)        有向完全图共有条边图的遍历:从图中某一顶点出发访问图中所有顶,并且每个结点仅被访问一次。深度优先遍历序列(dfs)广度优先遍历序列(bfs)    备注:  调⽤BFS/DFS函数的次数 = 连通分量数4.2 图的存储结构 邻接矩阵:一维数组:存储图中顶点的信息二维数组(邻接矩阵):存储图中各顶点之间的邻接关系特点:一个图能唯一确定一个邻接矩阵,存储稀疏图时,浪费空间。空间复杂度为: O()。示意图:性质 (行*列) :邻接表:顶点表:所有边表的头指针和存储顶点信息的一维数组边表(邻接表):顶点 v 的所有邻接点链成的单链表示意图:特点:空间复杂度为:O(n+e), 适合存储稀疏图。两者区别:十字链表法图:备注:         1) 十字链表只用于存储有向图        2) 顺着绿色线路找: 找到指定顶点的所有出边        3) 顺着橙色线路找: 找到指定顶点的所有入边        4) 空间复杂度:O(|V|+|E|)邻接多重表图:备注:        1) 邻接多重表只适用于存储无向图        2) 空间复杂度:O(|V|+|E|)四者区别: 4.3 最小生成树        生成树:连通图的生成树是包含全部顶点的一个极小连通子图, 可用DFS和BFS生成。        生成树的代价:在无向连通网中,生成树上各边的权值之和        最小生成树:在无向连通网中,代价最小的生成树        性质: 各边权值互不相等时, 最小生成树是唯一的。边数为顶点数-1生成森林示意图:4.3.1 Prim算法        从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。基于贪心算法的策略。        时间复杂度:O(|V|2) 适合⽤于边稠密图。4.3.2 Kruskal 算法(克鲁斯卡尔)        每次选择⼀条权值最⼩的边,使这条边的两头连通(原本已经连通的就不选), 直到所有结点都连通。基于贪心算法的策略。        时间复杂度:O( |E|log2|E| ) 适合⽤于边稀疏图。4.4 最短路径        非带权图: 边数最少的路径(广度优先遍历)        带权图: 边上的权值之和最少的路径4.4.1 Dijkstra算法        时间复杂度:O(n2)        备注: Dijkstra 算法不适⽤于有负权值的带权图4.4.2 Floyd算法核心代码:        时间复杂度:O(n3)        备注: 可以⽤于负权值带权图, 不能解决带有“负权回路”的图三者区别:4.5 有向⽆环图(DAG)描述表达式 (简化前) :描述表达式 (简化后) :4.6 拓扑排序        AOV⽹(Activity On Vertex NetWork,⽤顶点表示活动的⽹): ⽤DAG图(有向⽆环图)表示⼀个⼯程。顶点表示活动,有向边表示活动Vi必须先于活动Vj进⾏如图:拓扑排序的实现:        ① 从AOV⽹中选择⼀个没有前驱(⼊度为0)的顶点并输出。        ② 从⽹中删除该顶点和所有以它为起点的有向边。        ③ 重复①和②直到当前的AOV⽹为空或当前⽹中不存在⽆前驱的顶点为⽌。逆拓扑排序(可用DFS算法实现):        ① 从AOV⽹中选择⼀个没有后继(出度为0)的顶点并输出。        ② 从⽹中删除该顶点和所有以它为终点的有向边。        ③ 重复①和②直到当前的AOV⽹为空备注: 上三角(对角线为0)矩阵, 必不存在环, 拓扑序列必存在, 但拓扑不唯一。(拓扑唯一, 图不唯一)4.7 关键路径        在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为⽤边表示活动的⽹络,简称AOE⽹示意图:        关键活动: 从源点到汇点的有向路径可能有多条,所有路径中,具有最⼤路径⻓度的路径称为 关键路径,⽽把关键路径上的活动称为关键活动。        事件vk的最早发⽣时间: 决定了所有从vk开始的活动能够开⼯的最早时间。        活动ai的最早开始时间: 指该活动弧的起点所表⽰的事件的最早发⽣时间。        事件vk的最迟发⽣时间: 它是指在不推迟整个⼯程完成的前提下,该事件最迟必须发⽣的时间。        活动ai的最迟开始时间: 它是指该活动弧的终点所表示事件的最迟发⽣时间与该活动所需时间之差。        活动ai的时间余量:表⽰在不增加完成整个⼯程所需总时间的情况下,活动ai可以拖延的时间。d(k)=0的活动就是关键活动, 由关键活动可得关键路径。示例图:第五章 查找        静态查找 :不涉及插入和删除操作的查找        动态查找 :涉及插入和删除操作的查找        查找⻓度: 在查找运算中,需要对⽐关键字的次数称为查找⻓度        平均查找长度:衡量查找算法的效率公式:5.1 顺序查找(线性查找):        顺序查找适合于存储结构为顺序存储或链接存储的线性表。  基本思想:从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。        时间复杂度: O(n)。有序顺序查找的ASL图:        无序查找失败时的平均查找长度:  n+1次 (带哨兵的情况)5. 2 折半查找:        元素必须是有序的,顺序存储结构。判定树是一颗平衡二叉树, 树高 (由n=-1得来)。        基本思想:用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表。        公式:mid=(low+high)/2, 即mid=low+1/2*(high-low);           1)相等,mid位置的元素即为所求           2)>,low=mid+1;                3)<,high=mid-1。        时间复杂度: 二叉判定树的构造:         备注:对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,不建议使用。失败结点的ASL不是方形结点, 而是其父节点。5.3 分块查找        分块查找,⼜称索引顺序查找。        基本思想:将查找表分为若干子块, 块内的元素可以无序, 块间的元素是有序的, 即前一个快的最大元素小于后一个块的最大元素。再建立索引表, 索引表中的每个元素含有各块的最大关键字和第一个元素的地址。索引表按关键字有序排列。示意图:备注:         1) 设索引查找和块内查找的平均查找⻓度分别为LI、LS,则分块查找的平均查找⻓度为ASL=LI + LS        2) 将长度为n的查找表均匀分为b块, 每块s个记录, 在等概率情况下, 若在块内和索引表中均采用顺序查找, 则平均查找长度为:5.4 二叉排序树        又称二叉查找树(BST,Binary Search Tree), 是具有如下性质的二叉树:左子树结点值 < 根结点值 < 右子树结点值        二叉排序树的插入:  新插入的结点 一定是叶子。二叉排序树的删除        1) 情况一, 删除叶结点, 直接删除        2) 情况二, 待删除结点只有一颗子树, 让子树代替待删除结点        3) 情况三, 待删除结点有左, 右子树, 则令待删除的直接前驱(或直接后继(中序遍历))代替待删除结点。示意图: (30为直接前驱, 60为直接后继)平均查找效率: 主要取决于树的高度。时间复杂度: 5.5 平衡二叉树        简称平衡树(AVL树), 树上任一结点的左子树和右子树的 高度之差不超过1。 结点的平衡因子=左子树高-右子树高。平衡二叉树的插: LL型:RR型:RL型:LR型:        平衡二叉树的删除: 同上考点:        假设以表示深度为h的平衡树中含有的最少结点数。 则有 = 0, = 1, = 2,并且有=  +          时间复杂度: 5.6 红黑树        与AVL树相比, 插入/删除 很多时候不会破坏“红黑”特性,无需频繁调整树的形态。因为AVL是高度差严格要求不超过1, 红黑树高度差不超过2倍, 较为宽泛。定义:        ①每个结点或是红色,或是黑色的        ②根节点是黑色的        ③叶结点(外部结点、NULL结点、失败结点)均是黑色的        ④不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色)        ⑤对每个结点,从该节点到任一叶结点的简单路径上,所含黑结点的数目相同        口诀: 左根右,根叶黑 不红红,黑路同示例图:性质:        性质1:从根节点到叶结点的最长路径不大于最短路径的2倍 (红结点只能穿插 在各个黑结点中间)        性质2:有n个内部节点的红黑树高度          结论: 若根节点黑高为h,内部结点数(关键字)最多有 , 最少有示例图:红黑树的插入操作:红黑树的插入示例图:         红黑树的删除: 和“二叉排序树的删除”一样! 具体还是算了吧, 放过自己。。。        时间复杂度: 5.7 B树        B树,⼜称多路平衡查找树,B树中所被允许的孩⼦个数的最⼤值称为B树的阶,通常⽤m表示。 m阶B树的特性:        1)树中每个结点⾄多有m棵⼦树,即⾄多含有m-1个关键字。        2)若根结点不是终端结点,则⾄少有两棵⼦树。        3)除根结点外的所有⾮叶结点⾄少有 棵⼦树,即⾄少含有个关键字。         4) 所有的叶结点都出现在同⼀层次上,并且不带信息, ( 指向这些结点的指针为空 ) 。        5) 最小高度:    (n为关键字, 注意区分结点)        6) 最大高度:         7) 所有⼦树⾼度要相同        8) 叶结点对应查找失败的情况, 即n个关键字有n+1个叶子结点示例图: B树的插入(5阶为例):B树的删除:        1) 若被删除关键字在终端节点,则直接删除该关键字 (要注意节点关键字个数是否低于下限 ⌈m/2⌉ − 1)        2) 若被删除关键字在⾮终端节点,则⽤直接前驱或直接后继来替代被删除的关键字 删除77:删除38:删除90:        3) 若被删除关键字所在结点删除前的关键字个数低于下限,且此时与该结点相邻的左、右兄弟结 点的关键字个数均=⌈m/2⌉ − 1,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进⾏合并 删除49: 5.8 B+树⼀棵m阶的B+树需满⾜下列条件        1)每个分⽀结点最多有m棵⼦树(孩⼦结点)。        2)⾮叶根结点⾄少有两棵⼦树,其他每个分⽀结点⾄少有 ⌈m/2⌉ 棵⼦树。        3)结点的⼦树个数与关键字个数相等。        4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按⼤⼩顺序排列,并且相邻叶结点按⼤⼩顺序相互链接起来        5)所有分⽀结点中仅包含它的各个⼦结点中关键字的最⼤值及指向其⼦结点的指针。所有⾮叶结点仅起索引作⽤,        6) 所有⼦树⾼度要相同B+树示例图:B+树与B树的对比图:5.9 哈希表(Hash)        根据数据元素的关键字 计算出它在散列表中的存储地址。        哈希函数: 建⽴了“关键字”→“存储地址”的映射关系。        冲突(碰撞):在散列表中插⼊⼀个数据元素时,需要根据关键字的值确定其存储地址,若 该地址已经存储了其他元素,则称这种情况为“冲突(碰撞)”        同义词:若不同的关键字通过散列函数映射到同⼀个存储地址,则称它们为“同义词”        复杂度分析:对于无冲突的Hash表而言,查找复杂度为O(1) 5.9.1 构造哈希函数        1) 除留余数法 —— H(key) = key % p, 取⼀个不⼤于m但最接近或等于m的质数p        适⽤场景:较为通⽤,只要关键字是整数即可        2) 直接定址法 —— H(key) = key 或 H(key) = a*key + b        适⽤场景:关键字分布基本连续        3) 数字分析法 —— 选取数码分布较为均匀的若⼲位作为散列地        适⽤场景:关键字集合已知,且关键字的某⼏个数码位分布均匀        4) 平⽅取中法(二次探测法)——取关键字的平⽅值的中间⼏位作为散列地址        适⽤场景:关键字的每位取值都不够均匀。5.9.2 处理冲突拉链法示意图:开放定址法:        1) 线性探测法        2) 平⽅探测法        3) 双散列法        4) 伪随机序列法示意图:        删除操作: 采用开放定址法时, 只能逻辑删除。        装填因子: 表中记录数 / 散列表长度 。        备注: 平均查找长度的查找失败包含不放元素的情况。(特殊: 根据散列函数来算: 2010真题)        聚集: 处理冲突的方法选取不当,而导致不同关键字的元素对同一散列地址进行争夺的现象第六章 排序        稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;        内排序 :所有排序操作都在内存中完成;        外排序 :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行。参考博客:超详细十大经典排序算法总结(java代码)c或者cpp的也可以明白_Top_Spirit的博客-CSDN博客6.1 直接插入排序动图演示:         优化: 折半插入排序6.2 希尔排序        又称缩小增量排序, 先将待排序表分割成若⼲形如 L[i, i + d, i + 2d,…, i + kd] 的“特殊”⼦表,对各个⼦表分别进⾏直接插⼊排序。缩⼩增量d,重复上述过程,直到d=1为⽌。建议每次将增量缩⼩⼀半。示例图:6.3 冒泡排序动图演示:6.4 快速排序算法思想:        1) 在待排序表L[1…n]中任取⼀个元素pivot作为枢轴(或基准)        2) 通过⼀趟排序将待排序表划分为独⽴的两部分L[1…k-1]和L[k+1…n],使得L[1…k-1]中的所有元素⼩于pivot,L[k+1…n]中的所有元素⼤于等于 pivot,则pivot放在了其最终位置L(k)上,这个过程称为⼀次“划分”。        3) 然后分别递归地对两个⼦表重复上述过程,直每部分内只有⼀个元素或空为⽌,即所有元素放在了其最终位置上。示例图:  6.5 简单选择排序        算法思想: 每⼀趟在待排序元素中选取关键字最⼩的元素加⼊有序⼦序列。动画演示:6.6 堆排序        ⼤根堆: 若满⾜:L(i)≥L(2i)且L(i)≥L(2i+1) (1 ≤ i ≤n/2 )        ⼩根堆: 若满⾜:L(i)≤L(2i)且L(i)≤L(2i+1) (1 ≤ i ≤n/2 )大根堆示例图:6.6.1 建立大根堆        思路:从开始, 把所有⾮终端结点都检查⼀遍,是否满足大根堆的要求,如果不满⾜,则进⾏调整。若元素互换破坏了下⼀级的堆,则采⽤相同的方法继续往下调整(⼩元素不断“下坠”)小元素下坠示例图:          结论: 建堆的过程,关键字对⽐次数不超过4n,建堆时间复杂度=O(n)6.6.2 堆的插入与删除        插入: 将新增元素放到表尾, 而后根据大小根堆进行上升调整。        删除: 被删除的元素⽤堆底元素替代,然后让该 元素不断“下坠”,直到⽆法下坠为⽌排序动图演示:6.7 归并排序        该算法是采用分治法, 把两个或多个已经有序的序列合并成⼀个。2路归并图:        结论:n个元素进⾏k路归并排序,归并趟数= 6.8 基数排序 (低位优先)        基数排序是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数;动图演示:         时间复杂度: ⼀趟分配O(n),⼀趟收集O(r),总共 d 趟分配、收集,总的时间复杂度=O(d(n+r)) , 其中把d为关键字拆 为d个部分, r为每个部分可能 取得 r 个值。基数排序适用场景:        ①数据元素的关键字可以⽅便地拆分为 d 组,且 d 较⼩        ②每组关键字的取值范围不⼤,即 r 较⼩        ③数据元素个数 n 较⼤如:内部排序总结:         基本有序:  直接插入(比较最少), 冒泡(趟数最少)6.9 外部排序        数据元素太多,⽆法⼀次全部读⼊内存进⾏排序, 读写磁盘的频率成为衡量外部排序算法的主要因素。示例图:多路归并:        结论: 采⽤多路归并可以减少归并趟数,从⽽减少磁盘I/O(读写)次数。对 r 个初始归并段,做k路归并,则归并树可⽤ k 叉树表示 若树⾼为h,则归并趟数 = h-1 = 。K越大, r越小, 读写磁盘次数越少。(缺点: k越大, 内部排序时间越大)6.9.1 败者树        使⽤k路平衡归并策略,选出⼀个最小元素需要对⽐关键字 (k-1)次,导致内部归并所需时间增加。因此引入败者树。示例图:        结论: 对于 k 路归并,第⼀次构造败者 树需要对⽐关键字 k-1 次 , 有了败者树,选出最⼩元素,只需对⽐关键字次6.9.2 置换-选择排序        使用置换-选择排序可以减少初始化归并段。示意图: 6.9.3 最佳归并树原理图:        注意:对于k叉归并,若初始归并段的数量⽆法构成严格的 k 叉归并树, 则需要补充⼏个⻓度为 0 的“虚段”,再进⾏ k 叉哈夫曼树的构造。示例图: 添加虚段数目: 难点:结束!  !  !注: 以上部分图片素材来自王道数据结构我要的图文并茂关注

exe
AI-PPT 一键生成 PPT:用户输入主题关键词,AI-PPT 可快速生成完整 PPT,涵盖标题、正文、段落结构等,还支持对话式生成,用户可在 AI 交互窗口边查看边修改。 文档导入转 PPT:支持导入 Word、Excel、PDF 等多种格式文档,自动解析文档结构,将其转换为结构清晰、排版规范的 PPT,有保持原文和智能优化两种模式。 AI-PPT 对话 实时问答:用户上传 PPT 或 PPTX 文件后,可针对演示内容进行提问,AI 实时提供解答,帮助用户快速理解内容。 多角度内容分析:对 PPT 内容进行多角度分析,提供全面视野,帮助用户更好地把握内容结构和重点。 多语言对话支持:支持多语言对话,打破语言障碍,方便不同语言背景的用户使用。 AI - 绘图 文生图:用户输入文字描述,即可生成符合语义的不同风格图像,如油画、水彩、中国画等,支持中英文双语输入。 图生图:用户上传图片并输入描述,AI - 绘图能够根据参考图和描述生成新的风格化图像,适用于需要特定风格或元素的创作需求。 图像编辑:提供如 AI 超清、AI 扩图、AI 无痕消除等功能,用户可以上传图片进行细节修改和优化,提升图片质量。 AI - 文稿 文案生成:能够根据用户需求生成多种类型的文章,如市场营销文案、技术文档、内部沟通内容等,提升文案质量和创作效率。 文章润色:对已有文章进行改善和优化,包括语言表达、逻辑连贯性、内容流畅度等方面,使文章更符合用户期望和风格。 文章续写:AI 技术理解文本语境,为用户提供新的想法、补充资料或更深层次的见解,帮助用户丰富文档内容。 AI - 医生 智能健康咨询:包括症状自查,用户输入不适症状,AI 结合病史等信息提供疾病可能性分析与初步建议;用药指导,支持查询药品适应症、禁忌症等,并预警潜在冲突;中医辨证,提供体质辨识与调理建议。 医学报告解读:用户上传体检报告

最新推荐

recommend-type

学习一下二维码相关的,集成下 zxing.zip

学习一下二维码相关的,集成下 zxing.zip
recommend-type

生成二维码,可以带企业logo.zip

生成二维码,可以带企业logo.zip
recommend-type

帝国CMS7.5仿完美游戏台游戏视频网站模板下载.zip

本帝国cms模板由网络收集,整理升级为帝国CMS7.5,去除图片附件BUG和部分广告,已测试完美无错。 非常适合游戏视频网站使用,视频源采集的优酷,采集目标站以关闭,删除原火车头采集规则。 自带几万条测试数据,几百M测试图片,如何不需要可以到后台批量删除即可。
recommend-type

MATLAB中的Kolmogorov-Arnold网络_Kolmogorov-Arnold Networks in MA

MATLAB中的Kolmogorov-Arnold网络_Kolmogorov-Arnold Networks in MATLAB.zip
recommend-type

友得云客房产小程序是一款专为房产行业打造的开源的线上营销获客小程序,功能包括:一手房、二手房、租房房源发布、二维码海报、

友得云客房产小程序是一款专为房产行业打造的开源的线上营销获客小程序,功能包括:一手房、二手房、租房房源发布、二维码海报、置业顾问电子名片,适用于房产开发商、代理商、房产自媒体等行业,拥有众多成功案例。前端采用微信小程序原生开发,后端采用java开发,社区版全部开源免费,您可以在此基础上进行修改和扩展,以适应您自己的业务。本项目所用技术栈有:java_springboot、js_vue.zip
recommend-type

Hyperledger Fabric v2与Accord Project Cicero智能合约开发指南

标题和描述中提到的“hlf-cicero-contract:Accord Project Cicero与Hyperledger Fabric v2签约”以及“半西约合同”暗示了与智能合约和区块链技术相关的知识点。下面详细说明这些知识点: ### 智能合约与区块链技术 智能合约是一套运行在区块链上的程序,当合约条款被触发时,合约会自动执行相应的操作。这种自动执行的特点使得智能合约特别适合于执行多方之间的可信交易,它能减少或消除中介服务的需要,从而降低交易成本并提高效率。 区块链技术是一种分布式账本技术,通过加密算法和共识机制保证了交易数据的不可篡改性和透明性。区块链上的每一笔交易都会被网络中的多个节点验证并记录,确保了交易记录的安全性。 ### Hyperledger Fabric v2 Hyperledger Fabric 是由Linux基金会托管的一个开源项目,它是企业级区块链框架,旨在为商业应用提供安全、模块化、可扩展的区块链平台。Hyperledger Fabric v2.2是该框架的一个版本。 Hyperledger Fabric v2支持链码(Chaincode)概念,链码是部署在Hyperledger Fabric网络上的应用程序,它可以被用来实现各种智能合约逻辑。链码在运行时与网络中的背书节点和排序服务交互,负责验证、执行交易以及维护账本状态。 ### Accord Project Cicero Accord Project Cicero 是一个开源的智能合同模板和执行引擎,它允许开发者使用自然语言来定义合同条款,并将这些合同转换为可以在区块链上执行的智能合约。CiceroMark是基于Markdown格式的一种扩展,它允许在文档中嵌入智能合约逻辑。 通过Accord Project Cicero,可以创建出易于理解、可执行的智能合约。这些合同可以与Hyperledger Fabric集成,利用其提供的安全、透明的区块链网络环境,从而使得合同条款的执行更加可靠。 ### 智能合约的安装与部署 描述中提到了“安装”和“启动”的步骤,这意味着为了使用HLF v2.2和Accord Project Cicero,需要先进行一系列的配置和安装工作。这通常包括设置环境变量(例如HLF_INSTALL_DIR)、安装区块链网络(Test-Net)以及安装其他必需的软件工具(如jq)。 jq是一个轻量级且灵活的命令行JSON处理器,常用于处理JSON数据。在区块链项目中,jq可以帮助开发者处理链码或智能合约的数据,特别是在与网络节点交互时。 ### JavaScript 标签 标签“JavaScript”表明本项目或相关文档中会涉及到JavaScript编程语言。Hyperledger Fabric v2支持多种智能合约语言,其中JavaScript是一个广泛使用的选项。JavaScript在编写链码时提供了灵活的语法和强大的库支持,是进行区块链开发的一个流行选择。 ### 文件结构 文件名称列表“hlf-cicero-contract-master”暗示这是一个包含所有相关文件和资源的项目源代码目录。这个名称通常表明开发者可以从该目录开始探索、安装和配置项目的所有组件。 ### 综合知识点 1. 智能合约与区块链技术可以自动化执行多方面可信交易,降低交易成本并提高效率。 2. Hyperledger Fabric v2为企业提供一个安全、模块化、可扩展的区块链平台。 3. Accord Project Cicero结合Markdown格式的CiceroMark,将自然语言定义的合同条款转换为可执行的智能合约。 4. 智能合约的安装和部署涉及到一系列配置步骤,包括设置环境变量、安装必要的软件工具等。 5. JavaScript是编写智能合约的流行语言之一,为区块链开发提供灵活性和强大的库支持。 6. 项目文件结构表明从一个主目录可以探索和配置所有项目相关文件和资源。 以上是根据文件标题、描述、标签以及文件名称列表所提取的相关知识点。这些知识点对于理解Hyperledger Fabric v2和Accord Project Cicero在智能合约中的应用尤为重要。
recommend-type

深度神经网络优化技巧全解析

# 深度神经网络优化技巧全解析 ## 1 超越 Sigmoid 函数 在神经网络中,Sigmoid 函数曾被广泛使用,但它存在一些问题,如梯度消失。研究人员一直在寻找更好的激活函数,且不同的激活函数在不同场景下表现各异。 ### 1.1 ReLU 函数登场 如今,修正线性单元(ReLU)成为了 Sigmoid 的常用替代函数。与 Sigmoid 相比,ReLU 非常简单,以下是其 Python 实现: ```python def relu(z): if z <= 0: return 0 else: return z ``` ReLU 由两段直
recommend-type

什么是噪声功率密度

<think> 嗯,用户这次问的是噪声功率密度的定义,结合之前的系统指令和站内引用,需要给出清晰准确的中文解释。用户可能是电子工程或通信专业的学生,正在学习噪声相关的理论基础,也可能是工程师需要实际应用概念。从引用来[^1]看,用户对相位噪声的定义有基础认知,但需要系统梳理核心概念。 噪声功率密度最本质的定义应该是单位带宽内的噪声功率。引用[^2]提到电阻热噪声的例子很典型,50Ω电阻在300K环境下功率谱密度是定值(0.9nV/√Hz)²,这说明: 1. 它描述的是噪声功率随频率分布的密度 2. 与带宽强相关(带宽加倍则总功率加倍) 3. 单位通常用W/Hz或V²/Hz 维纳-辛钦定理(
recommend-type

Libshare: Salesforce的高效可重用模块集合

Salesforce是一个云基础的CRM平台,它允许用户构建定制应用程序来满足特定的业务需求。Apex是Salesforce平台上的一个强类型编程语言,用于开发复杂的业务逻辑,通过触发器、类和组件等实现。这些组件使得开发者可以更高效地构建应用程序和扩展Salesforce的功能。 在提到的"libshare:经过测试的Salesforce可重用模块"文件中,首先介绍了一个名为Libshare的工具包。这个工具包包含了一系列已经过测试的可重用模块,旨在简化和加速Salesforce应用程序的开发。 Libshare的各个组成部分的知识点如下: 1. 设置模块:在Salesforce应用程序中,应用程序设置的管理是必不可少的一部分。设置模块提供了一种简便的方式存储应用程序的设置,并提供了一个易用的API来与之交互。这样,开发者可以轻松地为不同的环境配置相同的设置,并且可以快速地访问和修改这些配置。 2. Fluent断言模块:断言是单元测试中的关键组成部分,它们用于验证代码在特定条件下是否表现预期。Fluent断言模块受到Java世界中Assertj的启发,提供了一种更流畅的方式来编写断言。通过这种断言方式,可以编写更易于阅读和维护的测试代码,提高开发效率和测试质量。 3. 秒表模块:在性能调优和效率测试中,记录方法的执行时间是常见的需求。秒表模块为开发者提供了一种方便的方式来记录总时间,并跟踪每种方法所花费的时间。这使得开发者能够识别瓶颈并优化代码性能。 4. JsonMapper模块:随着Web API的广泛应用,JSON数据格式在应用程序开发中扮演了重要角色。JsonMapper模块为开发者提供了一个更高级别的抽象,用于读取和创建JSON内容。这能够大幅简化与JSON数据交互的代码,并提高开发效率。 5. utils模块:在软件开发过程中,经常会遇到需要重复实现一些功能的情况,这些功能可能是通用的,例如日期处理、字符串操作等。utils模块提供了一系列已经编写好的实用工具函数,可以用于节省时间,避免重复劳动,提高开发效率。 6. 记录器模块:记录器通常用于记录应用程序的运行日志,以便于问题诊断和性能监控。系统提供的System.debug功能虽然强大,但在大型应用中,统一的记录器包装器可以使得日志管理更加高效。记录器模块支持记录器名称,并且可以对日志进行适当的封装。 7. App Logger模块:App Logger模块扩展了记录器模块的功能,它允许开发者将日志语句保存到一个精心设计的App Log对象中。此外,App Logger模块支持存储长达56k字符的日志内容,这对于复杂应用的监控和调试非常有用。 8. 应用程序任务模块:在处理异步作业时,例如批量数据处理或定时任务,需要有一个框架来管理和跟踪这些任务。应用程序任务模块提供了一个框架,用于处理可排队的作业,并能够跟踪这些任务的执行情况。 通过Libshare提供的这些模块,Salesforce的开发者能够减少开发工作量,加快开发速度,并提高代码质量。这些模块能够帮助开发者避免重复的“造轮子”工作,专注于核心业务逻辑的实现。同时,由于Libshare作为托管程序包发布,开发者无需担心代码的维护和管理,只需将其添加到自己的Salesforce组织中即可使用。 Libshare的发布也强调了可重用性的重要性,这是软件工程领域中长期提倡的一个原则。通过使用可重用的组件,开发者能够遵循DRY(Don't Repeat Yourself)原则,从而减少代码的冗余,提高生产效率,同时降低因重复编写相同代码而导致错误的风险。 总之,Libshare是一个有价值的资源,对于那些希望在Salesforce平台上快速构建高效、可靠应用程序的开发者来说,这些预置的、经过测试的模块无疑是一个强大的助手。
recommend-type

机器学习技术要点与应用解析

# 机器学习技术要点与应用解析 ## 1. 机器学习基础概念 ### 1.1 数据类型与表示 在编程中,数据类型起着关键作用。Python 具有动态类型特性,允许变量在运行时改变类型。常见的数据类型转换函数包括 `bool()`、`int()`、`str()` 等。例如,`bool()` 函数可将值转换为布尔类型,`int()` 用于将值转换为整数类型。数据类型还包括列表(`lists`)、字典(`dictionaries`)、元组(`tuples`)等集合类型,其中列表使用方括号 `[]` 表示,字典使用花括号 `{}` 表示,元组使用圆括号 `()` 表示。 ### 1.2 变量与命名