博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
堆排序的简单实现
阅读量:6847 次
发布时间:2019-06-26

本文共 5435 字,大约阅读时间需要 18 分钟。

堆排序是排序的一种,一般有大根对和小根堆之说,大根对,根节点的值比左右子树的根节点的值要大。建堆我们一般是一个完全二叉树。堆排序一般面向数据量比较大的时候,数据量比较小的时候,不适合使用堆排序,比如有种情况就是topN算法的实现,一般都是借助于一个大根对来实现,扫描海量数据,把海量数据中的把最大的前N个数据放到堆中。下面实现的时候,为了简单起见,使用的数组存储二叉树,也就是一个顺序树,这个例子不仅是回顾了堆排序的内容,还回顾了二叉树的一些相关操作。

完全二叉树的一些性质,根节点root为1,总的节点的个数为n,则第一个非叶节点是第[n/2]个节点。在顺序书中,节点i的左孩子应当是2*i,右孩子为2*i+1。

堆排序,首先要做的是先建一个初始堆,假设待排序的数据是10个,那么存储二叉树的就是用一个arr[10]的数组

(1)初始堆,这里是大根堆。从[n/2]的位置开始按照大根对的原则开始调整,一直调整到根节点。于是便得到了初始大根堆

(2)大根堆顶端的根节点一定是最大的元素。接下来我们通过n-1次进行排序。i = 0; i < n-1,每次先把root和arr[n-i+1]进行交换,然后把arr[n-i+1]从书上面断开,然后从上往下进行调整。直接看代码。

1 /** 2      * 按照大(小)根堆的规则,从上到下来调整堆  递归实现 3      * @param i        当前子树根节点在顺序树中的数组索引 4      * @param heap    表示堆的二叉树 其实就是一个顺序数组 5      * @param end    标志这个二叉树的最后一个节点的索引位置 6      */ 7     public static void upToDown(int i, int[] heap, int end){ 8         int left = i*2+1;            //左孩子的索引 9         int right = left + 1;        //右孩子的索引10         11         if(right <= end)// 左右孩子都不空12         {13             int pos = heap[left]>heap[right]?left:right;14             if(heap[pos] > heap[i])15             {16                 int tmp = heap[i];17                 heap[i] = heap[pos];18                 heap[pos] = tmp;19             }20             upToDown(left, heap, end);21             upToDown(right, heap, end);22         }else{23             if(left > end)24                 return;25             else{26                 if(heap[left] > heap[i])27                 {28                     int tmp = heap[i];29                     heap[i] = heap[left];30                     heap[left] = tmp;31                 }32                 upToDown(left, heap, end);33             }34         }35     }

接着,创建初始堆:

1 /**2      * 穿件初始堆3      * @param heap    堆对应的存储数组4      */5     public static void createHeap(int[] heap) {6         for(int i = heap.length/2-1; i >=0; i--)7             upToDown(i, heap, heap.length - 1);8     }

进行堆排序:

1 /* 2      * 使用数组简单的模拟堆排序 3      */ 4     public static void heapSort(int[] heap){ 5          6         for(int i = 0; i < heap.length - 1; i++) 7         { 8             int tmp = heap[0]; 9             int end = heap.length-i-1;10             heap[0] = heap[end];11             heap[end] = tmp;12             upToDown(0, heap, end-1);14         }15     }

经过上面的排序,对中的元素就是有序的了,然后按照顺序疏忽堆数组即可。

下面的操作都是和树相关的操作,主要包括求书的节点数,求树的高度,树的递归遍历和非递归遍历。最后是画一个简图作为例子。

(1)求树的叶节点的个数,思路:左子树节点数+右子树节点数+1.

1 /** 2      * 递归求树的高度 3      * @param heap    存储树的数组 4      * @param root    根节点 5      * @return        返回树的高度。 6      */ 7     public static int getNode(int[] heap, int root) 8     {
//递归求树的节点个数(左子树节点个数+右子树节点个数+根节点个数) 9 int left = root*2+1;10 int right = left +1;11 int end = heap.length - 1; //当前树的最后一个元素的索引12 13 if(root > end)14 return 0;15 if(left>end && right >end)16 return 1;17 return getNode(heap, left) + getNode(heap, right) + 1;18 }

(2)求树的高度,思路:max(左子树的高度,右子树的高度)+1

1 public static int getHeight(int[] heap, int root) 2     {
// 左子树 和 右子树中 高度较大的一个 再加1 3 int left = root*2+1; 4 int right = left +1; 5 int end = heap.length - 1; 6 7 8 if(root > end) return 0; 9 10 if(left>end && right >end)11 return 1;12 13 return Math.max(getHeight(heap, left), getHeight(heap, right)) + 1;14 }

(3)树的递归遍历,先根序,中根序,后根序便利都很相似,这里就只写一下先根序递归遍历的代码

1 public static void preTransverse(int[] heap, int i){ 2         int left = i*2+1; 3         int right = left +1; 4          5         if (i > heap.length - 1) 6             return; 7         System.out.print(heap[i]+","); 8         preTransverse(heap, left); 9         preTransverse(heap, right);10     }

preTransverse(heap, left)和preTransverse(heap, right)的放置的位置,主要决定了是先中后的遍历顺序。

  非递归,遍历一个二叉树,这里面就要使用栈数据结构。当然,非递归的遍历,也可以分为先序、中序和后序,思路相同,这里这记录借助栈的非递归先序遍历二叉树。树结构其实就是图的一种特例,而二叉树无疑又是一种更为特别的图,二叉树的先序遍历其实就相当于是图图的深度优先DFS遍历。非递归的二叉树线序遍历的思想很简单:

(1)当前节点root是否为空,不空的话访问之,并将该节点入栈,并取得当前节点的左孩子p = p*2+1,顺序存储二叉树,二叉树的根节点索引为0

(2)若当前访问的节点是一个空节点,那么弹出栈顶元素p = stack[--cn],并获取其右孩子,p = p*2+2。

(3)重复循环(1)(2)知道访问玩所有的节点,或者栈为空

1 /** 2      * 非递归现需便利一个二叉树  使用栈 3      * @param heap 4      */ 5     public static void prePrint(int[] heap) 6     { 7         int height = getHeight(heap, 0); 8         int[] stack = new int[height]; 9         int p = 0;10         int cn = 0;11         int end = heap.length - 1;12         13         while(p<=end || cn!=0){14             if(p <= end)15             {16                 System.out.print(heap[p]+",");17                 stack[cn++] = p;    // lft child18                 p = p*2+1;19             }20             else{21                 p = stack[--cn];22                 p = p*2+2;    //right child23             }24         }25     }

借助队列,分层遍历二叉树。思想也很简单:

(1)首先根节点入队

(2)队列出队的时候就访问之,并且把被访问的这个元素的孩子节点一次入队

(3)重复上面两部操作,知道队列为空的时候结束。

1 // 非递归  分层便利 借助于队列 2     public static void broadTranverse(int[] heap) 3     { 4         LinkedList
que = new LinkedList
(); 5 int end = heap.length - 1; 6 7 que.offer(0); 8 while(que.isEmpty()==false) 9 {10 int p = que.poll();11 System.out.print(heap[p]+",");12 int left = p*2+1;13 int right = left +1;14 if(left <= end) que.offer(left);15 if(right <=end) que.offer(right);16 }17 }

 

转载于:https://www.cnblogs.com/OliverZhang/p/6580713.html

你可能感兴趣的文章
阿里云亮相2019联通合作伙伴大会,边缘计算等3款云产品助力5G时代产业数字化转型...
查看>>
dubbo源码分析-服务端发布流程-笔记
查看>>
阿里云发布Apsara SA系列混合云存储阵列
查看>>
GoJS教程:链接模版
查看>>
QListWidget方式显示缩略图
查看>>
金三银四:蚂蚁金服JAVA后端面试题及答案之二面
查看>>
Ubuntu 外网不通解决方案
查看>>
OSChina 周六乱弹 —— 历史总是惊人的相似
查看>>
MySQL 大小写
查看>>
div块上下左右居中
查看>>
eclipse远程debug tomcat
查看>>
CentOs6.5基本环境配置(六):Maven配置
查看>>
Python 创建Django项目
查看>>
JS获取当前项目的根路径
查看>>
操作系统引导区代码
查看>>
程序员有五种错误不应犯
查看>>
无线认证知识点
查看>>
基于python的REST框架eve测试与mongodb的数据操作
查看>>
epoll模型的理解封装与应用
查看>>
Lync 2013部署图片赏析-证书服务安装配置
查看>>