算法学习之一:堆排序
2017-04-25 19:09
337 查看
堆的属性:
1. 它是一颗完全的二叉树
2. 每个结点大于或等于她的任意一个孩子。
堆排序:
堆排序:使用的是二叉堆,它是一颗完全的二叉树。如何将一个ArrayList中存储的数据用堆标示?
排列顺序如下:树根在位置0处(0是索引),它的2个孩子就在索引1和2处,类推==》对于位置在i处的结点,它的左孩子在2i+1处,右孩子在2i+2处,父亲在(i-1)/2处。
好了,理论知识具备了,我们就来举例子看看一个数组如何表示成堆:
先来推导第一层:a是第一个元素:索引 i=0,是根节点,左孩子(2i+1),索引是2*0+1=1,对应元素是b; 右孩子是(2i+2),索引是2*0+2=2,对应元素是c.
再来推导第二层: b做为父节点,i=1,左孩子(2i+1),索引是2*1+1=3,对应元素是d,右孩子是(2i+2),索引是2*1+2=4,对应元素是e.
……….
依此类推,就形成了一颗二叉树。
好,再来一个例子试试:
给定一个ArrayList, 数据是:62, 42, 59, 32, 39, 44, 13, 22, 29, 14, 33, 30, 17, 9
这个二叉树元素是怎么摆放的呢?
好了,ArrayList用堆表示就讲得差不多了,下面再来讲添加和删除结点。
1.添加一个新结点:(大家记住是从树的末尾添加,并且要保证根永远是最大的)
举例子:一个集合的数据:3, 5, 1, 19 ,11, 22 ,如何依次放入?首先堆是空的,先放3, 3就作为根节点。来个元素5,5就作为3的左孩子放置,放置好之后,拿5和它的父结点3比较,5大,调换位置,5在根节点,3跑到左孩子位置。再来一个元素1,结点末尾放置,然后拿1和父结点5比较,小于父结点,位置不变。再来一个元素19, 19也是放在末尾位置,然后也和父结点比较…….
按照以上方法插入,保证根元素最大。为什么这样做呢?因为当删除的时候,是从根结点删除,这样删除拿到的每个元素就是按照从大到小排列的。并且,堆在插入键值和删除根元素时,执行效率很高。
2.再来看删除根结点:删除根结点之后,末尾元素跑到根结点处,再依次比较,再重构这颗树,以保持堆的特征。
先看下怎么删除堆中的一个元素?1.从根元素62删除,将末尾元素9放在根结点处,然后拿9和左右孩子比较,哪个孩子大,哪个孩子就放在根结点处,调整顺序,保证根结点最大
2. 9现在在59的位置了,这时候,继续和左右孩子比较,如果顺序需要调整,就继续调整。一直比较到没有孩子或是值都大于孩子,就不比较了。真样做的目的是保证每个父节点是大于左右孩子的。
3 如果还要删除元素,重复1和2的步骤即可。
拿图演示:删除62
现在再来删除59,看看演示过程:
以上就是对排序的过程。
堆的具体实现:
现在设计一个类,来实现堆排序算法。所有有疑惑的地方或是这一步为什么这么做,都已经在代码中详细注释了,我就不单独再写文字说明。
public class Heap { private ArrayList<Integer> list = new ArrayList<>(); public Heap(int[] elements){ for(int i = 0;i<elements.length;i++){ addElemet(elements[i]); } } /** * 往堆中添加一个元素:从末尾添加,依次和父结点比较大小,保证根元素最大 * @param newElement */ public void addElemet(int newElement) { list.add(newElement);//往根节点添加 int currentIndex = list.size()-1;//默认最后一个元素的索引 //比较最后添加的元素和父结点的大小,直到到跟结点为止: while(currentIndex>0){ int parentIndex = (currentIndex-1)/2;//父索引的位置 if(list.get(currentIndex)>list.get(parentIndex)){ //孩子比父亲要大,需要调整顺序 int temp = list.get(parentIndex); list.set(parentIndex,list.get(currentIndex)); list.set(currentIndex,temp); currentIndex = parentIndex;//指针也要向上挪动一步 }else{ break; } } } /** * 删除结点,先从根结点删除,然后调整顺序 * @return: 返回删除的是哪个元素 */ public int removeElemet(){ if(list.size()==0) return -Integer.MAX_VALUE;// 如果堆为空,返回一个最大的负整数 int removeElement = list.get(0);//要删除的根元素 list.set(0,list.get(list.size()-1));//把最后一个元素放在根元素位置 list.remove(list.size()-1);//最后一个元素要删除掉 //然后比较根元素和孩子的大小并做相应调整: int currentIndex = 0; //一直要比较到最后一个元素 while(currentIndex<=list.size()-1){ int lefeChildIndex = 2*currentIndex+1; int rightChilIndex = 2*currentIndex+2; if(lefeChildIndex>=list.size()) break;//删掉根结点之后,只剩下一个,不需要再去比较 int maxIndex = lefeChildIndex;//把左孩子当作最大的 if(rightChilIndex<list.size()){//右边孩子比较大,将最大指针指向右孩子 if(list.get(maxIndex)<list.get(rightChilIndex)){ maxIndex = rightChilIndex; } } //比较父结点和maxIndex大小 if(list.get(currentIndex)<list.get(maxIndex)){ int temp = list.get(currentIndex); list.set(currentIndex,list.get(maxIndex)); list.set(maxIndex,temp); //指针往下移动: currentIndex = maxIndex; }else{ break; } } return removeElement; } public int size(){ return list.size(); } }
再来一个测试类:(直接就在android一个activity中做的测试,没有去单独建java工程)
private TextView mTv; int[] list = {2,15,12,5,8,55,33,62,7,10}; private Heap heap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main5); mTv = (TextView) findViewById(R.id.tv_show1); heap = new Heap(list); mTv.setText(getData()); } private String getData() { StringBuilder sb = new StringBuilder(""); for(int i =0;i<list.length;i++){ int ele = heap.removeElemet(); sb.append(ele+"---"); } return sb.toString(); }
可以看到打印结果是:62—55—33—15—12—10—8—7—5—2
以上就是整个堆的排序过程的学习,至于堆的时间复杂度,这里没有讲,后面如果有时间的话就补充,没有的话,大家可以在网上查找资料。
参考资料:java程序设计基础(进阶篇)。
相关文章推荐
- 【算法学习】堆排序(Heap Sorting)
- 安迪的找工作日志——算法基础学习之堆排序
- 【算法导论】学习笔记——第6章 堆排序
- 学习算法导论——堆排序
- 经典算法学习——堆排序
- 算法学习导论学习笔记-第6章 堆排序
- 有趣算法-堆排序学习整理
- 【算法学习】排序算法-堆排序
- 算法导论学习笔记 第6章 堆排序
- 【算法导论学习-013】堆排序(Heapsort)
- 算法设计和数据结构学习堆排序
- 算法学习笔记----第二部分:排序和顺序统计量----第6章、堆排序
- 算法学习笔记----第二部分:排序和顺序统计量----第6章、堆排序
- 算法学习 - 堆排序 ( HeapSort ) C++实现
- 数据结构与算法学习笔记——堆排序
- 堆排序(Heap Sort)算法学习
- 算法学习之堆排序(java实现)
- 算法学习之排序学习之堆排序和如何建堆
- 算法学习与代码实现5——堆排序
- [算法学习笔记]排序算法——堆排序