当前位置: 首页 > news >正文

大冶市建设局网站腾讯广告

大冶市建设局网站,腾讯广告,企业自建网站营销论文,wordpress大前端dux3.0报名明年4月蓝桥杯软件赛的同学们#xff0c;如果你是大一零基础#xff0c;目前懵懂中#xff0c;不知该怎么办#xff0c;可以看看本博客系列#xff1a;备赛20周合集 20周的完整安排请点击#xff1a;20周计划 每周发1个博客#xff0c;共20周#xff08;读者可以按…报名明年4月蓝桥杯软件赛的同学们如果你是大一零基础目前懵懂中不知该怎么办可以看看本博客系列备赛20周合集 20周的完整安排请点击20周计划 每周发1个博客共20周读者可以按自己的进度选“正常”和“快进”两种计划。 每周3次集中答疑周三、周五、周日晚上在QQ群上答疑 文章目录 1. 选择排序2. 冒泡排序3. 插入排序4. 希尔排序5. 计数排序6. 桶排序7. 基数排序8. 归并排序9. 快速排序10. 堆排序 第8周第1讲:  十大排序 排序是数据处理的基本操作每次算法竞赛都必定用到排序。在绝大多数情况下并不需要自己手写排序代码而是直接用系统提供的函数sort()。   不过还是强烈建议学习各种排序算法掌握原理自己实现代码因为各种排序算法有不同的思路很多算法来源于这些排序算法。   用下面的题目详解十种排序算法。   例题排序 1. 选择排序 选择排序Selection sort是最简单直观的排序算法。   排序的目的是什么对n个数从小到大排序就是把杂乱无序的n个数放到它们应该在的位置。   最简单的做法是找到最小的数放在第1个位置找到第2小的数放在第2个位置…找到第n大的数放在第n个位置。   这个思路就是选择排序。具体操作   1第一轮在n个数中找到最小的数然后与第1个位置的数交换。这样就把最小的数放到了第1个位置。   2第二轮在第2 ~ 第n个数中找到最小的数然后与2个位置的数交换。   …   一共执行n轮操作第i轮找到第i大的数放到第i个位置。结束。   C代码 #includebits/stdc.h using namespace std; int a[100005], n; void selection_sort() {for (int i 0; i n-1; i) { int m i; //m: 记录a[i]~a[n-1]的最小数所在位置for (int j i1; j n; j) //找a[i]~a[n-1]的最小数if (a[j] a[m]) m j;swap(a[i], a[m]); //交换} } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);selection_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int n;public static void selection_sort() {for (int i 0; i n-1; i) { //找a[i]~a[n-1]的最小数int m i; //m: 记录a[i]~a[n-1]的最小数所在位置for (int j i1; j n; j)if (a[j] a[m])m j;int temp a[i]; a[i] a[m]; a[m] temp; //交换}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();selection_sort();for (int i 0; i n; i) System.out.print(a[i] );} } Python代码 def selection_sort():global a, nfor i in range(n-1):m ifor j in range(i1, n):if a[j] a[m]: m ja[i], a[m] a[m], a[i] #交换 n int(input()) a list(map(int, input().split())) selection_sort() for i in range(n): print(a[i], end )选择排序算法的计算量是多少找最小的数需要比较n-1次找第2小的必要比较n-2次…一共需要比较约 n 2 / 2 n^2/2 n2/2次把它的计算复杂度记为 O ( n 2 ) O(n^2) O(n2)。看代码也能分析出来有两重for循环分别循环约n次共循环 O ( n 2 ) O(n^2) O(n2)次。   上述代码提交到判题系统只能通过20%的测试。判题系统一般给一秒的执行时间计算机一秒约能计算1亿次。本题若测试 n 1 0 5 n10^5 n105个数选择排序需要计算 n 2 n^2 n2100亿次超时。   选择排序是一种“死脑筋”的算法它与原数列的特征无关不管原数列是不是有序都得计算 O ( n 2 ) O(n^2) O(n2)次。下一个“冒泡算法”就聪明得多如果第一轮找最大数时发现数列已经有序就停止不再做排序计算。   选择排序虽然低效但也有优点1简单易写2不占用额外的空间排序就在原来数列上操作。 2. 冒泡排序 冒泡排序Bubble sort也是一种简单直观的排序算法。它的原理和选择排序差不多   第一轮找到第1大的数放在第n个位置   第二轮找到第2大的数放在第n-1个位置   …   第n轮找到最小的数放到第1个位置。   不过与选择排序的简单粗暴相比冒泡排序用到了“冒泡”这个小技巧。以“第一轮找最大的数并放到第n个位置”为例操作是   1从第1个数a[0]开始比较a[0]和a[1]如果a[0]a[1]交换。这一步把前2个数中的大数放到了第2个位置。   2比较a[1]和a[2]如果a[1]a[2]交换。这一步把前3个数中的最大数放到了第3个位置。   …   依次比较相邻的两个数一直到最后的a[n-2]、a[n-1]就把最大的数放到了第n个位置。   这个过程形象地比喻为“冒泡”。   其他的数也用同样的方法处理一共做n轮。第i轮找第i大的数并放到第i个位置冒泡到a[i-1]就把第i大的数放到了第i个位置。 C代码 #includebits/stdc.h using namespace std; int a[100005], n; void bubble_sort() {for (int i 0; i n-1; i) {bool swapped false; // 优化如果某次比较没有发生交换说明已经有序结束for (int j 0; j n-i-1; j)if (a[j] a[j1]) {swap(a[j], a[j1]);swapped true;}if (!swapped) break; //这一轮没有发生交换说明已经有序结束} } int main() {cin n;for (int i 0; i n; i) scanf(%d, a[i]);bubble_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }冒泡算法的计算复杂度第5行和第7行的两重for循环计算复杂度为 O ( n 2 ) O(n^2) O(n2)。   冒泡算法可以做一点优化若两个相邻的数已经有序那么不用冒泡在第i轮求第i大的数时若一次冒泡都没有发生说明整个数列已经有序结束。代码第6行的swapped判断是否发生了冒泡。冒泡算法是“聪明”的排序算法。 Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int n;public static void bubble_sort() {boolean swapped;for (int i 0; i n-1; i) {swapped false;for (int j 0; j n-i-1; j)if (a[j] a[j1]) {int temp a[j]; a[j] a[j1]; a[j1] temp; //交换swapped true;}if (!swapped) break;}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();bubble_sort();for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def bubble_sort():global a, nfor i in range(n-1):swapped Falsefor j in range(n-i-1):if a[j] a[j1]:a[j], a[j1] a[j1], a[j]swapped Trueif not swapped: break n int(input()) a list(map(int, input().split())) bubble_sort() for i in range(n): print(a[i], end )3. 插入排序 插入排序Insertion sort是一种“动态”算法在一个有序数列上新增一个数x把它插入到有序数列中的合适位置使数列仍保持有序。   如何插简单的做法是从有序数列的最后一个数开始逐个与x比较若这个数比x大就继续往前找直到找到比x小的数把x插到它后面。   具体操作以{3,7,4,5,6,1,8,2}为例   1从第一个数a[0]开始。它构成了长度为1的有序数列{a[0]}。   2新增a[1]把它插到有序数列{a[0]}中。若a[1]≥a[0]完成。若a[1]a[0]把a[1]插到a[0]前面方法是先把a[0]挪到数列的第2个位置然后把a[1]放到数列第1个位置。得到新的有序数列{a[0], a[1]}。   3新增a[2]把它插到有序数列{a[0],a[1]}中。   …   下面是代码概况执行过程从a[1]开始遍历数组将当前的数作为key然后将它和前面的有序数列一一比较如果发现前一个数大于key则将它后移一位直到找到一个前面的数不再大于key就找到了key应该插入的位置将key插入到该位置即可。 C代码 #includebits/stdc.h using namespace std; int a[100005], n; void insertion_sort() {for (int i 1; i n; i) {int key a[i]; //记下a[i]准备把它插到前面合适的地方int j i - 1; //i前面的数已经有序把key插到合适位置while (j 0 a[j] key) { a[j1] a[j]; //如果key比a[j]小就把a[j]往后挪给key腾位置j--;}a[j1] key; //把key插到这里} } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);insertion_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }插入排序的计算复杂度取决于第5行的for循环和第8行的while循环这是两重循环各循环O(n)次总计算复杂度 O ( n 2 ) O(n^2) O(n2)。   插入排序是不是和冒泡排序一样“聪明”也就是说如果在一个有序的数列上运行插入排序算法需要计算多少次此时第8行的while的判断条件 a[j] key始终不成立while内的第9、10行不会执行而第12行实际上就是a[i]key没有任何变化。那么for、while这两重循环实际上变成了只有for一个循环一共计算O(n)次即结束。所以插入排序和冒泡排序一样“聪明”。 Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int n;public static void insertion_sort() {for (int i 1; i n; i) {int key a[i];int j i - 1;while (j 0 a[j] key) {a[j1] a[j];j--;}a[j1] key;}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();insertion_sort();for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def insertion_sort():global a, nfor i in range(1, n):key a[i]j i - 1while j 0 and a[j] key:a[j1] a[j]j - 1a[j1] key n int(input()) a list(map(int, input().split())) insertion_sort() for i in range(n): print(a[i], end )下一节的希尔排序是对插入排序的优化其核心思想是让数列尽量有序从而减少while循环。 4. 希尔排序 希尔排序Shell sort是一种基于插入排序的高效算法。   先给出代码可以看到shell_sort()仅仅是分多次做插入排序insertation_sort()。代码的关键处是称为“排序间距”的变量gap通过多轮gap操作减少了插入排序时的while循环的计算。 C代码 #includebits/stdc.h using namespace std; int a[100005], n; void insertion_sort(int gap) { //插入排序for (int i gap; i n; i){int key a[i];int j i-1;while(j gap-1 a[j-gap1] key){a[j1] a[j-gap1];j - gap; //测试计算量在这里统计 cnt}a[j1] key;} } void shell_sort() { for (int gap n/2; gap 0; gap / 2)insertion_sort(gap); } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);shell_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }以int a[]{8, 7, 6, 5, 4, 3, 2, 1}这8个数从小到大排序为例说明希尔排序的步骤。   1gap 8/2 4对间距为4的数排序。共有4组数的间距为4这四组数分别是{8, 4}、{7, 3}、{6, 2}、{5, 1}。分别在这4组数内部做插入排序。例如{8, 4}做插入排序结果是{4, 8}。在这一轮每组内的数做插入排序时都要进入代码第8行的while执行交换操作共执行4次。经过这一轮操作较大的数挪到了右边更靠近它们排序后的终止位置。   2gap 4/2 2对间距为2的数排序。共有2组数的间距为2分别是{4, 2, 8, 6}、{3, 1, 7, 5}。分别做插入排序例如{4, 2, 8, 6}做插入排序的结果是{2, 4, 6, 8}。在这一轮每组内的数做插入排序时有些不需要进入代码第8行的while。例如处理8时它前面的2、4已经排好序且更小8不用做什么操作。这就是上一轮gap4的排序操作带来的好处。   3gap 2/2 1对间距为1的数排序实际上gap1的希尔排序就是基本的插入排序。由于前2轮的操作到这一轮有很多数不用进入代码第8行的while例如{4, 6, 8}这3个数。即使进入了while也只需做极少的交换操作例如处理到{5}时它前面已经得到{1, 2, 3, 4, 6}那么{5}只需要插到{6}前面即可。   希尔排序在多大程度上改善了插入排序可以直接对比两个代码的计算量。定义一个全局变量cnt在insertion_sort()的while中累加cnt统计进入了多少次while。输入数列{8, 7, 6, 5, 4, 3, 2, 1}分别执行上一节的插入排序和这一节的希尔排序得插入排序cnt 28次希尔排序cnt 12。希尔排序显然好很多。   根据严格的算法分析希尔排序的计算复杂度约为 O ( n 1.5 ) O(n^{1.5}) O(n1.5)。当 n 1 0 5 n 10^5 n105时计算量约3000万次远远小于 O ( n 2 ) O(n^2) O(n2)的100亿次。   最后概况希尔排序的思路。希尔排序是一种基于插入排序的排序算法它将一个序列分成若干个子序列对每个子序列使用插入排序最后再对整个序列使用一次插入排序。函数shell_sort()使用gap对数组进行分组然后对每个子序列使用插入排序最后将整个序列使用插入排序。在插入排序的过程中每次将元素插入到已经排好序的序列中而这个已经排好序的序列是由前面的插入排序操作得到的每次操作都相当于将元素插入到一个较小的序列中因此可以更快地将元素插入到正确的位置上。 Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int n;public static void insertion_sort(int gap) {for (int i gap; i n; i){int key a[i];int j i-1;while(j gap-1 a[j-gap1] key){a[j1] a[j-gap1];j - gap;}a[j1] key;}}public static void shell_sort() {for (int gap n/2; gap 0; gap / 2)insertion_sort(gap);}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();shell_sort();for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def insertion_sort(gap):global a, nfor i in range(gap, n):key a[i]j i-1while j gap-1 and a[j-gap1] key:a[j1] a[j-gap1]j - gapa[j1] key def shell_sort(): for gap in range(n//2, 0, -1): insertion_sort(gap) n int(input()) a list(map(int, input().split())) shell_sort() for i in range(n): print(a[i], end )5. 计数排序 计数排序Counting Sort是基于哈希思想的一种排序算法它使用一个额外的数组来统计每个数出现的次数然后基于次数输出排序后的数组。   以数列a[] {5,2,7,3,4,3}为例说明计数排序的操作步骤。   1找到数列的最大值7建计数数组cnt[8]   2把数列中的每个数看成cnt[i]的下标i对应的cnt[i]计数。例如{5}对应cnt[5]1{2}对应cnt[2]12个{3}对应cnt[3]2等等。   3遍历cnt[]若cnt[i]k输出k次i。输出结果就是排序结果。   概况地说计数排序是一种非比较性的排序算法它利用元素的值作为键值确定元素在序列中出现的次数从而实现排序。 C代码 #includebits/stdc.h using namespace std; int a[100005], n; void counting_sort() {int i;int max a[0];for (i 1; i n; i) //找到最大值if (a[i] max) max a[i];int* cnt (int*) calloc(max1, sizeof(int)); //建数组cnt[]for (i 0; i n; i) cnt[a[i]]; //把a[i]放到对应的空间里i 0;for (int j 0; j max; j) //输出排序的结果while (cnt[j] 0){a[i] j;cnt[j]--;}free(cnt); } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);counting_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }该函数首先遍历数组找到数组中的最大值max然后创建计数数组cnt[]大小为 max1用于存储元素出现的次数。然后遍历数组把每个数的出现次数存储在计数数组中。最后遍历计数数组将数组中的元素按照次数从小到大依次输出从而得到一个有序数组。   计数排序的时间复杂度取决于第12行的for循环共循环max次计算量由max决定。   这使得计数排序的应用场景非常狭窄只适合“小而紧凑”的数列当所有的数值都不太大且均匀分布时计数排序才有好的效果。例如 n 1 0 5 n10^5 n105且 0 a [ i ] 1 0 5 0a[i]10^5 0a[i]105时效率极高可以在O(n)的时间内排序。   如果数列的数值很大就不适合用计数排序。此时需要创建极大的数组cnt[]不仅浪费空间而且计算时间极长。例如对{5, 1 0 9 10^9 109}排序虽然只有2个数却需要建长度为 1 0 9 10^9 1091G的数组且需要在代码第12行循环 1 0 9 10^9 109次。   代码提交到判题系统有40%的测试返回Memory Limit Exceeded说明代码对这40%的测试数据排序时使用的空间超出了内存限制。 Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int n;public static void counting_sort() {int i;int max a[0];for (i 1; i n; i)if (a[i] max) max a[i];int[] cnt new int[max1];for (i 0; i n; i) cnt[a[i]];i 0;for (int j 0; j max; j)while (cnt[j] 0){a[i] j;cnt[j]--;}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();counting_sort();for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def counting_sort():global a, nmax_num max(a)cnt [0] * (max_num1)for i in range(n): cnt[a[i]] 1i 0for j in range(max_num1):while cnt[j] 0:a[i] ji 1cnt[j] - 1 n int(input()) a list(map(int, input().split())) counting_sort() for i in range(n): print(a[i], end )6. 桶排序 桶排序Bucket sort是分治思想的应用它的要点是   1有k个桶把要排序的n个数尽量均匀分到每个桶中   2要求桶之间也是有序的即第i个桶内所有的数小于第i1个桶内所有的数   3在每个桶内部排序   4最后把所有的桶合起来就是排序的结果。   例如有100个数这些数的大小在1~100之间把它们分到10个桶中第1个桶放1~10内的数第2个桶放11~20内的数…等等。然后分别在每个桶内排序最后把所有的桶的数合起来输出就是排序的结果。   为什么分成多个桶然后在各桶内排序能提高效率这就是分治的威力下面做个简单分析。设有一个排序算法计算复杂度为 O ( n 2 ) O(n^2) O(n2)。把这n个数分到k个桶中每个桶装 n k \frac{n}{k} kn​个数且第i个桶的所有数小于第i1个桶的所有数。每个桶内排序的计算量是 n 2 k 2 \frac{n^2}{k^2} k2n2​k个桶的总计算量是 k × n 2 k 2 n 2 k k\times\frac{n^2}{k^2}\frac{n^2}{k} k×k2n2​kn2​。所以结论是利用k个桶进行分治能把原来 O ( n 2 ) O(n^2) O(n2)的计算量减少到 O ( n 2 k ) O(\frac{n^2}{k}) O(kn2​)优化了k倍。   不过桶排序并不是一个简单的算法它的性能取决于   1桶的数量k。要求k≤n极端情况下kn且一个桶内只有一个数那么总计算量就减少到了O(n)达到了最优的计算复杂度。但是太多的桶可能需要大量的存储空间所以需要设定一个合适的k。   2保证桶之间也是有序的即让第i个桶的所有数小于第i1个桶的所有数。简单的做法是按数字的大小分到每个桶里。例如数的大小在1~100之间设置k10个桶第1个桶放1~10内的数第2个桶放11~20内的数…等等。这样桶之间就有序了。但是这个简单的方法可能导致分布不均衡有些桶内数太少有些桶内数太多。   3快速地把n个数分开放到k个桶中。桶排序的总时间包括两部分把n个数分到k个桶的时间以及分别在k个桶内排序的时间。如果前者耗时太长也影响桶排序的性能。   4均匀地把n个数分开放到k个桶中。只有均匀分布才能发挥分治的威力。而极端情况下有可能所有的数都分到了1个桶里其他桶都是空的等于没有分桶。例如100个数第1个数是1其他数都在90~100之间把这100个数分到10个桶里第1个桶放1~10的数第2个桶放11~20内的数…第10个桶放90~100的数那么结果是第1个桶内有1个数第10个桶有99个数其他桶是空的。这样的分桶毫无意义。所以需要设计一个合理的分桶算法。   5桶内排序。在桶内的排序选用适合的算法会更高效。    由于上述原因桶排序不是一个简单的算法而是多个技术的综合所以本节没有给出代码。   上一节的计数排序可以看作桶排序的一个特例此时k远远大于n大部分桶是空的。 7. 基数排序 基数排序Radix sort是一种非比较性的排序算法它不是直接比较各数据的大小而是按照数据的位数依次进行排序。   “基数”指的是每个元素的进制位上的取值范围。例如对于十进制数每个元素的进制位上的取值范围是0~9因此基数为10对于二进制数每个元素的进制位上的取值范围是0~1因此基数为2。   有两种基数排序最低位优先法 (LSD, Least Significant Digit first)、最高位优先法 (MSD, Most Significant Digit first)。这里介绍较为简单的LSD基数排序。   LSD基数排序是一种反常识的排序方法它不是先比较高位再比较低位而是反过来先比较低位再比较高位。例如排序{5, 47, 23, 19, 17, 31}。   第1步先按个位的大小排序得到{31, 23, 05, 47, 17, 19}。   第2步再按十位的大小排序得到{05, 17, 19, 23, 31, 47}。其中5只有个位把它的十位补0。基数排序需要将所有待比较的数值统一为同样的数位长度如果某些数的位不够在前面补零即可。   因为所有数字中最大的只有两位所以只需2步就结束得到有序排列。   更特别的是上述操作并不是用比较的方法得到的而是用“哈希”的思路直接把数字放到对应的“桶”里。有10个桶分别标记为0~9。第1步按个位的数字放第2步按十位的数字放。下表中第2步得到的序列就是结果。 基数排序的复杂度n个数每个数有d位例如上面例子的17 ~ 47都是2位数每一位有k种可能十进制0~9共十种情况。复杂度是O(d*(nk))存储空间是O(nk)。例如对长度10000的字符串进行一次后缀排序n 10000d ≤ 5k 10复杂度d*(nk) ≈ 10000*5。而一次快排的复杂度nlogn ≈ 10000*13。   对比快速排序等排序方法基数排序在d比较小的情况下即所有的数字差不多大时是更好的方法。如果d比较大基数排序并不比快速排序更好。   LSD基数排序也可以看成桶排序的一个特例上面处理十进制的0~9基数为10就是10个桶。   当基数为其他数值时桶的数量也不同。例如   1把数字看成二进制每一位是0~1基数为2有2个桶。   2把数字看成二进制并且把它按每16位划分例如数字0xE1BE13DC34划分成00E1-BE13-DC3416位二进制范围0~65535基数为65536有65536个桶。这样做的好处是可以用位操作来处理速度稍快一些。   3对字符串排序一个字符串的一个字符是char型8位二进制范围0~255基数256就是256个桶。   下面给出例题的LSD基数排序的代码。函数radix_sort()首先遍历数组找到数组中的最大值max然后使用exp记录当前排序的位数从个位开始依次按照位数进行排序直到最高位。在每次排序中使用计数排序的方法将元素分配到桶中然后按照桶的顺序将元素重新排序。最后将排序后的数组复制到原数组中。 C代码 #includebits/stdc.h using namespace std; int a[100005], tmp[100005],n; void radix_sort() {int exp 1;int max a[0];for (int i 1; i n; i) //找出最大值目的是找到最大数有多少位if (a[i] max) max a[i];while (max / exp 0) { //从个位开始一直到最高位int bucket[10] {0};for (int i 0; i n; i) bucket[(a[i] / exp) % 10];for (int i 1; i 10; i) bucket[i] bucket[i-1];for (int i n-1; i 0; i--) {int k (a[i] / exp) % 10;tmp[bucket[k]-1] a[i];bucket[k]--;}for (int i 0; i n; i) a[i] tmp[i];exp * 10;} } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);radix_sort();for (int i 0; i n; i) printf(%d , a[i]);return 0; }Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int tmp[] new int[100005];static int n;public static void radix_sort() {int exp 1;int max a[0];for (int i 1; i n; i)if (a[i] max) max a[i];while (max / exp 0) {int bucket[] new int[10];for (int i 0; i n; i) bucket[(a[i] / exp) % 10];for (int i 1; i 10; i) bucket[i] bucket[i-1];for (int i n-1; i 0; i--) {int k (a[i] / exp) % 10;tmp[bucket[k]-1] a[i];bucket[k]--;}for (int i 0; i n; i) a[i] tmp[i];exp * 10;}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();radix_sort();for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def radix_sort():global a, n exp 1max_num max(a)tmp [0] * len(a) while max_num // exp 0:bucket [0] * 10for i in range(n): bucket[(a[i] // exp) % 10] 1for i in range(1, 10): bucket[i] bucket[i-1]for i in range(n-1, -1, -1):k (a[i] // exp) % 10tmp[bucket[k]-1] a[i]bucket[k] - 1for i in range(n): a[i] tmp[i]exp * 10 n int(input()) a list(map(int, input().split())) radix_sort() for i in range(n): print(a[i], end )基数排序的计算复杂度证明参考《算法导论》 8. 归并排序 归并排序Merge sort和下一小节的快速排序是分治法思想的应用极为精美。学习它们对于理解分治法、提高算法思维能力十分有帮助。   先思考一个问题如何用分治思想设计排序算法   根据分治法的分解、解决、合并三步骤具体思路如下   1分解。把原来无序的数列分成两部分对每个部分再继续分解成更小的两部分……在归并排序中只是简单地把数列分成两半。在快速排序中是把序列分成左右两部分左部分的元素都小于右部分的元素分解操作是快速排序的核心操作。   2解决。分解到最后不能再分解排序。   3合并。把每次分开的两个部分合并到一起。归并排序的核心操作是合并其过程类似于交换排序。快速排序并不需要合并操作因为在分解过程中左右部分已经是有序的。   下图的例子给出了归并排序的操作步骤。初始数列经过3趟归并之后得到一个从小到大的有序数列。   归并排序的主要操作   1分解。把初始序列分成长度相同的左右两个子序列然后把每个子序列再分成更小的两个子序列……直到子序列只包含1个数。这个过程用递归实现上图中的第一行是初始序列每个数是一个子序列可以看成递归到达的最底层。   2求解子问题对子序列排序。最底层的子序列只包含1个数其实不用排序。   3合并。归并两个有序的子序列这是归并排序的主要操作过程如下图。例如图 (1)中 i和j分别指向子序列{13, 94, 99}和{34, 56}的第1个数进行第一次比较发现a[i] a[j]把a[i]放到临时空间b[]中。总共经过四次比较得到b[] {13, 34 ,56, 94, 99}。   下面分析归并排序的计算复杂度。对n个数进行归并排序1需要logn趟归并2在每一趟归并中有很多次合并操作一共需要O(n)次比较。总计算复杂度是O(nlogn)。   空间复杂度由于需要一个临时的b[]存储结果所以空间复杂度是O(n)。 C代码 #includebits/stdc.h using namespace std; int a[100005], b[100005], n; void Merge(int L, int mid, int R){int iL, j mid1, t0;while(i mid j R){if(a[i] a[j]) b[t] a[j];else b[t]a[i];}//一个子序列中的数都处理完了另一个还没有把剩下的直接复制过来while(i mid) b[t]a[i];while(j R) b[t]a[j];for(i0; it; i) a[Li] b[i]; //把排好序的b[]复制回a[] } void Mergesort(int L, int R){if(LR){int mid (LR)/2; //平分成两个子序列Mergesort(L, mid);Mergesort(mid1, R);Merge(L, mid, R); //合并} } int main() {cinn;for (int i 0; i n; i) scanf(%d, a[i]);Mergesort(0,n-1);for (int i 0; i n; i) printf(%d , a[i]);return 0; }Java代码 import java.util.Scanner; public class Main {static int a[] new int[100005];static int b[] new int[100005];static int n;public static void Merge(int L, int mid, int R){int iL, j mid1, t0;while(i mid j R){if(a[i] a[j]) b[t] a[j];else b[t]a[i];}while(i mid) b[t]a[i];while(j R) b[t]a[j];for(i0; it; i) a[Li] b[i];}public static void Mergesort(int L, int R){if(LR){int mid (LR)/2;Mergesort(L, mid);Mergesort(mid1, R);Merge(L, mid, R);}}public static void main(String[] args) {Scanner scanner new Scanner(System.in);n scanner.nextInt();for (int i 0; i n; i) a[i] scanner.nextInt();Mergesort(0,n-1);for (int i 0; i n; i) System.out.print(a[i] );} } Python代码 def Merge(L, mid, R):global a, biLj mid1t0while(i mid and j R):if(a[i] a[j]):b[t] a[j]j 1else:b[t] a[i]i 1t 1while(i mid):b[t] a[i]i 1t 1while(j R):b[t] a[j]j 1t 1for i in range(t): a[Li] b[i] def Mergesort(L, R):if L R:mid (LR)//2Mergesort(L, mid)Mergesort(mid1, R)Merge(L, mid, R) n int(input()) a list(map(int, input().split())) b [0] * len(a) Mergesort(0,n-1) for i in range(n): print(a[i], end )9. 快速排序 上一节提到快速排序Quick sort和归并排序都是分治法的应用。   快速排序的思路是把序列分成左右两部分使得左边所有的数都比右边的数小递归这个过程直到不能再分为止。如何把序列分成左右两部分最简单的办法是设定两个临时空间X、Y和一个基准数t检查序列中所有的元素比t小的放在X中比t大的放在Y中。不过其实不用这么麻烦直接在原序列上操作就行了不需要使用临时空间X、Y。   直接在原序列上进行划分的方法有很多种下面的图示介绍了一种很容易操作的方法   下面分析复杂度。   每一次划分都把序列分成了左右两部分在这个过程中需要比较所有的元素有O(n)次。如果每次划分是对称的也就是说左右两部分的长度差不多那么一共需要划分O(logn)次。总复杂度O(nlogn)。   如果划分不是对称的左部分和右部分的数量差别很大那么复杂度会高一些。在极端情况下例如左部分只有1个数剩下的全部都在右部分那么最多可能划分n次总复杂度是 O ( n 2 ) O(n^2) O(n2)。所以快速排序的效率和数据本身有关。   不过一般情况下快速排序效率很高甚至比归并排序更好。读者可以观察到下面给出的快速排序的代码比归并排序的代码更简洁代码中的比较、交换、拷贝操作很少。 快速排序几乎是目前所有排序法中速度最快的方法。STL的sort()函数就是基于快速排序算法的并针对快速排序的缺点做了很多优化。 C代码 #includebits/stdc.h using namespace std; int a[100005],n; void qsort(int L,int R){int iL,jR;int keya[(LR)/2];while(ij) {while(a[i]key) i;while(a[j]key) j--;if(ij){swap(a[i],a[j]);i; j--;}}if(jL) qsort(L,j);if(iR) qsort(i,R); } int main(){cinn;for(int i0;in;i) scanf(%d,a[i]);qsort(0,n-1);for(int i0;in;i) printf(%d ,a[i]);return 0; }Java代码 import java.util.Scanner; public class Main {static int[] a new int[100005];static int n;public static void qsort(int L, int R) {int i L, j R;int key a[(L R) / 2];while (i j) {while (a[i] key) i;while (a[j] key) j--;if (i j) {int temp a[i];a[i] a[j];a[j] temp;i;j--;}}if (j L) qsort(L, j);if (i R) qsort(i, R);}public static void main(String[] args) {Scanner input new Scanner(System.in);n input.nextInt();for (int i 0; i n; i) a[i] input.nextInt();qsort(0, n - 1);for (int i 0; i n; i) System.out.print(a[i] );} }Python代码 def qsort(L, R):i, j L, Rkey a[(L R) // 2]while i j:while a[i] key: i 1while a[j] key: j - 1if i j:a[i], a[j] a[j], a[i]i 1j - 1if j L: qsort(L, j)if i R: qsort(i, R) n int(input()) a list(map(int, input().split())) qsort(0,n-1) for i in range(n): print(a[i], end )10. 堆排序 堆排序heap sort利用二叉堆的属性来排序。二叉堆是一棵二叉树如果是一棵最小堆那么树根是最小值。把树根取出后新的树根仍然是剩下的树上的最小值。首先把需要排序的n个数放进二叉堆然后依次取出树根就从小到大排好了序。由于把数放进二叉堆和取出二叉堆计算复杂度都是O(logn)的所以n个数的总复杂度是O(nlogn)的。   由于自己编程实现二叉堆比较麻烦这里直接使用优先队列。优先队列是用二叉堆实现的。 C代码 #includebits/stdc.h using namespace std; int main() {int n; cin n;priority_queueint, vectorint, greaterint pq; // 优先队列是小根堆for (int i 0; i n; i) {int a; cin a;pq.push(a); // 将输入的数插入小根堆}while (!pq.empty()) {cout pq.top() ; // 依次输出堆顶元素树根就是从小到大输出pq.pop(); // 弹出堆顶元素}return 0; }Java代码 import java.util.PriorityQueue; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner input new Scanner(System.in);int n input.nextInt();PriorityQueueInteger pq new PriorityQueueInteger();for (int i 0; i n; i) {int a input.nextInt();pq.offer(a);}while (!pq.isEmpty())System.out.print(pq.poll() );} }Python代码 import queue n int(input()) a list(map(int, input().split())) pq queue.PriorityQueue() for i in range(n): pq.put(a[i]) while not pq.empty(): print(pq.get(), end )
http://www.tj-hxxt.cn/news/231736.html

相关文章:

  • 西安市阎良区建设局网站产品推广运营的公司
  • 网站建设的中期检查表网站建设必须要服务器吗
  • 有哪些企业建设网站潮南最新消息今晚
  • 易无忧建站淘宝店铺运营推广
  • 网站建设制作周期wordpress后台编辑框 自定义按钮
  • 网站备案是怎么回事小米的企业网站建设思路
  • 蓝色风格的网站html5网站开发环境的搭建
  • 如何做网站拉动条网站开发方向学啥
  • 顶尖手机网站建设网站网页设计多少钱
  • 放心的网站设计制作网站正在建设html
  • 建设网站能赚钱电脑网站转换手机网站怎么做
  • 衡水做网站建设公司上海远丰电商网站建设公司怎么样
  • 网站设计网络推广杭州建设网双标化工地2022年
  • 业务推广网站wordpress页面修改
  • 学校网站源码html电子商务网站的后台管理系统
  • 太原网站维护安溪县住房和城乡建设网站
  • 做教育网站wordpress怎么换空间
  • 企业电子商务网站建设规划方案帝国+只做网站地图
  • 网站建设方案预计效果中国网站备案取消
  • 浏览网站内下载文件域名注册的网址
  • 大型门户网站建设功能小程序建站网站
  • 直播网站开发费建网站需成本多少钱
  • 网站可以制作ios用vue开发好看的官网
  • 网站图片上传代码基本型电商网站举例
  • 公司网站404wordpress安装出现eof
  • 湖南网站建设公司磐石网络商业网站策划书模板范文
  • 重庆高端网站seowordpress管理工具
  • 中国做网站最好的楼市最新消息:2023年房价走势
  • 企业网站内容建筑网校培训机构排名
  • 计生网站生育文明建设蓝色经典网站