堆排序

排序 之 堆排序 归并排序

陌路散爱 提交于 2019-12-28 04:05:01
堆的概念 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子结点的值,称为大顶堆;或着每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。 堆排序 堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是对顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,就可以得到一个有序序列了。 有上述思想可知,堆排序的两个要点: 1.如何由一个无序序列构建成一个堆? 2.如何在输出堆顶元素后,调整剩余元素称为一个新的堆? /* 堆排序********************************** */ /* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义, */ /* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */ void HeapAdjust(SqList *L,int s,int m) { int temp,j; temp=L->r[s]; for(j=2*s;j<=m;j*=2) /* 沿关键字较大的孩子结点向下筛选 */ { if(j<m && L->r[j]<L->r[j+1]) ++j; /*

归并,快速,堆排序~

拈花ヽ惹草 提交于 2019-12-28 04:04:35
堆排序 堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。   (1)用大根堆排序的基本思想   ① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区   ② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key   ③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。   ……   直到无序区只有一个元素为止。   (2)大根堆排序算法的基本操作:   ① 初始化操作:将R[1..n]构造为初始堆;   ② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。   注意:   ①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。   ②用小根堆排序与利用大根堆类似

heapsort--堆排序简单实现

∥☆過路亽.° 提交于 2019-12-28 04:03:46
1,什么是二叉堆? 1),父节点总是大于等于(或者小于等于)子节点。 2),每个节点及其部分都是一个二叉堆。 3),他是一个完全二叉树。 2,堆排序。 1),调整堆,首先排序序列是一个物理上的顺序存储表,逻辑上的完全二叉树。调整为二叉堆的方式就是从最后一个非叶子节点(N/2-1)开始调整为满足二叉堆的性质。 2),堆排序。就是记录第一节点根节点的值,把最后一个替换第一值,重新调整新的堆步骤1),直到剩下最后一个元素。时间复杂度为O(N*logN)。 3),python简单实现。(python2.7) #!/usr/bin/env python# -*- coding:utf-8 -*- #the max heapdef MaxHeapAdjust(dist): length=len(dist) halflen=length/2-1 for index in range(halflen,-1,-1): leftindex=2*index rightindex=2*index+1 if leftindex>=length: break if dist[index]<dist[leftindex]: templ=dist[index] dist[index]=dist[leftindex] dist[leftindex]=templ #MaxHeapAdjust(dist) if

堆排序(heapsort)

谁说胖子不能爱 提交于 2019-12-28 04:03:31
堆数据结构是一种数组对象,它可以被视为一棵完全二叉树。时间复杂度为:O(n*logn),空间复杂度:O(1); 平均时间复杂度和最坏时间复杂度都为:O(n*logn),但堆排序是不稳定排序。 堆排序思路: 1.建立小堆:把堆看成一个完全二叉树,然后从这棵树的最后一个非叶子节点开始,比较它与它的左儿子与 右儿子的大小,若大于它的儿子,则与最小的交换,交换位置后然后继续与它现在位置的儿子比较;若小于它的儿子 ,则不用交换,直接跳到下一个非叶子节点,重复以上步骤。(注:是从最后一个非叶子节点到第一个非叶子节点的顺序) 2.输出排序的结果:将堆顶元素输出,然后再将堆的最后一个节点赋值给堆顶元素,删除最后一个堆元 素。然后将堆顶元素与它的儿子比较,若大于它的儿子,则与最小的交换,交换位置后继续与它现在位置的儿子比较; 若小于它的儿子则不用交换,继续输出堆顶元素,重复以上步骤。 建立小堆排序的示例代码: #include <stdio.h> void BuildHeap(int array[], int n) //建立一个小堆 { int mark; //mark作为标记变量 int i, j, k; for(i = n/2; i>=1; i--) //从最大的非叶子节点开始 { j = 2*i; while(j<=n) //开始比较它与儿子节点的大小,若大则交换位置并继续比较 { if(j

堆排序-HeapSort

冷暖自知 提交于 2019-12-28 04:03:22
堆排序是一种选择算法,他的时间复杂为O(nlogn)一、堆排序实现过程  (1)创建初始堆       初始化堆的时候是对所有的非叶子结点进行筛选。       最后一个非终端元素的下标是[n/2]向下取整,所以筛选只需要从第[n/2]向下取整个元素开始,从后往前进行调整。       比如,给定一个数组,首先根据该数组元素构造一个完全二叉树。       然后从最后一个非叶子结点开始,每次都是从父结点、左孩子、右孩子中进行比较交换,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。        void HeapAdjust(ElemType H[], int start, int end) { ElemType temp = H[start]; for(int i = 2*start + 1; i<=end; i*=2) { //因为假设根结点的序号为0而不是1,所以i结点左孩子和右孩子分别为2i+1和2i+2 if(i<end && H[i]<H[i+1])//左右孩子的比较 { ++i;//i为较大的记录的下标 } if(temp > H[i])//左右孩子中获胜者与父亲的比较 { break; } //将孩子结点上位,则以孩子结点的位置进行下一轮的筛选 H[start]= H[i]; start = i; } H[start]=

堆排序-HeapSort

给你一囗甜甜゛ 提交于 2019-12-28 04:03:04
1. 堆排序 与 快速排序 , 归并排序 一样都是时间复杂度为O(N*logN)的几种常见排序方法。学习堆排序前,先讲解下什么是数据结构中的二叉堆。 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树。 二叉堆满足二个特性: 1)父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。 2)每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。 当父结点的键值总是大于或等于任何一个子节点的键值时为 最大堆 。当父结点的键值总是小于或等于任何一个子节点的键值时为 最小堆 堆分为大顶堆和小顶堆,其中下图(1)中是大顶堆,(2)为小顶堆 2 . 堆排序的思想 利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。 其基本思想为(大顶堆): 1)将初始待排序关键字序列(R1,R2....Rn)构建成 大顶堆 ,此堆为初始的无序区; 2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区

python 实现堆和堆排序

人走茶凉 提交于 2019-12-28 04:02:47
""" 堆是一种完全二叉树,有最大堆和最小堆两种。 最大堆:对于每个非叶子结点V,V的值都比它的两个孩子结点大,称为最大堆特性(heap order property), 最大堆里面的根总是储存最大值,最小值储存在叶子结点。 最小堆:和最大堆相反,每个非叶子结点V,它的两个孩子的值都比V的值大。 """ # 实现最大堆 # 首先实现一个数组 class Array(object): def __init__(self, size=32): self._size = size self._items = [None] * size def __getitem__(self, index): return self._items[index] def __setitem__(self, index, value): self._items[index] = value def __len__(self): return self._size def clear(self, value = None): for i in range(self._size): self._items[i] = value def __iter__(self): for item in self._items: yield item # 用数组来实现堆。 # 因为堆是完全二叉树,舍某结点的下标为i, #

堆排序

天大地大妈咪最大 提交于 2019-12-28 04:02:36
此次分享的堆排序实现代码是没有使用递归的,在开始之前,要先补给几个知识。 知识补给1:完全二叉树 满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。 在上图中,只有(a)是满二叉树。 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。 (也就是说,相对比满二叉树而言,他可以缺少子节点,但是只能从最后按顺序缺) 在上图中,b、c、d都缺少子节点,但b是在最底层而且从最右边开始缺,所以b是完全二叉树。 而我们的推排序是一种特殊的完全二叉树!!!!! 知识补给2:二叉树的顺序储存方式 二叉树的存储方式(表示方式)有两种 链式存储方式 顺序存储方式(列表) 而堆排序用的是二叉树的顺序存储方式。 我们来看一下这颗完全二叉树里面存储的数据的下标规律: 它是根据列表从左到右的顺序存储到树里面的。这样我们就可以观察一下父节点和子节点的下标关系了 父节点和左孩子节点的关系(节点的值:下标) 9 - 8 : 0 - 1  8 - 6 : 1 - 3  6 - 2 : 3 - 7 用数学归纳法可以发现规律: i = 2i + 1 父节点和右孩子节点的关系(节点的值:下标) 9 - 7:0 - 2   8 - 5:1 - 4  7 - 1:2 - 6 用数学归纳法可以发现规律: i = 2i + 2 相同的

八大基本排序---堆排序、堆结构

别说谁变了你拦得住时间么 提交于 2019-12-28 04:02:18
堆排序很重要,但是更重要的是堆这个结构 堆结构 :实际上是一棵 完全二叉树 一个数组可以根据父结点、左子结点、右子结点的关系,脑补出一棵完全二叉树 算法 1 :一个数组变为大顶堆 heapInsert() 数组:2、1、3、6、0、4 (1)只有2的时候 (2) 2、1【认为完全二叉树的范围是0~1的,超过1就越界】 (3)2、1、3 3(下标2) 找到自己的父结点 :(2-1)/2=0 此时不满足大顶堆了,将2跟3交换 (4)2、1、3、6 6(下标3)找到它的父结点:(3-1)/2=1,发现6>1,将3位置上的数和1位置上的数交换 然后继续比较6所在的位置和它的父结点的大小 (5) 再看4位置:2、1、3、6、0 4位置的父结点是(4-1)/2=1,0(4位置)<3(1位置),继续往下 (6)再看5位置:2、1、3、6、0、4 这样每一步都会形成一个0~i的大顶堆,遍历完整个数组就形成了0~N-1的大顶堆 //一个数据插入已经是一个大顶堆的数组中,并且调整为新的大顶堆 public static void heapInsert(int[] arr,int index) { while (arr[index]>arr[(index-1)/2]) { swap(arr, index, (index-1)/2); index = (index-1)/2; } } public

堆和堆排序

ε祈祈猫儿з 提交于 2019-12-28 04:02:05
1. 堆的概念 堆是一种特殊的树,一个堆需要满足如下两个条件: 一个堆是一个完全二叉树; 堆中每个节点的值都必须大于等于或者小于等于其子树中的每个节点。 第一条,完全二叉树要求,除了最后一层,其它层的节点个数都是满的,并且最后一层的节点都靠左排列。 第二条,也等价于,每个节点的值大于等于或者小于等于其左右子节点的值。节点值大于等于其子树中每个节点值的堆称为 “大顶堆”,节点值小于等于其子树中每个节点值的堆称为 “小顶堆”。 上图中,第 1 个和第 2 个是大顶堆,第 3 个是小顶堆,第 4 个不是堆。而且,可以看到,对于同一组数据,我们可以构建多种不同形态的堆。 2. 堆的实现 之前我们知道,完全二叉树比较适合用数组来存储,这样非常节省空间,因为不需要额外的空间来存储左右子节点的指针,单纯通过下标我们就可以找到一个节点的左右子节点。 可以看到,下标为 \(i\) 的节点的左子节点下标为 \(2i\),右子节点下标为 \(2i+1\),而父节点下标就为 \(\frac{i}{2}\)。 2.1. 往堆中插入一个元素 往堆中插入一个元素后,我们需要继续保持堆满足它的两个特性。 如果我们将新插入的元素放到堆的最后,此时,这依旧还是一棵完全二叉树,但就是节点的大小关系不满足堆的要求。因此,我们需要对节点进行调整,使之满足堆的第二个特性,这个过程称为 堆化 (heapify)。 堆化非常简单