堆排序

10种排序算法总结

余生长醉 提交于 2020-01-03 05:17:10
10种排序算法总结 排序算法有很多,所以在特定情景中使用哪一种算法很重要。为了选择合适的算法,可以按照建议的顺序考虑以下标准: (1)执行时间 (2)存储空间 (3) 编程 工作 对于数据量较小的情形,(1)(2)差别不大,主要考虑(3);而对于数据量大的,(1)为首要。 主要排序法有: 一、冒泡(Bubble)排序——相邻交换 二、选择排序——每次最小/大排在相应的位置 三、插入排序——将下一个插入已排好的序列中 四、壳(Shell)排序——缩小增量 五、归并排序 六、快速排序 七、堆排序 八、拓扑排序 九、锦标赛排序 十、基数排序 一、冒泡(Bubble)排序 void BubbleSortArray() { for(int i=1;i<n;i++) { for(int j=0;i<n-i;j++) { if(a[j]>a[j+1])//比较交换相邻元素 { int temp; temp=a[j];           a[j]=a[j+1];            a[j+1]=temp; } } } } 效率 O(n²),适用于排序小列表。 二、选择排序 void SelectSortArray() { int min_index; for(int i=0;i<n-1;i++) { min_index=i; for(int j=i+1;j<n;j++)//每次扫描选择最小项

链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)

不打扰是莪最后的温柔 提交于 2020-01-03 05:01:37
这篇文章分析一下链表的各种排序方法。 以下排序算法的正确性都可以在LeetCode的 链表排序 这一题检测。本文用到的链表结构如下(排序算法都是传入链表头指针作为参数,返回排序后的头指针) struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; 插入排序( 算法中是直接交换节点,时间复杂度O(n^2),空间复杂度O(1) ) class Solution { public: ListNode *insertionSortList(ListNode *head) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. if(head == NULL || head->next == NULL)return head; ListNode *p = head->next, *pstart = new ListNode(0), *pend = head; pstart->next = head; //为了操作方便,添加一个头结点 while(p != NULL) {

面试准备(算法部分)

断了今生、忘了曾经 提交于 2020-01-03 04:58:40
No.1 冒泡排序 冒泡排序无疑是最为出名的排序算法之一,从序列的一端开始往另一端冒泡(你可以从左往右冒泡,也可以从右往左冒泡,看心情),依次比较相邻的两个数的大小(到底是比大还是比小也看你心情)。 冒泡排序动图演示 图解冒泡排序 以 [ 8,2,5,9,7 ] 这组数字来做示例,上图来战: 从左往右依次冒泡,将小的往右移动 冒泡排序1 首先比较第一个数和第二个数的大小,我们发现 2 比 8 要小,那么保持原位,不做改动。位置还是 8,2,5,9,7 。 指针往右移动一格,接着比较: 冒泡排序2 比较第二个数和第三个数的大小,发现 2 比 5 要小,所以位置交换,交换后数组更新为:[ 8,5,2,9,7 ]。 指针再往右移动一格,继续比较: 冒泡排序3 比较第三个数和第四个数的大小,发现 2 比 9 要小,所以位置交换,交换后数组更新为:[ 8,5,9,2,7 ] 同样,指针再往右移动,继续比较: 冒泡排序4 比较第 4 个数和第 5 个数的大小,发现 2 比 7 要小,所以位置交换,交换后数组更新为:[ 8,5,9,7,2 ] 下一步,指针再往右移动,发现已经到底了,则本轮冒泡结束,处于最右边的 2 就是已经排好序的数字。 通过这一轮不断的对比交换,数组中最小的数字移动到了最右边。 接下来继续第二轮冒泡: 冒泡排序5 冒泡排序6 冒泡排序7 由于右边的 2 已经是排好序的数字

排序算法合集(冒泡,选择,插入,堆排,快排)

孤者浪人 提交于 2020-01-01 14:57:17
1、冒泡排序 最初在学c语言时,老师就教的这个排序算法,原理比较简单: 从数组下标为0处开始遍历,相邻之间进行比较,若a[i]>a[i+1],则exchange(a[i],a[i+1]),当然也可以将小的往后传递,将此过程不断进行,那么最后数组就有序了。 要点:(1) 每遍历一遍,末尾就得到一个最大值(或最小值),那么接下来的遍历是不是每次都减少一个元素就好了,因为后边的已经排好序了啊。 (2) 遍历n-1遍就排好序了,因为最后一遍只剩一个元素了,它一定放那儿,所以最后一遍就不用遍历了。 当然如果数据小,又懒得优化,多进行几遍也一样可以排序的,比如这样: 1 for(int i=0;i<n;i++){ //遍历了n遍 2 3 for(int j=0;j<n-1;j++){ //每次比较当前元素和下一个元素,所以循环结束条件为n-1 4 5 { 6 7 if(a[j]>a[j+1]) 8 9 { int t=a[j]; 10 11 a[j]=a[j+1]; 12 13 a[j+1]=t; } 14 15 } 16 17 } 那么标准的冒泡排序,就是算法复杂度为n*(n-i)次也就是n^2如下: 1 for(int i=0;i<a.length-1;i++){ //遍历n-1遍就够了 2 3 for(int j=0;j<a.length-i-1;j++){ /

算法导论学习之——堆排序

血红的双手。 提交于 2019-12-31 02:36:34
在这一篇文章中,首先介绍一下堆的属性和性质。然后讲解一下建堆的过程,最后讲解堆排序。 1、堆的介绍 堆的物理存储结构就是一个一维的数组,其数据结构就是一个完全的二叉树。需要注意的是堆中的每个结点不需要后继指针,其父节点和左右孩子结点都可以通过计算得到。假设要计算结点i(i为数组的下标)的父节点和左右孩子结点,可以使用以下公式计算: 在此计算的都是结点的数组下标, 由于堆的数据结构是一个完全二叉树,设该完全二叉树有n个结点。则其内部结点下标分别为:1,2,。。。 故其叶子结点下标分别为: 堆高度:就是从根节点到最长叶子结点的距离。包含N个结点的堆高为: 其次是最大堆和最小堆问题,最大堆就是对任意的父节点的值总是大于或等于孩子节点的值,这样的话就能保证根节点保存堆中的最大元素。 即:A[parent(i)] >=A[i] (最大堆) 同样,最小堆就是对任意的父节点的值小于或等于孩子结点的值,这样就能保证根节点的值最小。 即:A[parent(i)] <=A[i] (最小堆) 一般情况下,最大堆用于堆排序中,最小堆用于优先队列中。 2、建堆 根据堆的性质,我们最终要得到堆的根节点是一个最值,因而,我们需要自底向上进行建堆,若自顶向下建堆的话则不能保证根节点是最值。在此仅讨论建立最大堆的情况。 首先我们可以认为每个叶子结点已经是一个最大堆,然后从最末一个非叶子结点 开始进行对调整

算法导论-堆排序

北慕城南 提交于 2019-12-31 02:36:19
堆排序的时间复杂度是 ,具有空间原址性,即任何时候都只需要常数个额外的元素空间存储临时数据。 一、堆 二叉堆是一个数组,可看成一个近似的完全二叉树,树上的每个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左到右填充。 二叉堆可以分为两种形式:最大堆和最小堆。在最大堆中除根节点外所有结点i都要满足: ,即某个结点的值至多与其父结点一样大。在最小堆中除根节点外所有 结点i都要满足: 。 说明: 堆排序中,我们使用最大堆,最小堆通常用于构造优先队列。 二、维护堆的性质 函数MAX-HEAPIFY的输入为一个数组A和下标i,假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,通过让A[i]的值在最大堆中逐级下降,从而使得以下标i为根结点的子树为最大堆。 函数MAX-HEAPIFY的时间代价包括:调整A[i]、A[LEFT[i]]和A[RIGHT[i]]关系的时间代价 ,加上以一颗i的一个孩子为根结点的子树上运行MAX-HEAPIFY的时间代价(假设递归调用会发生)。 下面首先证明每个子树的大小至多为2n/3。 证明: 设堆的高度为h,最后一层结点个数为m,则整个堆的结点总数为: 。 根结点的左子树结点总数为: , 根结点的右子树结点总数为: ,其中 。 当最底层恰好半满的时候, ,则 , 。 解出: , 。 因此,每个子树的大小至多为2n/3

算法导论:堆排序

拜拜、爱过 提交于 2019-12-31 02:36:09
堆 堆是一个数组,它可以被看成一个近似的完全二叉树,树上的每一个结点对应数组中的一个元素。除去最底层外,该树是完全充满的,而且从左到右填充。 用数组A表示堆,从数组第1个元素开始,数组中第i(1<=i <=n)个元素,其父结点,左孩子,右孩子的下标如下 // 父结点 public int parent( int i){ return i/2; } // 左孩子 public int left(int i){ return 2*i; } // 右孩子 public int right(int i){ return 2*i+1; } 当数组起始下标是0的时候,其父结点,左右孩子结点如下 // 父结点 public int parent( int i){ return (i-1)/2; } // 左孩子 public int left(int i){ return 2*i+1; } // 右孩子 public int right(int i){ return 2*i+2; } 堆可以分为大顶堆和小顶堆 大顶堆:结点 i 的值 都大于其左右孩子结点的值 小顶堆:结点 i 的值 都小于其左右孩子结点的值 二叉树的形式与数组形式表达堆之间元素的关系 练习1,高度为h的堆,元素最少和最多是多少? 最多:这个完全二叉树第h层元素填满:2^h - 1 最少:第h-1层填满,第h层只有一个元素:2^

堆排序(摘自算法导论)

筅森魡賤 提交于 2019-12-31 02:35:58
(二叉)堆是一个数组,他可以被看成一个近似的完全二叉树。树上的每一个节点对应数组中的一个元素,除了最底层之外,该树是完全填满的,而且是从左向右填充。表示堆的数组A包括两个属性,A.length给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。也就是说,虽然A[1..A.length]可能都有数据,但只有A[1..A.heap-size]中存放的是堆的有效元素。0<=A.heap-size<=A.length。树的根节点是A[1],这样给定一个节点的下标i,我们很容易计算得到它的父节点、左孩子和右孩子的下标。 PARENT(i) return[i/2] LEFT(i) return 2i RIGHT(i) return 2i+1 二叉堆可以分为两种形式:最大堆和最小堆。 在最大堆中,最大堆是指除了根节点之外的所有节点i都要满足 A[PARENT(i)]>=A[i] 也就是说,某个节点的最大值至多与根节点一样大。因此,堆中最大的元素存在根节点中;并且,在任一子树中,该子树所包含的所有节点的值都不大于该子树根节点的值。最小堆的组织方式正好相反 最小堆性质市值除了根节点之外的所有节点i都有 A[PARENT[i]]<=A[i] 在堆排序算法中,我们使用的是最大堆,最小堆通常用于构造优先队列。 如果把堆看成一棵树

堆排序原理

不打扰是莪最后的温柔 提交于 2019-12-30 04:25:11
1、什么是堆? 堆是一种 非线性结构 ,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被看作一个完全二叉树,通俗来讲 堆其实就是利用完全二叉树的结构来维护的一维数组 按照堆的特点可以把堆分为 大顶堆 和 小顶堆 大顶堆:每个结点的值都 大于 或 等于 其左右孩子结点的值 小顶堆:每个结点的值都 小于 或 等于 其左右孩子结点的值 (堆的这种特性非常的有用,堆常常被当做优先队列使用,因为可以快速的访问到“最重要”的元素) 2、堆的特点(数组实现) 我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子 我们用简单的公式来描述一下堆的定义就是:(读者可以对照上图的数组来理解下面两个公式) 大顶堆: arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 小顶堆: arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] 3、堆和普通树的区别 内存占用: 普通树占用的内存空间比它们存储的数据要多。你必须为节点对象以及左/右子节点指针分配额外的内存。堆仅仅使用数组,且不使用指针 (可以使用普通树来模拟堆,但空间浪费比较大,不太建议这么做) 平衡 : 二叉搜索树必须是“平衡”的情况下,其大部分操作的复杂度才能达到 O(nlog2n) 。你可以按任意顺序位置插入/删除数据,或者使用 AVL 树或者红黑树

快速排序算法原理及实现(单轴快速排序、三向切分快速排序、双轴快速排序)

情到浓时终转凉″ 提交于 2019-12-28 04:10:46
1. 工作原理(定义)   快速排序(Quicksort)是对冒泡排序的一种改进。(分治法策略)   快速排序的基本思想是在待排序的n个记录中任取一个记录(通常取第一个记录)作为基准,把该记录放入适当位置后,数据序列被此记录划分成两部分,分别是比基准小和比基准大的记录;然后再对基准两边的序列用同样的策略,分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。    2. 算法步骤   实现方法两种方法:挖坑法和指针交换法   基准的选择三种方法:1.选择最左边记录 2.随机选择 3.选择平均值 2.1 挖坑法 设置两个变量i、j,排序开始的时候:i=0,j=N-1; 以第一个数组元素作为关键数据,赋值给key,即key=A[0]; 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换; 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换; 重复第3、4步,直到i=j;   【3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束】。