图解堆排序,带你彻底了解清楚!

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"写在前面:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"大家好,我是时光。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"今天给大家带来的是排序算法中的堆排序,这种排序跟二叉树相关。我采用图解方式讲解,争取写透彻。话不多说,开始!","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"思维导图:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/3672a13f483e65c17c9b97f926f7cc26.png","alt":"堆排序导图","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"堆排序导图","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1,堆排序概念","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆排序","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(Heapsort)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相关概念:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.1,二叉树","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/51/51cd55cc60aab46dc095975d1b497b04.png","alt":"二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"二叉树","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"特征:每个节点最多只有2个子节点(不存在度大于2的节点)","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.2,满二叉树","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b7a3823ab5d08f86b05970848f3c7ea3.png","alt":"满二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"满二叉树","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"满二叉树:叶子节点全部都在最底层;除叶子节点外,每个节点都有左右孩子;","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.3,完全二叉树","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4c4f3f8f299314ff1bea892fe76bad7f.png","alt":"完全二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"完全二叉树","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"完全二叉树:叶子节点全部都在最底的两层;最后一层只缺少右边的叶子节点,左边一定有叶子节点;除了最后一层,其他层的节点个数均达到最大值;","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.4,最大堆和最小堆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最大堆:堆顶是整个堆中","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"最大元素","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最小堆:堆顶是整个堆中","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"最小元素","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2,算法思路","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先搞清楚这个堆排序思想,先把逻辑搞清楚,不着急写代码。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们首先有一个无序数组,比方说","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"int[] arr={4,2,8,0,5,7,1,3,9};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然用到堆,肯定先要将数组构建成二叉堆。需要对数组从小到大排序,则构建成最大堆;需要对数组从小到大排序,则构建成最小堆。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2.1,第一个步骤,初始化堆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先来看看数组是如何存储二叉树的","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2b/2b3992fe22f3ac7ed0b9288b6f9e3474.png","alt":"","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"注意:这里考虑的当前节点,必须是完全二叉树的","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"非叶子节点","attrs":{}},{"type":"text","text":"。并且节点的左孩子和右孩子必须小于数组长度,所以后面需要添加限制条件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"看到上图中的公式,我们明白了数组是可以存储完全二叉树,同时保留节点之间的关系。以上述数组为例","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e0/e0aeba5870f1c54f466cb3310f0ee62d.png","alt":"数组存储完全二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"数组存储完全二叉树","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那么存储好之后,如何将二叉树构建成二叉堆呢?继续往下看","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/2427bf59c00d437254806711a993c34f.png","alt":"完全二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"完全二叉树","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"以这个图为例,我们以最大堆举例,若要构建二叉堆,则A要比B和C都大,B要比D和E都大,C要比F和G都大。也就是说父节点要比子节点都大才行。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3c/3c5f5c29c37421afb624bc0dd6297610.png","alt":"记录数组下标的二叉树","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"记录数组下标的二叉树","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"在这个图中,明显不满足最大堆的要求。我们先拿序号为3,7,8的三个节点来研究,i=3的节点比i=7和i=8的节点都小,所以需要交换位置。注意此图是从0开始,也就是模拟数组下标从0开始。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"怎么交换呢?很简单。我们看节点0,设为当前节点","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",那么它的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lchild=index*2+1","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",它的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rchild=index*2+2","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";注意下标从0开始。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//初始化堆\npublic static void HeapAdjust(int[] arr,int index,int len){\n        //先保存当前节点的下标\n        int max = index;\n        //保存左右孩子数组下标\n        int lchild = index*2+1;\n        int rchild = index*2+2;\n        //开始调整,左右孩子下标必须小于len,也就是确保数组不会越界\n        if(lchild arr[max]){\n            max=lchild;\n        }\n        if(rchild arr[max]){\n            max=rchild;\n        }\n        //若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值\n        if(max!=index){\n            int temp=arr[index];\n            arr[index]=arr[max];\n            arr[max]=temp;\n            //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆\n            HeapAdjust(arr,max,len);\n        }\n    }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"上面代码很好理解,中间的两个if语句就是交换节点索引值,只要有一个孩子节点大于","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",就需要进行交换。若父节点","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"比两个孩子节点都大,那么就不需要交换了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"最后面的if语句是交换节点值,我们知道,只要","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"与","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lchild","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rchild","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"有交换,则","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"一定不等于","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"max","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"了!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那为什么最后的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"if","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"语句里面还要加上一个递归写法呢?我们举个例子就明白了,还是以上述数组为例,先看看一轮交换之后的样子:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第一次交换,0与9交换,此时Index=9;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be2ed9d975beb97adff3774147fe17c6.png","alt":"0与9交换","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"0与9交换","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第二次交换,8已经比7和1都大了,此时不需要交换;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第三次交换,2与9交换,此时","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=9;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b8/b840da4fa7151a27802e47909d399666.png","alt":"2与9交换","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"2与9交换","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第四次交换,4与9交换,此时","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=9,第一轮交换到此结束。","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f6/f6c23a6d4bd4af6a34aaa1004d17009f.png","alt":"4与9交换","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"4与9交换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"一轮结束后,有小伙伴儿已经发现问题了,虽然9成功的成为最大堆的堆顶元素,但是下面的其他元素并不满足最大堆的要求,比如说左下角的元素2,元素3,元素0等这种二叉树就不满足,元素4,元素2,元素5也不满足。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"因此在交换节点值这个步骤里,我们需要进行递归操作,交换完之后再次对","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"进行堆调整:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值\nif(max!=index){\n    int temp=arr[index];\n    arr[index]=arr[max];\n    arr[max]=temp;\n    //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆\n    HeapAdjust(arr,max,len);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"堆排序的测试:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//堆排序\n    public static int[] HeapSort(int[] arr){\n        int len=arr.length;\n        /**\n         * 第一步,初始化堆,最大堆,从小到大\n         * i从完全二叉树的第一个非叶子节点开始,也就是len/2-1开始(数组下标从0开始)\n         */\n        for(int i=len/2-1;i>=0;i--){\n            HeapAdjust(arr,i,len);\n        }\n        //打印堆顶元素,应该为最大元素9\n        System.out.println(arr[0]);\n        return arr;\n    }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"上述代码就是从完全二叉树的第一个非叶子节点开始调换,还顺便打印堆顶元素,此时应为9;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"至此,第一个步骤,初始化堆完成,最后的结果应该为下图:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f8/f8be4cb384050d77f18667e5d1d4796e.png","alt":"初始化堆结束","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"初始化堆结束","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2.2,第二个步骤,堆排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆排序的过程就是将堆顶元素(最大值或者最小值)与二叉堆的最末尾叶子节点进行调换,不停的调换,直到二叉堆的顺序变成从小到大或者从大到小,也就实现了我们的目的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们这里以最大堆的堆顶元素(最大元素)为例,最后调换的结果就是从小到大排序的结果。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第一次交换,我们直接将元素9与元素0交换,此时堆顶元素为0,设当前节点","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=0;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a223543ae4c743741760df8bb55e7470.png","alt":"0与9交换","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"0与9交换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"这时,我们需要将剩下的元素(排除元素9)进行堆排序,直到下面这个结果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0a/0a9b0d3f8272d85eba25685cbcf8e746.png","alt":"除元素9以外剩下的元素排序","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"除元素9以外剩下的元素排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"代码:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 第二步,交换堆顶(最大)元素和二叉堆的最后一个叶子节点元素。目的是交换元素\n * i从二叉堆的最后一个叶子节点元素开始,也就是len-1开始(数组下标从0开始)\n */\nfor(int i=len-1;i>=0;i--){\n    int temp=arr[i];\n    arr[i]=arr[0];\n    arr[0]=temp;\n    //交换完之后需要重新调整二叉堆,从头开始调整,此时Index=0\n    HeapAdjust(arr,0,i);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"注意:这里有个小细节问题,前面我们写的初始化堆方法有三个参数,分别是数组","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",当前节点","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"以及数组长度","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"len","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"HeapAdjust(int[] arr,int index,int len)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那么,为何不直接传入两个参数即可,数组长度直接用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr.length","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"表示不就行了吗?初始化堆的时候是可以,但是后面在交换堆顶元素和末尾的叶子节点时,在对剩下的元素进行排序时,此时的数组长度可不是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr.length","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"了,应该是变化的参数","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"i","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",因为交换之后的元素(比如9)就不计入堆中排序了,所以需要3个参数。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"我们进行第二次交换,我们直接将元素8与元素2交换,此时堆顶元素为2,设当前节点","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=2;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e5/e51aa1aab63f70c8a589aecb087be57f.png","alt":"8与2交换","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"8与2交换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"这时,我们需要将剩下的元素(排除元素9和8)进行堆排序,直到下面这个结果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7f7a8d2a714a44cbcf6f421f00b75470.png","alt":"除元素9和8以外剩下的元素排序","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"除元素9和8以外剩下的元素排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"到这个时候,我们再重复上述步骤,不断调换堆顶和最末尾的节点元素即可,再不断地对剩下的元素进行排序,最后就能得到从小到大排序好的堆了,如下图所示,这就是我们想要的结果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fd/fd8fbbbbc0bfbaba6d6b65cbb8b2b560.png","alt":"最终排序结果:从小到大","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"最终排序结果:从小到大","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"3,完整代码","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.Arrays;\n\npublic class Head_Sort {\n    public static void main(String[] args) {\n        int[] arr={4,2,8,0,5,7,1,3,9};\n        System.out.println(\"排序前:\"+Arrays.toString(arr));\n        System.out.println(\"排序后:\"+Arrays.toString(HeapSort(arr)));\n    }\n\n    //堆排序\n    public static int[] HeapSort(int[] arr){\n        int len=arr.length;\n        /**\n         * 第一步,初始化堆,最大堆,从小到大。目的是对元素排序\n         * i从完全二叉树的第一个非叶子节点开始,也就是len/2-1开始(数组下标从0开始)\n         */\n        for(int i=len/2-1;i>=0;i--){\n            HeapAdjust(arr,i,len);\n        }\n        /**\n         * 第二步,交换堆顶(最大)元素和二叉堆的最后一个叶子节点元素。目的是交换元素\n         * i从二叉堆的最后一个叶子节点元素开始,也就是len-1开始(数组下标从0开始)\n         */\n        for(int i=len-1;i>=0;i--){\n            int temp=arr[i];\n            arr[i]=arr[0];\n            arr[0]=temp;\n            //交换完之后需要重新调整二叉堆,从头开始调整,此时Index=0\n            HeapAdjust(arr,0,i);\n        }\n        return arr;\n    }\n\n    /**\n     *初始化堆\n     * @param arr 待调整的二叉树(数组)\n     * @param index 待调整的节点下标,二叉树的第一个非叶子节点\n     * @param len 待调整的数组长度\n     */\n    public static void HeapAdjust(int[] arr,int index,int len){\n        //先保存当前节点的下标\n        int max = index;\n        //保存左右孩子数组下标\n        int lchild = index*2+1;\n        int rchild = index*2+2;\n        //开始调整,左右孩子下标必须小于len,也就是确保index必须是非叶子节点\n        if(lchild arr[max]){\n            max=lchild;\n        }\n        if(rchild arr[max]){\n            max=rchild;\n        }\n        //若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值\n        if(max!=index){\n            int temp=arr[index];\n            arr[index]=arr[max];\n            arr[max]=temp;\n            //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆\n            HeapAdjust(arr,max,len);\n        }\n    }\n}\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行结果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/03/03806df817263a5c283f7a22931001ab.png","alt":"运行结果","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"运行结果","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4,算法分析","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.1,时间复杂度","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"建堆的时候初始化堆过程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(HeapAdjust)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"是堆排序的关键,时间复杂度为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(log n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",下面看堆排序的两个过程;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一步,初始化堆,这一步时间复杂度是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二步,交换堆顶元素过程,需要用到n-1次循环,且每一次都要用到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(HeapAdjust)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",所以时间复杂度为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"((n-1)*log n)~O(nlog n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最终时间复杂度:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(n)+O(nlog n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",后者复杂度高于前者,所以","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"堆排序的时间复杂度为O(nlog n)","attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.2,空间复杂度","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空间复杂度是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(1)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",因为没有用到额外开辟的集合空间。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.3,算法稳定性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"堆排序是不稳定的","attrs":{}},{"type":"text","text":",比方说二叉树[6a,8,13,6b],(这里的6a和6b数值上都是6,只不过为了区别6所以这样)经过堆初始化后以及排序过后就变成[6b,6a,8,13];可见堆排序是不稳定的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微信搜索公众号《","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":12}},{"type":"color","attrs":{"color":"#FF7021","name":"orange"}}],"text":"程序员的时光","attrs":{}},{"type":"text","text":"》","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,今天就先分享到这里了,下期继续给大家带来 排序算法面试内容!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更多干货、优质文章,欢迎关注我的原创技术公众号~","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章